NB. file access utilities
NB.
NB. read verbs take a right argument of a filename, optionally
NB. linked with one or two numbers (as for 1!:11):
NB.   0 = start of read (may be negative)
NB.   1 = length of read (default rest of file)
NB.
NB. write verbs return number of characters written.
NB.
NB. filenames may be open or boxed character strings.
NB.
NB. string verbs write out text delimited with the host OS delimiter,
NB. and read in text delimited by LF.
NB.
NB. on error, the result is _1,
NB. e.g. for file not found/file read error/file write error

NB.    dat fappend fl     append
NB.    dat fappends fl    append string
NB.    to  fcopynew fls   copy files (if changed)
NB.    to  fcopynews fls  copy files as strings (if changed)
NB.        fdir           file directory
NB.        ferase fl      erase file
NB.        fexist fl      return 1 if file exists
NB.    opt fread fl       read file
NB.        freadr fl      read records (flat file)
NB.        freads fl      read string
NB.    dat freplace fl    replace in file
NB.    opt fselect txt    select file
NB.        fsize fl       size of file
NB.    str fss fl         string search file
NB. oldnew fssrplc fl     search and replace in file
NB.        fstamp fl      file timestamp
NB.        fview fl       view file
NB.    dat fwrite fl      write file
NB.    dat fwrites fl     write string

require 'strings'

cocurrent 'z'

NB. =========================================================
NB.*fappend v append text to file
NB.	The text is first ravelled. The file is created if necessary.
NB. Returns number of characters written, or an error message.
NB. form: text fappend filename
NB. example:
NB.   'chatham' fappend 'newfile.txt'
NB. 7
fappend=: 4 : 0
y=. boxopen y.
dat=. ,x.
f=. #@[ [ 1!:3
dat f :: _1: y
)

NB. =========================================================
NB.*fappends v append string to file
NB. The text is first ravelled into a vector with each row
NB. terminated by the CRLF pair. Any single LF or CR characters
NB. in the text are converted into the CRLF pair.
NB. The file is created if necessary. Returns number of characters
NB. written, or an error message.
fappends=: 4 : 0
y=. boxopen y.
dat=. x.
if. -. 0 e. $dat do.
  if. 1>:#$dat do.
    dat=. toHOST dat
    dat=. dat,(LF ~: {:dat)#CRLF
  else. dat=. dat,"1 CRLF
  end.
end.
dat=. ,dat
f=. #@[ [ 1!:3
dat f :: _1: y
)

NB. =========================================================
NB.*fcopynew v copies files if changed
NB. form: tofile fcopynew fromfiles
NB. returns: 0, size    not changed
NB.          1, size    changed
NB.          _1         failure
fcopynew=: 4 : 0
dat=. fread each boxopen y.
if. (<_1) e. dat do. _1 return. end.
dat=. ; dat
if. dat -: fread :: 0: x. do. 0,#dat else.
  if. _1=dat fwrite x. do. _1 else. 1,#dat end.
end.
)

NB. =========================================================
NB.*fcopynews v copies files as strings if changed
NB. form: tofile fcopynews fromfiles
NB. returns: 0, size    not changed
NB.          1, size    changed
NB.          _1         failure
fcopynews=: 4 : 0
dat=. freads each boxopen y.
if. (<_1) e. dat do. _1 return. end.
dat=. toHOST ; dat
if. dat -: fread :: 0: x. do. 0,#dat else.
  if. _1=dat fwrite x. do. _1 else. 1,#dat end.
end.
)

NB. =========================================================
NB.*fdir v file directory
NB. example:
NB.   fdir jpath '~system\main\s*.ijs'
fdir=: 1!:0

NB. =========================================================
NB.*ferase v erases a file
NB. Returns 1 if successful, otherwise _1
ferase=: (1!:55 :: _1:) @ boxopen

NB. =========================================================
NB.*fexist v test if a file exists
NB. Returns 1 if the file exists, otherwise 0.
fexist=: 1:@(1!:4)@boxopen :: 0:

