1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
6 (in-package :io.zeta-streams
)
8 ;;;-------------------------------------------------------------------------
10 ;;;-------------------------------------------------------------------------
12 (defclass file-device
(device)
13 ((filename :initarg
:filename
14 :accessor file-device-filename
)
16 :accessor file-device-flags
)
18 :accessor file-device-mode
)
19 (delete-if-exists :initarg
:delete-if-exists
20 :accessor file-device-delete-if-exists-p
)))
22 (defclass memory-mapped-file-device
(file-device direct-device
) ())
24 (defvar *default-open-mode
* #o666
)
26 (defclass file-zstream
(file-device single-channel-zstream
) ())
29 ;;;-------------------------------------------------------------------------
31 ;;;-------------------------------------------------------------------------
33 (defgeneric open-file
(filename &key direction if-exists if-does-not-exist
34 truncate append extra-flags mode synchronized
35 buffering buffer-size external-format
))
38 ;;;-------------------------------------------------------------------------
40 ;;;-------------------------------------------------------------------------
42 (defmethod shared-initialize :after
43 ((stream file-zstream
) slot-names
&rest initargs
)
45 (device-open stream slot-names initargs
))
46 (add-zstream-instance-flags stream
:zeta
)
47 (setf (slot-value stream
'base-device
) stream
48 (slot-value stream
'device
) stream
))
51 ;;;-------------------------------------------------------------------------
53 ;;;-------------------------------------------------------------------------
55 (defmethod print-object ((file file-zstream
) stream
)
56 (print-unreadable-object (file stream
:identity t
:type t
)
57 (format stream
"File stream for ~S"
58 (file-device-filename (zstream-device file
)))))
61 ;;;-------------------------------------------------------------------------
63 ;;;-------------------------------------------------------------------------
65 (defmethod device-open ((device file-device
) slot-names initargs
)
66 (destructuring-bind (&key handle filename flags delete-if-exists
67 (mode *default-open-mode
*))
69 ;; FIXME: use new pathnames
70 (check-type filename string
)
71 (setf (file-device-filename device
) (copy-seq filename
))
72 (labels ((handle-error (c)
73 (posix-file-error c filename
"opening"))
76 (%sys-unlink filename
)
77 (syscall-error (c) (handle-error c
))))
78 (try-open (&optional
(retry-on-delete t
))
80 (%sys-open filename flags mode
)
82 (cond ((and retry-on-delete delete-if-exists
)
83 (try-delete) (try-open nil
))
84 (t (handle-error c
))))
87 (:no-error
(fd) fd
))))
88 (let ((fd (or handle
(try-open))))
90 (setf (device-handle device
) fd
))))
94 ;;;-------------------------------------------------------------------------
96 ;;;-------------------------------------------------------------------------
98 (defmethod relinquish ((device file-device
) &key abort
)
99 (declare (ignore abort
))
100 (%sys-close
(device-handle device
))
101 (setf (device-handle device
) nil
)
105 ;;;-------------------------------------------------------------------------
107 ;;;-------------------------------------------------------------------------
109 (defmethod device-position ((device file-device
))
111 (%sys-lseek
(device-handle device
) 0 seek-cur
)
113 (posix-file-error err device
"seeking on"))))
115 (defmethod (setf device-position
)
116 (position (device file-device
) &optional
(from :start
))
118 (%sys-lseek
(device-handle device
) position
124 (posix-file-error err device
"seeking on"))))
127 ;;;-------------------------------------------------------------------------
129 ;;;-------------------------------------------------------------------------
131 (defmethod device-length ((device file-device
))
133 (stat-size (%sys-fstat
(device-handle device
)))
135 (posix-file-error err device
"getting status of"))))
137 (defmethod (setf device-length
) (length (device file-device
))
139 (%sys-ftruncate
(device-handle device
) length
)
141 (posix-file-error err device
"truncating"))))
144 ;;;-------------------------------------------------------------------------
146 ;;;-------------------------------------------------------------------------
148 (defmethod device-poll ((device file-device
) direction
&optional timeout
)
149 (multiple-value-bind (readp rhupp writep whupp
)
150 (poll-fd (device-handle device
) direction timeout
)
152 (:input
(values readp rhupp
))
153 (:output
(values writep whupp
)))))
156 ;;;-------------------------------------------------------------------------
158 ;;;-------------------------------------------------------------------------
160 (defmethod device-read/non-blocking
((device file-device
) vector start end
)
161 (with-device (device)
162 (%read-octets
/non-blocking
(device-handle device
) vector start end
)))
164 (defmethod device-read/timeout
((device file-device
) vector
166 (with-device (device)
167 (%read-octets
/timeout
(device-handle device
) vector start end timeout
)))
170 ;;;-------------------------------------------------------------------------
172 ;;;-------------------------------------------------------------------------
174 (defmethod device-write/non-blocking
((device file-device
) vector start end
)
175 (with-device (device)
176 (%write-octets
/non-blocking
(device-handle device
) vector start end
)))
178 (defmethod device-write/timeout
((device file-device
) vector
180 (with-device (device)
181 (%write-octets
/timeout
(device-handle device
) vector start end timeout
)))
184 ;;;-------------------------------------------------------------------------
186 ;;;-------------------------------------------------------------------------
189 (filename &key
(direction :input
) (if-exists :default
)
190 (if-does-not-exist :default
) truncate append
(extra-flags 0)
191 (mode *default-open-mode
*) synchronized
(buffering :full
)
192 (buffer-size +default-iobuf-size
+) (external-format :default
))
193 (check-type direction file-direction
)
194 (check-type extra-flags file-flags
)
195 (check-type mode file-mode
)
196 (check-type buffering
(or null stream-buffering
))
197 (when (or (and (null if-exists
)
198 (null if-does-not-exist
))
199 (and (eql :error if-exists
)
200 (eql :error if-does-not-exist
)))
201 (error 'program-error
))
202 ;; FIXME: check for file type TTY and adjust buffering
204 (setf (values flags if-exists if-does-not-exist
)
205 (process-file-direction direction flags
206 if-exists if-does-not-exist
))
207 (setf (values flags if-exists if-does-not-exist
)
208 (process-file-flags direction flags if-exists if-does-not-exist
209 truncate append extra-flags
))
211 (make-instance 'file-zeta-stream
212 :filename
(namestring filename
)
213 :flags
(logior flags extra-flags
)
215 :delete-if-exists
(eql :delete if-exists
)
216 :synchronized synchronized
219 :external-format external-format
)
220 (posix-file-error (error)
221 (case (posix-file-error-identifier error
)
223 (if (null if-does-not-exist
) nil
(error error
)))
225 (if (null if-exists
) nil
(error error
)))
227 (:no-error
(file) file
))))
229 (defun process-file-direction (direction flags if-exists if-does-not-exist
)
230 (macrolet ((add-flags (&rest %flags
)
231 `(setf flags
(logior flags
,@%flags
))))
232 (when (eql :default if-exists
) (setf if-exists
:overwrite
))
236 (check-type if-exists
(member :overwrite
:error-if-symlink
))
237 (check-type if-does-not-exist
(member :default
:error
))
238 (when (eql :default if-does-not-exist
)
239 (setf if-does-not-exist
:error
)))
241 (add-flags (if (eql :io direction
) o-rdwr o-wronly
))
242 (check-type if-exists file-if-exists
)
243 (check-type if-does-not-exist file-if-does-not-exist
)
244 (when (eql :default if-does-not-exist
)
245 (setf if-does-not-exist
:create
))))
246 (values flags if-exists if-does-not-exist
)))
248 (defun process-file-flags (direction flags if-exists if-does-not-exist
249 truncate append extra-flags
)
250 (macrolet ((add-flags (&rest %flags
)
251 `(setf flags
(logior flags
,@%flags
))))
254 (unless (eql :input direction
) (add-flags o-excl
)))
256 (add-flags o-excl o-creat
))
258 (add-flags o-nofollow
)))
259 (case if-does-not-exist
260 (:create
(add-flags o-creat
)))
263 (unless (eql :input direction
) (add-flags o-trunc
)))
265 (when (eql :output direction
) (add-flags o-append
)))
267 (add-flags extra-flags
))))
268 (values flags if-exists if-does-not-exist
))