/[cmucl]/src/code/filesys.lisp
ViewVC logotype

Contents of /src/code/filesys.lisp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.65 - (hide annotations)
Thu Dec 13 13:45:08 2001 UTC (12 years, 4 months ago) by pmai
Branch: MAIN
Changes since 1.64: +3 -2 lines
Fix bug reported by Lynn Quam, which caused (pathname "*") to fail
because of a missing bounds check.
1 ram 1.1 ;;; -*- Log: code.log; Package: Lisp -*-
2     ;;; **********************************************************************
3 ram 1.8 ;;; This code was written as part of the CMU Common Lisp project at
4     ;;; Carnegie Mellon University, and has been placed in the public domain.
5     ;;; If you want to use this code or any part of CMU Common Lisp, please contact
6     ;;; Scott Fahlman or slisp-group@cs.cmu.edu.
7     ;;;
8     (ext:file-comment
9 pmai 1.65 "$Header: /tiger/var/lib/cvsroots/cmucl/src/code/filesys.lisp,v 1.65 2001/12/13 13:45:08 pmai Exp $")
10 ram 1.8 ;;;
11 ram 1.1 ;;; **********************************************************************
12     ;;;
13 wlott 1.16 ;;; File system interface functions. This file is pretty UNIX specific.
14 ram 1.1 ;;;
15 wlott 1.16 ;;; Written by William Lott
16 ram 1.1 ;;;
17     ;;; **********************************************************************
18 ram 1.2
19     (in-package "LISP")
20    
21 wlott 1.16 (export '(truename probe-file user-homedir-pathname directory
22     rename-file delete-file file-write-date file-author))
23 ram 1.1
24     (use-package "EXTENSIONS")
25    
26     (in-package "EXTENSIONS")
27     (export '(print-directory complete-file ambiguous-files default-directory
28 wlott 1.5 file-writable unix-namestring))
29 ram 1.2 (in-package "LISP")
30 ram 1.1
31 wlott 1.5
32 wlott 1.16 ;;;; Unix pathname host support.
33 ram 1.1
34 wlott 1.16 ;;; Unix namestrings have the following format:
35     ;;;
36     ;;; namestring := [ directory ] [ file [ type [ version ]]]
37     ;;; directory := [ "/" | search-list ] { file "/" }*
38     ;;; search-list := [^:/]*:
39     ;;; file := [^/]*
40     ;;; type := "." [^/.]*
41 pw 1.62 ;;; version := ".*" | ".~" ([0-9]+ | "*") "~"
42 wlott 1.16 ;;;
43 dtc 1.59 ;;; Note: this grammer is ambiguous. The string foo.bar.~5~ can be parsed
44 wlott 1.16 ;;; as either just the file specified or as specifying the file, type, and
45     ;;; version. Therefore, we use the following rules when confronted with
46     ;;; an ambiguous file.type.version string:
47     ;;;
48     ;;; - If the first character is a dot, it's part of the file. It is not
49     ;;; considered a dot in the following rules.
50     ;;;
51     ;;; - If there is only one dot, it seperates the file and the type.
52     ;;;
53     ;;; - If there are multiple dots and the stuff following the last dot
54     ;;; is a valid version, then that is the version and the stuff between
55     ;;; the second to last dot and the last dot is the type.
56     ;;;
57     ;;; Wildcard characters:
58     ;;;
59     ;;; If the directory, file, type components contain any of the following
60     ;;; characters, it is considered part of a wildcard pattern and has the
61     ;;; following meaning.
62     ;;;
63     ;;; ? - matches any character
64     ;;; * - matches any zero or more characters.
65     ;;; [abc] - matches any of a, b, or c.
66     ;;; {str1,str2,...,strn} - matches any of str1, str2, ..., or strn.
67     ;;;
68     ;;; Any of these special characters can be preceeded by a backslash to
69     ;;; cause it to be treated as a regular character.
70     ;;;
71 ram 1.1
72 wlott 1.16 (defun remove-backslashes (namestr start end)
73     "Remove and occurences of \\ from the string because we've already
74     checked for whatever they may have been backslashed."
75     (declare (type simple-base-string namestr)
76     (type index start end))
77     (let* ((result (make-string (- end start)))
78     (dst 0)
79     (quoted nil))
80     (do ((src start (1+ src)))
81     ((= src end))
82     (cond (quoted
83     (setf (schar result dst) (schar namestr src))
84     (setf quoted nil)
85     (incf dst))
86     (t
87     (let ((char (schar namestr src)))
88     (cond ((char= char #\\)
89     (setq quoted t))
90     (t
91     (setf (schar result dst) char)
92     (incf dst)))))))
93     (when quoted
94     (error 'namestring-parse-error
95     :complaint "Backslash in bad place."
96     :namestring namestr
97     :offset (1- end)))
98     (shrink-vector result dst)))
99 ram 1.1
100 wlott 1.16 (defvar *ignore-wildcards* nil)
101 ram 1.1
102 wlott 1.16 (defun maybe-make-pattern (namestr start end)
103     (declare (type simple-base-string namestr)
104     (type index start end))
105     (if *ignore-wildcards*
106     (subseq namestr start end)
107     (collect ((pattern))
108     (let ((quoted nil)
109     (any-quotes nil)
110     (last-regular-char nil)
111     (index start))
112     (flet ((flush-pending-regulars ()
113     (when last-regular-char
114     (pattern (if any-quotes
115     (remove-backslashes namestr
116     last-regular-char
117     index)
118     (subseq namestr last-regular-char index)))
119     (setf any-quotes nil)
120     (setf last-regular-char nil))))
121     (loop
122     (when (>= index end)
123     (return))
124     (let ((char (schar namestr index)))
125     (cond (quoted
126     (incf index)
127     (setf quoted nil))
128     ((char= char #\\)
129     (setf quoted t)
130     (setf any-quotes t)
131     (unless last-regular-char
132     (setf last-regular-char index))
133     (incf index))
134     ((char= char #\?)
135     (flush-pending-regulars)
136     (pattern :single-char-wild)
137     (incf index))
138     ((char= char #\*)
139     (flush-pending-regulars)
140     (pattern :multi-char-wild)
141     (incf index))
142     ((char= char #\[)
143     (flush-pending-regulars)
144     (let ((close-bracket
145     (position #\] namestr :start index :end end)))
146     (unless close-bracket
147     (error 'namestring-parse-error
148     :complaint "``['' with no corresponding ``]''"
149     :namestring namestr
150     :offset index))
151     (pattern (list :character-set
152     (subseq namestr
153     (1+ index)
154     close-bracket)))
155     (setf index (1+ close-bracket))))
156     (t
157     (unless last-regular-char
158     (setf last-regular-char index))
159     (incf index)))))
160     (flush-pending-regulars)))
161     (cond ((null (pattern))
162     "")
163 ram 1.31 ((null (cdr (pattern)))
164     (let ((piece (first (pattern))))
165     (typecase piece
166     ((member :multi-char-wild) :wild)
167     (simple-string piece)
168     (t
169     (make-pattern (pattern))))))
170 wlott 1.16 (t
171     (make-pattern (pattern)))))))
172 ram 1.1
173 dtc 1.59 ;;; extract-name-type-and-version -- Internal.
174     ;;;
175 wlott 1.16 (defun extract-name-type-and-version (namestr start end)
176     (declare (type simple-base-string namestr)
177     (type index start end))
178 pw 1.62 (labels
179     ((explicit-version (namestr start end)
180     (cond ((or (< (- end start) 5)
181     (char/= (schar namestr (1- end)) #\~))
182     (values :newest end))
183     ((and (char= (schar namestr (- end 2)) #\*)
184     (char= (schar namestr (- end 3)) #\~)
185     (char= (schar namestr (- end 4)) #\.))
186     (values :wild (- end 4)))
187     (t
188     (do ((i (- end 2) (1- i)))
189     ((< i (+ start 2)) (values :newest end))
190     (let ((char (schar namestr i)))
191     (when (eql char #\~)
192     (return (if (char= (schar namestr (1- i)) #\.)
193     (values (parse-integer namestr :start (1+ i)
194     :end (1- end))
195     (1- i))
196     (values :newest end))))
197     (unless (char<= #\0 char #\9)
198     (return (values :newest end))))))))
199     (any-version (namestr start end)
200     ;; process end of string looking for a version candidate.
201     (multiple-value-bind (version where)
202     (explicit-version namestr start end)
203     (cond ((not (eq version :newest))
204     (values version where))
205 pmai 1.65 ((and (>= (- end 2) start)
206     (char= (schar namestr (- end 1)) #\*)
207 pw 1.62 (char= (schar namestr (- end 2)) #\.)
208 pw 1.63 (find #\. namestr
209     :start (min (1+ start) (- end 2))
210     :end (- end 2)))
211 pw 1.62 (values :wild (- end 2)))
212     (t (values version where)))))
213     (any-type (namestr start end)
214 pw 1.63 ;; Process end of string looking for a type. A leading "."
215     ;; is part of the name.
216     (let ((where (position #\. namestr
217     :start (min (1+ start) end)
218     :end end :from-end t)))
219 pw 1.62 (when where
220     (values where end))))
221     (any-name (namestr start end)
222     (declare (ignore namestr))
223     (values start end)))
224     (multiple-value-bind
225     (version vstart)(any-version namestr start end)
226     (multiple-value-bind
227     (tstart tend)(any-type namestr start vstart)
228     (multiple-value-bind
229     (nstart nend)(any-name namestr start (or tstart vstart))
230     (values
231     (maybe-make-pattern namestr nstart nend)
232     (and tstart (maybe-make-pattern namestr (1+ tstart) tend))
233     version))))))
234 ram 1.1
235 ram 1.31 ;;; Take a string and return a list of cons cells that mark the char
236     ;;; separated subseq. The first value t if absolute directories location.
237     ;;;
238     (defun split-at-slashes (namestr start end)
239     (declare (type simple-base-string namestr)
240 wlott 1.16 (type index start end))
241     (let ((absolute (and (/= start end)
242 ram 1.31 (char= (schar namestr start) #\/))))
243 wlott 1.16 (when absolute
244     (incf start))
245 ram 1.31 ;; Next, split the remainder into slash seperated chunks.
246 wlott 1.16 (collect ((pieces))
247     (loop
248 ram 1.31 (let ((slash (position #\/ namestr :start start :end end)))
249     (pieces (cons start (or slash end)))
250     (unless slash
251 wlott 1.16 (return))
252 ram 1.31 (setf start (1+ slash))))
253 wlott 1.16 (values absolute (pieces)))))
254 ram 1.1
255 wlott 1.16 (defun maybe-extract-search-list (namestr start end)
256     (declare (type simple-base-string namestr)
257     (type index start end))
258     (let ((quoted nil))
259     (do ((index start (1+ index)))
260     ((= index end)
261     (values nil start))
262     (if quoted
263     (setf quoted nil)
264     (case (schar namestr index)
265     (#\\
266     (setf quoted t))
267     (#\:
268     (return (values (remove-backslashes namestr start index)
269     (1+ index)))))))))
270 ram 1.1
271 wlott 1.16 (defun parse-unix-namestring (namestr start end)
272     (declare (type simple-base-string namestr)
273     (type index start end))
274     (multiple-value-bind
275     (absolute pieces)
276     (split-at-slashes namestr start end)
277     (let ((search-list
278     (if absolute
279     nil
280     (let ((first (car pieces)))
281     (multiple-value-bind
282     (search-list new-start)
283     (maybe-extract-search-list namestr
284     (car first) (cdr first))
285     (when search-list
286     (setf absolute t)
287     (setf (car first) new-start))
288     search-list)))))
289     (multiple-value-bind
290     (name type version)
291     (let* ((tail (car (last pieces)))
292     (tail-start (car tail))
293     (tail-end (cdr tail)))
294     (unless (= tail-start tail-end)
295     (setf pieces (butlast pieces))
296     (extract-name-type-and-version namestr tail-start tail-end)))
297 dtc 1.54 ;; PVE: Make sure there are no illegal characters in the name
298     ;; such as #\Null and #\/.
299     (when (and (stringp name)
300     (find-if #'(lambda (x)
301     (or (char= x #\Null) (char= x #\/)))
302     name))
303     (error 'parse-error))
304 wlott 1.16 ;; Now we have everything we want. So return it.
305     (values nil ; no host for unix namestrings.
306     nil ; no devices for unix namestrings.
307     (collect ((dirs))
308     (when search-list
309     (dirs (intern-search-list search-list)))
310     (dolist (piece pieces)
311     (let ((piece-start (car piece))
312     (piece-end (cdr piece)))
313     (unless (= piece-start piece-end)
314 ram 1.31 (cond ((string= namestr ".." :start1 piece-start
315     :end1 piece-end)
316     (dirs :up))
317     ((string= namestr "**" :start1 piece-start
318     :end1 piece-end)
319     (dirs :wild-inferiors))
320     (t
321     (dirs (maybe-make-pattern namestr
322     piece-start
323     piece-end)))))))
324 wlott 1.25 (cond (absolute
325     (cons :absolute (dirs)))
326     ((dirs)
327     (cons :relative (dirs)))
328     (t
329     nil)))
330 wlott 1.16 name
331     type
332     version)))))
333 ram 1.1
334 wlott 1.16 (defun unparse-unix-host (pathname)
335     (declare (type pathname pathname)
336     (ignore pathname))
337     "Unix")
338 ram 1.1
339 wlott 1.16 (defun unparse-unix-piece (thing)
340     (etypecase thing
341 ram 1.31 ((member :wild) "*")
342 wlott 1.16 (simple-string
343     (let* ((srclen (length thing))
344     (dstlen srclen))
345     (dotimes (i srclen)
346     (case (schar thing i)
347     ((#\* #\? #\[)
348     (incf dstlen))))
349     (let ((result (make-string dstlen))
350     (dst 0))
351     (dotimes (src srclen)
352     (let ((char (schar thing src)))
353     (case char
354     ((#\* #\? #\[)
355     (setf (schar result dst) #\\)
356     (incf dst)))
357     (setf (schar result dst) char)
358     (incf dst)))
359     result)))
360     (pattern
361     (collect ((strings))
362     (dolist (piece (pattern-pieces thing))
363     (etypecase piece
364     (simple-string
365     (strings piece))
366     (symbol
367 ram 1.31 (ecase piece
368 wlott 1.16 (:multi-char-wild
369     (strings "*"))
370     (:single-char-wild
371 ram 1.31 (strings "?"))))
372 wlott 1.16 (cons
373     (case (car piece)
374     (:character-set
375     (strings "[")
376     (strings (cdr piece))
377     (strings "]"))
378     (t
379     (error "Invalid pattern piece: ~S" piece))))))
380     (apply #'concatenate
381     'simple-string
382     (strings))))))
383 ram 1.1
384 wlott 1.16 (defun unparse-unix-directory-list (directory)
385     (declare (type list directory))
386     (collect ((pieces))
387     (when directory
388     (ecase (pop directory)
389     (:absolute
390     (cond ((search-list-p (car directory))
391     (pieces (search-list-name (pop directory)))
392     (pieces ":"))
393     (t
394     (pieces "/"))))
395     (:relative
396     ;; Nothing special.
397     ))
398     (dolist (dir directory)
399     (typecase dir
400     ((member :up)
401     (pieces "../"))
402     ((member :back)
403     (error ":BACK cannot be represented in namestrings."))
404 ram 1.31 ((member :wild-inferiors)
405     (pieces "**/"))
406 wlott 1.16 ((or simple-string pattern)
407     (pieces (unparse-unix-piece dir))
408     (pieces "/"))
409     (t
410     (error "Invalid directory component: ~S" dir)))))
411     (apply #'concatenate 'simple-string (pieces))))
412 ram 1.1
413 wlott 1.16 (defun unparse-unix-directory (pathname)
414     (declare (type pathname pathname))
415     (unparse-unix-directory-list (%pathname-directory pathname)))
416 toy 1.64
417 wlott 1.16 (defun unparse-unix-file (pathname)
418     (declare (type pathname pathname))
419     (collect ((strings))
420     (let* ((name (%pathname-name pathname))
421     (type (%pathname-type pathname))
422     (type-supplied (not (or (null type) (eq type :unspecific))))
423 toy 1.64 (logical-p (logical-pathname-p pathname))
424 wlott 1.16 (version (%pathname-version pathname))
425     (version-supplied (not (or (null version) (eq version :newest)))))
426     (when name
427     (strings (unparse-unix-piece name)))
428     (when type-supplied
429     (unless name
430     (error "Cannot specify the type without a file: ~S" pathname))
431     (strings ".")
432     (strings (unparse-unix-piece type)))
433     (when version-supplied
434     (strings (if (eq version :wild)
435 toy 1.64 (if logical-p ".*" ".~*~")
436     (format nil (if logical-p ".~D" ".~~~D~~")
437     version)))))
438 pw 1.55 (and (strings) (apply #'concatenate 'simple-string (strings)))))
439 ram 1.1
440 wlott 1.16 (defun unparse-unix-namestring (pathname)
441     (declare (type pathname pathname))
442     (concatenate 'simple-string
443     (unparse-unix-directory pathname)
444     (unparse-unix-file pathname)))
445 ram 1.1
446 wlott 1.16 (defun unparse-unix-enough (pathname defaults)
447     (declare (type pathname pathname defaults))
448     (flet ((lose ()
449     (error "~S cannot be represented relative to ~S"
450     pathname defaults)))
451     (collect ((strings))
452     (let* ((pathname-directory (%pathname-directory pathname))
453     (defaults-directory (%pathname-directory defaults))
454     (prefix-len (length defaults-directory))
455     (result-dir
456     (cond ((and (> prefix-len 1)
457     (>= (length pathname-directory) prefix-len)
458     (compare-component (subseq pathname-directory
459     0 prefix-len)
460     defaults-directory))
461     ;; Pathname starts with a prefix of default. So just
462     ;; use a relative directory from then on out.
463     (cons :relative (nthcdr prefix-len pathname-directory)))
464     ((eq (car pathname-directory) :absolute)
465     ;; We are an absolute pathname, so we can just use it.
466     pathname-directory)
467     (t
468     ;; We are a relative directory. So we lose.
469     (lose)))))
470     (strings (unparse-unix-directory-list result-dir)))
471     (let* ((pathname-version (%pathname-version pathname))
472     (version-needed (and pathname-version
473     (not (eq pathname-version :newest))))
474     (pathname-type (%pathname-type pathname))
475     (type-needed (or version-needed
476     (and pathname-type
477     (not (eq pathname-type :unspecific)))))
478     (pathname-name (%pathname-name pathname))
479     (name-needed (or type-needed
480     (and pathname-name
481     (not (compare-component pathname-name
482     (%pathname-name
483     defaults)))))))
484     (when name-needed
485     (unless pathname-name (lose))
486     (strings (unparse-unix-piece pathname-name)))
487     (when type-needed
488     (when (or (null pathname-type) (eq pathname-type :unspecific))
489     (lose))
490     (strings ".")
491     (strings (unparse-unix-piece pathname-type)))
492     (when version-needed
493     (typecase pathname-version
494     ((member :wild)
495 pw 1.61 (strings ".~*~"))
496 wlott 1.16 (integer
497 dtc 1.59 (strings (format nil ".~~~D~~" pathname-version)))
498 wlott 1.16 (t
499     (lose)))))
500     (apply #'concatenate 'simple-string (strings)))))
501 ram 1.1
502    
503 wlott 1.16 (defstruct (unix-host
504     (:include host
505     (:parse #'parse-unix-namestring)
506     (:unparse #'unparse-unix-namestring)
507     (:unparse-host #'unparse-unix-host)
508     (:unparse-directory #'unparse-unix-directory)
509     (:unparse-file #'unparse-unix-file)
510     (:unparse-enough #'unparse-unix-enough)
511     (:customary-case :lower))
512     (:make-load-form-fun make-unix-host-load-form))
513     )
514 ram 1.1
515 wlott 1.16 (defvar *unix-host* (make-unix-host))
516 ram 1.1
517 wlott 1.16 (defun make-unix-host-load-form (host)
518     (declare (ignore host))
519     '*unix-host*)
520 ram 1.1
521 wlott 1.16
522     ;;;; Wildcard matching stuff.
523 ram 1.1
524 wlott 1.16 (defmacro enumerate-matches ((var pathname &optional result
525 dtc 1.57 &key (verify-existance t) (follow-links t))
526 wlott 1.16 &body body)
527     (let ((body-name (gensym)))
528     `(block nil
529     (flet ((,body-name (,var)
530     ,@body))
531     (%enumerate-matches (pathname ,pathname)
532 dtc 1.57 ,verify-existance ,follow-links
533 wlott 1.16 #',body-name)
534     ,result))))
535 ram 1.1
536 dtc 1.57 (defun %enumerate-matches (pathname verify-existance follow-links function)
537 ram 1.34 (when (pathname-type pathname)
538     (unless (pathname-name pathname)
539     (error "Cannot supply a type without a name:~% ~S" pathname)))
540     (let ((directory (pathname-directory pathname)))
541     (if directory
542     (ecase (car directory)
543     (:absolute
544     (%enumerate-directories "/" (cdr directory) pathname
545 dtc 1.57 verify-existance follow-links
546     nil function))
547 ram 1.34 (:relative
548     (%enumerate-directories "" (cdr directory) pathname
549 dtc 1.57 verify-existance follow-links
550     nil function)))
551 ram 1.34 (%enumerate-files "" pathname verify-existance function))))
552 ram 1.1
553 dtc 1.57 ;;; %enumerate-directories -- Internal
554     ;;;
555     ;;; The directory node and device numbers are maintained for the current path
556     ;;; during the search for the detection of paths loops upon :wild-inferiors.
557     ;;;
558     (defun %enumerate-directories (head tail pathname verify-existance
559     follow-links nodes function)
560 ram 1.37 (declare (simple-string head))
561 dtc 1.57 (macrolet ((unix-xstat (name)
562     `(if follow-links
563     (unix:unix-stat ,name)
564     (unix:unix-lstat ,name)))
565     (with-directory-node-noted ((head) &body body)
566     `(multiple-value-bind (res dev ino mode)
567     (unix-xstat ,head)
568     (when (and res (eql (logand mode unix:s-ifmt) unix:s-ifdir))
569     (let ((nodes (cons (cons dev ino) nodes)))
570     ,@body))))
571     (do-directory-entries ((name directory) &body body)
572     `(let ((dir (unix:open-dir ,directory)))
573     (when dir
574     (unwind-protect
575     (loop
576     (let ((,name (unix:read-dir dir)))
577     (cond ((null ,name)
578     (return))
579     ((string= ,name "."))
580     ((string= ,name ".."))
581     (t
582     ,@body))))
583     (unix:close-dir dir))))))
584     (if tail
585     (let ((piece (car tail)))
586     (etypecase piece
587     (simple-string
588     (let ((head (concatenate 'string head piece)))
589     (with-directory-node-noted (head)
590     (%enumerate-directories (concatenate 'string head "/")
591     (cdr tail) pathname
592     verify-existance follow-links
593     nodes function))))
594     ((member :wild-inferiors)
595     (%enumerate-directories head (rest tail) pathname
596     verify-existance follow-links
597     nodes function)
598     (do-directory-entries (name head)
599     (let ((subdir (concatenate 'string head name)))
600     (multiple-value-bind (res dev ino mode)
601     (unix-xstat subdir)
602     (declare (type (or fixnum null) mode))
603     (when (and res (eql (logand mode unix:s-ifmt) unix:s-ifdir))
604     (unless (dolist (dir nodes nil)
605     (when (and (eql (car dir) dev)
606     (eql (cdr dir) ino))
607     (return t)))
608     (let ((nodes (cons (cons dev ino) nodes))
609     (subdir (concatenate 'string subdir "/")))
610     (%enumerate-directories subdir tail pathname
611     verify-existance follow-links
612     nodes function))))))))
613     ((or pattern (member :wild))
614     (do-directory-entries (name head)
615     (when (or (eq piece :wild) (pattern-matches piece name))
616     (let ((subdir (concatenate 'string head name)))
617     (multiple-value-bind (res dev ino mode)
618     (unix-xstat subdir)
619     (declare (type (or fixnum null) mode))
620     (when (and res
621     (eql (logand mode unix:s-ifmt) unix:s-ifdir))
622     (let ((nodes (cons (cons dev ino) nodes))
623     (subdir (concatenate 'string subdir "/")))
624     (%enumerate-directories subdir (rest tail) pathname
625     verify-existance follow-links
626     nodes function))))))))
627     ((member :up)
628     (let ((head (concatenate 'string head "..")))
629     (with-directory-node-noted (head)
630     (%enumerate-directories (concatenate 'string head "/")
631     (rest tail) pathname
632     verify-existance follow-links
633     nodes function))))))
634     (%enumerate-files head pathname verify-existance function))))
635 ram 1.1
636 wlott 1.16 (defun %enumerate-files (directory pathname verify-existance function)
637 ram 1.37 (declare (simple-string directory))
638 ram 1.36 (let ((name (%pathname-name pathname))
639     (type (%pathname-type pathname))
640     (version (%pathname-version pathname)))
641 ram 1.33 (cond ((member name '(nil :unspecific))
642 wlott 1.16 (when (or (not verify-existance)
643 wlott 1.26 (unix:unix-file-kind directory))
644 wlott 1.16 (funcall function directory)))
645     ((or (pattern-p name)
646     (pattern-p type)
647 ram 1.33 (eq name :wild)
648     (eq type :wild))
649 wlott 1.26 (let ((dir (unix:open-dir directory)))
650 wlott 1.16 (when dir
651     (unwind-protect
652     (loop
653 wlott 1.26 (let ((file (unix:read-dir dir)))
654 wlott 1.16 (if file
655 wlott 1.21 (unless (or (string= file ".")
656     (string= file ".."))
657     (multiple-value-bind
658     (file-name file-type file-version)
659     (let ((*ignore-wildcards* t))
660     (extract-name-type-and-version
661     file 0 (length file)))
662     (when (and (components-match file-name name)
663     (components-match file-type type)
664     (components-match file-version
665     version))
666     (funcall function
667     (concatenate 'string
668     directory
669     file)))))
670 wlott 1.16 (return))))
671 wlott 1.26 (unix:close-dir dir)))))
672 wlott 1.16 (t
673     (let ((file (concatenate 'string directory name)))
674 wlott 1.19 (unless (or (null type) (eq type :unspecific))
675 wlott 1.16 (setf file (concatenate 'string file "." type)))
676 ram 1.33 (unless (member version '(nil :newest :wild))
677 wlott 1.19 (setf file (concatenate 'string file "."
678 wlott 1.20 (quick-integer-to-string version))))
679 wlott 1.16 (when (or (not verify-existance)
680 ram 1.40 (unix:unix-file-kind file t))
681 wlott 1.16 (funcall function file)))))))
682 ram 1.1
683     (defun quick-integer-to-string (n)
684 wlott 1.16 (declare (type integer n))
685 ram 1.38 (cond ((not (fixnump n))
686     (write-to-string n :base 10 :radix nil))
687     ((zerop n) "0")
688 ram 1.1 ((eql n 1) "1")
689     ((minusp n)
690     (concatenate 'simple-string "-"
691     (the simple-string (quick-integer-to-string (- n)))))
692     (t
693     (do* ((len (1+ (truncate (integer-length n) 3)))
694     (res (make-string len))
695     (i (1- len) (1- i))
696     (q n)
697     (r 0))
698     ((zerop q)
699     (incf i)
700     (replace res res :start2 i :end2 len)
701 wlott 1.3 (shrink-vector res (- len i)))
702 ram 1.1 (declare (simple-string res)
703 ram 1.38 (fixnum len i r q))
704 ram 1.1 (multiple-value-setq (q r) (truncate q 10))
705     (setf (schar res i) (schar "0123456789" r))))))
706    
707 wlott 1.16
708     ;;;; UNIX-NAMESTRING -- public
709 wlott 1.5 ;;;
710 wlott 1.39 (defun unix-namestring (pathname &optional (for-input t) executable-only)
711 wlott 1.16 "Convert PATHNAME into a string that can be used with UNIX system calls.
712 pw 1.56 Search-lists and wild-cards are expanded. If optional argument
713     FOR-INPUT is true and PATHNAME doesn't exist, NIL is returned.
714     If optional argument EXECUTABLE-ONLY is true, NIL is returned
715     unless an executable version of PATHNAME exists."
716 ram 1.41 ;; toy@rtp.ericsson.se: Let unix-namestring also handle logical
717     ;; pathnames too.
718     (let ((path (let ((lpn (pathname pathname)))
719     (if (logical-pathname-p lpn)
720     (namestring (translate-logical-pathname lpn))
721     pathname))))
722 wlott 1.16 (enumerate-search-list
723 ram 1.41 (pathname path)
724 wlott 1.16 (collect ((names))
725 dtc 1.57 (enumerate-matches (name pathname nil :verify-existance for-input
726     :follow-links t)
727 wlott 1.39 (when (or (not executable-only)
728     (and (eq (unix:unix-file-kind name) :file)
729     (unix:unix-access name unix:x_ok)))
730     (names name)))
731 wlott 1.16 (let ((names (names)))
732     (when names
733     (when (cdr names)
734 dtc 1.54 (error 'simple-file-error
735     :format-control "~S is ambiguous:~{~% ~A~}"
736     :format-arguments (list pathname names)))
737 ram 1.41 (return (car names))))))))
738 wlott 1.4
739 ram 1.1
740 wlott 1.16 ;;;; TRUENAME and PROBE-FILE.
741 ram 1.1
742     ;;; Truename -- Public
743     ;;;
744 wlott 1.4 ;;; Another silly file function trivially different from another function.
745 ram 1.1 ;;;
746     (defun truename (pathname)
747     "Return the pathname for the actual file described by the pathname
748 pw 1.47 An error of type file-error is signalled if no such file exists,
749     or the pathname is wild."
750     (if (wild-pathname-p pathname)
751 pw 1.51 (error 'simple-file-error
752 pw 1.47 :format-control "Bad place for a wild pathname."
753     :pathname pathname)
754     (let ((result (probe-file pathname)))
755     (unless result
756 pw 1.51 (error 'simple-file-error
757 pw 1.47 :pathname pathname
758     :format-control "The file ~S does not exist."
759     :format-arguments (list (namestring pathname))))
760     result)))
761 ram 1.1
762     ;;; Probe-File -- Public
763     ;;;
764 wlott 1.4 ;;; If PATHNAME exists, return it's truename, otherwise NIL.
765 ram 1.1 ;;;
766     (defun probe-file (pathname)
767     "Return a pathname which is the truename of the file if it exists, NIL
768 pw 1.47 otherwise. An error of type file-error is signaled if pathname is wild."
769     (if (wild-pathname-p pathname)
770 pw 1.51 (error 'simple-file-error
771 pw 1.47 :pathname pathname
772     :format-control "Bad place for a wild pathname.")
773     (let ((namestring (unix-namestring pathname t)))
774     (when (and namestring (unix:unix-file-kind namestring))
775     (let ((truename (unix:unix-resolve-links
776     (unix:unix-maybe-prepend-current-directory
777     namestring))))
778     (when truename
779     (let ((*ignore-wildcards* t))
780     (pathname (unix:unix-simplify-pathname truename)))))))))
781 ram 1.1
782    
783 wlott 1.4 ;;;; Other random operations.
784 ram 1.1
785     ;;; Rename-File -- Public
786     ;;;
787     (defun rename-file (file new-name)
788     "Rename File to have the specified New-Name. If file is a stream open to a
789 ram 1.35 file, then the associated file is renamed."
790 wlott 1.4 (let* ((original (truename file))
791 wlott 1.16 (original-namestring (unix-namestring original t))
792 wlott 1.4 (new-name (merge-pathnames new-name original))
793     (new-namestring (unix-namestring new-name nil)))
794 wlott 1.17 (unless new-namestring
795 pw 1.51 (error 'simple-file-error
796 pw 1.47 :pathname new-name
797     :format-control "~S can't be created."
798     :format-arguments (list new-name)))
799 wlott 1.4 (multiple-value-bind (res error)
800 wlott 1.26 (unix:unix-rename original-namestring
801 wlott 1.4 new-namestring)
802     (unless res
803 pw 1.51 (error 'simple-file-error
804 pw 1.47 :pathname new-name
805     :format-control "Failed to rename ~A to ~A: ~A"
806     :format-arguments (list original new-name
807     (unix:get-unix-error-msg error))))
808 wlott 1.4 (when (streamp file)
809     (file-name file new-namestring))
810 wlott 1.16 (values new-name original (truename new-name)))))
811 ram 1.1
812     ;;; Delete-File -- Public
813     ;;;
814     ;;; Delete the file, Man.
815     ;;;
816     (defun delete-file (file)
817     "Delete the specified file."
818 wlott 1.4 (let ((namestring (unix-namestring file t)))
819 ram 1.1 (when (streamp file)
820     (close file :abort t))
821 ram 1.24 (unless namestring
822 pw 1.51 (error 'simple-file-error
823 pw 1.47 :pathname file
824     :format-control "~S doesn't exist."
825     :format-arguments (list file)))
826 ram 1.24
827 wlott 1.26 (multiple-value-bind (res err) (unix:unix-unlink namestring)
828 ram 1.24 (unless res
829 pw 1.51 (error 'simple-file-error
830 pw 1.47 :pathname namestring
831     :format-control "Could not delete ~A: ~A."
832     :format-arguments (list namestring
833     (unix:get-unix-error-msg err))))))
834 ram 1.1 t)
835 wlott 1.4
836 ram 1.1
837     ;;; User-Homedir-Pathname -- Public
838     ;;;
839 ram 1.12 ;;; Return Home:, which is set up for us at initialization time.
840 ram 1.1 ;;;
841     (defun user-homedir-pathname (&optional host)
842     "Returns the home directory of the logged in user as a pathname.
843 ram 1.12 This is obtained from the logical name \"home:\"."
844 ram 1.1 (declare (ignore host))
845 ram 1.12 #p"home:")
846 ram 1.1
847     ;;; File-Write-Date -- Public
848     ;;;
849     (defun file-write-date (file)
850 pw 1.47 "Return file's creation date, or NIL if it doesn't exist.
851     An error of type file-error is signaled if file is a wild pathname"
852     (if (wild-pathname-p file)
853 pw 1.51 (error 'simple-file-error
854 pw 1.47 :pathname file
855     :format-control "Bad place for a wild pathname.")
856     (let ((name (unix-namestring file t)))
857     (when name
858     (multiple-value-bind
859     (res dev ino mode nlink uid gid rdev size atime mtime)
860     (unix:unix-stat name)
861     (declare (ignore dev ino mode nlink uid gid rdev size atime))
862     (when res
863     (+ unix-to-universal-time mtime)))))))
864 ram 1.1
865     ;;; File-Author -- Public
866     ;;;
867     (defun file-author (file)
868     "Returns the file author as a string, or nil if the author cannot be
869 pw 1.47 determined. Signals an error of type file-error if file doesn't exist,
870     or file is a wild pathname."
871     (if (wild-pathname-p file)
872 pw 1.51 (error 'simple-file-error
873 pw 1.47 :pathname file
874     "Bad place for a wild pathname.")
875     (let ((name (unix-namestring (pathname file) t)))
876     (unless name
877 pw 1.51 (error 'simple-file-error
878 pw 1.47 :pathname file
879     :format-control "~S doesn't exist."
880     :format-arguments (list file)))
881     (multiple-value-bind (winp dev ino mode nlink uid)
882     (unix:unix-stat name)
883     (declare (ignore dev ino mode nlink))
884     (if winp (lookup-login-name uid))))))
885 ram 1.1
886    
887     ;;;; DIRECTORY.
888    
889 wlott 1.4 ;;; DIRECTORY -- public.
890     ;;;
891 ram 1.23 (defun directory (pathname &key (all t) (check-for-subdirs t)
892 dtc 1.58 (truenamep t) (follow-links t))
893 ram 1.1 "Returns a list of pathnames, one for each file that matches the given
894 ram 1.23 pathname. Supplying :ALL as nil causes this to ignore Unix dot files. This
895 dtc 1.58 never includes Unix dot and dot-dot in the result. If :TRUENAMEP is NIL,
896     then symblolic links in the result are not expanded which is not the
897 ram 1.23 default because TRUENAME does follow links, and the result pathnames are
898     defined to be the TRUENAME of the pathname (the truename of a link may well
899 dtc 1.58 be in another directory.) If FOLLOW-LINKS is NIL then symbolic links are
900     not followed."
901 wlott 1.16 (let ((results nil))
902     (enumerate-search-list
903 wlott 1.21 (pathname (merge-pathnames pathname
904     (make-pathname :name :wild
905     :type :wild
906     :version :wild)))
907 dtc 1.57 (enumerate-matches (name pathname nil :follow-links follow-links)
908 wlott 1.16 (when (or all
909     (let ((slash (position #\/ name :from-end t)))
910     (or (null slash)
911     (= (1+ slash) (length name))
912     (char/= (schar name (1+ slash)) #\.))))
913     (push name results))))
914     (let ((*ignore-wildcards* t))
915     (mapcar #'(lambda (name)
916 ram 1.23 (let ((name (if (and check-for-subdirs
917 wlott 1.26 (eq (unix:unix-file-kind name)
918 ram 1.23 :directory))
919     (concatenate 'string name "/")
920     name)))
921 dtc 1.58 (if truenamep (truename name) (pathname name))))
922 wlott 1.16 (sort (delete-duplicates results :test #'string=) #'string<)))))
923 ram 1.1
924 wlott 1.4
925     ;;;; Printing directories.
926 ram 1.1
927     ;;; PRINT-DIRECTORY is exported from the EXTENSIONS package.
928     ;;;
929     (defun print-directory (pathname &optional stream &key all verbose return-list)
930     "Like Directory, but prints a terse, multi-coloumn directory listing
931     instead of returning a list of pathnames. When :all is supplied and
932     non-nil, then Unix dot files are included too (as ls -a). When :vervose
933     is supplied and non-nil, then a long listing of miscellaneous
934     information is output one file per line."
935 dtc 1.50 (let ((*standard-output* (out-synonym-of stream))
936 wlott 1.16 (pathname pathname))
937 ram 1.1 (if verbose
938     (print-directory-verbose pathname all return-list)
939     (print-directory-formatted pathname all return-list))))
940    
941     (defun print-directory-verbose (pathname all return-list)
942 ram 1.23 (let ((contents (directory pathname :all all :check-for-subdirs nil
943 dtc 1.58 :truenamep nil))
944 wlott 1.7 (result nil))
945     (format t "Directory of ~A :~%" (namestring pathname))
946     (dolist (file contents)
947     (let* ((namestring (unix-namestring file))
948     (tail (subseq namestring
949     (1+ (or (position #\/ namestring
950     :from-end t
951     :test #'char=)
952     -1)))))
953     (multiple-value-bind
954     (reslt dev-or-err ino mode nlink uid gid rdev size atime mtime)
955 wlott 1.26 (unix:unix-stat namestring)
956 wlott 1.7 (declare (ignore ino gid rdev atime)
957     (fixnum uid mode))
958     (cond (reslt
959     ;;
960     ;; Print characters for file modes.
961     (macrolet ((frob (bit name &optional sbit sname negate)
962     `(if ,(if negate
963     `(not (logbitp ,bit mode))
964     `(logbitp ,bit mode))
965     ,(if sbit
966     `(if (logbitp ,sbit mode)
967     (write-char ,sname)
968     (write-char ,name))
969     `(write-char ,name))
970     (write-char #\-))))
971     (frob 15 #\d nil nil t)
972     (frob 8 #\r)
973     (frob 7 #\w)
974     (frob 6 #\x 11 #\s)
975     (frob 5 #\r)
976     (frob 4 #\w)
977     (frob 3 #\x 10 #\s)
978     (frob 2 #\r)
979     (frob 1 #\w)
980     (frob 0 #\x))
981     ;;
982     ;; Print the rest.
983     (multiple-value-bind (sec min hour date month year)
984     (get-decoded-time)
985     (declare (ignore sec min hour date month))
986     (format t "~2D ~8A ~8D ~12A ~A~@[/~]~%"
987     nlink
988     (or (lookup-login-name uid) uid)
989     size
990     (decode-universal-time-for-files mtime year)
991     tail
992 wlott 1.27 (= (logand mode unix:s-ifmt) unix:s-ifdir))))
993 wlott 1.7 (t (format t "Couldn't stat ~A -- ~A.~%"
994     tail
995 wlott 1.26 (unix:get-unix-error-msg dev-or-err))))
996 ram 1.1 (when return-list
997 wlott 1.27 (push (if (= (logand mode unix:s-ifmt) unix:s-ifdir)
998 wlott 1.7 (pathname (concatenate 'string namestring "/"))
999     file)
1000     result)))))
1001     (nreverse result)))
1002 ram 1.1
1003     (defun decode-universal-time-for-files (time current-year)
1004     (multiple-value-bind (sec min hour day month year)
1005     (decode-universal-time (+ time unix-to-universal-time))
1006     (declare (ignore sec))
1007     (format nil "~A ~2,' D ~:[ ~D~;~*~2,'0D:~2,'0D~]"
1008     (svref '#("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug"
1009     "Sep" "Oct" "Nov" "Dec")
1010     (1- month))
1011     day (= current-year year) year hour min)))
1012    
1013     (defun print-directory-formatted (pathname all return-list)
1014     (let ((width (or (line-length *standard-output*) 80))
1015     (names ())
1016     (cnt 0)
1017     (max-len 0)
1018 dtc 1.58 (result (directory pathname :all all :truenamep nil)))
1019 ram 1.1 (declare (list names) (fixnum max-len cnt))
1020     ;;
1021     ;; Get the data.
1022 wlott 1.7 (dolist (file result)
1023     (let* ((name (unix-namestring file))
1024     (length (length name))
1025     (end (if (and (plusp length)
1026     (char= (schar name (1- length)) #\/))
1027     (1- length)
1028     length))
1029     (slash-name (subseq name
1030     (1+ (or (position #\/ name
1031     :from-end t
1032     :end end
1033     :test #'char=)
1034     -1))))
1035     (len (length slash-name)))
1036     (declare (simple-string slash-name)
1037     (fixnum len))
1038     (if (> len max-len) (setq max-len len))
1039     (incf cnt)
1040     (push slash-name names)))
1041     (setq names (nreverse names))
1042     ;;
1043     ;; Do the output.
1044     (let* ((col-width (1+ max-len))
1045     (cols (max (truncate width col-width) 1))
1046     (lines (ceiling cnt cols)))
1047     (declare (fixnum cols lines))
1048     (format t "Directory of ~A :~%" (namestring pathname))
1049     (dotimes (i lines)
1050     (declare (fixnum i))
1051     (dotimes (j cols)
1052     (declare (fixnum j))
1053     (let ((name (nth (+ i (the fixnum (* j lines))) names)))
1054     (when name
1055     (write-string name)
1056     (unless (eql j (1- cols))
1057 wlott 1.15 (dotimes (i (- col-width (length (the simple-string name))))
1058     (write-char #\space))))))
1059 wlott 1.7 (terpri)))
1060     (when return-list
1061     result)))
1062 ram 1.1
1063    
1064    
1065     ;;;; Translating uid's and gid's.
1066    
1067     (defvar *uid-hash-table* (make-hash-table)
1068     "Hash table for keeping track of uid's and login names.")
1069    
1070     ;;; LOOKUP-LOGIN-NAME translates a user id into a login name. Previous
1071     ;;; lookups are cached in a hash table since groveling the passwd(s) files
1072     ;;; is somewhat expensive. The table may hold nil for id's that cannot
1073     ;;; be looked up since this means the files are searched in their entirety
1074     ;;; each time this id is translated.
1075     ;;;
1076     (defun lookup-login-name (uid)
1077     (multiple-value-bind (login-name foundp) (gethash uid *uid-hash-table*)
1078     (if foundp
1079     login-name
1080     (setf (gethash uid *uid-hash-table*)
1081     (get-group-or-user-name :user uid)))))
1082    
1083     (defvar *gid-hash-table* (make-hash-table)
1084     "Hash table for keeping track of gid's and group names.")
1085    
1086     ;;; LOOKUP-GROUP-NAME translates a group id into a group name. Previous
1087     ;;; lookups are cached in a hash table since groveling the group(s) files
1088     ;;; is somewhat expensive. The table may hold nil for id's that cannot
1089     ;;; be looked up since this means the files are searched in their entirety
1090     ;;; each time this id is translated.
1091     ;;;
1092     (defun lookup-group-name (gid)
1093     (multiple-value-bind (group-name foundp) (gethash gid *gid-hash-table*)
1094     (if foundp
1095     group-name
1096     (setf (gethash gid *gid-hash-table*)
1097     (get-group-or-user-name :group gid)))))
1098    
1099    
1100     ;;; GET-GROUP-OR-USER-NAME first tries "/etc/passwd" ("/etc/group") since it is
1101     ;;; a much smaller file, contains all the local id's, and most uses probably
1102     ;;; involve id's on machines one would login into. Then if necessary, we look
1103     ;;; in "/etc/passwds" ("/etc/groups") which is really long and has to be
1104     ;;; fetched over the net.
1105     ;;;
1106     (defun get-group-or-user-name (group-or-user id)
1107     "Returns the simple-string user or group name of the user whose uid or gid
1108     is id, or NIL if no such user or group exists. Group-or-user is either
1109     :group or :user."
1110     (let ((id-string (let ((*print-base* 10)) (prin1-to-string id))))
1111     (declare (simple-string id-string))
1112     (multiple-value-bind (file1 file2)
1113     (ecase group-or-user
1114     (:group (values "/etc/group" "/etc/groups"))
1115     (:user (values "/etc/passwd" "/etc/passwd")))
1116     (or (get-group-or-user-name-aux id-string file1)
1117     (get-group-or-user-name-aux id-string file2)))))
1118    
1119     (defun get-group-or-user-name-aux (id-string passwd-file)
1120     (with-open-file (stream passwd-file)
1121     (loop
1122     (let ((entry (read-line stream nil)))
1123     (unless entry (return nil))
1124     (let ((name-end (position #\: (the simple-string entry)
1125     :test #'char=)))
1126     (when name-end
1127     (let ((id-start (position #\: (the simple-string entry)
1128     :start (1+ name-end) :test #'char=)))
1129     (when id-start
1130     (incf id-start)
1131     (let ((id-end (position #\: (the simple-string entry)
1132     :start id-start :test #'char=)))
1133     (when (and id-end
1134     (string= id-string entry
1135     :start2 id-start :end2 id-end))
1136     (return (subseq entry 0 name-end))))))))))))
1137    
1138    
1139 wlott 1.4 ;;;; File completion.
1140 ram 1.1
1141 chiles 1.13 ;;; COMPLETE-FILE -- Public
1142     ;;;
1143 wlott 1.4 (defun complete-file (pathname &key (defaults *default-pathname-defaults*)
1144     ignore-types)
1145 wlott 1.22 (let ((files (directory (complete-file-directory-arg pathname defaults)
1146 ram 1.23 :check-for-subdirs nil
1147 dtc 1.58 :truenamep nil)))
1148 wlott 1.4 (cond ((null files)
1149     (values nil nil))
1150     ((null (cdr files))
1151     (values (merge-pathnames (file-namestring (car files))
1152     pathname)
1153     t))
1154     (t
1155     (let ((good-files
1156     (delete-if #'(lambda (pathname)
1157 wlott 1.16 (and (simple-string-p
1158     (pathname-type pathname))
1159 wlott 1.4 (member (pathname-type pathname)
1160     ignore-types
1161     :test #'string=)))
1162     files)))
1163     (cond ((null good-files))
1164     ((null (cdr good-files))
1165     (return-from complete-file
1166     (values (merge-pathnames (file-namestring
1167     (car good-files))
1168     pathname)
1169     t)))
1170     (t
1171     (setf files good-files)))
1172     (let ((common (file-namestring (car files))))
1173     (dolist (file (cdr files))
1174     (let ((name (file-namestring file)))
1175     (dotimes (i (min (length common) (length name))
1176     (when (< (length name) (length common))
1177     (setf common name)))
1178     (unless (char= (schar common i) (schar name i))
1179     (setf common (subseq common 0 i))
1180     (return)))))
1181     (values (merge-pathnames common pathname)
1182     nil)))))))
1183 ram 1.1
1184 chiles 1.13 ;;; COMPLETE-FILE-DIRECTORY-ARG -- Internal.
1185     ;;;
1186     (defun complete-file-directory-arg (pathname defaults)
1187 wlott 1.22 (let* ((pathname (merge-pathnames pathname (directory-namestring defaults)))
1188 wlott 1.16 (type (pathname-type pathname)))
1189     (flet ((append-multi-char-wild (thing)
1190     (etypecase thing
1191     (null :wild)
1192     (pattern
1193     (make-pattern (append (pattern-pieces thing)
1194     (list :multi-char-wild))))
1195     (simple-string
1196     (make-pattern (list thing :multi-char-wild))))))
1197     (if (or (null type) (eq type :unspecific))
1198     ;; There is no type.
1199     (make-pathname :defaults pathname
1200     :name (append-multi-char-wild (pathname-name pathname))
1201     :type :wild)
1202     ;; There already is a type, so just extend it.
1203     (make-pathname :defaults pathname
1204     :name (pathname-name pathname)
1205     :type (append-multi-char-wild (pathname-type pathname)))))))
1206 chiles 1.13
1207 wlott 1.4 ;;; Ambiguous-Files -- Public
1208 ram 1.1 ;;;
1209 wlott 1.16 (defun ambiguous-files (pathname
1210     &optional (defaults *default-pathname-defaults*))
1211 wlott 1.4 "Return a list of all files which are possible completions of Pathname.
1212 chiles 1.13 We look in the directory specified by Defaults as well as looking down
1213     the search list."
1214 wlott 1.16 (directory (complete-file-directory-arg pathname defaults)
1215 dtc 1.58 :truenamep nil
1216 wlott 1.16 :check-for-subdirs nil))
1217 wlott 1.4
1218 wlott 1.16
1219 ram 1.1
1220     ;;; File-writable -- exported from extensions.
1221     ;;;
1222     ;;; Determines whether the single argument (which should be a pathname)
1223 dtc 1.46 ;;; can be written by the current task.
1224 wlott 1.16 ;;;
1225 ram 1.1 (defun file-writable (name)
1226     "File-writable accepts a pathname and returns T if the current
1227     process can write it, and NIL otherwise."
1228 wlott 1.16 (let ((name (unix-namestring name nil)))
1229 wlott 1.17 (cond ((null name)
1230     nil)
1231 wlott 1.26 ((unix:unix-file-kind name)
1232     (values (unix:unix-access name unix:w_ok)))
1233 wlott 1.17 (t
1234     (values
1235 wlott 1.26 (unix:unix-access (subseq name
1236 wlott 1.17 0
1237     (or (position #\/ name :from-end t)
1238     0))
1239 wlott 1.26 (logior unix:w_ok unix:x_ok)))))))
1240 ram 1.1
1241    
1242     ;;; Pathname-Order -- Internal
1243     ;;;
1244     ;;; Predicate to order pathnames by. Goes by name.
1245     ;;;
1246     (defun pathname-order (x y)
1247     (let ((xn (%pathname-name x))
1248     (yn (%pathname-name y)))
1249     (if (and xn yn)
1250     (let ((res (string-lessp xn yn)))
1251     (cond ((not res) nil)
1252     ((= res (length (the simple-string xn))) t)
1253     ((= res (length (the simple-string yn))) nil)
1254     (t t)))
1255     xn)))
1256    
1257    
1258     ;;; Default-Directory -- Public
1259     ;;;
1260     (defun default-directory ()
1261     "Returns the pathname for the default directory. This is the place where
1262     a file will be written if no directory is specified. This may be changed
1263     with setf."
1264     (multiple-value-bind (gr dir-or-error)
1265 wlott 1.26 (unix:unix-current-directory)
1266 ram 1.1 (if gr
1267 wlott 1.16 (let ((*ignore-wildcards* t))
1268     (pathname (concatenate 'simple-string dir-or-error "/")))
1269 wlott 1.3 (error dir-or-error))))
1270 ram 1.1
1271     ;;; %Set-Default-Directory -- Internal
1272     ;;;
1273     (defun %set-default-directory (new-val)
1274 wlott 1.17 (let ((namestring (unix-namestring new-val t)))
1275     (unless namestring
1276     (error "~S doesn't exist." new-val))
1277     (multiple-value-bind (gr error)
1278 wlott 1.26 (unix:unix-chdir namestring)
1279 wlott 1.17 (if gr
1280     (setf (search-list "default:") (default-directory))
1281 wlott 1.26 (error (unix:get-unix-error-msg error))))
1282 wlott 1.17 new-val))
1283 wlott 1.16 ;;;
1284     (defsetf default-directory %set-default-directory)
1285    
1286     (defun filesys-init ()
1287     (setf *default-pathname-defaults*
1288     (%make-pathname *unix-host* nil nil nil nil :newest))
1289     (setf (search-list "default:") (default-directory))
1290     nil)
1291 dtc 1.42
1292     ;;; Ensure-Directories-Exist -- Public
1293     ;;;
1294     (defun ensure-directories-exist (pathspec &key verbose (mode #o777))
1295 pw 1.44 "Tests whether the directories containing the specified file
1296     actually exist, and attempts to create them if they do not.
1297     Portable programs should avoid using the :MODE keyword argument."
1298 dtc 1.43 (let* ((pathname (pathname pathspec))
1299     (pathname (if (logical-pathname-p pathname)
1300     (translate-logical-pathname pathname)
1301     pathname))
1302     (created-p nil))
1303     (when (wild-pathname-p pathname)
1304 pw 1.51 (error 'simple-file-error
1305 pw 1.47 :format-control "Bad place for a wild pathname."
1306     :pathname pathspec))
1307 dtc 1.45 (enumerate-search-list (pathname pathname)
1308     (let ((dir (pathname-directory pathname)))
1309     (loop for i from 1 upto (length dir)
1310     do (let ((newpath (make-pathname
1311     :host (pathname-host pathname)
1312     :device (pathname-device pathname)
1313     :directory (subseq dir 0 i))))
1314     (unless (probe-file newpath)
1315     (let ((namestring (namestring newpath)))
1316     (when verbose
1317     (format *standard-output* "~&Creating directory: ~A~%"
1318     namestring))
1319     (unix:unix-mkdir namestring mode)
1320     (unless (probe-file namestring)
1321 pw 1.51 (error 'simple-file-error
1322 pw 1.47 :pathname pathspec
1323     :format-control "Can't create directory ~A."
1324 pw 1.48 :format-arguments (list namestring)))
1325 dtc 1.45 (setf created-p t)))))
1326     ;; Only the first path in a search-list is considered.
1327     (return (values pathname created-p))))))

  ViewVC Help
Powered by ViewVC 1.1.5