NB. =========================================================
NB.*fread v read file
NB. y. is filename {;start size}
NB. x. is optional:
NB.    = b    read as boxed vector
NB.    = m    read as matrix
NB.    = s    read as string (same as freads)
fread=: 3 : 0
'' fread y.
:
y=. boxopen y.
f=. 1!:(1 11 {~ 2=#y)
dat=. f :: _1: y
if. dat -: _1 do. return. end.
if. (0=#x.) +. 0=#dat do. dat return. end.
dat=. toJ dat
if. 0=#dat do. '' $~ 0 $~ >:'m'e.x. return. end.
dat=. dat,LF -. {:dat
if. 'b'e.x. do. dat=. <;._2 dat
elseif. 'm'e.x. do. dat=. ];._2 dat
end.
)

NB. =========================================================
NB.*freadr v read records from flat file
NB. y. is filename {;record start, # of records}
NB. records are assumed of fixed length delimited by
NB. one (only) of CR, LF, or CRLF.
NB. the result is a matrix of records.
freadr=: 3 : 0
'f s'=. 2{.boxopen y.
max=. 1!:4 :: _1: <f
if. max -: _1 do. return. end.
pos=. 0
step=. 10000
whilst. blk = cls
do.
  blk=. step<.max-pos
  if. 0=blk do. 'file not organized in records' return. end.
  dat=. 1!:11 f;pos,blk
  cls=. <./dat i.CRLF
  pos=. pos+step
end.
len=. cls+pos-step
dat=. 1!:11 f;len,2<.max-len
dlm=. +/CRLF e. dat
wid=. len+dlm
s=. wid*s,0 #~ 0=#s
dat=. 1!:11 f;s
dat=. (-wid)[\dat
(-dlm)}."1 dat
)

NB. =========================================================
NB.*freads v read file as string
NB. y. is filename {;start size}
NB. x. is optional (b and m same as fread):
NB.    = b    read as boxed vector
NB.    = m    read as matrix
NB. freads
freads=: 3 : 0
'' freads y.
:
y=. boxopen y.
f=. 1!:(1 11 {~ 2=#y)
dat=. f :: _1: y
if. (dat -: _1) +. 0=#dat do. return. end.
dat=. dat,LF -. {:dat=. toJ dat
if. 'b'e.x. do. dat=. <;._2 dat
elseif. 'm'e.x. do. dat=. ];._2 dat
end.
)

NB. =========================================================
NB.*freplace v replace text in file
NB. form: dat freplace file;pos
freplace=: 4 : 0
y=. boxopen y.
dat=. ,x.
f=. #@[ [ 1!:12
dat f :: _1: y
)

NB. =========================================================
NB.*fselect v file selection dialog
NB. y. = DOS filespec or ''
NB. x. = optional file type list
NB. returns user selection
fselect=: 'All Files (*.*)|*.*'&$: : (4 : 0)
y=. y.,(0 e. #y.)#'*'
path=. }: y #~ b=. +./\.y=PATHSEP_j_
def=. y #~ -. b
t=. 'mbopen "Select File" "',path,'" "',def,'" '
t=. t,'"',x.,'"'
t=. t,' ofn_filemustexist ofn_pathmustexist;'
wd t
)

NB. =========================================================
NB.*fsize v return file size
NB. returns file size or _1 if error
fsize=: 1!:4 :: _1: @ boxopen

NB. =========================================================
NB.*fss v file string search
NB. form: str fss file
NB. search file for string, returning indices
fss=: 4 : 0
y=. boxopen y.
size=. 1!:4 :: _1: y
if. size -: _1 do. return. end.

blk=. (#x.) >. 100000 <. size
r=. i.pos=. 0
while. pos < size do.
  dat=. 1!:11 y,<pos,blk <. size-pos
  r=. r,pos+I. x. E. dat
  pos=. pos+blk+1-#x.
end.
r
)

NB. =========================================================
NB.*fssrplc v file string search and replace
NB. form: (old;new) fssrplc file
fssrplc=: fstringreplace

NB. =========================================================
NB.*fstamp v returns file timestamp
fstamp=: (1: >@{ , @ (1!:0)) :: _1:

NB. =========================================================
NB.*fview v view file
NB. uses standard Windows edit control,
NB. which is limited to around 20K size.
fview=: 3 : 0
y=. boxopen y.
f=. 1!:(1 11 {~ 2=#y)
dat=. f :: _1: y
if. dat -: _1 do. return. end.
empty (>{.y) wdview dat
)

NB. =========================================================
NB.*fwrite v write text to file
fwrite=: 4 : 0
y=. boxopen y.
dat=. ,x.
f=. #@[ [ 1!:2
dat f :: _1: y
)

NB. =========================================================
NB.*fwrites v write string to file
fwrites=: 4 : 0
y=. boxopen y.
dat=. ":x.
if. -. 0 e. $dat do.
  if. 1>:#$dat do.
    dat=. toHOST dat
    dat=. dat,(LF ~: {:dat)#CRLF
  else. dat=. dat,"1 CRLF
  end.
end.
dat=. ,dat
f=. #@[ [ 1!:2
dat f :: _1: y
)
