source: project/release/5/directory-utils/trunk/directory-utils.scm @ 38262

Last change on this file since 38262 was 38262, checked in by Kon Lovett, 8 months ago

add optional directory stack argument, reflow

File size: 8.3 KB
Line 
1;;;; directory-utils.scm
2;;;; Kon Lovett, Aug '10
3
4;; Issues
5;;
6;; - Need a routine that provides filename and stat info to the fold func.
7;; stat info: Posix + platform specific:
8;; Windows Hidden attribute ...
9;; macOS birthtime ...
10
11(module directory-utils
12
13(;export
14  pathname? check-pathname error-pathname
15  filename? check-filename error-filename
16  check-directory error-directory
17  ;
18  dot-filename-prefix?
19  dot-pathname? dot-filename?
20  remove-dotfiles
21  ;
22  directory-fold
23  ;
24  directory-utility-stack
25  push-directory
26  pop-directory
27  pop-toplevel-directory
28  ;
29  create-pathname-directory
30  make-program-filename
31  make-shell-filename
32  file-exists-in-directory?
33  find-file-pathnames
34  find-program-pathnames
35  which-command-pathnames
36  which-command-pathname)
37
38(import scheme)
39(import (chicken process-context))
40(import (chicken base))
41(import (chicken type))
42(import (only (chicken keyword) get-keyword))
43(import (only (chicken process-context) current-directory change-directory))
44(import (only (chicken string) string-split))
45(import (only (chicken pathname)
46  make-pathname
47  pathname-directory pathname-extension pathname-file
48  decompose-pathname decompose-directory))
49(import (only (chicken file) file-exists? directory create-directory))
50(import (only (chicken file posix) directory?))
51(import (only (srfi 1) first fold append! filter-map remove any))
52(import (only (srfi 13) string-null? string-prefix?))
53(import (only miscmacros until))
54(import (only moremacros define-warning-parameter))
55(import (only type-errors warning-argument-type)) ;BUG for define-warning-parameter
56(import (only list-utils not-null? ensure-list))
57(import (only stack make-stack stack? stack-empty? stack-push! stack-pop!))
58(import (only type-checks define-check+error-type check-procedure))
59
60;;; Helpers
61
62;;
63
64(define-type stack (struct stack#stack))
65
66(define-type optional-list (or boolean list))
67
68(define-type filename string)
69(define-type extension string)
70(define-type basename string)
71(define-type pathname string)
72
73;;
74
75(define (->boolean obj)
76  (and obj #t) )
77
78(cond-expand
79  (windows  (define-constant PATH-DELIMITER ";"))
80  (else     (define-constant PATH-DELIMITER ":")))
81
82(define +dot-directory+ (make-pathname "." #f))
83
84;;
85
86;no . or .. since directoryname
87(: dot-filename-prefix? (filename -> boolean))
88;
89(define (dot-filename-prefix? str)
90  (and
91    (string-prefix? "." str)
92    #+unix
93    (not (or (string=? "." str) (string=? ".." str)))) )
94
95;;;
96
97;;
98
99(define-check+error-type directory)
100
101;NOTE do not type these as 'predicate', ex: (: filename? (* -> boolean : filename))
102;since the compiler will treat a literal ".." as meeting the criteria at compile time!
103
104;; A null pathname or only extension is not a pathname, here at least
105
106; detecting only an extension is impossible with string pathnames
107
108(: pathname? (* --> boolean))
109;
110(define (pathname? obj)
111  (and
112    (string? obj)
113    (receive (dir fil ext) (decompose-pathname obj)
114      (->boolean (or dir fil)))) )
115
116(define-check+error-type pathname)
117
118;; Just a filename, no directory
119
120(: filename? (* --> boolean))
121;
122(define (filename? obj)
123  (and
124    (string? obj)
125    (receive (dir fil ext) (decompose-pathname obj)
126      (not dir))) )
127
128(define-check+error-type filename)
129
130;;
131
132(: dot-filename? (* --> boolean))
133;
134(define (dot-filename? obj)
135  (and
136    (filename? obj)
137    (dot-filename-prefix? obj)) )
138
139;; Is any pathname component is a dot-filename?
140
141(: dot-pathname? (* --> boolean))
142;
143(define (dot-pathname? obj)
144  (and
145    (string? obj)
146    (let-values (
147      ((dir fil ext) (decompose-pathname obj)) )
148      (or
149        (dot-filename-prefix? fil)
150        (let-values (
151          ((org dir elts) (decompose-directory dir)) )
152          (and
153            elts
154            (any dot-filename-prefix? elts)))))) )
155
156;; Remove dot files from a directory list
157
158(: remove-dotfiles ((list-of pathname) --> (list-of pathname)))
159;
160(define (remove-dotfiles files)
161        (remove (lambda (pn) (dot-filename? (pathname-file pn))) files) )
162
163;;
164
165(: directory-fold (procedure * pathname #!rest -> *))
166;
167(define (directory-fold func ident dir . opts)
168  (let (
169    (show-dotfiles? (get-keyword #:dotfiles? opts (lambda () #f))) )
170    (fold
171      (check-procedure 'directory-fold func)
172      ident
173      (directory (check-directory 'directory-fold dir) show-dotfiles?)) ) )
174
175;; Directory Stack
176
177(: directory-utility-stack (#!optional stack -> stack))
178;
179(define-warning-parameter directory-utility-stack (make-stack) stack)
180
181;;
182
183(: ignored-directory? (pathname --> boolean))
184;
185(define (ignored-directory? dir)
186  (or
187    (string-null? dir)
188    (string=? +dot-directory+ (make-pathname dir #f))) )
189
190(: push-directory ((or boolean pathname) #!optional stack -> void))
191;
192(define (push-directory dir #!optional (dirstack (directory-utility-stack)))
193  (stack-push! dirstack (current-directory))
194  ;don't cd unless necessary
195  (when (and dir (not (ignored-directory? dir)))
196    (change-directory dir) ) )
197
198(: pop-directory (#!optional stack -> void))
199;
200(define (pop-directory #!optional (dirstack (directory-utility-stack)))
201  (unless (stack-empty? dirstack)
202    (change-directory (stack-pop! dirstack)) ) )
203
204(: pop-toplevel-directory (#!optional stack -> void))
205;
206(define (pop-toplevel-directory #!optional (dirstack (directory-utility-stack)))
207  (until (stack-empty? dirstack)
208    (pop-directory dirstack) ) )
209
210;; Ensure the directory for the specified path exists.
211
212(: create-pathname-directory (pathname -> boolean))
213;
214(define (create-pathname-directory pn)
215  (->boolean
216    (create-directory
217      (pathname-directory (check-pathname 'create-pathname-directory pn))
218      #t)) )
219
220;; Platform specific program filename.
221
222(: make-program-filename (basename --> filename))
223;
224(define (make-program-filename bn)
225  (cond-expand
226    (windows
227      (if (pathname-extension bn) bn
228        (make-pathname #f bn "exe")) )
229    (else
230      bn ) ) )
231
232(: make-shell-filename (basename -> filename))
233;
234(define (make-shell-filename bn)
235  (cond-expand
236    (windows
237      (if (pathname-extension bn) bn
238        (make-pathname #f bn "bat")) )
239    (else
240      (if (pathname-extension bn) bn
241        (make-pathname #f bn "sh")) ) ) )
242
243;; Pathname if file exists in directory.
244
245(: file-exists-in-directory? (filename #!rest (list pathname) -> (or boolean pathname)))
246;
247(define (file-exists-in-directory? fil . opts)
248  (let (
249    (dir (optional opts #f)) )
250    (let (
251      (path (make-pathname dir fil)) )
252      (and (file-exists? path) path) ) ) )
253
254;; List of all found pathnames.
255
256(: find-file-pathnames-in-directory (filename pathname -> (list-of pathname)))
257;
258(define (find-file-pathnames-in-directory fil dir)
259  (filter-map
260    (cut file-exists-in-directory? fil <>)
261    (ensure-list dir)) )
262
263(: *find-file-pathnames (filename (list-of pathname) -> (or boolean (list-of pathname))))
264;
265(define (*find-file-pathnames fil dirs)
266  (let loop (
267    (dirs dirs)
268    (paths '()) )
269    (if (null? dirs) (not-null? paths)
270      (loop
271        (cdr dirs)
272        (append!
273          paths
274          (find-file-pathnames-in-directory fil (car dirs))))) ) )
275
276(: find-file-pathnames (filename #!rest -> (or boolean (list-of pathname))))
277;
278(define (find-file-pathnames fil . dirs)
279  (*find-file-pathnames fil dirs) )
280
281;; All found program pathname in directories.
282
283(: find-program-pathnames (filename #!rest (list-of pathname) -> optional-list))
284;
285(define (find-program-pathnames cmd . dirs)
286  (cond-expand
287    (windows
288      (if (pathname-extension cmd) (*find-file-pathnames cmd dirs)
289        (let (
290          (founds
291            (append!
292              (or (*find-file-pathnames (make-program-filename cmd) dirs) '())
293              (or (*find-file-pathnames (make-shell-filename cmd) dirs) '()))) )
294          (not-null? founds))) )
295    (else
296      (*find-file-pathnames (make-program-filename cmd) dirs) ) ) )
297
298;; All found program pathname in path.
299
300(: which-command-pathnames (filename #!rest (list string) -> optional-list))
301;
302(define (which-command-pathnames cmd . opts)
303  (let (
304    (varnam (optional opts "PATH")) )
305    (and-let* (
306      (path (get-environment-variable varnam)) )
307      (find-program-pathnames cmd (string-split path PATH-DELIMITER)) ) ) )
308
309;; First found program pathname in path.
310
311(: which-command-pathname (filename #!rest (list string) -> optional-list))
312;
313(define (which-command-pathname cmd . opts)
314  (let (
315    (varnam (optional opts "PATH")) )
316    (and-let* (
317      (ps (which-command-pathnames cmd varnam))
318      ((not (zero? (length ps)))) )
319      (first ps) ) ) )
320
321) ;directory-utils
Note: See TracBrowser for help on using the repository browser.