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

Contents of /src/code/filesys.lisp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.66 - (hide annotations)
Tue Feb 19 15:48:25 2002 UTC (12 years, 1 month ago) by toy
Branch: MAIN
Changes since 1.65: +4 -3 lines
%ENUMERATE-FILES was not putting the version number on to pathname
correctly (forgot the ~s).  From Lynn Quam.
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 toy 1.66 "$Header: /tiger/var/lib/cvsroots/cmucl/src/code/filesys.lisp,v 1.66 2002/02/19 15:48:25 toy 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 toy 1.66 (setf file (concatenate 'string file ".~"
678     (quick-integer-to-string version)
679     "~")))
680 wlott 1.16 (when (or (not verify-existance)
681 ram 1.40 (unix:unix-file-kind file t))
682 wlott 1.16 (funcall function file)))))))
683 ram 1.1
684     (defun quick-integer-to-string (n)
685 wlott 1.16 (declare (type integer n))
686 ram 1.38 (cond ((not (fixnump n))
687     (write-to-string n :base 10 :radix nil))
688     ((zerop n) "0")
689 ram 1.1 ((eql n 1) "1")
690     ((minusp n)
691     (concatenate 'simple-string "-"
692     (the simple-string (quick-integer-to-string (- n)))))
693     (t
694     (do* ((len (1+ (truncate (integer-length n) 3)))
695     (res (make-string len))
696     (i (1- len) (1- i))
697     (q n)
698     (r 0))
699     ((zerop q)
700     (incf i)
701     (replace res res :start2 i :end2 len)
702 wlott 1.3 (shrink-vector res (- len i)))
703 ram 1.1 (declare (simple-string res)
704 ram 1.38 (fixnum len i r q))
705 ram 1.1 (multiple-value-setq (q r) (truncate q 10))
706     (setf (schar res i) (schar "0123456789" r))))))
707    
708 wlott 1.16
709     ;;;; UNIX-NAMESTRING -- public
710 wlott 1.5 ;;;
711 wlott 1.39 (defun unix-namestring (pathname &optional (for-input t) executable-only)
712 wlott 1.16 "Convert PATHNAME into a string that can be used with UNIX system calls.
713 pw 1.56 Search-lists and wild-cards are expanded. If optional argument
714     FOR-INPUT is true and PATHNAME doesn't exist, NIL is returned.
715     If optional argument EXECUTABLE-ONLY is true, NIL is returned
716     unless an executable version of PATHNAME exists."
717 ram 1.41 ;; toy@rtp.ericsson.se: Let unix-namestring also handle logical
718     ;; pathnames too.
719     (let ((path (let ((lpn (pathname pathname)))
720     (if (logical-pathname-p lpn)
721     (namestring (translate-logical-pathname lpn))
722     pathname))))
723 wlott 1.16 (enumerate-search-list
724 ram 1.41 (pathname path)
725 wlott 1.16 (collect ((names))
726 dtc 1.57 (enumerate-matches (name pathname nil :verify-existance for-input
727     :follow-links t)
728 wlott 1.39 (when (or (not executable-only)
729     (and (eq (unix:unix-file-kind name) :file)
730     (unix:unix-access name unix:x_ok)))
731     (names name)))
732 wlott 1.16 (let ((names (names)))
733     (when names
734     (when (cdr names)
735 dtc 1.54 (error 'simple-file-error
736     :format-control "~S is ambiguous:~{~% ~A~}"
737     :format-arguments (list pathname names)))
738 ram 1.41 (return (car names))))))))
739 wlott 1.4
740 ram 1.1
741 wlott 1.16 ;;;; TRUENAME and PROBE-FILE.
742 ram 1.1
743     ;;; Truename -- Public
744     ;;;
745 wlott 1.4 ;;; Another silly file function trivially different from another function.
746 ram 1.1 ;;;
747     (defun truename (pathname)
748     "Return the pathname for the actual file described by the pathname
749 pw 1.47 An error of type file-error is signalled if no such file exists,
750     or the pathname is wild."
751     (if (wild-pathname-p pathname)
752 pw 1.51 (error 'simple-file-error
753 pw 1.47 :format-control "Bad place for a wild pathname."
754     :pathname pathname)
755     (let ((result (probe-file pathname)))
756     (unless result
757 pw 1.51 (error 'simple-file-error
758 pw 1.47 :pathname pathname
759     :format-control "The file ~S does not exist."
760     :format-arguments (list (namestring pathname))))
761     result)))
762 ram 1.1
763     ;;; Probe-File -- Public
764     ;;;
765 wlott 1.4 ;;; If PATHNAME exists, return it's truename, otherwise NIL.
766 ram 1.1 ;;;
767     (defun probe-file (pathname)
768     "Return a pathname which is the truename of the file if it exists, NIL
769 pw 1.47 otherwise. An error of type file-error is signaled if pathname is wild."
770     (if (wild-pathname-p pathname)
771 pw 1.51 (error 'simple-file-error
772 pw 1.47 :pathname pathname
773     :format-control "Bad place for a wild pathname.")
774     (let ((namestring (unix-namestring pathname t)))
775     (when (and namestring (unix:unix-file-kind namestring))
776     (let ((truename (unix:unix-resolve-links
777     (unix:unix-maybe-prepend-current-directory
778     namestring))))
779     (when truename
780     (let ((*ignore-wildcards* t))
781     (pathname (unix:unix-simplify-pathname truename)))))))))
782 ram 1.1
783    
784 wlott 1.4 ;;;; Other random operations.
785 ram 1.1
786     ;;; Rename-File -- Public
787     ;;;
788     (defun rename-file (file new-name)
789     "Rename File to have the specified New-Name. If file is a stream open to a
790 ram 1.35 file, then the associated file is renamed."
791 wlott 1.4 (let* ((original (truename file))
792 wlott 1.16 (original-namestring (unix-namestring original t))
793 wlott 1.4 (new-name (merge-pathnames new-name original))
794     (new-namestring (unix-namestring new-name nil)))
795 wlott 1.17 (unless new-namestring
796 pw 1.51 (error 'simple-file-error
797 pw 1.47 :pathname new-name
798     :format-control "~S can't be created."
799     :format-arguments (list new-name)))
800 wlott 1.4 (multiple-value-bind (res error)
801 wlott 1.26 (unix:unix-rename original-namestring
802 wlott 1.4 new-namestring)
803     (unless res
804 pw 1.51 (error 'simple-file-error
805 pw 1.47 :pathname new-name
806     :format-control "Failed to rename ~A to ~A: ~A"
807     :format-arguments (list original new-name
808     (unix:get-unix-error-msg error))))
809 wlott 1.4 (when (streamp file)
810     (file-name file new-namestring))
811 wlott 1.16 (values new-name original (truename new-name)))))
812 ram 1.1
813     ;;; Delete-File -- Public
814     ;;;
815     ;;; Delete the file, Man.
816     ;;;
817     (defun delete-file (file)
818     "Delete the specified file."
819 wlott 1.4 (let ((namestring (unix-namestring file t)))
820 ram 1.1 (when (streamp file)
821     (close file :abort t))
822 ram 1.24 (unless namestring
823 pw 1.51 (error 'simple-file-error
824 pw 1.47 :pathname file
825     :format-control "~S doesn't exist."
826     :format-arguments (list file)))
827 ram 1.24
828 wlott 1.26 (multiple-value-bind (res err) (unix:unix-unlink namestring)
829 ram 1.24 (unless res
830 pw 1.51 (error 'simple-file-error
831 pw 1.47 :pathname namestring
832     :format-control "Could not delete ~A: ~A."
833     :format-arguments (list namestring
834     (unix:get-unix-error-msg err))))))
835 ram 1.1 t)
836 wlott 1.4
837 ram 1.1
838     ;;; User-Homedir-Pathname -- Public
839     ;;;
840 ram 1.12 ;;; Return Home:, which is set up for us at initialization time.
841 ram 1.1 ;;;
842     (defun user-homedir-pathname (&optional host)
843     "Returns the home directory of the logged in user as a pathname.
844 ram 1.12 This is obtained from the logical name \"home:\"."
845 ram 1.1 (declare (ignore host))
846 ram 1.12 #p"home:")
847 ram 1.1
848     ;;; File-Write-Date -- Public
849     ;;;
850     (defun file-write-date (file)
851 pw 1.47 "Return file's creation date, or NIL if it doesn't exist.
852     An error of type file-error is signaled if file is a wild pathname"
853     (if (wild-pathname-p file)
854 pw 1.51 (error 'simple-file-error
855 pw 1.47 :pathname file
856     :format-control "Bad place for a wild pathname.")
857     (let ((name (unix-namestring file t)))
858     (when name
859     (multiple-value-bind
860     (res dev ino mode nlink uid gid rdev size atime mtime)
861     (unix:unix-stat name)
862     (declare (ignore dev ino mode nlink uid gid rdev size atime))
863     (when res
864     (+ unix-to-universal-time mtime)))))))
865 ram 1.1
866     ;;; File-Author -- Public
867     ;;;
868     (defun file-author (file)
869     "Returns the file author as a string, or nil if the author cannot be
870 pw 1.47 determined. Signals an error of type file-error if file doesn't exist,
871     or file is a wild pathname."
872     (if (wild-pathname-p file)
873 pw 1.51 (error 'simple-file-error
874 pw 1.47 :pathname file
875     "Bad place for a wild pathname.")
876     (let ((name (unix-namestring (pathname file) t)))
877     (unless name
878 pw 1.51 (error 'simple-file-error
879 pw 1.47 :pathname file
880     :format-control "~S doesn't exist."
881     :format-arguments (list file)))
882     (multiple-value-bind (winp dev ino mode nlink uid)
883     (unix:unix-stat name)
884     (declare (ignore dev ino mode nlink))
885     (if winp (lookup-login-name uid))))))
886 ram 1.1
887    
888     ;;;; DIRECTORY.
889    
890 wlott 1.4 ;;; DIRECTORY -- public.
891     ;;;
892 ram 1.23 (defun directory (pathname &key (all t) (check-for-subdirs t)
893 dtc 1.58 (truenamep t) (follow-links t))
894 ram 1.1 "Returns a list of pathnames, one for each file that matches the given
895 ram 1.23 pathname. Supplying :ALL as nil causes this to ignore Unix dot files. This
896 dtc 1.58 never includes Unix dot and dot-dot in the result. If :TRUENAMEP is NIL,
897     then symblolic links in the result are not expanded which is not the
898 ram 1.23 default because TRUENAME does follow links, and the result pathnames are
899     defined to be the TRUENAME of the pathname (the truename of a link may well
900 dtc 1.58 be in another directory.) If FOLLOW-LINKS is NIL then symbolic links are
901     not followed."
902 wlott 1.16 (let ((results nil))
903     (enumerate-search-list
904 wlott 1.21 (pathname (merge-pathnames pathname
905     (make-pathname :name :wild
906     :type :wild
907     :version :wild)))
908 dtc 1.57 (enumerate-matches (name pathname nil :follow-links follow-links)
909 wlott 1.16 (when (or all
910     (let ((slash (position #\/ name :from-end t)))
911     (or (null slash)
912     (= (1+ slash) (length name))
913     (char/= (schar name (1+ slash)) #\.))))
914     (push name results))))
915     (let ((*ignore-wildcards* t))
916     (mapcar #'(lambda (name)
917 ram 1.23 (let ((name (if (and check-for-subdirs
918 wlott 1.26 (eq (unix:unix-file-kind name)
919 ram 1.23 :directory))
920     (concatenate 'string name "/")
921     name)))
922 dtc 1.58 (if truenamep (truename name) (pathname name))))
923 wlott 1.16 (sort (delete-duplicates results :test #'string=) #'string<)))))
924 ram 1.1
925 wlott 1.4
926     ;;;; Printing directories.
927 ram 1.1
928     ;;; PRINT-DIRECTORY is exported from the EXTENSIONS package.
929     ;;;
930     (defun print-directory (pathname &optional stream &key all verbose return-list)
931     "Like Directory, but prints a terse, multi-coloumn directory listing
932     instead of returning a list of pathnames. When :all is supplied and
933     non-nil, then Unix dot files are included too (as ls -a). When :vervose
934     is supplied and non-nil, then a long listing of miscellaneous
935     information is output one file per line."
936 dtc 1.50 (let ((*standard-output* (out-synonym-of stream))
937 wlott 1.16 (pathname pathname))
938 ram 1.1 (if verbose
939     (print-directory-verbose pathname all return-list)
940     (print-directory-formatted pathname all return-list))))
941    
942     (defun print-directory-verbose (pathname all return-list)
943 ram 1.23 (let ((contents (directory pathname :all all :check-for-subdirs nil
944 dtc 1.58 :truenamep nil))
945 wlott 1.7 (result nil))
946     (format t "Directory of ~A :~%" (namestring pathname))
947     (dolist (file contents)
948     (let* ((namestring (unix-namestring file))
949     (tail (subseq namestring
950     (1+ (or (position #\/ namestring
951     :from-end t
952     :test #'char=)
953     -1)))))
954     (multiple-value-bind
955     (reslt dev-or-err ino mode nlink uid gid rdev size atime mtime)
956 wlott 1.26 (unix:unix-stat namestring)
957 wlott 1.7 (declare (ignore ino gid rdev atime)
958     (fixnum uid mode))
959     (cond (reslt
960     ;;
961     ;; Print characters for file modes.
962     (macrolet ((frob (bit name &optional sbit sname negate)
963     `(if ,(if negate
964     `(not (logbitp ,bit mode))
965     `(logbitp ,bit mode))
966     ,(if sbit
967     `(if (logbitp ,sbit mode)
968     (write-char ,sname)
969     (write-char ,name))
970     `(write-char ,name))
971     (write-char #\-))))
972     (frob 15 #\d nil nil t)
973     (frob 8 #\r)
974     (frob 7 #\w)
975     (frob 6 #\x 11 #\s)
976     (frob 5 #\r)
977     (frob 4 #\w)
978     (frob 3 #\x 10 #\s)
979     (frob 2 #\r)
980     (frob 1 #\w)
981     (frob 0 #\x))
982     ;;
983     ;; Print the rest.
984     (multiple-value-bind (sec min hour date month year)
985     (get-decoded-time)
986     (declare (ignore sec min hour date month))
987     (format t "~2D ~8A ~8D ~12A ~A~@[/~]~%"
988     nlink
989     (or (lookup-login-name uid) uid)
990     size
991     (decode-universal-time-for-files mtime year)
992     tail
993 wlott 1.27 (= (logand mode unix:s-ifmt) unix:s-ifdir))))
994 wlott 1.7 (t (format t "Couldn't stat ~A -- ~A.~%"
995     tail
996 wlott 1.26 (unix:get-unix-error-msg dev-or-err))))
997 ram 1.1 (when return-list
998 wlott 1.27 (push (if (= (logand mode unix:s-ifmt) unix:s-ifdir)
999 wlott 1.7 (pathname (concatenate 'string namestring "/"))
1000     file)
1001     result)))))
1002     (nreverse result)))
1003 ram 1.1
1004     (defun decode-universal-time-for-files (time current-year)
1005     (multiple-value-bind (sec min hour day month year)
1006     (decode-universal-time (+ time unix-to-universal-time))
1007     (declare (ignore sec))
1008     (format nil "~A ~2,' D ~:[ ~D~;~*~2,'0D:~2,'0D~]"
1009     (svref '#("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug"
1010     "Sep" "Oct" "Nov" "Dec")
1011     (1- month))
1012     day (= current-year year) year hour min)))
1013    
1014     (defun print-directory-formatted (pathname all return-list)
1015     (let ((width (or (line-length *standard-output*) 80))
1016     (names ())
1017     (cnt 0)
1018     (max-len 0)
1019 dtc 1.58 (result (directory pathname :all all :truenamep nil)))
1020 ram 1.1 (declare (list names) (fixnum max-len cnt))
1021     ;;
1022     ;; Get the data.
1023 wlott 1.7 (dolist (file result)
1024     (let* ((name (unix-namestring file))
1025     (length (length name))
1026     (end (if (and (plusp length)
1027     (char= (schar name (1- length)) #\/))
1028     (1- length)
1029     length))
1030     (slash-name (subseq name
1031     (1+ (or (position #\/ name
1032     :from-end t
1033     :end end
1034     :test #'char=)
1035     -1))))
1036     (len (length slash-name)))
1037     (declare (simple-string slash-name)
1038     (fixnum len))
1039     (if (> len max-len) (setq max-len len))
1040     (incf cnt)
1041     (push slash-name names)))
1042     (setq names (nreverse names))
1043     ;;
1044     ;; Do the output.
1045     (let* ((col-width (1+ max-len))
1046     (cols (max (truncate width col-width) 1))
1047     (lines (ceiling cnt cols)))
1048     (declare (fixnum cols lines))
1049     (format t "Directory of ~A :~%" (namestring pathname))
1050     (dotimes (i lines)
1051     (declare (fixnum i))
1052     (dotimes (j cols)
1053     (declare (fixnum j))
1054     (let ((name (nth (+ i (the fixnum (* j lines))) names)))
1055     (when name
1056     (write-string name)
1057     (unless (eql j (1- cols))
1058 wlott 1.15 (dotimes (i (- col-width (length (the simple-string name))))
1059     (write-char #\space))))))
1060 wlott 1.7 (terpri)))
1061     (when return-list
1062     result)))
1063 ram 1.1
1064    
1065    
1066     ;;;; Translating uid's and gid's.
1067    
1068     (defvar *uid-hash-table* (make-hash-table)
1069     "Hash table for keeping track of uid's and login names.")
1070    
1071     ;;; LOOKUP-LOGIN-NAME translates a user id into a login name. Previous
1072     ;;; lookups are cached in a hash table since groveling the passwd(s) files
1073     ;;; is somewhat expensive. The table may hold nil for id's that cannot
1074     ;;; be looked up since this means the files are searched in their entirety
1075     ;;; each time this id is translated.
1076     ;;;
1077     (defun lookup-login-name (uid)
1078     (multiple-value-bind (login-name foundp) (gethash uid *uid-hash-table*)
1079     (if foundp
1080     login-name
1081     (setf (gethash uid *uid-hash-table*)
1082     (get-group-or-user-name :user uid)))))
1083    
1084     (defvar *gid-hash-table* (make-hash-table)
1085     "Hash table for keeping track of gid's and group names.")
1086    
1087     ;;; LOOKUP-GROUP-NAME translates a group id into a group name. Previous
1088     ;;; lookups are cached in a hash table since groveling the group(s) files
1089     ;;; is somewhat expensive. The table may hold nil for id's that cannot
1090     ;;; be looked up since this means the files are searched in their entirety
1091     ;;; each time this id is translated.
1092     ;;;
1093     (defun lookup-group-name (gid)
1094     (multiple-value-bind (group-name foundp) (gethash gid *gid-hash-table*)
1095     (if foundp
1096     group-name
1097     (setf (gethash gid *gid-hash-table*)
1098     (get-group-or-user-name :group gid)))))
1099    
1100    
1101     ;;; GET-GROUP-OR-USER-NAME first tries "/etc/passwd" ("/etc/group") since it is
1102     ;;; a much smaller file, contains all the local id's, and most uses probably
1103     ;;; involve id's on machines one would login into. Then if necessary, we look
1104     ;;; in "/etc/passwds" ("/etc/groups") which is really long and has to be
1105     ;;; fetched over the net.
1106     ;;;
1107     (defun get-group-or-user-name (group-or-user id)
1108     "Returns the simple-string user or group name of the user whose uid or gid
1109     is id, or NIL if no such user or group exists. Group-or-user is either
1110     :group or :user."
1111     (let ((id-string (let ((*print-base* 10)) (prin1-to-string id))))
1112     (declare (simple-string id-string))
1113     (multiple-value-bind (file1 file2)
1114     (ecase group-or-user
1115     (:group (values "/etc/group" "/etc/groups"))
1116     (:user (values "/etc/passwd" "/etc/passwd")))
1117     (or (get-group-or-user-name-aux id-string file1)
1118     (get-group-or-user-name-aux id-string file2)))))
1119    
1120     (defun get-group-or-user-name-aux (id-string passwd-file)
1121     (with-open-file (stream passwd-file)
1122     (loop
1123     (let ((entry (read-line stream nil)))
1124     (unless entry (return nil))
1125     (let ((name-end (position #\: (the simple-string entry)
1126     :test #'char=)))
1127     (when name-end
1128     (let ((id-start (position #\: (the simple-string entry)
1129     :start (1+ name-end) :test #'char=)))
1130     (when id-start
1131     (incf id-start)
1132     (let ((id-end (position #\: (the simple-string entry)
1133     :start id-start :test #'char=)))
1134     (when (and id-end
1135     (string= id-string entry
1136     :start2 id-start :end2 id-end))
1137     (return (subseq entry 0 name-end))))))))))))
1138    
1139    
1140 wlott 1.4 ;;;; File completion.
1141 ram 1.1
1142 chiles 1.13 ;;; COMPLETE-FILE -- Public
1143     ;;;
1144 wlott 1.4 (defun complete-file (pathname &key (defaults *default-pathname-defaults*)
1145     ignore-types)
1146 wlott 1.22 (let ((files (directory (complete-file-directory-arg pathname defaults)
1147 ram 1.23 :check-for-subdirs nil
1148 dtc 1.58 :truenamep nil)))
1149 wlott 1.4 (cond ((null files)
1150     (values nil nil))
1151     ((null (cdr files))
1152     (values (merge-pathnames (file-namestring (car files))
1153     pathname)
1154     t))
1155     (t
1156     (let ((good-files
1157     (delete-if #'(lambda (pathname)
1158 wlott 1.16 (and (simple-string-p
1159     (pathname-type pathname))
1160 wlott 1.4 (member (pathname-type pathname)
1161     ignore-types
1162     :test #'string=)))
1163     files)))
1164     (cond ((null good-files))
1165     ((null (cdr good-files))
1166     (return-from complete-file
1167     (values (merge-pathnames (file-namestring
1168     (car good-files))
1169     pathname)
1170     t)))
1171     (t
1172     (setf files good-files)))
1173     (let ((common (file-namestring (car files))))
1174     (dolist (file (cdr files))
1175     (let ((name (file-namestring file)))
1176     (dotimes (i (min (length common) (length name))
1177     (when (< (length name) (length common))
1178     (setf common name)))
1179     (unless (char= (schar common i) (schar name i))
1180     (setf common (subseq common 0 i))
1181     (return)))))
1182     (values (merge-pathnames common pathname)
1183     nil)))))))
1184 ram 1.1
1185 chiles 1.13 ;;; COMPLETE-FILE-DIRECTORY-ARG -- Internal.
1186     ;;;
1187     (defun complete-file-directory-arg (pathname defaults)
1188 wlott 1.22 (let* ((pathname (merge-pathnames pathname (directory-namestring defaults)))
1189 wlott 1.16 (type (pathname-type pathname)))
1190     (flet ((append-multi-char-wild (thing)
1191     (etypecase thing
1192     (null :wild)
1193     (pattern
1194     (make-pattern (append (pattern-pieces thing)
1195     (list :multi-char-wild))))
1196     (simple-string
1197     (make-pattern (list thing :multi-char-wild))))))
1198     (if (or (null type) (eq type :unspecific))
1199     ;; There is no type.
1200     (make-pathname :defaults pathname
1201     :name (append-multi-char-wild (pathname-name pathname))
1202     :type :wild)
1203     ;; There already is a type, so just extend it.
1204     (make-pathname :defaults pathname
1205     :name (pathname-name pathname)
1206     :type (append-multi-char-wild (pathname-type pathname)))))))
1207 chiles 1.13
1208 wlott 1.4 ;;; Ambiguous-Files -- Public
1209 ram 1.1 ;;;
1210 wlott 1.16 (defun ambiguous-files (pathname
1211     &optional (defaults *default-pathname-defaults*))
1212 wlott 1.4 "Return a list of all files which are possible completions of Pathname.
1213 chiles 1.13 We look in the directory specified by Defaults as well as looking down
1214     the search list."
1215 wlott 1.16 (directory (complete-file-directory-arg pathname defaults)
1216 dtc 1.58 :truenamep nil
1217 wlott 1.16 :check-for-subdirs nil))
1218 wlott 1.4
1219 wlott 1.16
1220 ram 1.1
1221     ;;; File-writable -- exported from extensions.
1222     ;;;
1223     ;;; Determines whether the single argument (which should be a pathname)
1224 dtc 1.46 ;;; can be written by the current task.
1225 wlott 1.16 ;;;
1226 ram 1.1 (defun file-writable (name)
1227     "File-writable accepts a pathname and returns T if the current
1228     process can write it, and NIL otherwise."
1229 wlott 1.16 (let ((name (unix-namestring name nil)))
1230 wlott 1.17 (cond ((null name)
1231     nil)
1232 wlott 1.26 ((unix:unix-file-kind name)
1233     (values (unix:unix-access name unix:w_ok)))
1234 wlott 1.17 (t
1235     (values
1236 wlott 1.26 (unix:unix-access (subseq name
1237 wlott 1.17 0
1238     (or (position #\/ name :from-end t)
1239     0))
1240 wlott 1.26 (logior unix:w_ok unix:x_ok)))))))
1241 ram 1.1
1242    
1243     ;;; Pathname-Order -- Internal
1244     ;;;
1245     ;;; Predicate to order pathnames by. Goes by name.
1246     ;;;
1247     (defun pathname-order (x y)
1248     (let ((xn (%pathname-name x))
1249     (yn (%pathname-name y)))
1250     (if (and xn yn)
1251     (let ((res (string-lessp xn yn)))
1252     (cond ((not res) nil)
1253     ((= res (length (the simple-string xn))) t)
1254     ((= res (length (the simple-string yn))) nil)
1255     (t t)))
1256     xn)))
1257    
1258    
1259     ;;; Default-Directory -- Public
1260     ;;;
1261     (defun default-directory ()
1262     "Returns the pathname for the default directory. This is the place where
1263     a file will be written if no directory is specified. This may be changed
1264     with setf."
1265     (multiple-value-bind (gr dir-or-error)
1266 wlott 1.26 (unix:unix-current-directory)
1267 ram 1.1 (if gr
1268 wlott 1.16 (let ((*ignore-wildcards* t))
1269     (pathname (concatenate 'simple-string dir-or-error "/")))
1270 wlott 1.3 (error dir-or-error))))
1271 ram 1.1
1272     ;;; %Set-Default-Directory -- Internal
1273     ;;;
1274     (defun %set-default-directory (new-val)
1275 wlott 1.17 (let ((namestring (unix-namestring new-val t)))
1276     (unless namestring
1277     (error "~S doesn't exist." new-val))
1278     (multiple-value-bind (gr error)
1279 wlott 1.26 (unix:unix-chdir namestring)
1280 wlott 1.17 (if gr
1281     (setf (search-list "default:") (default-directory))
1282 wlott 1.26 (error (unix:get-unix-error-msg error))))
1283 wlott 1.17 new-val))
1284 wlott 1.16 ;;;
1285     (defsetf default-directory %set-default-directory)
1286    
1287     (defun filesys-init ()
1288     (setf *default-pathname-defaults*
1289     (%make-pathname *unix-host* nil nil nil nil :newest))
1290     (setf (search-list "default:") (default-directory))
1291     nil)
1292 dtc 1.42
1293     ;;; Ensure-Directories-Exist -- Public
1294     ;;;
1295     (defun ensure-directories-exist (pathspec &key verbose (mode #o777))
1296 pw 1.44 "Tests whether the directories containing the specified file
1297     actually exist, and attempts to create them if they do not.
1298     Portable programs should avoid using the :MODE keyword argument."
1299 dtc 1.43 (let* ((pathname (pathname pathspec))
1300     (pathname (if (logical-pathname-p pathname)
1301     (translate-logical-pathname pathname)
1302     pathname))
1303     (created-p nil))
1304     (when (wild-pathname-p pathname)
1305 pw 1.51 (error 'simple-file-error
1306 pw 1.47 :format-control "Bad place for a wild pathname."
1307     :pathname pathspec))
1308 dtc 1.45 (enumerate-search-list (pathname pathname)
1309     (let ((dir (pathname-directory pathname)))
1310     (loop for i from 1 upto (length dir)
1311     do (let ((newpath (make-pathname
1312     :host (pathname-host pathname)
1313     :device (pathname-device pathname)
1314     :directory (subseq dir 0 i))))
1315     (unless (probe-file newpath)
1316     (let ((namestring (namestring newpath)))
1317     (when verbose
1318     (format *standard-output* "~&Creating directory: ~A~%"
1319     namestring))
1320     (unix:unix-mkdir namestring mode)
1321     (unless (probe-file namestring)
1322 pw 1.51 (error 'simple-file-error
1323 pw 1.47 :pathname pathspec
1324     :format-control "Can't create directory ~A."
1325 pw 1.48 :format-arguments (list namestring)))
1326 dtc 1.45 (setf created-p t)))))
1327     ;; Only the first path in a search-list is considered.
1328     (return (values pathname created-p))))))

  ViewVC Help
Powered by ViewVC 1.1.5