1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
6 (in-package :io.zeta-streams
)
8 ;;;-----------------------------------------------------------------------------
9 ;;; File Classes and Types
10 ;;;-----------------------------------------------------------------------------
12 (defclass file-device
(single-channel-device)
13 ((filename :initarg
:filename
:accessor filename-of
)
14 (direction :initarg
:direction
:accessor direction-of
)
15 (if-exists :initarg
:if-exists
:accessor if-exists-of
)
16 (if-does-not-exist :initarg
:if-does-not-exist
:accessor if-does-not-exist-of
)))
18 (defclass memory-mapped-file-device
(file-device direct-device
) ())
20 (deftype file-direction
()
21 '(member :input
:output
:io
))
23 (deftype file-if-exists
()
24 '(member :default
:error
:error-if-symlink
:unlink
:overwrite
))
26 (deftype file-if-does-not-exist
()
27 '(member :default
:error
:create
))
30 ;;;-----------------------------------------------------------------------------
32 ;;;-----------------------------------------------------------------------------
34 (defmethod initialize-instance :after
((device file-device
)
35 &key filename
(direction :input
)
36 (if-exists :default
) (if-does-not-exist :default
)
37 truncate append
(extra-flags 0)
39 (when (and (eql :error if-exists
)
40 (eql :error if-does-not-exist
))
41 (error 'program-error
))
43 (setf (values flags if-exists if-does-not-exist
)
44 (process-file-direction direction flags if-exists if-does-not-exist
))
45 (setf (values flags if-exists if-does-not-exist
)
46 (process-file-flags direction flags if-exists if-does-not-exist
47 truncate append extra-flags
))
48 (setf (filename-of device
) (copy-seq filename
)
49 (direction-of device
) direction
50 (if-exists-of device
) if-exists
51 (if-does-not-exist-of device
) if-does-not-exist
)
53 (device-open device
:filename filename
:flags flags
54 :mode mode
:if-exists if-exists
55 :if-does-not-exist if-does-not-exist
))))
58 ;;;-----------------------------------------------------------------------------
60 ;;;-----------------------------------------------------------------------------
62 (defmethod print-object ((file file-device
) stream
)
63 (print-unreadable-object (file stream
:identity t
:type nil
)
64 (format stream
"File device for ~S" (filename-of file
))))
67 ;;;-----------------------------------------------------------------------------
69 ;;;-----------------------------------------------------------------------------
71 (defmethod device-open ((device file-device
) &key filename flags mode
72 if-exists if-does-not-exist
)
73 (declare (ignore if-does-not-exist
))
74 (labels ((handle-error (c)
75 (posix-file-error c filename
"opening"))
78 (%sys-unlink filename
)
79 (posix-error (c) (handle-error c
))))
80 (try-open (&optional
(retry-on-unlink t
))
82 (%sys-open filename flags mode
)
84 (cond ((and retry-on-unlink
(eql :unlink if-exists
))
85 (try-unlink) (try-open nil
))
86 (t (handle-error c
))))
89 (:no-error
(fd) fd
))))
90 (let ((fd (try-open)))
92 (setf (input-handle-of device
) fd
93 (output-handle-of device
) fd
)))
96 (defun process-file-direction (direction flags if-exists if-does-not-exist
)
97 (macrolet ((add-flags (&rest %flags
)
98 `(setf flags
(logior flags
,@%flags
))))
99 (when (eql :default if-exists
) (setf if-exists
:overwrite
))
103 (check-type if-exists
(member :overwrite
:error-if-symlink
))
104 (check-type if-does-not-exist
(member :default
:error
))
105 (when (eql :default if-does-not-exist
) (setf if-does-not-exist
:error
)))
107 (add-flags (if (eql :io direction
) o-rdwr o-wronly
))
108 (check-type if-exists file-if-exists
)
109 (check-type if-does-not-exist file-if-does-not-exist
)
110 (when (eql :default if-does-not-exist
) (setf if-does-not-exist
:create
))))
111 (values flags if-exists if-does-not-exist
)))
113 (defun process-file-flags (direction flags if-exists if-does-not-exist
114 truncate append extra-flags
)
115 (macrolet ((add-flags (&rest %flags
)
116 `(setf flags
(logior flags
,@%flags
))))
119 (unless (eql :input direction
) (add-flags o-excl
)))
121 (add-flags o-nofollow
)))
122 (case if-does-not-exist
123 (:create
(add-flags o-creat
)))
126 (unless (eql :input direction
) (add-flags o-trunc
)))
128 (when (eql :output direction
) (add-flags o-append
)))
130 (add-flags extra-flags
))))
131 (values flags if-exists if-does-not-exist
))
134 ;;;-----------------------------------------------------------------------------
135 ;;; File DEVICE-CLOSE
136 ;;;-----------------------------------------------------------------------------
138 (defmethod device-close ((device file-device
) &optional abort
)
139 (declare (ignore abort
))
140 (ignore-errors (%sys-close
(input-handle-of device
)))
141 (setf (input-handle-of device
) nil
142 (output-handle-of device
) nil
)
146 ;;;-----------------------------------------------------------------------------
147 ;;; File DEVICE-POSITION
148 ;;;-----------------------------------------------------------------------------
150 (defmethod device-position ((device file-device
))
152 (%sys-lseek
(input-handle-of device
) 0 seek-cur
)
154 (posix-file-error err device
"seeking on"))))
156 (defmethod (setf device-position
) (position (device file-device
) &key
(from :start
))
158 (%sys-lseek
(input-handle-of device
) position
164 (posix-file-error err device
"seeking on"))))
167 ;;;-----------------------------------------------------------------------------
168 ;;; File DEVICE-LENGTH
169 ;;;-----------------------------------------------------------------------------
171 (defmethod device-length ((device file-device
))
173 (%sys-fstat
(input-handle-of device
))
175 (posix-file-error err device
"getting status of"))))
178 ;;;-----------------------------------------------------------------------------
180 ;;;-----------------------------------------------------------------------------
182 (defmethod device-poll-input ((device file-device
) &optional timeout
)
183 (poll-fd (input-handle-of device
) :input timeout
))
185 (defmethod device-poll-output ((device file-device
) &optional timeout
)
186 (poll-fd (output-handle-of device
) :output timeout
))
189 ;;;-----------------------------------------------------------------------------
191 ;;;-----------------------------------------------------------------------------
193 (defmethod read-octets/non-blocking
((device file-device
) vector start end
)
194 (with-device (device)
195 (%read-octets
/non-blocking
(input-handle-of device
) vector start end
)))
197 (defmethod read-octets/timeout
((device file-device
) vector start end timeout
)
198 (with-device (device)
199 (%read-octets
/timeout
(input-handle-of device
) vector start end timeout
)))
202 ;;;-----------------------------------------------------------------------------
203 ;;; File DEVICE-WRITE
204 ;;;-----------------------------------------------------------------------------
206 (defmethod write-octets/non-blocking
((device file-device
) vector start end
)
207 (with-device (device)
208 (%write-octets
/non-blocking
(output-handle-of device
) vector start end
)))
210 (defmethod write-octets/timeout
((device file-device
) vector start end timeout
)
211 (with-device (device)
212 (%write-octets
/timeout
(output-handle-of device
) vector start end timeout
)))
215 ;;;-----------------------------------------------------------------------------
217 ;;;-----------------------------------------------------------------------------
219 (defun open-file (filename &key
(direction :input
)
220 (if-exists :default
) (if-does-not-exist :default
)
221 truncate append
(extra-flags 0) (mode #o666
))
222 (when (and (null if-exists
)
223 (null if-does-not-exist
))
224 (error 'program-error
))
226 (make-instance 'file-device
227 :filename
(namestring filename
)
230 :if-does-not-exist if-does-not-exist
233 :extra-flags extra-flags
235 (posix-file-error (error)
236 (case (posix-file-error-identifier error
)
238 (if (null if-does-not-exist
) nil
(error error
)))
240 (if (null if-exists
) nil
(error error
)))
242 (:no-error
(file) file
)))