1if not modules then modules = { } end modules ['l-dir'] = {
2 version = 1.001,
3 comment = "companion to luat-lib.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9
10
11
12local type, select = type, select
13local find, gmatch, match, gsub, sub = string.find, string.gmatch, string.match, string.gsub, string.sub
14local concat, insert, remove, unpack = table.concat, table.insert, table.remove, table.unpack
15local lpegmatch = lpeg.match
16
17local P, S, R, C, Cc, Cs, Ct, Cv, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cv, lpeg.V
18
19dir = dir or { }
20local dir = dir
21local lfs = lfs
22
23local attributes = lfs.attributes
24local walkdir = lfs.dir
25local isdir = lfs.isdir
26local isfile = lfs.isfile
27local currentdir = lfs.currentdir
28local chdir = lfs.chdir
29local mkdir = lfs.mkdir
30
31local onwindows = os.type == "windows" or find(os.getenv("PATH"),";",1,true)
32
33
34
35if onwindows then
36
37
38
39
40 local tricky = S("/\\") * P(-1)
41
42 isdir = function(name)
43 if lpegmatch(tricky,name) then
44 return attributes(name,"mode") == "directory"
45 else
46 return attributes(name.."/.","mode") == "directory"
47 end
48 end
49
50 isfile = function(name)
51 return attributes(name,"mode") == "file"
52 end
53
54 lfs.isdir = isdir
55 lfs.isfile = isfile
56
57else
58
59 isdir = function(name)
60 return attributes(name,"mode") == "directory"
61 end
62
63 isfile = function(name)
64 return attributes(name,"mode") == "file"
65 end
66
67 lfs.isdir = isdir
68 lfs.isfile = isfile
69
70end
71
72
73
74function dir.current()
75 return (gsub(currentdir(),"\\","/"))
76end
77
78
79
80
81local function glob_pattern_function(path,patt,recurse,action)
82 if isdir(path) then
83 local usedpath
84 if path == "/" then
85 usedpath = "/."
86 elseif not find(path,"/$") then
87 usedpath = path .. "/."
88 path = path .. "/"
89 else
90 usedpath = path
91 end
92 local dirs
93 local nofdirs = 0
94 for name, mode, size, time in walkdir(usedpath) do
95 if name ~= "." and name ~= ".." then
96 local full = path .. name
97 if mode == nil then
98 mode = attributes(full,'mode')
99 end
100 if mode == 'file' then
101 if not patt or find(full,patt) then
102 action(full,size,time)
103 end
104 elseif recurse and mode == "directory" then
105 if dirs then
106 nofdirs = nofdirs + 1
107 dirs[nofdirs] = full
108 else
109 nofdirs = 1
110 dirs = { full }
111 end
112 end
113 end
114 end
115 if dirs then
116 for i=1,nofdirs do
117 glob_pattern_function(dirs[i],patt,recurse,action)
118 end
119 end
120 end
121end
122
123local function glob_pattern_table(path,patt,recurse,result)
124 if not result then
125 result = { }
126 end
127 local usedpath
128 if path == "/" then
129 usedpath = "/."
130 elseif not find(path,"/$") then
131 usedpath = path .. "/."
132 path = path .. "/"
133 else
134 usedpath = path
135 end
136 local dirs
137 local nofdirs = 0
138 local noffiles = #result
139 for name, mode in walkdir(usedpath) do
140 if name ~= "." and name ~= ".." then
141 local full = path .. name
142 if mode == nil then
143 mode = attributes(full,'mode')
144 end
145 if mode == 'file' then
146 if not patt or find(full,patt) then
147 noffiles = noffiles + 1
148 result[noffiles] = full
149 end
150 elseif recurse and mode == "directory" then
151 if dirs then
152 nofdirs = nofdirs + 1
153 dirs[nofdirs] = full
154 else
155 nofdirs = 1
156 dirs = { full }
157 end
158 end
159 end
160 end
161 if dirs then
162 for i=1,nofdirs do
163 glob_pattern_table(dirs[i],patt,recurse,result)
164 end
165 end
166 return result
167end
168
169local function globpattern(path,patt,recurse,method)
170 local kind = type(method)
171 if patt and sub(patt,1,-3) == path then
172 patt = false
173 end
174 local okay = isdir(path)
175 if kind == "function" then
176 return okay and glob_pattern_function(path,patt,recurse,method) or { }
177 elseif kind == "table" then
178 return okay and glob_pattern_table(path,patt,recurse,method) or method
179 else
180 return okay and glob_pattern_table(path,patt,recurse,{ }) or { }
181 end
182end
183
184dir.globpattern = globpattern
185
186
187
188local function collectpattern(path,patt,recurse,result)
189 local ok, scanner
190 result = result or { }
191 if path == "/" then
192 ok, scanner, first = xpcall(function() return walkdir(path..".") end, function() end)
193 else
194 ok, scanner, first = xpcall(function() return walkdir(path) end, function() end)
195 end
196 if ok and type(scanner) == "function" then
197 if not find(path,"/$") then
198 path = path .. '/'
199 end
200 for name in scanner, first do
201 if name == "." then
202
203 elseif name == ".." then
204
205 else
206 local full = path .. name
207 local attr = attributes(full)
208 local mode = attr.mode
209 if mode == 'file' then
210 if find(full,patt) then
211 result[name] = attr
212 end
213 elseif recurse and mode == "directory" then
214 attr.list = collectpattern(full,patt,recurse)
215 result[name] = attr
216 end
217 end
218 end
219 end
220 return result
221end
222
223dir.collectpattern = collectpattern
224
225local separator, pattern
226
227if onwindows then
228
229 local slash = S("/\\") / "/"
230
231
232 pattern = {
233 (Cs(P(".") + slash^1) + Cs(R("az","AZ") * P(":") * slash^0) + Cc("./")) * V(2) * V(3),
234 Cs(((1-S("*?/\\"))^0 * slash)^0),
235 Cs(P(1)^0)
236 }
237
238else
239
240
241 pattern = {
242 (C(P(".") + P("/")^1) + Cc("./")) * V(2) * V(3),
243 C(((1-S("*?/"))^0 * P("/"))^0),
244 C(P(1)^0)
245 }
246
247end
248
249local filter = Cs ( (
250 P("**") / ".*" +
251 P("*") / "[^/]*" +
252 P("?") / "[^/]" +
253 P(".") / "%%." +
254 P("+") / "%%+" +
255 P("-") / "%%-" +
256 P(1)
257)^0 )
258
259local function glob(str,t)
260 if type(t) == "function" then
261 if type(str) == "table" then
262 for s=1,#str do
263 glob(str[s],t)
264 end
265 elseif isfile(str) then
266 t(str)
267 else
268 local root, path, base = lpegmatch(pattern,str)
269 if root and path and base then
270 local recurse = find(base,"**",1,true)
271 local start = root .. path
272 local result = lpegmatch(filter,start .. base)
273 globpattern(start,result,recurse,t)
274 end
275 end
276 else
277 if type(str) == "table" then
278 local t = t or { }
279 for s=1,#str do
280 glob(str[s],t)
281 end
282 return t
283 elseif isfile(str) then
284 if t then
285 t[#t+1] = str
286 return t
287 else
288 return { str }
289 end
290 else
291 local root, path, base = lpegmatch(pattern,str)
292 if root and path and base then
293 local recurse = find(base,"**",1,true)
294 local start = root .. path
295 local result = lpegmatch(filter,start .. base)
296 return globpattern(start,result,recurse,t)
297 else
298 return { }
299 end
300 end
301 end
302end
303
304dir.glob = glob
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321local function globfiles(path,recurse,func,files)
322 if type(func) == "string" then
323 local s = func
324 func = function(name) return find(name,s) end
325 end
326 files = files or { }
327 local noffiles = #files
328 for name, mode in walkdir(path) do
329 if find(name,"^%.") then
330
331 else
332 if mode == nil then
333 mode = attributes(name,'mode')
334 end
335 if mode == "directory" then
336 if recurse then
337 globfiles(path .. "/" .. name,recurse,func,files)
338 end
339 elseif mode == "file" then
340 if not func or func(name) then
341 noffiles = noffiles + 1
342 files[noffiles] = path .. "/" .. name
343 end
344 end
345 end
346 end
347 return files
348end
349
350dir.globfiles = globfiles
351
352local function globdirs(path,recurse,func,files)
353 if type(func) == "string" then
354 local s = func
355 func = function(name) return find(name,s) end
356 end
357 files = files or { }
358 local noffiles = #files
359 for name, mode in walkdir(path) do
360 if find(name,"^%.") then
361
362 else
363 if mode == nil then
364 mode = attributes(name,'mode')
365 end
366 if mode == "directory" then
367 if not func or func(name) then
368 noffiles = noffiles + 1
369 files[noffiles] = path .. "/" .. name
370 if recurse then
371 globdirs(path .. "/" .. name,recurse,func,files)
372 end
373 end
374 end
375 end
376 end
377 return files
378end
379
380dir.globdirs = globdirs
381
382
383
384
385
386
387
388
389
390
391function dir.ls(pattern)
392 return concat(glob(pattern),"\n")
393end
394
395
396
397
398
399
400local make_indeed = true
401
402if onwindows then
403
404 function dir.mkdirs(...)
405 local n = select("#",...)
406 local str
407 if n == 1 then
408 str = select(1,...)
409 if isdir(str) then
410 return str, true
411 end
412 else
413 str = ""
414 for i=1,n do
415 local s = select(i,...)
416 if s == "" then
417
418 elseif str == "" then
419 str = s
420 else
421 str = str .. "/" .. s
422 end
423 end
424 end
425 local pth = ""
426 local drive = false
427 local first, middle, last = match(str,"^(//)(//*)(.*)$")
428 if first then
429
430 else
431 first, last = match(str,"^(//)/*(.-)$")
432 if first then
433 middle, last = match(str,"([^/]+)/+(.-)$")
434 if middle then
435 pth = "//" .. middle
436 else
437 pth = "//" .. last
438 last = ""
439 end
440 else
441 first, middle, last = match(str,"^([a-zA-Z]:)(/*)(.-)$")
442 if first then
443 pth, drive = first .. middle, true
444 else
445 middle, last = match(str,"^(/*)(.-)$")
446 if not middle then
447 last = str
448 end
449 end
450 end
451 end
452 for s in gmatch(last,"[^/]+") do
453 if pth == "" then
454 pth = s
455 elseif drive then
456 pth, drive = pth .. s, false
457 else
458 pth = pth .. "/" .. s
459 end
460 if make_indeed and not isdir(pth) then
461 mkdir(pth)
462 end
463 end
464 return pth, (isdir(pth) == true)
465 end
466
467
468
469
470
471
472
473
474
475
476
477
478
479else
480
481 function dir.mkdirs(...)
482 local n = select("#",...)
483 local str, pth
484 if n == 1 then
485 str = select(1,...)
486 if isdir(str) then
487 return str, true
488 end
489 else
490 str = ""
491 for i=1,n do
492 local s = select(i,...)
493 if s and s ~= "" then
494 if str ~= "" then
495 str = str .. "/" .. s
496 else
497 str = s
498 end
499 end
500 end
501 end
502 str = gsub(str,"/+","/")
503 if find(str,"^/") then
504 pth = "/"
505 for s in gmatch(str,"[^/]+") do
506 local first = (pth == "/")
507 if first then
508 pth = pth .. s
509 else
510 pth = pth .. "/" .. s
511 end
512 if make_indeed and not first and not isdir(pth) then
513 mkdir(pth)
514 end
515 end
516 else
517 pth = "."
518 for s in gmatch(str,"[^/]+") do
519 pth = pth .. "/" .. s
520 if make_indeed and not isdir(pth) then
521 mkdir(pth)
522 end
523 end
524 end
525 return pth, (isdir(pth) == true)
526 end
527
528
529
530
531
532
533
534
535
536end
537
538dir.makedirs = dir.mkdirs
539
540
541do
542
543
544
545
546
547 local chdir = sandbox and sandbox.original(chdir) or chdir
548
549 if onwindows then
550
551 local xcurrentdir = dir.current
552
553 function dir.expandname(str)
554 local first, nothing, last = match(str,"^(//)(//*)(.*)$")
555 if first then
556 first = xcurrentdir() .. "/"
557 end
558 if not first then
559 first, last = match(str,"^(//)/*(.*)$")
560 end
561 if not first then
562 first, last = match(str,"^([a-zA-Z]:)(.*)$")
563 if first and not find(last,"^/") then
564 local d = currentdir()
565 if chdir(first) then
566 first = xcurrentdir()
567 end
568 chdir(d)
569 end
570 end
571 if not first then
572 first, last = xcurrentdir(), str
573 end
574 last = gsub(last,"//","/")
575 last = gsub(last,"/%./","/")
576 last = gsub(last,"^/*","")
577 first = gsub(first,"/*$","")
578 if last == "" or last == "." then
579 return first
580 else
581 return first .. "/" .. last
582 end
583 end
584
585 else
586
587 function dir.expandname(str)
588 if not find(str,"^/") then
589 str = currentdir() .. "/" .. str
590 end
591 str = gsub(str,"//","/")
592 str = gsub(str,"/%./","/")
593 str = gsub(str,"(.)/%.$","%1")
594 return str
595 end
596
597 end
598
599end
600
601file.expandname = dir.expandname
602
603local stack = { }
604
605function dir.push(newdir)
606 local curdir = currentdir()
607 insert(stack,curdir)
608 if newdir and newdir ~= "" and chdir(newdir) then
609 return newdir
610 else
611 return curdir
612 end
613end
614
615function dir.pop()
616 local d = remove(stack)
617 if d then
618 chdir(d)
619 end
620 return d
621end
622
623local function found(...)
624 for i=1,select("#",...) do
625 local path = select(i,...)
626 local kind = type(path)
627 if kind == "string" then
628 if isdir(path) then
629 return path
630 end
631 elseif kind == "table" then
632
633 local path = found(unpack(path))
634 if path then
635 return path
636 end
637 end
638 end
639
640end
641
642dir.found = found
643 |