1 (cl:in-package
:sb-posix
)
3 (defmacro define-protocol-class
4 (name alien-type superclasses slots
&rest options
)
5 (let ((to-protocol (intern (format nil
"ALIEN-TO-~A" name
)))
6 (to-alien (intern (format nil
"~A-TO-ALIEN" name
))))
8 (export ',name
:sb-posix
)
9 (defclass ,name
,superclasses
10 ,(loop for slotd in slots
11 ;; KLUDGE: Splice out :ARRAY-LENGTH options (they're
12 ;; for the conversion functions, not for DEFCLASS).
13 for array-length-option
= (member :array-length slotd
)
14 collect
(append (ldiff slotd array-length-option
)
15 (cddr array-length-option
)))
17 (declaim (inline ,to-alien
,to-protocol
))
18 (declaim (inline ,to-protocol
,to-alien
))
19 (defun ,to-protocol
(alien &optional instance
)
20 (declare (type (sb-alien:alien
(* ,alien-type
)) alien
)
21 (type (or null
,name
) instance
))
23 (setf instance
(make-instance ',name
)))
24 ,@(loop for slotd in slots
25 ;; FIXME: slotds in source are more complicated in general
27 ;; FIXME: baroque construction of intricate fragility
28 for array-length
= (getf (cdr slotd
) :array-length
)
31 (let ((array (make-array ,array-length
)))
32 (setf (slot-value instance
',(car slotd
))
34 (dotimes (i ,array-length
)
37 (sb-alien:slot alien
',(car slotd
))
40 collect
`(setf (slot-value instance
',(car slotd
))
41 (sb-alien:slot alien
',(car slotd
))))
43 (defun ,to-alien
(instance &optional alien
)
44 (declare (type (or null
(sb-alien:alien
(* ,alien-type
))) alien
)
45 (type ,name instance
))
47 (setf alien
(sb-alien:make-alien
,alien-type
)))
48 ,@(loop for slotd in slots
49 for array-length
= (getf (cdr slotd
) :array-length
)
52 (let ((array (slot-value instance
',(car slotd
))))
53 (dotimes (i ,array-length
)
55 (sb-alien:slot alien
',(car slotd
))
59 collect
`(setf (sb-alien:slot alien
',(car slotd
))
60 (slot-value instance
',(car slotd
)))))
61 (find-class ',name
))))
63 (define-condition sb-posix
:syscall-error
(error)
64 ((errno :initarg
:errno
:reader sb-posix
:syscall-errno
)
65 (name :initarg
:name
:initform nil
:reader sb-posix
:syscall-name
))
66 (:report
(lambda (c s
)
67 (let ((errno (sb-posix:syscall-errno c
))
68 (name (sb-posix:syscall-name c
)))
70 (format s
"Error in ~S: ~A (~A)"
72 (sb-int:strerror errno
)
74 (format s
"Error in syscall: ~A (~A)"
75 (sb-int:strerror errno
)
78 (declaim (ftype (function (&optional symbol
) nil
) syscall-error
))
79 (defun syscall-error (&optional name
)
80 (error 'sb-posix
:syscall-error
84 (defun unsupported-error (lisp-name c-name
)
85 (error "~S is unsupported by SBCL on this platform due to lack of ~A()."
88 (defun unsupported-warning (lisp-name c-name
)
89 (warn "~S is unsupported by SBCL on this platform due to lack of ~A()."
92 (declaim (inline never-fails
))
93 (defun never-fails (&rest args
)
94 (declare (ignore args
))
97 ;;; Some systems may need C-level wrappers, which can live in the
98 ;;; runtime (so that save-lisp-and-die can produce standalone
99 ;;; executables). See REAL-C-NAME in macros.lisp for the use of this
101 (eval-when (:compile-toplevel
:load-toplevel
)
102 (setf *c-functions-in-runtime
*
103 #+netbsd
'("stat" "lstat" "fstat" "readdir" "opendir")
107 ;;; filesystem access
108 (defmacro define-call
* (name &rest arguments
)
109 #-win32
`(define-call ,name
,@arguments
)
110 #+win32
`(define-call ,(if (consp name
)
111 `(,(concatenate 'string
"_" (car name
))
113 (concatenate 'string
"_" name
))
116 (define-call* "access" int minusp
(pathname filename
) (mode int
))
117 (define-call* "chdir" int minusp
(pathname filename
))
118 (define-call* "chmod" int minusp
(pathname filename
) (mode mode-t
))
119 (define-call* "close" int minusp
(fd file-descriptor
))
120 (define-call* "creat" int minusp
(pathname filename
) (mode mode-t
))
121 (define-call* "dup" int minusp
(oldfd file-descriptor
))
122 (define-call* "dup2" int minusp
(oldfd file-descriptor
)
123 (newfd file-descriptor
))
124 (define-call* ("lseek" :options
:largefile
)
125 off-t minusp
(fd file-descriptor
) (offset off-t
)
127 (define-call* "mkdir" int minusp
(pathname filename
) (mode mode-t
))
130 (define-call-internally open-with-mode
,x int minusp
131 (pathname filename
) (flags int
) (mode mode-t
))
132 (define-call-internally open-without-mode
,x int minusp
133 (pathname filename
) (flags int
))
134 (define-entry-point ,x
135 (pathname flags
&optional
(mode nil mode-supplied
))
137 (open-with-mode pathname flags mode
)
138 (open-without-mode pathname flags
))))))
139 (def #-win32
"open" #+win32
"_open"))
140 (define-call "rename" int minusp
(oldpath filename
) (newpath filename
))
141 (define-call* "rmdir" int minusp
(pathname filename
))
142 (define-call* "unlink" int minusp
(pathname filename
))
143 (define-call #-netbsd
"opendir" #+netbsd
"_opendir"
144 (* t
) null-alien
(pathname filename
))
145 (define-call* "read" ssize-t minusp
146 (fd file-descriptor
) (buf (* t
)) (count size-t
))
147 (define-call* "write" ssize-t minusp
148 (fd file-descriptor
) (buf (* t
)) (count size-t
))
150 ;;; FIXME: to detect errors in readdir errno needs to be set to 0 and
151 ;;; then checked, like it's done in sb-unix:readdir.
153 (define-call ("readdir" :c-name
"readdir$INODE64" :options
:largefile
)
158 (define-call (#-netbsd
"readdir" #+netbsd
"_readdir" :options
:largefile
)
162 (define-call "closedir" int minusp
(dir (* t
)))
163 ;; need to do this here because we can't do it in the DEFPACKAGE
164 (define-call* "umask" mode-t never-fails
(mode mode-t
))
165 (define-call* "getpid" pid-t never-fails
)
169 (define-call "chown" int minusp
(pathname filename
)
170 (owner uid-t
) (group gid-t
))
171 (define-call "chroot" int minusp
(pathname filename
))
172 (define-call "fchdir" int minusp
(fd file-descriptor
))
173 (define-call "fchmod" int minusp
(fd file-descriptor
) (mode mode-t
))
174 (define-call "fchown" int minusp
(fd file-descriptor
)
175 (owner uid-t
) (group gid-t
))
176 (define-call "fdatasync" int minusp
(fd file-descriptor
))
177 (define-call ("ftruncate" :options
:largefile
)
178 int minusp
(fd file-descriptor
) (length off-t
))
179 (define-call "fsync" int minusp
(fd file-descriptor
))
180 (define-call "lchown" int minusp
(pathname filename
)
181 (owner uid-t
) (group gid-t
))
182 (define-call "link" int minusp
(oldpath filename
) (newpath filename
))
183 (define-call "lockf" int minusp
(fd file-descriptor
) (cmd int
) (len off-t
))
184 (define-call "mkfifo" int minusp
(pathname filename
) (mode mode-t
))
185 (define-call "symlink" int minusp
(oldpath filename
) (newpath filename
))
186 (define-call "sync" void never-fails
)
187 (define-call ("truncate" :options
:largefile
)
188 int minusp
(pathname filename
) (length off-t
))
190 (macrolet ((def-mk*temp
(lisp-name c-name result-type errorp dirp values
)
191 (declare (ignore dirp
))
192 (if (sb-sys:find-foreign-symbol-address c-name
)
194 (defun ,lisp-name
(template)
195 (let* ((external-format sb-alien
::*default-c-string-external-format
*)
196 (arg (sb-ext:string-to-octets
198 :external-format external-format
200 (sb-sys:with-pinned-objects
(arg)
201 ;; accommodate for the call-by-reference
202 ;; nature of mks/dtemp's template strings.
203 (let ((result (alien-funcall (extern-alien ,c-name
204 (function ,result-type system-area-pointer
))
205 (sb-alien::vector-sap arg
))))
206 (when (,errorp result
)
207 (syscall-error ',lisp-name
))
208 ;; FIXME: We'd rather return pathnames, but other
209 ;; SB-POSIX functions like this return strings...
210 (let ((pathname (sb-ext:octets-to-string
211 arg
:external-format external-format
212 :end
(1- (length arg
)))))
214 '(values result pathname
)
216 (export ',lisp-name
))
218 (defun ,lisp-name
(template)
219 (declare (ignore template
))
220 (unsupported-error ',lisp-name
,c-name
))
221 (define-compiler-macro ,lisp-name
(&whole form template
)
222 (declare (ignore template
))
223 (unsupported-warning ',lisp-name
,c-name
)
225 (export ',lisp-name
)))))
226 ;; FIXME: The man page for it says "Never use mktemp()"
227 (def-mk*temp mktemp
"mktemp" (* char
) null-alien nil nil
)
228 ;; FIXME: Windows does have _mktemp, which has a slightly different
230 (def-mk*temp mkstemp
"mkstemp" int minusp nil t
)
231 ;; FIXME: What about Windows?
232 (def-mk*temp mkdtemp
"mkdtemp" (* char
) null-alien t nil
))
233 (define-call-internally ioctl-without-arg
"ioctl" int minusp
234 (fd file-descriptor
) (cmd int
))
235 (define-call-internally ioctl-with-int-arg
"ioctl" int minusp
236 (fd file-descriptor
) (cmd int
) (arg int
))
237 (define-call-internally ioctl-with-pointer-arg
"ioctl" int minusp
238 (fd file-descriptor
) (cmd int
)
239 (arg alien-pointer-to-anything-or-nil
))
240 (define-entry-point "ioctl" (fd cmd
&optional
(arg nil argp
))
243 ((alien int
) (ioctl-with-int-arg fd cmd arg
))
244 ((or (alien (* t
)) null
) (ioctl-with-pointer-arg fd cmd arg
)))
245 (ioctl-without-arg fd cmd
)))
246 (define-call-internally fcntl-without-arg
"fcntl" int minusp
247 (fd file-descriptor
) (cmd int
))
248 (define-call-internally fcntl-with-int-arg
"fcntl" int minusp
249 (fd file-descriptor
) (cmd int
) (arg int
))
250 (define-call-internally fcntl-with-pointer-arg
"fcntl" int minusp
251 (fd file-descriptor
) (cmd int
)
252 (arg alien-pointer-to-anything-or-nil
))
253 (define-protocol-class flock alien-flock
()
254 ((type :initarg
:type
:accessor flock-type
255 :documentation
"Type of lock; F_RDLCK, F_WRLCK, F_UNLCK.")
256 (whence :initarg
:whence
:accessor flock-whence
257 :documentation
"Flag for starting offset.")
258 (start :initarg
:start
:accessor flock-start
259 :documentation
"Relative offset in bytes.")
260 (len :initarg
:len
:accessor flock-len
261 :documentation
"Size; if 0 then until EOF.")
262 ;; Note: PID isn't initable, and is read-only. But other stuff in
263 ;; SB-POSIX right now loses when a protocol-class slot is unbound,
264 ;; so we initialize it to 0.
265 (pid :initform
0 :reader flock-pid
267 "Process ID of the process holding the lock; returned with F_GETLK."))
268 (:documentation
"Class representing locks used in fcntl(2)."))
269 (define-entry-point "fcntl" (fd cmd
&optional
(arg nil argp
))
272 ((alien int
) (fcntl-with-int-arg fd cmd arg
))
273 ((or (alien (* t
)) null
) (fcntl-with-pointer-arg fd cmd arg
))
274 (flock (with-alien-flock a-flock
()
275 (flock-to-alien arg a-flock
)
276 (let ((r (fcntl-with-pointer-arg fd cmd a-flock
)))
277 (alien-to-flock a-flock arg
)
279 (fcntl-without-arg fd cmd
)))
282 (define-call "geteuid" uid-t never-fails
) ; "always successful", it says
283 (define-call "getuid" uid-t never-fails
)
284 (define-call "seteuid" int minusp
(uid uid-t
))
286 (define-call "setfsuid" int minusp
(uid uid-t
))
287 (define-call "setreuid" int minusp
(ruid uid-t
) (euid uid-t
))
289 (define-call "setresuid" int minusp
(ruid uid-t
) (euid uid-t
) (suid uid-t
))
290 (define-call "setuid" int minusp
(uid uid-t
))
291 (define-call "getegid" gid-t never-fails
)
292 (define-call "getgid" gid-t never-fails
)
295 (export '(getresgid getresuid
) :sb-posix
)
296 (declaim (inline getresgid getresuid
))
298 (with-alien ((rgid gid-t
)
302 (alien-funcall (extern-alien "getresgid"
303 (function int
(* gid-t
) (* gid-t
) (* gid-t
)))
304 (addr rgid
) (addr egid
) (addr sgid
))))
306 (syscall-error 'getresgid
)
307 (values rgid egid sgid
)))))
309 (with-alien ((ruid uid-t
)
313 (alien-funcall (extern-alien "getresuid"
314 (function int
(* uid-t
) (* uid-t
) (* uid-t
)))
315 (addr ruid
) (addr euid
) (addr suid
))))
317 (syscall-error 'getresuid
)
318 (values ruid euid suid
))))))
319 (define-call "setegid" int minusp
(gid gid-t
))
321 (define-call "setfsgid" int minusp
(gid gid-t
))
322 (define-call "setgid" int minusp
(gid gid-t
))
323 (define-call "setregid" int minusp
(rgid gid-t
) (egid gid-t
))
325 (define-call "setresgid" int minusp
(rgid gid-t
) (egid gid-t
) (sgid gid-t
))
327 ;; processes, signals
328 (define-call "alarm" int never-fails
(seconds unsigned
))
330 ;; exit and abort, not much point inlining these
331 (define-simple-call abort void
)
332 (define-simple-call exit void
(status int
))
333 (define-simple-call _exit void
(status int
))
335 ;; FIXME this is a lie, of course this can fail, but there's no
336 ;; error handling here yet!
338 (define-call "darwin_reinit" void never-fails
)
339 (define-call ("posix_fork" :c-name
"fork") pid-t minusp
)
341 "Forks the current process, returning 0 in the new process and the PID of
342 the child process in the parent. Forking while multiple threads are running is
345 (sb-thread::with-all-threads-lock
346 (when (cdr sb-thread
::*all-threads
*)
348 (let ((pid (posix-fork)))
352 (return-from fork pid
)))
354 (error "Cannot fork with multiple threads running.")))
355 (export 'fork
:sb-posix
)
357 (define-call "getpgid" pid-t minusp
(pid pid-t
))
358 (define-call "getppid" pid-t never-fails
)
359 (define-call "getpgrp" pid-t never-fails
)
360 (define-call "getsid" pid-t minusp
(pid pid-t
))
361 (define-call "kill" int minusp
(pid pid-t
) (signal int
))
362 (define-call "killpg" int minusp
(pgrp int
) (signal int
))
363 (define-call "pause" int minusp
)
364 (define-call "setpgid" int minusp
(pid pid-t
) (pgid pid-t
))
365 (define-call "setpgrp" int minusp
)
366 (define-call "setsid" pid-t minusp
))
368 (defmacro with-growing-c-string
((buffer size
) &body body
)
369 (sb-int:with-unique-names
(c-string-block)
370 `(block ,c-string-block
372 (flet ((,buffer
(&optional
(size-incl-null))
374 (setf (sb-sys:sap-ref-8
(sb-alien:alien-sap
,buffer
) size-incl-null
)
376 (return-from ,c-string-block
377 (sb-alien::c-string-to-string
378 (sb-alien:alien-sap
,buffer
)
379 (sb-impl::default-external-format
)
381 (loop for
,size
= 128 then
(* 2 ,size
)
384 (setf ,buffer
(make-alien c-string
,size
))
387 (free-alien ,buffer
)))))))))
391 (export 'readlink
:sb-posix
)
392 (defun readlink (pathspec)
393 "Returns the resolved target of a symbolic link as a string."
394 (flet ((%readlink
(path buf length
)
396 (extern-alien "readlink" (function int
(c-string :not-null t
) (* t
) int
))
398 (with-growing-c-string (buf size
)
399 (let ((count (%readlink
(filename pathspec
) buf size
)))
400 (cond ((minusp count
)
401 (syscall-error 'readlink
))
406 (export 'getcwd
:sb-posix
)
408 "Returns the process's current working directory as a string."
409 (flet ((%getcwd
(buffer size
)
411 (extern-alien #-win32
"getcwd"
412 #+win32
"_getcwd" (function c-string
(* t
) int
))
414 (with-growing-c-string (buf size
)
415 (let ((result (%getcwd buf size
)))
418 ((/= (get-errno) sb-posix
:erange
)
419 (syscall-error 'getcwd
))))))))
423 (export 'wait
:sb-posix
)
424 (declaim (inline wait
))
425 (defun wait (&optional statusptr
)
426 (declare (type (or null
(simple-array (signed-byte 32) (1))) statusptr
))
427 (let* ((ptr (or statusptr
(make-array 1 :element-type
'(signed-byte 32))))
428 (pid (sb-sys:with-pinned-objects
(ptr)
430 (extern-alien "wait" (function pid-t
(* int
)))
431 (sb-sys:vector-sap ptr
)))))
433 (syscall-error 'wait
)
434 (values pid
(aref ptr
0))))))
438 (export 'waitpid
:sb-posix
)
439 (declaim (inline waitpid
))
440 (defun waitpid (pid options
&optional statusptr
)
441 (declare (type (sb-alien:alien pid-t
) pid
)
442 (type (sb-alien:alien int
) options
)
443 (type (or null
(simple-array (signed-byte 32) (1))) statusptr
))
444 (let* ((ptr (or statusptr
(make-array 1 :element-type
'(signed-byte 32))))
445 (pid (sb-sys:with-pinned-objects
(ptr)
447 (extern-alien "waitpid" (function pid-t
449 pid
(sb-sys:vector-sap ptr
) options
))))
451 (syscall-error 'waitpid
)
452 (values pid
(aref ptr
0)))))
454 (define-call "wifexited" boolean never-fails
(status int
))
455 (define-call "wexitstatus" int never-fails
(status int
))
456 (define-call "wifsignaled" boolean never-fails
(status int
))
457 (define-call "wtermsig" int never-fails
(status int
))
458 (define-call "wifstopped" boolean never-fails
(status int
))
459 (define-call "wstopsig" int never-fails
(status int
))
460 (define-call "wifcontinued" boolean never-fails
(status int
)))
465 (define-call ("mmap" :options
:largefile
) sb-sys
:system-area-pointer
467 (= (sb-sys:sap-int res
) #.
(1- (expt 2 sb-vm
::n-machine-word-bits
))))
468 (addr sap-or-nil
) (length size-t
) (prot unsigned
)
469 (flags unsigned
) (fd file-descriptor
) (offset off-t
))
471 (define-call "munmap" int minusp
472 (start sb-sys
:system-area-pointer
) (length unsigned
))
475 (define-call "msync" int minusp
476 (addr sb-sys
:system-area-pointer
) (length unsigned
) (flags int
)))
479 ;; No attempt is made to offer a full mmap-like interface on Windows.
480 ;; It would be possible to do so (and has been done by AK on his
481 ;; branch), but the use case is unclear to me. However, the following
482 ;; definitions are needed to keep existing code in sb-simple-streams
484 (defconstant PROT-READ
#x02
)
485 (defconstant PROT-WRITE
#x04
)
486 (defconstant PROT-EXEC
#x10
)
487 (defconstant PROT-NONE
0)
488 (defconstant MAP-SHARED
0)
489 (defconstant MAP-PRIVATE
1)
490 (defconstant MS-ASYNC nil
)
491 (defconstant MS-SYNC nil
)
492 (export ;export on the fly like define-call
493 (defun msync (address length flags
)
494 (declare (ignore flags
))
495 (when (zerop (sb-win32:flush-view-of-file address length
))
496 (sb-win32::win32-error
"FlushViewOfFile")))))
498 ;;; mlockall, munlockall
499 (define-call "mlockall" int minusp
(flags int
))
500 (define-call "munlockall" int minusp
)
503 (define-call "getpagesize" int minusp
)
505 ;;; KLUDGE: This could be taken from GetSystemInfo
506 (export (defun getpagesize () 4096))
509 ;; The docstrings are copied from the descriptions in SUSv3,
512 (define-protocol-class passwd alien-passwd
()
513 ((name :initarg
:name
:accessor passwd-name
514 :documentation
"User's login name.")
515 ;; Note: SUSv3 doesn't require this member.
516 (passwd :initarg
:passwd
:accessor passwd-passwd
517 :documentation
"The account's encrypted password.")
518 (uid :initarg
:uid
:accessor passwd-uid
519 :documentation
"Numerical user ID.")
520 (gid :initarg
:gid
:accessor passwd-gid
521 :documentation
"Numerical group ID.")
522 ;; Note: SUSv3 doesn't require this member.
523 (gecos :initarg
:gecos
:accessor passwd-gecos
524 :documentation
"User's name or comment field.")
525 (dir :initarg
:dir
:accessor passwd-dir
526 :documentation
"Initial working directory.")
527 (shell :initarg
:shell
:accessor passwd-shell
528 :documentation
"Program to use as shell."))
530 "Instances of this class represent entries in the system's user database."))
534 (define-protocol-class group alien-group
()
535 ((name :initarg
:name
:accessor group-name
)
536 (passwd :initarg
:passwd
:accessor group-passwd
)
537 (gid :initarg
:gid
:accessor group-gid
)))
539 (defmacro define-obj-call
(name arg type conv
)
541 ;; FIXME: this isn't the documented way of doing this, surely?
542 (let ((lisp-name (intern (string-upcase name
) :sb-posix
)))
544 (export ',lisp-name
:sb-posix
)
545 (declaim (inline ,lisp-name
))
546 (defun ,lisp-name
(,arg
)
547 (let ((r (alien-funcall (extern-alien ,name
,type
) ,arg
)))
552 (define-obj-call "getpwnam" login-name
(function (* alien-passwd
) (c-string :not-null t
))
554 (define-obj-call "getpwuid" uid
(function (* alien-passwd
) uid-t
)
556 (define-obj-call "getgrnam" login-name
(function (* alien-group
) (c-string :not-null t
))
558 (define-obj-call "getgrgid" gid
(function (* alien-group
) gid-t
)
563 (define-protocol-class timeval alien-timeval
()
564 ((sec :initarg
:tv-sec
:accessor timeval-sec
565 :documentation
"Seconds.")
566 (usec :initarg
:tv-usec
:accessor timeval-usec
567 :documentation
"Microseconds."))
568 (:documentation
"Instances of this class represent time values."))
570 (define-protocol-class stat alien-stat
()
571 ((mode :initarg
:mode
:reader stat-mode
572 :documentation
"Mode of file.")
573 (ino :initarg
:ino
:reader stat-ino
574 :documentation
"File serial number.")
575 (dev :initarg
:dev
:reader stat-dev
576 :documentation
"Device ID of device containing file.")
577 (nlink :initarg
:nlink
:reader stat-nlink
578 :documentation
"Number of hard links to the file.")
579 (uid :initarg
:uid
:reader stat-uid
580 :documentation
"User ID of file.")
581 (gid :initarg
:gid
:reader stat-gid
582 :documentation
"Group ID of file.")
583 (size :initarg
:size
:reader stat-size
584 :documentation
"For regular files, the file size in
585 bytes. For symbolic links, the length
586 in bytes of the filename contained in
588 (rdev :initarg
:rdev
:reader stat-rdev
589 :documentation
"For devices the device number.")
590 (atime :initarg
:atime
:reader stat-atime
591 :documentation
"Time of last access.")
592 (mtime :initarg
:mtime
:reader stat-mtime
593 :documentation
"Time of last data modification.")
594 (ctime :initarg
:ctime
:reader stat-ctime
595 :documentation
"Time of last status change."))
596 (:documentation
"Instances of this class represent POSIX file metadata."))
598 (defmacro define-stat-call
(name arg designator-fun type
)
599 ;; FIXME: this isn't the documented way of doing this, surely?
600 (let ((lisp-name (lisp-for-c-symbol name
))
601 (real-name #+inode64
(format nil
"~A$INODE64" name
)
604 (export ',lisp-name
:sb-posix
)
605 (declaim (inline ,lisp-name
))
606 (defun ,lisp-name
(,arg
&optional stat
)
607 (declare (type (or null stat
) stat
))
608 (with-alien-stat a-stat
()
609 (let ((r (alien-funcall
610 (extern-alien ,(real-c-name (list real-name
:options
:largefile
)) ,type
)
611 (,designator-fun
,arg
)
614 (syscall-error ',lisp-name
))
615 (alien-to-stat a-stat stat
)))))))
617 (define-stat-call #-win32
"stat" #+win32
"_stat"
619 (function int
(c-string :not-null t
) (* alien-stat
)))
622 (define-stat-call "lstat"
624 (function int
(c-string :not-null t
) (* alien-stat
)))
625 ;;; No symbolic links on Windows, so use stat
628 (declaim (inline lstat
))
629 (export (defun lstat (filename &optional stat
)
630 (if stat
(stat filename stat
) (stat filename
)))))
632 (define-stat-call #-win32
"fstat" #+win32
"_fstat"
634 (function int int
(* alien-stat
)))
638 (define-call "s_isreg" boolean never-fails
(mode mode-t
))
639 (define-call "s_isdir" boolean never-fails
(mode mode-t
))
640 (define-call "s_ischr" boolean never-fails
(mode mode-t
))
641 (define-call "s_isblk" boolean never-fails
(mode mode-t
))
642 (define-call "s_isfifo" boolean never-fails
(mode mode-t
))
643 (define-call "s_islnk" boolean never-fails
(mode mode-t
))
644 (define-call "s_issock" boolean never-fails
(mode mode-t
))
648 (export 'pipe
:sb-posix
)
649 (declaim (inline pipe
))
650 (defun pipe (&optional filedes2
)
651 (declare (type (or null
(simple-array (signed-byte 32) (2))) filedes2
))
653 (setq filedes2
(make-array 2 :element-type
'(signed-byte 32))))
654 (let ((r (sb-sys:with-pinned-objects
(filedes2)
656 ;; FIXME: (* INT)? (ARRAY INT 2) would be better
657 (extern-alien "pipe" (function int
(* int
)))
658 (sb-sys:vector-sap filedes2
)))))
660 (syscall-error 'pipe
)))
661 (values (aref filedes2
0) (aref filedes2
1))))
664 (define-protocol-class termios alien-termios
()
665 ((iflag :initarg
:iflag
:accessor sb-posix
:termios-iflag
666 :documentation
"Input modes.")
667 (oflag :initarg
:oflag
:accessor sb-posix
:termios-oflag
668 :documentation
"Output modes.")
669 (cflag :initarg
:cflag
:accessor sb-posix
:termios-cflag
670 :documentation
"Control modes.")
671 (lflag :initarg
:lflag
:accessor sb-posix
:termios-lflag
672 :documentation
"Local modes.")
673 (cc :initarg
:cc
:accessor sb-posix
:termios-cc
:array-length nccs
674 :documentation
"Control characters."))
676 "Instances of this class represent I/O characteristics of the terminal."))
680 (export 'tcsetattr
:sb-posix
)
681 (declaim (inline tcsetattr
))
682 (defun tcsetattr (fd actions termios
)
683 (declare (type termios termios
))
684 (with-alien-termios a-termios
()
685 (termios-to-alien termios a-termios
)
686 (let ((fd (file-descriptor fd
)))
687 (let* ((r (alien-funcall
690 (function int int int
(* alien-termios
)))
691 fd actions a-termios
)))
693 (syscall-error 'tcsetattr
)))
695 (export 'tcgetattr
:sb-posix
)
696 (declaim (inline tcgetattr
))
697 (defun tcgetattr (fd &optional termios
)
698 (declare (type (or null termios
) termios
))
699 (with-alien-termios a-termios
()
700 (let ((r (alien-funcall
701 (extern-alien "tcgetattr"
702 (function int int
(* alien-termios
)))
706 (syscall-error 'tcgetattr
))
707 (setf termios
(alien-to-termios a-termios termios
))))
709 (define-call "tcdrain" int minusp
(fd file-descriptor
))
710 (define-call "tcflow" int minusp
(fd file-descriptor
) (action int
))
711 (define-call "tcflush" int minusp
(fd file-descriptor
) (queue-selector int
))
712 (define-call "tcgetsid" pid-t minusp
(fd file-descriptor
))
713 (define-call "tcsendbreak" int minusp
(fd file-descriptor
) (duration int
))
714 (export 'cfsetispeed
:sb-posix
)
715 (declaim (inline cfsetispeed
))
716 (defun cfsetispeed (speed &optional termios
)
717 (declare (type (or null termios
) termios
))
718 (with-alien-termios a-termios
()
719 (termios-to-alien termios a-termios
)
720 (let ((r (alien-funcall
721 (extern-alien "cfsetispeed"
722 (function int
(* alien-termios
) speed-t
))
726 (syscall-error 'cfsetispeed
))
727 (setf termios
(alien-to-termios a-termios termios
))))
729 (export 'cfsetospeed
:sb-posix
)
730 (declaim (inline cfsetospeed
))
731 (defun cfsetospeed (speed &optional termios
)
732 (declare (type (or null termios
) termios
))
733 (with-alien-termios a-termios
()
734 (termios-to-alien termios a-termios
)
735 (let ((r (alien-funcall
736 (extern-alien "cfsetospeed"
737 (function int
(* alien-termios
) speed-t
))
741 (syscall-error 'cfsetospeed
))
742 (setf termios
(alien-to-termios a-termios termios
))))
744 (export 'cfgetispeed
:sb-posix
)
745 (declaim (inline cfgetispeed
))
746 (defun cfgetispeed (termios)
747 (declare (type termios termios
))
748 (with-alien-termios a-termios
()
749 (termios-to-alien termios a-termios
)
750 (alien-funcall (extern-alien "cfgetispeed"
751 (function speed-t
(* alien-termios
)))
753 (export 'cfgetospeed
:sb-posix
)
754 (declaim (inline cfgetospeed
))
755 (defun cfgetospeed (termios)
756 (declare (type termios termios
))
757 (with-alien-termios a-termios
()
758 (termios-to-alien termios a-termios
)
759 (alien-funcall (extern-alien "cfgetospeed"
760 (function speed-t
(* alien-termios
)))
766 (export 'time
:sb-posix
)
768 (let ((result (alien-funcall (extern-alien "time"
769 (function time-t
(* time-t
)))
772 (syscall-error 'time
)
774 (export 'utime
:sb-posix
)
775 (defun utime (filename &optional access-time modification-time
)
776 (let ((fun (extern-alien #-netbsd
"utime" #+netbsd
"_utime"
777 (function int
(c-string :not-null t
)
779 (name (filename filename
)))
780 (if (not (and access-time modification-time
))
781 (alien-funcall fun name nil
)
782 (with-alien ((utimbuf (struct alien-utimbuf
)))
783 (setf (slot utimbuf
'actime
) (or access-time
0)
784 (slot utimbuf
'modtime
) (or modification-time
0))
785 (let ((result (alien-funcall fun name
(alien-sap utimbuf
))))
787 (syscall-error 'utime
)
789 (export 'utimes
:sb-posix
)
790 (defun utimes (filename &optional access-time modification-time
)
791 (flet ((seconds-and-useconds (time)
792 (multiple-value-bind (integer fractional
)
794 (values integer
(cl:truncate
(* fractional
1000000)))))
795 (maybe-syscall-error (value)
797 (syscall-error 'utimes
)
799 (let ((fun (extern-alien "sb_utimes" (function int
(c-string :not-null t
)
800 (* (array alien-timeval
2)))))
801 (name (filename filename
)))
802 (if (not (and access-time modification-time
))
803 (maybe-syscall-error (alien-funcall fun name nil
))
804 (with-alien ((buf (array alien-timeval
2)))
805 (let ((actime (deref buf
0))
806 (modtime (deref buf
1)))
807 (setf (values (slot actime
'sec
)
809 (seconds-and-useconds (or access-time
0))
810 (values (slot modtime
'sec
)
811 (slot modtime
'usec
))
812 (seconds-and-useconds (or modification-time
0)))
813 (maybe-syscall-error (alien-funcall fun name
814 (alien-sap buf
))))))))))
819 (eval-when (:compile-toplevel
:load-toplevel
)
820 ;; Do this at compile-time as Win32 code below refers to it as
822 (export 'getenv
:sb-posix
))
824 (let ((r (alien-funcall
825 (extern-alien "getenv" (function (* char
) (c-string :not-null t
)))
827 (declare (type (alien (* char
)) r
))
828 (unless (null-alien r
)
832 (define-call "setenv" int minusp
833 (name (c-string :not-null t
))
834 (value (c-string :not-null t
))
836 (define-call "unsetenv" int minusp
(name (c-string :not-null t
)))
837 (export 'putenv
:sb-posix
)
838 (defun putenv (string)
839 (declare (string string
))
840 ;; We don't want to call actual putenv: the string passed to putenv ends
841 ;; up in environ, and we any string we allocate GC might move.
843 ;; This makes our wrapper nonconformant if you squit hard enough, but
844 ;; users who care about that should really be calling putenv() directly in
845 ;; order to be able to manage memory sanely.
846 (let ((p (position #\
= string
))
850 (unsetenv (subseq string
0 p
))
851 (setenv (subseq string
0 p
) (subseq string
(1+ p
)) 1))
852 (error "Invalid argument to putenv: ~S" string
)))))
855 ;; Windows doesn't define a POSIX setenv, but happily their _putenv is sane.
856 (define-call* "putenv" int minusp
(string (c-string :not-null t
)))
857 (export 'setenv
:sb-posix
)
858 (defun setenv (name value overwrite
)
859 (declare (string name value
))
860 (if (and (zerop overwrite
) (sb-posix:getenv name
))
862 (putenv (concatenate 'string name
"=" value
))))
863 (export 'unsetenv
:sb-posix
)
864 (defun unsetenv (name)
865 (declare (string name
))
866 (putenv (concatenate 'string name
"="))))
871 (export 'openlog
:sb-posix
)
872 (export 'syslog
:sb-posix
)
873 (export 'closelog
:sb-posix
)
874 (defun openlog (ident options
&optional
(facility log-user
))
875 (alien-funcall (extern-alien
876 "openlog" (function void
(c-string :not-null t
) int int
))
877 ident options facility
))
878 (defun syslog (priority format
&rest args
)
879 "Send a message to the syslog facility, with severity level
880 PRIORITY. The message will be formatted as by CL:FORMAT (rather
881 than C's printf) with format string FORMAT and arguments ARGS."
882 (flet ((syslog1 (priority message
)
883 (alien-funcall (extern-alien
884 "syslog" (function void int
885 (c-string :not-null t
)
886 (c-string :not-null t
)))
887 priority
"%s" message
)))
888 (syslog1 priority
(apply #'format nil format args
))))
889 (define-call "closelog" void never-fails
))