root/trunk/thirdparty/cl-fad-0.6.2/fad.lisp

Revision 2878, 14.6 kB (checked in by ksprotte, 9 months ago)

added cl-fad to thirdparty

Line 
1 ;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-FAD; Base: 10 -*-
2 ;;; $Header: /usr/local/cvsrep/cl-fad/fad.lisp,v 1.33 2008/03/12 00:10:43 edi Exp $
3
4 ;;; Copyright (c) 2004, Peter Seibel.  All rights reserved.
5 ;;; Copyright (c) 2004-2008, Dr. Edmund Weitz.  All rights reserved.
6
7 ;;; Redistribution and use in source and binary forms, with or without
8 ;;; modification, are permitted provided that the following conditions
9 ;;; are met:
10
11 ;;;   * Redistributions of source code must retain the above copyright
12 ;;;     notice, this list of conditions and the following disclaimer.
13
14 ;;;   * Redistributions in binary form must reproduce the above
15 ;;;     copyright notice, this list of conditions and the following
16 ;;;     disclaimer in the documentation and/or other materials
17 ;;;     provided with the distribution.
18
19 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS 'AS IS' AND ANY EXPRESSED
20 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ;;; ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 ;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 ;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 ;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 ;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 ;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 ;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 ;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 (in-package :cl-fad)
32
33 (defun component-present-p (value)
34   "Helper function for DIRECTORY-PATHNAME-P which checks whether VALUE
35 is neither NIL nor the keyword :UNSPECIFIC."
36   (and value (not (eql value :unspecific))))
37
38 (defun directory-pathname-p (pathspec)
39   "Returns NIL if PATHSPEC \(a pathname designator) does not designate
40 a directory, PATHSPEC otherwise.  It is irrelevant whether file or
41 directory designated by PATHSPEC does actually exist."
42   (and
43     (not (component-present-p (pathname-name pathspec)))
44     (not (component-present-p (pathname-type pathspec)))
45     pathspec))
46
47 (defun pathname-as-directory (pathspec)
48   "Converts the non-wild pathname designator PATHSPEC to directory
49 form."
50   (let ((pathname (pathname pathspec)))
51     (when (wild-pathname-p pathname)
52       (error "Can't reliably convert wild pathnames."))
53     (cond ((not (directory-pathname-p pathspec))
54            (make-pathname :directory (append (or (pathname-directory pathname)
55                                                  (list :relative))
56                                              (list (file-namestring pathname)))
57                           :name nil
58                           :type nil
59                           :defaults pathname))
60           (t pathname))))
61
62 (defun directory-wildcard (dirname)
63   "Returns a wild pathname designator that designates all files within
64 the directory named by the non-wild pathname designator DIRNAME."
65   (when (wild-pathname-p dirname)
66     (error "Can only make wildcard directories from non-wildcard directories."))
67   (make-pathname :name #-:cormanlisp :wild #+:cormanlisp "*"
68                  :type #-(or :clisp :cormanlisp) :wild
69                        #+:clisp nil
70                        #+:cormanlisp "*"
71                  :defaults (pathname-as-directory dirname)))
72
73 #+:clisp
74 (defun clisp-subdirectories-wildcard (wildcard)
75   "Creates a wild pathname specifically for CLISP such that
76 sub-directories are returned by DIRECTORY."
77   (make-pathname :directory (append (pathname-directory wildcard)
78                                     (list :wild))
79                  :name nil
80                  :type nil
81                  :defaults wildcard))
82
83 (defun list-directory (dirname)
84   "Returns a fresh list of pathnames corresponding to the truenames of
85 all files within the directory named by the non-wild pathname
86 designator DIRNAME.  The pathnames of sub-directories are returned in
87 directory form - see PATHNAME-AS-DIRECTORY."
88   (when (wild-pathname-p dirname)
89     (error "Can only list concrete directory names."))
90   #+:ecl
91   (let ((dir (pathname-as-directory dirname)))
92     (concatenate 'list
93                  (directory (merge-pathnames (pathname "*/") dir))
94                  (directory (merge-pathnames (pathname "*.*") dir))))
95   #-:ecl
96   (let ((wildcard (directory-wildcard dirname)))
97     #+:abcl (system::list-directory dirname)
98     #+(or :sbcl :cmu :scl :lispworks) (directory wildcard)
99     #+(or :openmcl :digitool) (directory wildcard :directories t)
100     #+:allegro (directory wildcard :directories-are-files nil)
101     #+:clisp (nconc (directory wildcard :if-does-not-exist :keep)
102                     (directory (clisp-subdirectories-wildcard wildcard)))
103     #+:cormanlisp (nconc (directory wildcard)
104                          (cl::directory-subdirs dirname)))
105   #-(or :sbcl :cmu :scl :lispworks :openmcl :allegro :clisp :cormanlisp :ecl :abcl :digitool)
106   (error "LIST-DIRECTORY not implemented"))
107
108 (defun pathname-as-file (pathspec)
109   "Converts the non-wild pathname designator PATHSPEC to file form."
110   (let ((pathname (pathname pathspec)))
111     (when (wild-pathname-p pathname)
112       (error "Can't reliably convert wild pathnames."))
113     (cond ((directory-pathname-p pathspec)
114            (let* ((directory (pathname-directory pathname))
115                   (name-and-type (pathname (first (last directory)))))
116              (make-pathname :directory (butlast directory)
117                             :name (pathname-name name-and-type)
118                             :type (pathname-type name-and-type)
119                             :defaults pathname)))
120           (t pathname))))
121
122 (defun file-exists-p (pathspec)
123   "Checks whether the file named by the pathname designator PATHSPEC
124 exists and returns its truename if this is the case, NIL otherwise.
125 The truename is returned in `canonical' form, i.e. the truename of a
126 directory is returned as if by PATHNAME-AS-DIRECTORY."
127   #+(or :sbcl :lispworks :openmcl :ecl :digitool) (probe-file pathspec)
128   #+:allegro (or (excl:probe-directory (pathname-as-directory pathspec))
129                  (probe-file pathspec))
130   #+(or :cmu :scl :abcl) (or (probe-file (pathname-as-directory pathspec))
131                              (probe-file pathspec))
132   #+:cormanlisp (or (and (ccl:directory-p pathspec)
133                          (pathname-as-directory pathspec))
134                     (probe-file pathspec))
135   #+:clisp (or (ignore-errors
136                  (let ((directory-form (pathname-as-directory pathspec)))
137                    (when (ext:probe-directory directory-form)
138                      directory-form)))
139                (ignore-errors
140                  (probe-file (pathname-as-file pathspec))))
141   #-(or :sbcl :cmu :scl :lispworks :openmcl :allegro :clisp :cormanlisp :ecl :abcl :digitool)
142   (error "FILE-EXISTS-P not implemented"))
143
144 (defun directory-exists-p (pathspec)
145   "Checks whether the file named by the pathname designator PATHSPEC
146 exists and if it is a directory.  Returns its truename if this is the
147 case, NIL otherwise.  The truename is returned in directory form as if
148 by PATHNAME-AS-DIRECTORY."
149   #+:allegro
150   (and (excl:probe-directory pathspec)
151        (pathname-as-directory (truename pathspec)))
152   #+:lispworks
153   (and (lw:file-directory-p pathspec)
154        (pathname-as-directory (truename pathspec)))
155   #-(or :allegro :lispworks)
156   (let ((result (file-exists-p pathspec)))
157     (and result
158          (directory-pathname-p result)
159          result)))
160
161 (defun walk-directory (dirname fn &key directories
162                                        (if-does-not-exist :error)
163                                        (test (constantly t)))
164   "Recursively applies the function FN to all files within the
165 directory named by the non-wild pathname designator DIRNAME and all of
166 its sub-directories.  FN will only be applied to files for which the
167 function TEST returns a true value.  If DIRECTORIES is not NIL, FN and
168 TEST are applied to directories as well.  If DIRECTORIES is :DEPTH-FIRST,
169 FN will be applied to the directory's contents first.  If
170 DIRECTORIES is :BREADTH-FIRST and TEST returns NIL, the
171 directory's content will be skipped. IF-DOES-NOT-EXIST must be
172 one of :ERROR or :IGNORE where :ERROR means that an error will be
173 signaled if the directory DIRNAME does not exist."
174   (labels ((walk (name)
175              (cond
176                ((directory-pathname-p name)
177                 ;; the code is written in a slightly awkward way for
178                 ;; backward compatibility
179                 (cond ((not directories)
180                        (dolist (file (list-directory name))
181                          (walk file)))
182                       ((eql directories :breadth-first)
183                        (when (funcall test name)
184                          (funcall fn name)
185                          (dolist (file (list-directory name))
186                            (walk file))))
187                       ;; :DEPTH-FIRST is implicit
188                       (t (dolist (file (list-directory name))
189                            (walk file))
190                          (when (funcall test name)
191                            (funcall fn name)))))
192                ((funcall test name)
193                 (funcall fn name)))))
194     (let ((pathname-as-directory (pathname-as-directory dirname)))
195       (case if-does-not-exist
196         ((:error)
197          (cond ((not (file-exists-p pathname-as-directory))
198                 (error "File ~S does not exist."
199                        pathname-as-directory))
200                (t (walk pathname-as-directory))))
201         ((:ignore)
202          (when (file-exists-p pathname-as-directory)
203            (walk pathname-as-directory)))
204         (otherwise
205          (error "IF-DOES-NOT-EXIST must be one of :ERROR or :IGNORE."))))
206     (values)))
207
208 #-:sbcl
209 (defvar *stream-buffer-size* 8192)
210 #-:sbcl
211 (defun copy-stream (from to &optional (checkp t))
212   "Copies into TO \(a stream) from FROM \(also a stream) until the end
213 of FROM is reached, in blocks of *stream-buffer-size*.  The streams
214 should have the same element type.  If CHECKP is true, the streams are
215 checked for compatibility of their types."
216   (when checkp
217     (unless (subtypep (stream-element-type to) (stream-element-type from))
218       (error "Incompatible streams ~A and ~A." from to)))
219   (let ((buf (make-array *stream-buffer-size*
220                          :element-type (stream-element-type from))))
221     (loop
222      (let ((pos #-(or :clisp :cmu) (read-sequence buf from)
223                 #+:clisp (ext:read-byte-sequence buf from :no-hang nil)
224                 #+:cmu (sys:read-n-bytes from buf 0 *stream-buffer-size* nil)))
225        (when (zerop pos) (return))
226        (write-sequence buf to :end pos))))
227   (values))
228
229 #+:sbcl
230 (declaim (inline copy-stream))
231 #+:sbcl
232 (defun copy-stream (from to)
233   "Copies into TO \(a stream) from FROM \(also a stream) until the end
234 of FROM is reached.  The streams should have the same element type."
235   (sb-executable:copy-stream from to)
236   (values))
237
238 (defun copy-file (from to &key overwrite)
239   "Copies the file designated by the non-wild pathname designator FROM
240 to the file designated by the non-wild pathname designator TO.  If
241 OVERWRITE is true overwrites the file designtated by TO if it exists."
242   #+:allegro (excl.osi:copy-file from to :overwrite overwrite)
243   #-:allegro
244   (let ((element-type #-:cormanlisp '(unsigned-byte 8)
245                       #+:cormanlisp 'unsigned-byte))
246     (with-open-file (in from :element-type element-type)
247       (with-open-file (out to :element-type element-type
248                               :direction :output
249                               :if-exists (if overwrite
250                                            :supersede
251                                            #-:cormanlisp :error
252                                            #+:cormanlisp nil))
253         #+:cormanlisp
254         (unless out
255           (error (make-condition 'file-error
256                                  :pathname to
257                                  :format-control "File already exists.")))
258         (copy-stream in out))))
259   (values))
260
261 (defun delete-directory-and-files (dirname &key (if-does-not-exist :error))
262   "Recursively deletes all files and directories within the directory
263 designated by the non-wild pathname designator DIRNAME including
264 DIRNAME itself.  IF-DOES-NOT-EXIST must be one of :ERROR or :IGNORE
265 where :ERROR means that an error will be signaled if the directory
266 DIRNAME does not exist."
267   #+:allegro (excl.osi:delete-directory-and-files dirname
268                                                   :if-does-not-exist if-does-not-exist)
269   #-:allegro (walk-directory dirname
270                              (lambda (file)
271                                (cond ((directory-pathname-p file)
272                                       #+:lispworks (lw:delete-directory file)
273                                       #+:cmu (multiple-value-bind (ok err-number)
274                                                  (unix:unix-rmdir (namestring (truename file)))
275                                                (unless ok
276                                                  (error "Error number ~A when trying to delete ~A"
277                                                         err-number file)))
278                                       #+:scl (multiple-value-bind (ok errno)
279                                                  (unix:unix-rmdir (ext:unix-namestring (truename file)))
280                                                (unless ok
281                                                  (error "~@<Error deleting ~S: ~A~@:>"
282                                                         file (unix:get-unix-error-msg errno))))
283                                       #+:sbcl (sb-posix:rmdir file)
284                                       #+:clisp (ext:delete-dir file)
285                                       #+:openmcl (ccl:delete-directory file)
286                                       #+:cormanlisp (win32:delete-directory file)
287                                       #+:ecl (si:rmdir file)
288                                       #+(or :abcl :digitool) (delete-file file))
289                                      (t (delete-file file))))
290                              :directories t
291                              :if-does-not-exist if-does-not-exist)
292   (values))
293
294 (pushnew :cl-fad *features*)
295
296 ;; stuff for Nikodemus Siivola's HYPERDOC
297 ;; see <http://common-lisp.net/project/hyperdoc/>
298 ;; and <http://www.cliki.net/hyperdoc>
299 ;; also used by LW-ADD-ONS
300
301 #-:abcl
302 (defvar *hyperdoc-base-uri* "http://weitz.de/cl-fad/")
303
304 #-:abcl
305 (let ((exported-symbols-alist
306        (loop for symbol being the external-symbols of :cl-fad
307              collect (cons symbol
308                            (concatenate 'string
309                                         "#"
310                                         (string-downcase symbol))))))
311   (defun hyperdoc-lookup (symbol type)
312     (declare (ignore type))
313     (cdr (assoc symbol
314                 exported-symbols-alist
315                 :test #'eq))))
Note: See TracBrowser for help on using the browser.