1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
6 (in-package :io.zeta-streams
)
8 (deftype file-direction
()
9 '(member :input
:output
:io
))
11 (deftype file-if-exists
()
12 '(member :default
:error
:error-if-symlink
:unlink
:overwrite
))
14 (deftype file-if-does-not-exist
()
15 '(member :default
:error
:create
))
17 (defmethod initialize-instance :after
((device file-device
)
18 &key filename
(direction :input
)
19 (if-exists :default
) (if-does-not-exist :default
)
20 truncate append nonblocking
(extra-flags 0)
22 (when (and (eql :error if-exists
)
23 (eql :error if-does-not-exist
))
24 (error 'program-error
))
26 (setf (values flags if-exists if-does-not-exist
)
27 (process-file-direction direction flags if-exists if-does-not-exist
))
28 (setf (values flags if-exists if-does-not-exist
)
29 (process-file-flags direction flags if-exists if-does-not-exist
30 truncate append nonblocking extra-flags
))
31 (setf (filename-of device
) (copy-seq filename
)
32 (direction-of device
) direction
33 (if-exists-of device
) if-exists
34 (if-does-not-exist-of device
) if-does-not-exist
)
36 (device-open device
:filename filename
:flags flags
37 :mode mode
:if-exists if-exists
38 :if-does-not-exist if-does-not-exist
)
40 (setf (position-of device
) (device-length device
))))))
42 (defmethod device-open ((device file-device
) &key filename flags mode
43 if-exists if-does-not-exist
&allow-other-keys
)
44 (declare (ignore if-does-not-exist
))
45 (labels ((handle-error (error)
46 (error 'posix-file-error
47 :code
(osicat-sys:system-error-code error
)
48 :identifier
(osicat-sys:system-error-identifier error
)
49 :pathname filename
:action
"opening"))
50 (try-open (&optional
(retry-on-unlink t
))
52 (nix:open filename flags mode
)
54 (cond ((and retry-on-unlink
(eql :unlink if-exists
))
57 (nix:posix-error
(error) (handle-error error
)))
59 (t (handle-error error
))))
60 (nix:posix-error
(error)
62 (:no-error
(fd) fd
))))
63 (let ((fd (try-open)))
64 (%set-fd-nonblock-mode fd t
)
65 (setf (input-handle-of device
) fd
66 (output-handle-of device
) fd
)))
69 (defun process-file-direction (direction flags if-exists if-does-not-exist
)
70 (macrolet ((add-flags (&rest %flags
)
71 `(setf flags
(logior flags
,@%flags
))))
74 (add-flags nix
:o-rdonly
)
75 (check-type if-exists
(member nil
:error-if-symlink
))
76 (check-type if-does-not-exist
(member nil
:error
))
77 (when (eql :default if-does-not-exist
) (setf if-does-not-exist
:error
)))
79 (add-flags nix
:o-wronly
)
80 (check-type if-exists file-if-exists
)
81 (check-type if-does-not-exist file-if-does-not-exist
)
82 (when (eql :default if-exists
) (setf if-exists
:overwrite
))
83 (when (eql :default if-does-not-exist
) (setf if-does-not-exist
:create
)))
85 (add-flags nix
:o-rdwr
)
86 (check-type if-exists file-if-exists
)
87 (check-type if-does-not-exist file-if-does-not-exist
)
88 (when (eql :default if-exists
) (setf if-exists
:overwrite
))
89 (when (eql :default if-does-not-exist
) (setf if-does-not-exist
:create
))))
90 (values flags if-exists if-does-not-exist
)))
92 (defun process-file-flags (direction flags if-exists if-does-not-exist
93 truncate append nonblocking extra-flags
)
94 (macrolet ((add-flags (&rest %flags
)
95 `(setf flags
(logior flags
,@%flags
))))
98 (unless (eql :input direction
) (add-flags nix
:o-excl
)))
100 (add-flags nix
:o-nofollow
)
101 (setf if-exists
:overwrite
)))
102 (case if-does-not-exist
103 (:create
(add-flags nix
:o-creat
)))
106 (unless (eql :input direction
) (add-flags nix
:o-trunc
)))
108 (when (eql :output direction
) (add-flags nix
:o-append
)))
110 (add-flags nix
:o-nonblock
))
112 (add-flags extra-flags
))))
113 (values flags if-exists if-does-not-exist
))
115 (defmethod device-close ((device file-device
))
116 (ignore-errors (nix:close
(input-handle-of device
)))
117 (setf (input-handle-of device
) nil
118 (output-handle-of device
) nil
)
121 (defmethod device-position ((device file-device
))
122 (position-of device
))
124 (defmethod (setf device-position
) (offset (device file-device
) &key
(from :start
))
127 (nix:lseek
(input-handle-of device
) offset nix
:seek-set
)
128 (setf (position-of device
) offset
))
130 (nix:lseek
(input-handle-of device
) offset nix
:seek-cur
)
131 (incf (position-of device
) offset
))
133 (nix:lseek
(input-handle-of device
) offset nix
:seek-end
)
134 ;; WARNING: possible race condition here: if betweek lseek() and fstat()
135 ;; the file size is modified, this calculation will be wrong
136 ;; perhaps fcntl() or ioctl() can be used to find out the file offset
137 (setf (position-of device
) (+ (device-length device
) offset
))))
138 (position-of device
))
140 (defmethod device-length ((device file-device
))
141 (nix:stat-size
(nix:fstat
(input-handle-of device
))))
143 (defun open-file (filename &key
(direction :input
)
144 (if-exists :default
) (if-does-not-exist :default
)
145 truncate append nonblocking
(extra-flags 0) (mode #o666
))
146 (when (and (null if-exists
)
147 (null if-does-not-exist
))
148 (error 'program-error
))
150 (make-instance 'file-device
151 :filename
(namestring filename
)
154 :if-does-not-exist if-does-not-exist
157 :nonblocking nonblocking
158 :extra-flags extra-flags
160 (posix-file-error (error)
161 (case (posix-file-error-identifier error
)
163 (if (null if-does-not-exist
) nil
(error error
)))
165 (if (null if-exists
) nil
(error error
)))
167 (:no-error
(file) file
)))