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

Contents of /src/code/filesys.lisp

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.5