NB. directory utilities

NB. main verbs:
NB.   dir            directory
NB.   dircompare     compare directories
NB.   dircompares    compare directories (called by dircompare)
NB.   dirpath        directory paths
NB.   dirfind        find name in directory
NB.   dirs           directory browse
NB.   dirss          directory string search
NB.   dirssrplc      search and replace in directory
NB.   dirtree        files in directory tree
NB.   dirused        file count, space in directory tree
NB.
NB. note: dirtree excludes hidden directories, and other
NB. directories specified in the session configuration.
NB.
NB. This is also used by:
NB.   dircompare, dirfind, dirss, dirssrplc

require 'compare files'

cocurrent 'z'

NB. =========================================================
NB.*dir v directory listings
NB.
NB. y. = dos file specification:
NB.      if empty, defaults to *
NB.
NB. x. is optional:
NB.    - if not given, defaults to 'n'
NB.    - if character, returns a formatted directory,
NB.        where x. is the sort option:
NB.          d=by date
NB.          n=by name
NB.          s=by size
NB.    - if numeric, there are 1 or 2 elements:
NB.          0{  0= list short names
NB.              1= boxed list of full pathnames
NB.              2= full directory list
NB.          1{  0= filenames only (default)
NB.              1= include subdirectories
NB.
NB. subdirectories are shown first
NB. filenames are returned in lower case
NB.
NB. e.g.  dir ''
NB.       1 dir jpath '~system\main\d*.ijs'
dir=: 3 : 0
'n' dir y.
:
ps=. PATHSEP_j_
y=. y.,(0=#y.)#'*'
y=. y,((':',ps) e.~ {:y)#'*'
if. 0=#dr=. 1!:0 y do. empty'' return. end.
fls=. 'd' ~: 4{"1>4{"1 dr
if. (1=#dr) *. 0={.fls do.
  r=. x. dir y,ps,'*'
  if. #r do. r return. end.
end.
if. fmt=. 2=3!:0 x. do. opt=. 2 1
else. opt=. 2{.x. end.
if. 0={:opt do. fls=. 1#~#dr=. fls#dr end.
if. 0=#dr do. empty'' return. end.
nms=. {."1 dr
nms=. nms ,&.> fls{ps;''
if. IFWIN32 do.
  nms=. tolower &.> nms
end.
ndx=. /: (":,.fls),.>nms
if. 0=opt do.
  list >ndx{nms
elseif. 1=opt do.
  path=. (+./\.y=ps)#y
  path&,&.>ndx{nms
elseif. fmt<2=opt do.
  ndx{nms,.}."1 dr
elseif. fmt do.
  'nms ts size'=. |:3{."1 dr
  if. IFWIN32 do.
    nms=. tolower L:0 nms
  end.
  ds=. '   <dir>    ' ((-.fls)#i.#fls) } 12 ":,.size
  mth=. _3[\'   JanFebMarAprMayJunJulAugSepOctNovDec'
  f=. > @ ([: _2&{. [: '0'&, ": )&.>
  'y m d h n s'=. f&> ,<"1 |: 100|ts
  m=. (1{"1 ts){mth
  time=. d,.'-',.m,.'-',.y,.' ',.h,.':',.n,.':',.s
  dat=. (>nms),.ds,.' ',.time
  dat /: fls,. /:/: >(3|'dns'i.x.){ts;nms;size
elseif. 1 do.
  'invalid left argument'
end.
)

NB. =========================================================
NB.*dircompare v compare files in directories
NB.
NB. form: [opt] dircompare dirs
NB.
NB.   dirs = directory names
NB.   opt is optional, with up to three elements:
NB.     0{  =0 short file comparison (default)
NB.         =1 long file comparison
NB.     1{  =0 given directory only (default)
NB.         =1 recurse through subdirectories
NB.     2{  =0 file contents only (default)
NB.         =1 also compare timestamps
NB.
NB. e.g.  dircompare 'main \jbak\main'
NB.
NB. returns text result or error message
dircompare=: 3 : 0
0 dircompare y.
:
if. 0=#y. do.
  '''long dirtree timestamps'' dircompare dir1;dir2'
  return.
end.

opt=. 3 {. x.
res=. opt dircompares y.
if. 0 = L. res do. return. end.

ps=. PATHSEP_j_
'a b c'=. res

'x y'=. cutopen y.
x=. x, ps #~ (*#x) *. ps~:_1{.x
y=. y, ps #~ (*#y) *. ps~:_1{.y

r=. 'comparing  ',x,'  and  ',y,LF

if. #a do.
  r=. r,LF,'not in  ',y,':',LF,,(list a),.LF
end.

if. #b do.
  r=. r,LF,'not in  ',x,':',LF,,(list b),.LF
end.

if. +/ # &> c do.
  'cf cd'=. c
  r=. r,LF,'not same in both:',LF,,(list cf),.LF
  if. {.opt do.
    r=. r,LF,;(,&(LF,LF)) &.> cd
  end.
  
end.

if. 0=#;res do. r=. r,'no difference',LF end.

}:r
)

NB. =========================================================
NB. dircompares v compare files in directories
NB.
NB. form: [opt] dircompares dirs
NB.
NB.   dirs = directory names
NB.   opt is optional, with up to three elements:
NB.     0{  =0 short file comparison (default)
NB.         =1 long file comparison
NB.     1{  =0 given directory only (default)
NB.         =1 recurse through subdirectories
NB.     2{  =0 file contents only (default)
NB.         =1 also compare timestamps
NB.
NB. e.g.  dircompares 'main \jbak\main'
NB.
NB. returns error message or 3-element boxed list
dircompares=: 3 : 0
0 dircompares y.
:
ps=. PATHSEP_j_
opt=. 3{. x.
'x y'=. cutopen y.
x=. x, ps #~ (*#x) *. ps~:_1{.x
y=. y, ps #~ (*#y) *. ps~:_1{.y

if. 1{opt do.
  dx=. dirtree x [ dy=. dirtree y
else.
  dx=. 2 0 dir x [ dy=. 2 0 dir y
end.

if. dx -: dy do. 'no difference' return. end.
if. 0 e. #dx do. 'first directory is empty' return. end.
if. 0 e. #dy do. 'second directory is empty' return. end.

f=. #~ [: +./\. =&ps
sx=. f x
sy=. f y
fx=. {."1 dx
fy=. {."1 dy

if. 1{opt do.
  fx=. (#sx)}.&.>fx
  fy=. (#sy)}.&.>fy
  dx=. fx 0 }"0 1 dx
  dy=. fy 0 }"0 1 dy
end.

r=. <fx -. fy
r=. r , <fy -. fx

dx=. (fx e. fy)#dx
dy=. (fy e. fx)#dy

if. #j=. dx -. dy do.
  j=. {."1 j
  cmp=. <@fcompare"1 (sx&,&.>j),.sy&,&.>j
  
  if. 0=2{opt do.
    f=. 'no difference'&-: @ (_13&{.)
    msk=. -. f &> cmp
    j=. msk#j
    cmp=. msk#cmp
  end.
  
  r=. r,< j;<cmp
else.
  r=. r,a:
end.

r
)

NB. =========================================================
NB.*dirfind v find name in directory
NB. find name in directory
NB.
NB. form: string dirfind directory
NB.
NB. returns filenames in directory tree containing string
NB.
NB. e.g. 'jfile' dirfind 'packages'
dirfind=: 4 : 0
f=. [: 1&e. x.&E.
g=. #~ [: -. [: +./\. =&PATHSEP_j_
(#~ f@g"1) > {."1 dirtree y.
)

NB. =========================================================
NB.*dirpath v directory paths
NB. return directory paths starting from y.
NB. optional x.=0  all paths (default)
NB.             1  non-empty paths (i.e. having files)
NB. e.g. dirpath 'examples'
dirpath=: 3 : 0
0 dirpath y.
:
r=. ''
t=. y.
ps=. PATHSEP_j_
if. #t do. t=. t, ps -. {:t end.
dirs=. <t
ifdir=. 'd'&= @ (4&{"1) @ > @ (4&{"1)
subdir=. ifdir # ]
while. #dirs do.
  fpath=. (>{.dirs) &,
  dirs=. }.dirs
  dat=. 1!:0 fpath '*'
  if. #dat do.
    dat=. subdir dat
    if. #dat do.
      r=. r, fpath each /:~ {."1 dat
      dirs=. (fpath @ (,&ps) each {."1 dat),dirs
    end.
  end.
end.
if. x. do.
  f=. 1!:0 @ (,&(ps,'*'))
  g=. 0:`(0: e. ifdir)
  h=. g @. (*@#) @ f
  r=. r #~ h &> r
end.
if. #t do. r=. r,<}:t end.
if. IFWIN32 do.
  r=. tolower each r
end.
/:~ r
)

NB. =========================================================
NB.*dirs v browse files in directory
NB. e.g.  dirs jpath '~system\main\*.ijs'
dirs=: 3 : 0
if. IFCONSOLE do.
  'dirs is not supported in the console' return.
else.
  'require'~ 'jdirs'
  empty y. conew 'jdirs'
end.
)

NB. =========================================================
NB.*dirss v directory string search
NB.
NB. form: string dirss directory
NB.
NB. searches for files in directory tree containing string,
NB. returning formatted display where found.
NB.
NB. e.g. 'create' dirss 'main'
NB.
NB. If x. is a 2-element boxed list, calls dirssrplc
dirss=: 4 : 0
if. (2=#x.) *. 1=L. x. do.
  x. dirssrplc y. return.
end.

sub=. ' '&(I.@(e.&(TAB,CRLF))@]})
fls=. {."1 dirtree y.
if. 0 e. #fls do.
  'no files in directory: ',y. return.
end.
fnd=. ''
while. #fls do.
  dat=. 1!:1 <fl=. >{.fls
  fls=. }.fls
  ndx=. I. x. E. dat
  if. rws=. #ndx do.
    dat=. (20$' '),dat,30$' '
    dat=. (rws,50)$sub(,ndx+/i.50){dat
    fnd=. fnd,LF,LF,fl,' (',(":#ndx),')'
    fnd=. fnd,,LF,.dat
  end.
end.
if. #fnd do. 2}.fnd
else. 'not found: ',x.
end.
)

NB. =========================================================
NB.*dirssrplc v directory string search and replace
NB. form: (old;new) dirssrplc files
NB. example:
NB.    ('old';'new') dirssrplc jpath '~system\main\*.ijs'
dirssrplc=: 4 : 0
fls=. {."1 dirtree y.
if. 0 e. #fls do.
  wdinfo 'dirssrplc';'no files found' return.
end.
r=. (x.&fssrplc) each fls
b=. r ~: <'no match found'
j=. >b # fls , each ': '&, each r
}: , j ,. LF
)

NB. =========================================================
NB.*dirtree v get filenames in directory tree
NB. return filenames in directory tree as boxed matrix
NB. optional x. is a timestamp to exclude earlier files
NB. each row contains:  filename;timestamp;size
NB.
NB. directory search is recursive through subdirectories
NB. filenames are returned in lower case
NB.
NB. ignores hidden directories
NB.
NB. global DIRTREEX_j_ (set in session configuration)
NB. is a list of directories to exclude from the search,
NB. e.g. DIRTREEX_j_=: <'CVS' to exclude cvs directories.
NB.
NB. e.g.  dirtree ''
NB.       dirtree 'main'
NB.       dirtree jpath '~system\packages\*.ijs'
NB. 2004 5 23 dirtree ''   - files dated on or after date.
dirtree=: 3 : 0
0 dirtree y.
:
if. 0=4!:0 <'DIRTREEX_j_' do.
  ex=. boxxopen DIRTREEX_j_
else.
  ex=. ''
end.
r=. i.0 3
ps=. PATHSEP_j_
y=. y. #~ (+./\ *. +./\.) y.~:' '
y=. y,(0=#y)#'*'
if. ps={:y do. y=. y,'*' end.

NB. ---------------------------------------------------------
NB. if no wildcard, check if directory:
if. -. '*' e. y do.
  if. 1 = #j=. 1!:0 y do.
    select. 'hd' = 1 4 { >4{,j
    case. 0 1 do. x. dirtree y,ps,'*' return.
    case. 1 1 do. i.0 3 return.
    end.
  end.
end.

NB. ---------------------------------------------------------
ts=. 100"_ #. 6: {. 0: >. <. - # {. 1980"_
'path ext'=. (b#y);(-.b=. +./\.y=ps)#y
NB. read files in current directory:
if. #dl=. 1!:0 y do.
  att=. > 4{"1 dl
  fl=. (('h' ~: 1{"1 att) *. 'd' ~: 4{"1 att)#dl
  if. #fl do.
    r=. r,(path&,&.>{."1 fl),.1 2{"1 fl
  end.
end.
NB. read any subdirectories:
if. #dl=. 1!:0 path,'*' do.
  att=. > 4{"1 dl
  dr=. {."1 (('h' ~: 1{"1 att) *. 'd' = 4{"1 att) # dl
  if. #ex do.
    dr=. dr -. ex
  end.
  if. #dr do.
    r=. r,;x.&dirtree@(path&,@,&(ps,ext)) &.> dr
  end.
end.
r=. r #~ (ts x.) <: ts &> 1{"1 r
if. IFWIN32 *. #r do.
  (tolower L:0 {."1 r) 0 }"0 1 r
end.
)

NB. =========================================================
NB.*dirused v get count and space of files in directory tree
dirused=: [: (# , +/ @ ; @ (2: {"1 ])) 0&dirtree
