0.8.7.15:
[sbcl.git] / contrib / sb-simple-streams / internal.lisp
1 ;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Package: STREAM -*-
2
3 ;;; **********************************************************************
4 ;;; This code was written by Paul Foley and has been placed in the public
5 ;;; domain.
6 ;;; 
7
8 ;;; Sbcl port by Rudi Schlatte.
9
10 (in-package "SB-SIMPLE-STREAMS")
11
12 ;;;
13 ;;; **********************************************************************
14 ;;;
15 ;;; Various functions needed by simple-streams
16 (declaim (inline buffer-sap bref (setf bref) buffer-copy
17                  allocate-buffer free-buffer))
18
19 (defun buffer-sap (thing &optional offset)
20   (declare (type simple-stream-buffer thing) (type (or fixnum null) offset)
21            (optimize (speed 3) (space 2) (debug 0) (safety 0)
22                      ;; Suppress the note about having to box up the return:
23                      (sb-ext:inhibit-warnings 3)))
24   (let ((sap (if (vectorp thing) (sb-sys:vector-sap thing) thing)))
25     (if offset (sb-sys:sap+ sap offset) sap)))
26
27 (defun bref (buffer index)
28   (declare (type simple-stream-buffer buffer)
29            (type (integer 0 #.most-positive-fixnum) index))
30   (if (vectorp buffer)
31       (sb-sys:sap-ref-8 (sb-sys:vector-sap buffer) index)
32       (sb-sys:sap-ref-8 buffer index)))
33
34 (defun (setf bref) (octet buffer index)
35   (declare (type (unsigned-byte 8) octet)
36            (type simple-stream-buffer buffer)
37            (type (integer 0 #.most-positive-fixnum) index))
38   (if (vectorp buffer)
39       (setf (sb-sys:sap-ref-8 (sb-sys:vector-sap buffer) index) octet)
40       (setf (sb-sys:sap-ref-8 buffer index) octet)))
41
42 (defun buffer-copy (src soff dst doff length)
43   (declare (type simple-stream-buffer src dst)
44            (type fixnum soff doff length))
45   (sb-sys:without-gcing ;; is this necessary??
46    (sb-kernel:system-area-copy (buffer-sap src) (* soff 8)
47                                (buffer-sap dst) (* doff 8)
48                                (* length 8))))
49
50 (defun allocate-buffer (size)
51   (if (= size sb-impl::bytes-per-buffer)
52       (sb-impl::next-available-buffer)
53       (make-array size :element-type '(unsigned-byte 8))))
54
55 (defun free-buffer (buffer)
56   (when (sb-sys:system-area-pointer-p buffer)
57     (push buffer sb-impl::*available-buffers*))
58   t)
59
60
61 (defun make-control-table (&rest inits)
62   (let ((table (make-array 32 :initial-element nil)))
63     (do* ((char (pop inits) (pop inits))
64           (func (pop inits) (pop inits)))
65          ((null char))
66       (when (< (char-code char) 32)
67         (setf (aref table (char-code char)) func)))
68     table))
69
70 (defun std-newline-out-handler (stream character)
71   (declare (ignore character))
72   (with-stream-class (simple-stream stream)
73     (setf (sm charpos stream) -1)
74     nil))
75
76 (defun std-tab-out-handler (stream character)
77   (declare (ignore character))
78   (with-stream-class (simple-stream stream)
79     (let ((col (sm charpos stream)))
80       (when col
81         (setf (sm charpos stream) (1- (* 8 (1+ (floor col 8)))))))
82     nil))
83
84 (defun std-dc-newline-in-handler (stream character)
85   (with-stream-class (dual-channel-simple-stream stream)
86     ;; FIXME: Currently, -1 is wrong, since callers of CHARPOS expect
87     ;; a result in (or null (and fixnum unsigned-byte)), so they must
88     ;; never see this temporary value.  Note that in
89     ;; STD-NEWLINE-OUT-HANDLER it is correct to use -1, since CHARPOS
90     ;; is incremented to zero before WRITE-CHAR returns.  Perhaps the
91     ;; same should happen for input?
92     (setf (sm charpos stream) 0) ; was -1
93     character))
94
95 (defvar *std-control-out-table*
96   (make-control-table #\Newline #'std-newline-out-handler
97                       #\Tab     #'std-tab-out-handler))
98
99 (defvar *default-external-format* :iso8859-1)
100
101 (defvar *external-formats* (make-hash-table))
102 (defvar *external-format-aliases* (make-hash-table))
103
104 (defstruct (external-format
105              (:conc-name ef-)
106              (:print-function %print-external-format)
107              (:constructor make-external-format (name octets-to-char
108                                                       char-to-octets)))
109   (name (sb-int:missing-arg) :type keyword :read-only t)
110   (octets-to-char (sb-int:missing-arg) :type function :read-only t)
111   (char-to-octets (sb-int:missing-arg) :type function :read-only t))
112
113 (defun %print-external-format (ef stream depth)
114   (declare (ignore depth))
115   (print-unreadable-object (ef stream :type t :identity t)
116     (princ (ef-name ef) stream)))
117
118 (defmacro define-external-format (name octets-to-char char-to-octets)
119   `(macrolet ((octets-to-char ((state input unput) &body body)
120                 `(lambda (,state ,input ,unput)
121                    (declare (type (function () (unsigned-byte 8)) ,input)
122                             (type (function (sb-int:index) t) ,unput)
123                             (ignorable ,state ,input ,unput)
124                             (values character sb-int:index t))
125                    ,@body))
126               (char-to-octets ((char state output) &body body)
127                 `(lambda (,char ,state ,output)
128                    (declare (type character ,char)
129                             (type (function ((unsigned-byte 8)) t) ,output)
130                             (ignorable state ,output)
131                             (values t))
132                    ,@body)))
133      (setf (gethash ,name *external-formats*)
134            (make-external-format ,name ,octets-to-char ,char-to-octets))))
135
136 ;;; TODO: make this work
137 (defun load-external-format-aliases ()
138   (let ((*package* (find-package "KEYWORD")))
139     (with-open-file (stm "ef:aliases" :if-does-not-exist nil)
140       (when stm
141         (do ((alias (read stm nil stm) (read stm nil stm))
142              (value (read stm nil stm) (read stm nil stm)))
143             ((or (eq alias stm) (eq value stm))
144              (unless (eq alias stm)
145                (warn "External-format aliases file ends early.")))
146           (if (and (keywordp alias) (keywordp value))
147               (setf (gethash alias *external-format-aliases*) value)
148               (warn "Bad entry in external-format aliases file: ~S => ~S."
149                     alias value)))))))
150
151 (defun find-external-format (name &optional (error-p t))
152   (when (external-format-p name)
153     (return-from find-external-format name))
154
155   (when (eq name :default)
156     (setq name *default-external-format*))
157
158   ;; TODO: make this work
159   #+nil
160   (unless (ext:search-list-defined-p "ef:")
161     (setf (ext:search-list "ef:") '("library:ef/")))
162
163   (when (zerop (hash-table-count *external-format-aliases*))
164     (setf (gethash :latin1 *external-format-aliases*) :iso8859-1)
165     (setf (gethash :latin-1 *external-format-aliases*) :iso8859-1)
166     (setf (gethash :iso-8859-1 *external-format-aliases*) :iso8859-1)
167     (load-external-format-aliases))
168
169   (do ((tmp (gethash name *external-format-aliases*)
170             (gethash tmp *external-format-aliases*))
171        (cnt 0 (1+ cnt)))
172       ((or (null tmp) (= cnt 50))
173        (unless (null tmp)
174          (error "External-format aliasing depth exceeded.")))
175     (setq name tmp))
176
177   (or (gethash name *external-formats*)
178       (and (let ((*package* (find-package "SB-SIMPLE-STREAMS")))
179              (load (format nil "ef:~(~A~)" name) :if-does-not-exist nil))
180            (gethash name *external-formats*))
181       (if error-p (error "External format ~S not found." name) nil)))
182
183 (define-condition void-external-format (error)
184   ()
185   (:report
186     (lambda (condition stream)
187       (declare (ignore condition))
188       (format stream "Attempting I/O through void external-format."))))
189
190 (define-external-format :void
191     (octets-to-char (state input unput)
192       (declare (ignore state input unput))
193       (error 'void-external-format))
194   (char-to-octets (char state output)
195     (declare (ignore char state output))
196     (error 'void-external-format)))
197
198 (define-external-format :iso8859-1
199     (octets-to-char (state input unput)
200       (declare (optimize (speed 3) (space 0) (safety 0) (debug 0)))
201       (values (code-char (funcall input)) 1 state))
202   (char-to-octets (char state output)
203     (declare (optimize (speed 3) (space 0) (safety 0) (debug 0)))
204     (let ((code (char-code char)))
205       #-(or)
206       (funcall output code)
207       #+(or)
208       (if (< code 256)
209           (funcall output code)
210           (funcall output (char-code #\?))))
211     state))
212
213 (defmacro octets-to-char (external-format state count input unput)
214   (let ((tmp1 (gensym)) (tmp2 (gensym)) (tmp3 (gensym)))
215     `(multiple-value-bind (,tmp1 ,tmp2 ,tmp3)
216          (funcall (ef-octets-to-char ,external-format) ,state ,input ,unput)
217        (setf ,state ,tmp3 ,count ,tmp2)
218        ,tmp1)))
219
220 (defmacro char-to-octets (external-format char state output)
221   `(progn
222      (setf ,state (funcall (ef-char-to-octets ,external-format)
223                            ,char ,state ,output))
224      nil))
225
226 (defun string-to-octets (string &key (start 0) end (external-format :default))
227   (declare (type string string)
228            (type sb-int:index start)
229            (type (or null sb-int:index) end))
230   (let ((ef (find-external-format external-format))
231         (buffer (make-array (length string) :element-type '(unsigned-byte 8)))
232         (ptr 0)
233         (state nil))
234     (flet ((out (b)
235              (setf (aref buffer ptr) b)
236              (when (= (incf ptr) (length buffer))
237                (setq buffer (adjust-array buffer (* 2 ptr))))))
238       (dotimes (i (- (or end (length string)) start))
239         (declare (type sb-int:index i))
240         (char-to-octets ef (char string (+ start i)) state #'out))
241       (sb-kernel:shrink-vector buffer ptr))))
242
243 (defun octets-to-string (octets &key (start 0) end (external-format :default))
244   (declare (type vector octets)
245            (type sb-int:index start)
246            (type (or null sb-int:index) end))
247   (let ((ef (find-external-format external-format))
248         (end (1- (or end (length octets))))
249         (string (make-string (length octets)))
250         (ptr (1- start))
251         (pos -1)
252         (count 0)
253         (state nil))
254     (flet ((input ()
255              (aref octets (incf ptr)))
256            (unput (n)
257              (decf ptr n)))
258       (loop until (>= ptr end)
259             do (setf (schar string (incf pos))
260                  (octets-to-char ef state count #'input #'unput))))
261     (sb-kernel:shrink-vector string (1+ pos))))
262
263 (defun vector-elt-width (vector)
264   ;; Return octet-width of vector elements
265   (etypecase vector
266     ;; (simple-array fixnum (*)) not supported
267     ;; (simple-array base-char (*)) treated specially; don't call this
268     ((simple-array bit (*)) 1)
269     ((simple-array (unsigned-byte 2) (*)) 1)
270     ((simple-array (unsigned-byte 4) (*)) 1)
271     ((simple-array (signed-byte 8) (*)) 1)
272     ((simple-array (unsigned-byte 8) (*)) 1)
273     ((simple-array (signed-byte 16) (*)) 2)
274     ((simple-array (unsigned-byte 16) (*)) 2)
275     ((simple-array (signed-byte 32) (*)) 4)
276     ((simple-array (unsigned-byte 32) (*)) 4)
277     ((simple-array single-float (*)) 4)
278     ((simple-array double-float (*)) 8)
279     ((simple-array (complex single-float) (*)) 8)
280     ((simple-array (complex double-float) (*)) 16)))
281
282 #-(or big-endian little-endian)
283 (eval-when (:compile-toplevel)
284   (push sb-c::*backend-byte-order* *features*))
285
286 (defun endian-swap-value (vector endian-swap)
287   #+big-endian (declare (ignore vector))
288   (case endian-swap
289     (:network-order #+big-endian 0
290                     #+little-endian (1- (vector-elt-width vector)))
291     (:byte-8 0)
292     (:byte-16 1)
293     (:byte-32 3)
294     (:byte-64 7)
295     (:byte-128 15)
296     (otherwise endian-swap)))
297
298 #+(or)
299 (defun %read-vector (vector stream start end endian-swap blocking)
300   (declare (type (kernel:simple-unboxed-array (*)) vector)
301            (type stream stream))
302   ;; move code from read-vector
303   )
304
305 #+(or)
306 (defun %write-vector (... blocking)
307   ;; implement me
308   )
309
310 (defun read-octets (stream buffer start end blocking)
311   (declare (type simple-stream stream)
312            (type (or null simple-stream-buffer) buffer)
313            (type fixnum start)
314            (type (or null fixnum) end)
315            (type blocking blocking)
316            (optimize (speed 3) (space 2) (safety 0) (debug 0)))
317   (with-stream-class (simple-stream stream)
318     (let ((fd (sm input-handle stream))
319           (end (or end (sm buf-len stream)))
320           (buffer (or buffer (sm buffer stream))))
321       (declare (fixnum end))
322       (typecase fd
323         (fixnum
324          (let ((flag (sb-sys:wait-until-fd-usable fd :input
325                                                   (if blocking nil 0))))
326            (cond
327              ((and (not blocking) (= start end)) (if flag -3 0))
328              ((and (not blocking) (not flag)) 0)
329              (t (block nil
330                   (let ((count 0))
331                     (declare (type fixnum count))
332                     (tagbody
333                      again
334                        ;; Avoid CMUCL gengc write barrier
335                        (do ((i start (+ i (the fixnum #.(sb-posix:getpagesize)))))
336                            ((>= i end))
337                          (declare (type fixnum i))
338                          (setf (bref buffer i) 0))
339                        (setf (bref buffer (1- end)) 0)
340                        (multiple-value-bind (bytes errno)
341                            (sb-unix:unix-read fd (buffer-sap buffer start)
342                                               (the fixnum (- end start)))
343                          (declare (type (or null fixnum) bytes)
344                                   (type (integer 0 100) errno))
345                          (when bytes
346                            (incf count bytes)
347                            (incf start bytes))
348                          (cond ((null bytes)
349                                 (format *debug-io* "~&;; UNIX-READ: errno=~D~%" errno)
350                                 (cond ((= errno sb-unix:eintr) (go again))
351                                       ((and blocking
352                                             (or (= errno ;;sb-unix:eagain
353                                                    ;; TODO: move
354                                                    ;; eagain into
355                                                    ;; sb-unix
356                                                    11)
357                                                 (= errno sb-unix:ewouldblock)))
358                                        (sb-sys:wait-until-fd-usable fd :input nil)
359                                        (go again))
360                                       (t (return (- -10 errno)))))
361                                ((zerop count) (return -1))
362                                (t (return count)))))))))))
363         (t (%read-vector buffer fd start end :byte-8
364                          (if blocking :bnb nil)))))))
365
366 (defun write-octets (stream buffer start end blocking)
367   (declare (type simple-stream stream)
368            (type simple-stream-buffer buffer)
369            (type fixnum start)
370            (type (or null fixnum) end))
371   (with-stream-class (simple-stream stream)
372     (when (sm handler stream)
373       (do ()
374           ((null (sm pending stream)))
375         (sb-sys:serve-all-events)))
376
377     (let ((fd (sm output-handle stream))
378           (end (or end (length buffer))))
379       (typecase fd
380         (fixnum
381          (let ((flag (sb-sys:wait-until-fd-usable fd :output
382                                                   (if blocking nil 0))))
383            (cond
384              ((and (not blocking) (= start end)) (if flag -3 0))
385              ((and (not blocking) (not flag)) 0)
386              (t
387               (block nil
388                 (let ((count 0))
389                   (tagbody again
390                      (multiple-value-bind (bytes errno)
391                          (sb-unix:unix-write fd (buffer-sap buffer) start
392                                           (- end start))
393                        (when bytes
394                          (incf count bytes)
395                          (incf start bytes))
396                        (cond ((null bytes)
397                               (format *debug-io* "~&;; UNIX-WRITE: errno=~D~%" errno)
398                               (cond ((= errno sb-unix:eintr) (go again))
399                                     ;; don't block for subsequent chars
400                                     (t (return (- -10 errno)))))
401                              (t (return count)))))))))))
402         (t (error "implement me"))))))
403
404 (defun do-some-output (stream)
405   ;; Do some pending output; return T if completed, NIL if more to do
406   (with-stream-class (simple-stream stream)
407     (let ((fd (sm output-handle stream)))
408       (loop
409         (let ((list (pop (sm pending stream))))
410           (unless list
411             (sb-sys:remove-fd-handler (sm handler stream))
412             (setf (sm handler stream) nil)
413             (return t))
414           (let* ((buffer (first list))
415                  (start (second list))
416                  (end (third list))
417                  (len (- end start)))
418             (declare (type simple-stream-buffer buffer)
419                      (type sb-int:index start end len))
420             (tagbody again
421                (multiple-value-bind (bytes errno)
422                    (sb-unix:unix-write fd (buffer-sap buffer) start len)
423                  (cond ((null bytes)
424                         (if (= errno sb-unix:eintr)
425                             (go again)
426                             (progn (push list (sm pending stream))
427                                    (return nil))))
428                        ((< bytes len)
429                         (setf (second list) (+ start bytes))
430                         (push list (sm pending stream))
431                         (return nil))
432                        ((= bytes len)
433                         (free-buffer buffer)))))))))))
434
435 (defun queue-write (stream buffer start end)
436   ;; Queue a write; return T if buffer needs changing, NIL otherwise
437   (declare (type simple-stream stream)
438            (type simple-stream-buffer buffer)
439            (type sb-int:index start end))
440   (with-stream-class (simple-stream stream)
441     (when (sm handler stream)
442       (unless (do-some-output stream)
443         (let ((last (last (sm pending stream))))
444           (setf (cdr last) (list (list buffer start end)))
445           (return-from queue-write t))))
446     (let ((bytes (write-octets stream buffer start end nil)))
447       (unless (or (= bytes (- end start)) ; completed
448                   (= bytes -3)) ; empty buffer; shouldn't happen
449         (setf (sm pending stream) (list (list buffer start end)))
450         (setf (sm handler stream)
451               (sb-sys:add-fd-handler (sm output-handle stream) :output
452                                      (lambda (fd)
453                                        (declare (ignore fd))
454                                        (do-some-output stream))))
455         t))))
456
457
458
459
460 (defun %fd-open (pathname direction if-exists if-exists-given
461                           if-does-not-exist if-does-not-exist-given)
462   (declare (type pathname pathname)
463            (type (member :input :output :io :probe) direction)
464            (type (member :error :new-version :rename :rename-and-delete
465                          :overwrite :append :supersede nil) if-exists)
466            (type (member :error :create nil) if-does-not-exist))
467   (multiple-value-bind (input output mask)
468       (ecase direction
469         (:input (values t nil sb-unix:o_rdonly))
470         (:output (values nil t sb-unix:o_wronly))
471         (:io (values t t sb-unix:o_rdwr))
472         (:probe (values t nil sb-unix:o_rdonly)))
473     (declare (type sb-int:index mask))
474     (let ((name (cond ((sb-int:unix-namestring pathname input))
475                       ((and input (eq if-does-not-exist :create))
476                        (sb-int:unix-namestring pathname nil)))))
477       ;; Process if-exists argument if we are doing any output.
478       (cond (output
479              (unless if-exists-given
480                (setf if-exists
481                      (if (eq (pathname-version pathname) :newest)
482                          :new-version
483                          :error)))
484              (case if-exists
485                ((:error nil)
486                 (setf mask (logior mask sb-unix:o_excl)))
487                ((:rename :rename-and-delete)
488                 (setf mask (logior mask sb-unix:o_creat)))
489                ((:new-version :supersede)
490                 (setf mask (logior mask sb-unix:o_trunc)))))
491             (t
492              (setf if-exists nil)))     ; :ignore-this-arg
493       (unless if-does-not-exist-given
494         (setf if-does-not-exist
495               (cond ((eq direction :input) :error)
496                     ((and output
497                           (member if-exists '(:overwrite :append)))
498                      :error)
499                     ((eq direction :probe)
500                      nil)
501                     (t
502                      :create))))
503       (if (eq if-does-not-exist :create)
504           (setf mask (logior mask sb-unix:o_creat)))
505
506       (let ((original (if (member if-exists
507                                   '(:rename :rename-and-delete))
508                           (sb-impl::pick-backup-name name)
509                           nil))
510             (delete-original (eq if-exists :rename-and-delete))
511             (mode #o666))
512         (when original
513           ;; We are doing a :rename or :rename-and-delete.
514           ;; Determine if the file already exists, make sure the original
515           ;; file is not a directory and keep the mode
516           (let ((exists
517                  (and name
518                       (multiple-value-bind
519                             (okay err/dev inode orig-mode)
520                           (sb-unix:unix-stat name)
521                         (declare (ignore inode)
522                                  (type (or sb-int:index null) orig-mode))
523                         (cond
524                           (okay
525                            (when (and output (= (logand orig-mode #o170000)
526                                                 #o40000))
527                              (error 'sb-int:simple-file-error
528                                  :pathname pathname
529                                  :format-control
530                                  "Cannot open ~S for output: Is a directory."
531                                  :format-arguments (list name)))
532                            (setf mode (logand orig-mode #o777))
533                            t)
534                           ((eql err/dev sb-unix:enoent)
535                            nil)
536                           (t
537                            (error 'sb-int:simple-file-error
538                                   :pathname pathname
539                                   :format-control "Cannot find ~S: ~A"
540                                   :format-arguments
541                                     (list name
542                                       (sb-int:strerror err/dev)))))))))
543             (unless (and exists
544                          (rename-file name original))
545               (setf original nil)
546               (setf delete-original nil)
547               ;; In order to use SUPERSEDE instead, we have
548               ;; to make sure unix:o_creat corresponds to
549               ;; if-does-not-exist.  unix:o_creat was set
550               ;; before because of if-exists being :rename.
551               (unless (eq if-does-not-exist :create)
552                 (setf mask (logior (logandc2 mask sb-unix:o_creat)
553                                    sb-unix:o_trunc)))
554               (setf if-exists :supersede))))
555         
556         ;; Okay, now we can try the actual open.
557         (loop
558           (multiple-value-bind (fd errno)
559               (if name
560                   (sb-unix:unix-open name mask mode)
561                   (values nil sb-unix:enoent))
562             (cond ((sb-int:fixnump fd)
563                    (when (eql if-exists :append)
564                      (sb-unix:unix-lseek fd 0 sb-unix:l_xtnd))
565                    (return (values fd name original delete-original)))
566                   ((eql errno sb-unix:enoent)
567                    (case if-does-not-exist
568                      (:error
569                        (cerror "Return NIL."
570                                'sb-int:simple-file-error
571                                :pathname pathname
572                                :format-control "Error opening ~S, ~A."
573                                :format-arguments
574                                    (list pathname
575                                          (sb-int:strerror errno))))
576                      (:create
577                       (cerror "Return NIL."
578                                'sb-int:simple-file-error
579                                :pathname pathname
580                                :format-control
581                                    "Error creating ~S, path does not exist."
582                                :format-arguments (list pathname))))
583                    (return nil))
584                   ((eql errno sb-unix:eexist)
585                    (unless (eq nil if-exists)
586                      (cerror "Return NIL."
587                              'sb-int:simple-file-error
588                              :pathname pathname
589                              :format-control "Error opening ~S, ~A."
590                              :format-arguments
591                                  (list pathname
592                                        (sb-int:strerror errno))))
593                    (return nil))
594                   #+nil ; FIXME: reinstate this; error reporting is nice.
595                   ((eql errno sb-unix:eacces)
596                    (cerror "Try again."
597                            'sb-int:simple-file-error
598                            :pathname pathname
599                            :format-control "Error opening ~S, ~A."
600                            :format-arguments
601                                (list pathname
602                                      (sb-int:strerror errno))))
603                   (t
604                    (cerror "Return NIL."
605                            'sb-int:simple-file-error
606                            :pathname pathname
607                            :format-control "Error opening ~S, ~A."
608                            :format-arguments
609                                (list pathname
610                                      (sb-int:strerror errno)))
611                    (return nil)))))))))
612
613 (defun open-fd-stream (pathname &key (direction :input)
614                                 (element-type 'base-char)
615                                 (if-exists nil if-exists-given)
616                                 (if-does-not-exist nil if-does-not-exist-given)
617                                 (external-format :default))
618   (declare (type (or pathname string stream) pathname)
619            (type (member :input :output :io :probe) direction)
620            (type (member :error :new-version :rename :rename-and-delete
621                          :overwrite :append :supersede nil) if-exists)
622            (type (member :error :create nil) if-does-not-exist)
623            (ignore external-format))
624   (let ((filespec (merge-pathnames pathname)))
625     (multiple-value-bind (fd namestring original delete-original)
626         (%fd-open filespec direction if-exists if-exists-given
627                   if-does-not-exist if-does-not-exist-given)
628       (when fd
629         (case direction
630           ((:input :output :io)
631            (sb-sys:make-fd-stream fd
632                                   :input (member direction '(:input :io))
633                                   :output (member direction '(:output :io))
634                                   :element-type element-type
635                                   :file namestring
636                                   :original original
637                                   :delete-original delete-original
638                                   :pathname pathname
639                                   :input-buffer-p t
640                                   :auto-close t))
641           (:probe
642            (let ((stream (sb-impl::%make-fd-stream :name namestring :fd fd
643                                                    :pathname pathname
644                                                    :element-type element-type)))
645              (close stream)
646              stream)))))))
647
648
649 ;; Experimental "filespec" stuff
650
651 ;; sat: Hooks to parse URIs etc apparently go here
652
653 (defstruct (filespec-parser
654              (:constructor make-filespec-parser (name priority function)))
655   name
656   priority
657   function)
658
659 (defvar *filespec-parsers* ())
660
661 (defun add-filespec (name priority function)
662   (let ((filespec (make-filespec-parser name priority function)))
663     (setf *filespec-parsers*
664           (stable-sort (cons filespec (delete name *filespec-parsers*
665                                               :key #'filespec-parser-name))
666                        #'>
667                        :key #'filespec-parser-priority)))
668   t)
669
670 (defmacro define-filespec (name lambda-list &body body)
671   (let ((truename (if (consp name) (first name) name))
672         (priority (if (consp name) (second name) 0)))
673     `(add-filespec ',truename ,priority (lambda ,lambda-list
674                                           (block ,truename
675                                             ,@body)))))
676
677 (defun parse-filespec (string &optional (errorp t))
678   (dolist (i *filespec-parsers* (when errorp
679                                   (error "~S not recognised." string)))
680     (let ((result (ignore-errors
681                     (funcall (filespec-parser-function i) string))))
682       (when result (return result)))))
683
684 (define-filespec pathname (string)
685   (pathname string))