Newer
Older
;;; -*- Package: UNIX -*-
;;;
;;; **********************************************************************
;;; This code was written as part of the CMU Common Lisp project at
;;; Carnegie Mellon University, and has been placed in the public domain.
;;;
(ext:file-comment
"$Header: src/code/unix.lisp $")
;;;
;;; **********************************************************************
;;;
;;; This file contains the UNIX low-level support, just enough to run
;;; CMUCL.
(intl:textdomain "cmucl-unix")
;; Check the G_BROKEN_FILENAMES environment variable; if set the encoding
;; is locale-dependent...else use :utf-8 on Unicode Lisps. On 8 bit Lisps
;; it must be set to :iso8859-1 (or left as NIL), making files with
;; non-Latin-1 characters "mojibake", but otherwise they'll be inaccessible.
;; Must be set to NIL initially to enable building Lisp!
"The encoding to use for converting a namestring to a string that can
be used by the operations system. It must be a valid
external-format name or :NULL. :NULL means the string
is passed as is to the operating system. The operating system will
get the low 8 bits of each UTF-16 code unit of the string.")
(eval-when (:compile-toplevel :load-toplevel :execute)
,string
(string-encode ,string *filename-encoding*)))
`(if (eql *filename-encoding* :null)
,string
(string-decode ,string *filename-encoding*))))
;;;; Common machine independent structures.
#+linux
(defconstant +max-u-long+ 4294967295)
(def-alien-type int64-t (signed 64))
(def-alien-type uquad-t
#+alpha unsigned-long
#-alpha (array unsigned-long 2))
(def-alien-type u-int32-t unsigned-int)
(def-alien-type ino-t
#+(or netbsd linux darwin) u-int64-t
#+alpha unsigned-int
#-(or alpha netbsd linux darwin) unsigned-long)
(def-alien-type size-t
#-(or linux alpha) long
#+linux unsigned-int
#+alpha unsigned-long)
(def-alien-type time-t
#-(or bsd linux alpha) unsigned-long
#+linux long
#+(and bsd (not netbsd)) long
#+(and bsd netbsd) int64-t
#+alpha unsigned-int)
#-BSD
(progn
(deftype file-offset () '(signed-byte 32))
(def-alien-type off-t
#-(or alpha linux) long
#+linux int64-t
#+alpha unsigned-long)
(def-alien-type uid-t
#-(or alpha svr4 linux) unsigned-short
#+alpha unsigned-int
#+svr4 long)
(def-alien-type gid-t
#-(or alpha svr4 linux) unsigned-short
#+alpha unsigned-int
#+linux unsigned-int
#+svr4 long)
#+linux
(def-alien-type blkcnt-t u-int64-t)
)
#+BSD
(progn
(deftype file-offset () '(signed-byte 64))
(def-alien-type off-t int64-t)
(def-alien-type uid-t unsigned-long)
(def-alien-type gid-t unsigned-long))
(def-alien-type mode-t
#-(or alpha svr4 linux) unsigned-short
#+alpha unsigned-int
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#+svr4 unsigned-long)
;; not checked for linux...
(defmacro fd-clr (offset fd-set)
(let ((word (gensym))
(bit (gensym)))
`(multiple-value-bind (,word ,bit) (floor ,offset 32)
(setf (deref (slot ,fd-set 'fds-bits) ,word)
(logand (deref (slot ,fd-set 'fds-bits) ,word)
(32bit-logical-not
(truly-the (unsigned-byte 32) (ash 1 ,bit))))))))
;; not checked for linux...
(defmacro fd-isset (offset fd-set)
(let ((word (gensym))
(bit (gensym)))
`(multiple-value-bind (,word ,bit) (floor ,offset 32)
(logbitp ,bit (deref (slot ,fd-set 'fds-bits) ,word)))))
(defconstant fd-setsize
#-(or hpux alpha linux FreeBSD) 256
#+hpux 2048 #+alpha 4096 #+(or linux FreeBSD) 1024)
;; not checked for linux...
(def-alien-type nil
(struct fd-set
(fds-bits (array #-alpha unsigned-long #+alpha int #.(/ fd-setsize 32)))))
(def-alien-type nil
(struct timeval
(tv-sec #-linux time-t #+linux int) ; seconds
(tv-usec int))) ; and microseconds
#+(or linux BSD)
(def-alien-type nil
(struct timespec-t
(ts-sec time-t)
(ts-nsec long)))
;;; From ioctl.h
(def-alien-type nil
(struct tchars
(t-intrc char) ; interrupt
(t-quitc char) ; quit
#+linux (t-eofc char)
(t-startc char) ; start output
(t-stopc char) ; stop output
#-linux (t-eofc char) ; end-of-file
(t-brkc char))) ; input delimiter (like nl)
;; not found (semi) linux
(def-alien-type nil
(struct ltchars
#+linux (t-werasc char) ; word erase
(t-suspc char) ; stop process signal
(t-dsuspc char) ; delayed stop process signal
(t-rprntc char) ; reprint line
(t-flushc char) ; flush output (toggles)
#-linux (t-werasc char) ; word erase
(t-lnextc char))) ; literal next character
(def-alien-type nil
(struct sgttyb
#+linux (sg-flags #+mach short #-mach int) ; mode flags
(sg-ispeed char) ; input speed.
(sg-ospeed char) ; output speed
(sg-erase char) ; erase character
#-linux (sg-kill char) ; kill character
#-linux (sg-flags #+mach short #-mach int) ; mode flags
#+linux (sg-kill char)
#+linux (t (struct termios))
#+linux (check int)))
(def-alien-type nil
(struct winsize
(ws-row unsigned-short) ; rows, in characters
(ws-col unsigned-short) ; columns, in characters
(ws-xpixel unsigned-short) ; horizontal size, pixels
(ws-ypixel unsigned-short))) ; veritical size, pixels
(defmacro %syscall ((name (&rest arg-types) result-type)
success-form &rest args)
`(let* ((fn (extern-alien ,name (function ,result-type ,@arg-types)))
(result (alien-funcall fn ,@args)))
(if (eql -1 result)
(values nil (unix-errno))
,success-form)))
(defmacro syscall ((name &rest arg-types) success-form &rest args)
`(%syscall (,name (,@arg-types) int) ,success-form ,@args))
;;; Like syscall, but if it fails, signal an error instead of returing error
;;; codes. Should only be used for syscalls that will never really get an
;;; error.
;;;
(defmacro syscall* ((name &rest arg-types) success-form &rest args)
`(let ((result (alien-funcall (extern-alien ,name (function int ,@arg-types))
,@args)))
(if (eql -1 result)
(error _"Syscall ~A failed: ~A" ,name (get-unix-error-msg))
,success-form)))
(defmacro void-syscall ((name &rest arg-types) &rest args)
`(syscall (,name ,@arg-types) (values t 0) ,@args))
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
(defmacro int-syscall ((name &rest arg-types) &rest args)
`(syscall (,name ,@arg-types) (values result 0) ,@args))
(defmacro off-t-syscall ((name arg-types) &rest args)
`(%syscall (,name ,arg-types off-t) (values result 0) ,@args))
;;; Operations on Unix Directories.
(export '(open-dir read-dir close-dir))
(defstruct (%directory
(:conc-name directory-)
(:constructor make-directory)
(:print-function %print-directory))
name
(dir-struct (required-argument) :type system-area-pointer))
(defun %print-directory (dir stream depth)
(declare (ignore depth))
(format stream "#<Directory ~S>" (directory-name dir)))
(defun open-dir (pathname)
(declare (type unix-pathname pathname))
(when (string= pathname "")
(setf pathname "."))
(let ((kind (unix-file-kind pathname)))
(case kind
(:directory
(let ((dir-struct
(alien-funcall (extern-alien "opendir"
(function system-area-pointer
c-string))
(%name->file pathname))))
(if (zerop (sap-int dir-struct))
(values nil (unix-errno))
(make-directory :name pathname :dir-struct dir-struct))))
((nil)
(values nil enoent))
(t
(values nil enotdir)))))
#-(or solaris (and bsd (not solaris)) linux)
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
(defun read-dir (dir)
(declare (type %directory dir))
(let ((daddr (alien-funcall (extern-alien "readdir"
(function system-area-pointer
system-area-pointer))
(directory-dir-struct dir))))
(declare (type system-area-pointer daddr))
(if (zerop (sap-int daddr))
nil
(with-alien ((direct (* (struct direct)) daddr))
#-(or linux svr4)
(let ((nlen (slot direct 'd-namlen))
(ino (slot direct 'd-ino)))
(declare (type (unsigned-byte 16) nlen))
(let ((string (make-string nlen)))
#-unicode
(kernel:copy-from-system-area
(alien-sap (addr (slot direct 'd-name))) 0
string (* vm:vector-data-offset vm:word-bits)
(* nlen vm:byte-bits))
#+unicode
(let ((sap (alien-sap (addr (slot direct 'd-name)))))
(dotimes (k nlen)
(setf (aref string k)
(code-char (sap-ref-8 sap k)))))
(values (%file->name string) ino)))
#+(or linux svr4)
(values (%file->name (cast (slot direct 'd-name) c-string))
(slot direct 'd-ino))))))
;;; 64-bit readdir for Solaris
#+solaris
(defun read-dir (dir)
(declare (type %directory dir))
(let ((daddr (alien-funcall (extern-alien "readdir64"
(function system-area-pointer
system-area-pointer))
(directory-dir-struct dir))))
(declare (type system-area-pointer daddr))
(if (zerop (sap-int daddr))
nil
(with-alien ((direct (* (struct dirent64)) daddr))
#-(or linux svr4)
(let ((nlen (slot direct 'd-namlen))
(ino (slot direct 'd-ino)))
(declare (type (unsigned-byte 16) nlen))
(let ((string (make-string nlen)))
#-unicode
(kernel:copy-from-system-area
(alien-sap (addr (slot direct 'd-name))) 0
string (* vm:vector-data-offset vm:word-bits)
(* nlen vm:byte-bits))
#+unicode
(let ((sap (alien-sap (addr (slot direct 'd-name)))))
(dotimes (k nlen)
(setf (aref string k)
(code-char (sap-ref-8 sap k)))))
(values (%file->name string) ino)))
#+(or linux svr4)
(values (%file->name (cast (slot direct 'd-name) c-string))
(slot direct 'd-ino))))))
#+(and bsd (not solaris))
(defun read-dir (dir)
(declare (type %directory dir))
(let ((daddr (alien-funcall (extern-alien "readdir"
(function system-area-pointer
system-area-pointer))
(directory-dir-struct dir))))
(declare (type system-area-pointer daddr))
(if (zerop (sap-int daddr))
nil
(with-alien ((direct (* (struct direct)) daddr))
(let ((nlen (slot direct 'd-namlen))
(fino (slot direct 'd-fileno)))
(declare (type (unsigned-byte #+netbsd 16 #-netbsd 8) nlen)
(type (unsigned-byte #+netbsd 64 #-netbsd 32) fino))
(let ((string (make-string nlen)))
#-unicode
(kernel:copy-from-system-area
(alien-sap (addr (slot direct 'd-name))) 0
string (* vm:vector-data-offset vm:word-bits)
(* nlen vm:byte-bits))
#+unicode
(let ((sap (alien-sap (addr (slot direct 'd-name)))))
(dotimes (k nlen)
(setf (aref string k)
(code-char (sap-ref-8 sap k)))))
(values (%file->name string) fino)))))))
#+linux
(defun read-dir (dir)
(declare (type %directory dir))
(let ((daddr (alien-funcall (extern-alien "readdir64"
(function system-area-pointer
system-area-pointer))
(directory-dir-struct dir))))
(declare (type system-area-pointer daddr))
(if (zerop (sap-int daddr))
nil
(with-alien ((dirent (* (struct dirent)) daddr))
(values (%file->name (cast (slot dirent 'd-name) c-string))
(slot dirent 'd-ino))))))
(defun close-dir (dir)
(declare (type %directory dir))
(alien-funcall (extern-alien "closedir"
(function void system-area-pointer))
(directory-dir-struct dir))
nil)
;; Use getcwd instead of getwd. But what should we do if the path
;; won't fit? Try again with a larger size? We don't do that right
;; now.
(defun unix-current-directory ()
;; 5120 is some randomly selected maximum size for the buffer for getcwd.
(with-alien ((buf (array c-call:char 5120)))
(let ((result
(alien-funcall
(extern-alien "getcwd"
(function (* c-call:char)
(* c-call:char) c-call:int))
(cast buf (* c-call:char))
5120)))
(values (not (zerop
(sap-int (alien-sap result))))
(%file->name (cast buf c-call:c-string))))))
;;; Unix-access accepts a path and a mode. It returns two values the
;;; first is T if the file is accessible and NIL otherwise. The second
;;; only has meaning in the second case and is the unix errno value.
(defconstant r_ok 4 _N"Test for read permission")
(defconstant w_ok 2 _N"Test for write permission")
(defconstant x_ok 1 _N"Test for execute permission")
(defconstant f_ok 0 _N"Test for presence of file")
(defun unix-access (path mode)
_N"Given a file path (a string) and one of four constant modes,
unix-access returns T if the file is accessible with that
mode and NIL if not. It also returns an errno value with
NIL which determines why the file was not accessible.
The access modes are:
r_ok Read permission.
w_ok Write permission.
x_ok Execute permission.
f_ok Presence of file."
(declare (type unix-pathname path)
(type (mod 8) mode))
(void-syscall ("access" c-string int) (%name->file path) mode))
;;; Unix-chdir accepts a directory name and makes that the
;;; current working directory.
(defun unix-chdir (path)
_N"Given a file path string, unix-chdir changes the current working
directory to the one specified."
(declare (type unix-pathname path))
(void-syscall ("chdir" c-string) (%name->file path)))
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
;;; Unix-chmod accepts a path and a mode and changes the mode to the new mode.
(defconstant setuidexec #o4000 _N"Set user ID on execution")
(defconstant setgidexec #o2000 _N"Set group ID on execution")
(defconstant savetext #o1000 _N"Save text image after execution")
(defconstant readown #o400 _N"Read by owner")
(defconstant writeown #o200 _N"Write by owner")
(defconstant execown #o100 _N"Execute (search directory) by owner")
(defconstant readgrp #o40 _N"Read by group")
(defconstant writegrp #o20 _N"Write by group")
(defconstant execgrp #o10 _N"Execute (search directory) by group")
(defconstant readoth #o4 _N"Read by others")
(defconstant writeoth #o2 _N"Write by others")
(defconstant execoth #o1 _N"Execute (search directory) by others")
(defun unix-chmod (path mode)
_N"Given a file path string and a constant mode, unix-chmod changes the
permission mode for that file to the one specified. The new mode
can be created by logically OR'ing the following:
setuidexec Set user ID on execution.
setgidexec Set group ID on execution.
savetext Save text image after execution.
readown Read by owner.
writeown Write by owner.
execown Execute (search directory) by owner.
readgrp Read by group.
writegrp Write by group.
execgrp Execute (search directory) by group.
readoth Read by others.
writeoth Write by others.
execoth Execute (search directory) by others.
Thus #o444 and (logior unix:readown unix:readgrp unix:readoth)
are equivalent for 'mode. The octal-base is familar to Unix users.
It returns T on successfully completion; NIL and an error number
otherwise."
(declare (type unix-pathname path)
(type unix-file-mode mode))
(void-syscall ("chmod" c-string int) (%name->file path) mode))
;;; Unix-fchmod accepts a file descriptor ("fd") and a file protection mode
;;; ("mode") and changes the protection of the file described by "fd" to
;;; "mode".
(defun unix-fchmod (fd mode)
_N"Given an integer file descriptor and a mode (the same as those
used for unix-chmod), unix-fchmod changes the permission mode
for that file to the one specified. T is returned if the call
was successful."
(declare (type unix-fd fd)
(type unix-file-mode mode))
(void-syscall ("fchmod" int int) fd mode))
;;; Unix-lseek accepts a file descriptor, an offset, and whence value.
(defconstant l_set 0 _N"set the file pointer")
(defconstant l_incr 1 _N"increment the file pointer")
(defconstant l_xtnd 2 _N"extend the file size")
(defun unix-lseek (fd offset whence)
_N"Unix-lseek accepts a file descriptor and moves the file pointer ahead
a certain offset for that file. Whence can be any of the following:
l_set Set the file pointer.
l_incr Increment the file pointer.
l_xtnd Extend the file size.
_N"
(declare (type unix-fd fd)
(type file-offset offset)
(type (integer 0 2) whence))
(off-t-syscall ("lseek" (int off-t int)) fd offset whence))
#+linux
(defun unix-lseek (fd offset whence)
_N"UNIX-LSEEK accepts a file descriptor and moves the file pointer ahead
a certain OFFSET for that file. WHENCE can be any of the following:
l_set Set the file pointer.
l_incr Increment the file pointer.
l_xtnd Extend the file size.
"
(declare (type unix-fd fd)
(type (signed-byte 64) offset)
(type (integer 0 2) whence))
(let ((result (alien-funcall
(extern-alien "lseek64" (function off-t int off-t int))
fd offset whence)))
(if (minusp result)
(values nil (unix-errno))
(values result 0))))
;;; Unix-mkdir accepts a name and a mode and attempts to create the
;;; corresponding directory with mode mode.
(defun unix-mkdir (name mode)
_N"Unix-mkdir creates a new directory with the specified name and mode.
(Same as those for unix-chmod.) It returns T upon success, otherwise
NIL and an error number."
(declare (type unix-pathname name)
(type unix-file-mode mode))
(void-syscall ("mkdir" c-string int) (%name->file name) mode))
;;; Unix-unlink accepts a name and deletes the directory entry for that
;;; name and the file if this is the last link.
(defun unix-unlink (name)
_N"Unix-unlink removes the directory entry for the named file.
NIL and an error code is returned if the call fails."
(declare (type unix-pathname name))
(void-syscall ("unlink" c-string) (%name->file name)))
;;; Unix-open accepts a pathname (a simple string), flags, and mode and
;;; attempts to open file with name pathname.
(defconstant o_rdonly 0 _N"Read-only flag.")
(defconstant o_wronly 1 _N"Write-only flag.")
(defconstant o_rdwr 2 _N"Read-write flag.")
#+(or hpux linux svr4)
(defconstant o_ndelay
#+linux o_nonblock
#-linux 4
_N"Non-blocking I/O")
(defconstant o_append #-linux #o10 #+linux #o2000 _N"Append flag.")
#+(or hpux svr4 linux)
(progn
(defconstant o_creat #-linux #o400 #+linux #o100 _N"Create if nonexistant flag.")
(defconstant o_trunc #o1000 _N"Truncate flag.")
(defconstant o_excl #-linux #o2000 #+linux #o200 _N"Error if already exists.")
(defconstant o_noctty #+linux #o400 #+hpux #o400000 #+(or irix solaris) #x800
_N"Don't assign controlling tty"))
#+(or hpux linux svr4 BSD)
(defconstant o_nonblock
#+hpux #o200000
#+(or irix solaris) #x80
#+BSD #x04
#+linux #o4000
(defconstant o_ndelay o_nonblock) ; compatibility
#+linux
(defconstant o_sync #o10000 _N"Synchronous writes (on ext2)")
(defconstant o_fsync o_sync)
(defconstant o_async #o20000 _N"Asynchronous I/O"))
(defconstant o_creat #o1000 _N"Create if nonexistant flag.")
(defconstant o_trunc #o2000 _N"Truncate flag.")
(defconstant o_excl #o4000 _N"Error if already exists."))
(defun unix-open (path flags mode)
_N"Unix-open opens the file whose pathname is specified by path
for reading and/or writing as specified by the flags argument.
The flags argument can be:
o_rdonly Read-only flag.
o_wronly Write-only flag.
o_rdwr Read-and-write flag.
o_append Append flag.
o_creat Create-if-nonexistant flag.
o_trunc Truncate-to-size-0 flag.
If the o_creat flag is specified, then the file is created with
a permission of argument mode if the file doesn't exist. An
integer file descriptor is returned by unix-open."
(declare (type unix-pathname path)
(type fixnum flags)
(type unix-file-mode mode))
(int-syscall (#+(or linux solaris) "open64" #-(or linux solaris) "open"
c-string int int)
;;; Unix-close accepts a file descriptor and attempts to close the file
;;; associated with it.
(defun unix-close (fd)
_N"Unix-close takes an integer file descriptor as an argument and
closes the file associated with it. T is returned upon successful
completion, otherwise NIL and an error number."
(declare (type unix-fd fd))
(void-syscall ("close" int) fd))
;;; Unix-creat accepts a file name and a mode. It creates a new file
;;; with name and sets it mode to mode (as for chmod).
(defun unix-creat (name mode)
_N"Unix-creat accepts a file name and a mode (same as those for
unix-chmod) and creates a file by that name with the specified
permission mode. It returns a file descriptor on success,
or NIL and an error number otherwise.
This interface is made obsolete by UNIX-OPEN."
(declare (type unix-pathname name)
(type unix-file-mode mode))
(int-syscall (#+(or linux solaris) "creat64" #-(or linux solaris) "creat" c-string int)
;;; Unix-read accepts a file descriptor, a buffer, and the length to read.
;;; It attempts to read len bytes from the device associated with fd
;;; and store them into the buffer. It returns the actual number of
;;; bytes read.
;;; Unix-dup returns a duplicate copy of the existing file-descriptor
;;; passed as an argument.
(defun unix-dup (fd)
_N"Unix-dup duplicates an existing file descriptor (given as the
argument) and return it. If FD is not a valid file descriptor, NIL
and an error number are returned."
(declare (type unix-fd fd))
(int-syscall ("dup" int) fd))
;;; Unix-dup2 makes the second file-descriptor describe the same file
;;; as the first. If the second file-descriptor points to an open
;;; file, it is first closed. In any case, the second should have a
;;; value which is a valid file-descriptor.
(defun unix-dup2 (fd1 fd2)
_N"Unix-dup2 duplicates an existing file descriptor just as unix-dup
does only the new value of the duplicate descriptor may be requested
through the second argument. If a file already exists with the
requested descriptor number, it will be closed and the number
assigned to the duplicate."
(declare (type unix-fd fd1 fd2))
(void-syscall ("dup2" int int) fd1 fd2))
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
;;; Unix-fcntl takes a file descriptor, an integer command
;;; number, and optional command arguments. It performs
;;; operations on the associated file and/or returns inform-
;;; ation about the file.
;;; Operations performed on file descriptors:
(defconstant F-DUPFD 0 _N"Duplicate a file descriptor")
(defconstant F-GETFD 1 _N"Get file desc. flags")
(defconstant F-SETFD 2 _N"Set file desc. flags")
(defconstant F-GETFL 3 _N"Get file flags")
(defconstant F-SETFL 4 _N"Set file flags")
#-(or linux svr4)
(defconstant F-GETOWN 5 _N"Get owner")
#+svr4
(defconstant F-GETOWN 23 _N"Get owner")
#+linux
(defconstant F-GETLK 5 _N"Get lock")
#-(or linux svr4)
(defconstant F-SETOWN 6 _N"Set owner")
#+svr4
(defconstant F-SETOWN 24 _N"Set owner")
#+linux
(defconstant F-SETLK 6 _N"Set lock")
#+linux
(defconstant F-SETLKW 7 _N"Set lock, wait for release")
#+linux
(defconstant F-SETOWN 8 _N"Set owner")
;;; File flags for F-GETFL and F-SETFL:
(defconstant FNDELAY
#+linux o_ndelay
#+osf1 #o100000
#-(or linux osf1) #o0004
_N"Non-blocking reads")
(defconstant FAPPEND #-linux #o0010 #+linux o_append _N"Append on each write")
(defconstant FASYNC #-(or linux svr4) #o0100 #+svr4 #o10000 #+linux o_async
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
_N"Signal pgrp when data ready")
;; doesn't exist in Linux ;-(
#-linux (defconstant FCREAT #-(or hpux svr4) #o1000 #+(or hpux svr4) #o0400
_N"Create if nonexistant")
#-linux (defconstant FTRUNC #-(or hpux svr4) #o2000 #+(or hpux svr4) #o1000
_N"Truncate to zero length")
#-linux (defconstant FEXCL #-(or hpux svr4) #o4000 #+(or hpux svr4) #o2000
_N"Error if already created")
(defun unix-fcntl (fd cmd arg)
_N"Unix-fcntl manipulates file descriptors according to the
argument CMD which can be one of the following:
F-DUPFD Duplicate a file descriptor.
F-GETFD Get file descriptor flags.
F-SETFD Set file descriptor flags.
F-GETFL Get file flags.
F-SETFL Set file flags.
F-GETOWN Get owner.
F-SETOWN Set owner.
The flags that can be specified for F-SETFL are:
FNDELAY Non-blocking reads.
FAPPEND Append on each write.
FASYNC Signal pgrp when data ready.
FCREAT Create if nonexistant.
FTRUNC Truncate to zero length.
FEXCL Error if already created.
"
(declare (type unix-fd fd)
(type (unsigned-byte 32) cmd)
(type (unsigned-byte 32) arg))
(int-syscall ("fcntl" int unsigned-int unsigned-int) fd cmd arg))
(defun unix-pipe ()
_N"Unix-pipe sets up a unix-piping mechanism consisting of
an input pipe and an output pipe. Unix-Pipe returns two
values: if no error occurred the first value is the pipe
to be read from and the second is can be written to. If
an error occurred the first value is NIL and the second
the unix error code."
(with-alien ((fds (array int 2)))
(syscall ("pipe" (* int))
(values (deref fds 0) (deref fds 1))
(cast fds (* int)))))
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
(defun unix-read (fd buf len)
_N"Unix-read attempts to read from the file described by fd into
the buffer buf until it is full. Len is the length of the buffer.
The number of bytes actually read is returned or NIL and an error
number if an error occured."
(declare (type unix-fd fd)
(type (unsigned-byte 32) len))
#+(or sunos gencgc)
;; Note: Under sunos we touch each page before doing the read to give
;; the segv handler a chance to fix the permissions. Otherwise,
;; read will return EFAULT. This also bypasses a bug in 4.1.1 in which
;; read fails with EFAULT if the page has never been touched even if
;; the permissions are okay.
;;
;; (Is this true for Solaris?)
;;
;; Also, with gencgc, the collector tries to keep raw objects like
;; strings in separate pages that are not write-protected. However,
;; this isn't always true. Thus, BUF will sometimes be
;; write-protected and the kernel doesn't like writing to
;; write-protected pages. So go through and touch each page to give
;; the segv handler a chance to unprotect the pages.
(without-gcing
(let* ((page-size (get-page-size))
(1-page-size (1- page-size))
(sap (etypecase buf
(system-area-pointer buf)
(vector (vector-sap buf))))
(end (sap+ sap len)))
(declare (type (and fixnum unsigned-byte) page-size 1-page-size)
(type system-area-pointer sap end)
(optimize (speed 3) (safety 0)))
;; Touch the beginning of every page
(do ((sap (int-sap (logand (sap-int sap)
(logxor 1-page-size (ldb (byte 32 0) -1))))
(sap+ sap page-size)))
((sap>= sap end))
(declare (type system-area-pointer sap))
(setf (sap-ref-8 sap 0) (sap-ref-8 sap 0)))))
(int-syscall ("read" int (* char) int) fd buf len))
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
#+linux
(defun unix-read (fd buf len)
_N"UNIX-READ attempts to read from the file described by fd into
the buffer buf until it is full. Len is the length of the buffer.
The number of bytes actually read is returned or NIL and an error
number if an error occured."
(declare (type unix-fd fd)
(type (unsigned-byte 32) len))
#+gencgc
;; With gencgc, the collector tries to keep raw objects like strings
;; in separate pages that are not write-protected. However, this
;; isn't always true. Thus, BUF will sometimes be write-protected
;; and the kernel doesn't like writing to write-protected pages. So
;; go through and touch each page to give the segv handler a chance
;; to unprotect the pages. (This is taken from unix.lisp.)
(without-gcing
(let* ((page-size (get-page-size))
(1-page-size (1- page-size))
(sap (etypecase buf
(system-area-pointer buf)
(vector (vector-sap buf))))
(end (sap+ sap len)))
(declare (type (and fixnum unsigned-byte) page-size 1-page-size)
(type system-area-pointer sap end)
(optimize (speed 3) (safety 0)))
;; Touch the beginning of every page
(do ((sap (int-sap (logand (sap-int sap)
(logxor 1-page-size (ldb (byte 32 0) -1))))
(sap+ sap page-size)))
((sap>= sap end))
(declare (type system-area-pointer sap))
(setf (sap-ref-8 sap 0) (sap-ref-8 sap 0)))))
(int-syscall ("read" int (* char) int) fd buf len))
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
(defun unix-readlink (path)
_N"Unix-readlink invokes the readlink system call on the file name
specified by the simple string path. It returns up to two values:
the contents of the symbolic link if the call is successful, or
NIL and the Unix error number."
(declare (type unix-pathname path))
(with-alien ((buf (array char 1024)))
(syscall ("readlink" c-string (* char) int)
(let ((string (make-string result)))
#-unicode
(kernel:copy-from-system-area
(alien-sap buf) 0
string (* vm:vector-data-offset vm:word-bits)
(* result vm:byte-bits))
#+unicode
(let ((sap (alien-sap buf)))
(dotimes (k result)
(setf (aref string k)
(code-char (sap-ref-8 sap k)))))
(%file->name string))
(%name->file path) (cast buf (* char)) 1024)))
;;; Unix-rename accepts two files names and renames the first to the second.
(defun unix-rename (name1 name2)
_N"Unix-rename renames the file with string name1 to the string
name2. NIL and an error code is returned if an error occured."
(declare (type unix-pathname name1 name2))
(void-syscall ("rename" c-string c-string)
(%name->file name1) (%name->file name2)))
;;; Unix-rmdir accepts a name and removes the associated directory.
(defun unix-rmdir (name)
_N"Unix-rmdir attempts to remove the directory name. NIL and
an error number is returned if an error occured."
(declare (type unix-pathname name))
(void-syscall ("rmdir" c-string) (%name->file name)))
;;; Unix-write accepts a file descriptor, a buffer, an offset, and the
;;; length to write. It attempts to write len bytes to the device
;;; associated with fd from the buffer starting at offset. It returns
;;; the actual number of bytes written.
(defun unix-write (fd buf offset len)
_N"Unix-write attempts to write a character buffer (buf) of length
len to the file described by the file descriptor fd. NIL and an
error is returned if the call is unsuccessful."
(declare (type unix-fd fd)
(type (unsigned-byte 32) offset len))
(int-syscall ("write" int (* char) int)
fd
(with-alien ((ptr (* char) (etypecase buf
((simple-array * (*))
(vector-sap buf))
(system-area-pointer
buf))))
(addr (deref ptr offset)))
len))
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
;;; Unix-ioctl is used to change parameters of devices in a device
;;; dependent way.
(defconstant terminal-speeds
'#(0 50 75 110 134 150 200 300 600 #+hpux 900 1200 1800 2400 #+hpux 3600
4800 #+hpux 7200 9600 19200 38400 57600 115200 230400
#+hpux 460800))
;;; from /usr/include/bsd/sgtty.h (linux)
(defconstant tty-raw #-linux #o40 #+linux 1)
(defconstant tty-crmod #-linux #o20 #+linux 4)
#-(or hpux svr4 bsd linux) (defconstant tty-echo #o10) ;; 8
(defconstant tty-lcase #-linux #o4 #+linux 2)
#-hpux
(defconstant tty-cbreak #-linux #o2 #+linux 64)
#-(or linux hpux)
(defconstant tty-tandem #o1)
#+(or hpux svr4 bsd linux)
(progn
(defmacro def-enum (inc cur &rest names)
(flet ((defform (name)
(prog1 (when name `(defconstant ,name ,cur))
(setf cur (funcall inc cur 1)))))
`(progn ,@(mapcar #'defform names))))
;; Input modes. Linux: /usr/include/asm/termbits.h
(def-enum ash 1 tty-ignbrk tty-brkint tty-ignpar tty-parmrk tty-inpck
tty-istrip tty-inlcr tty-igncr tty-icrnl #-bsd tty-iuclc
tty-ixon #-bsd tty-ixany tty-ixoff #+bsd tty-ixany
#+hpux tty-ienqak #+bsd nil tty-imaxbel)
;; output modes
#-bsd (def-enum ash 1 tty-opost tty-olcuc tty-onlcr tty-ocrnl tty-onocr
tty-onlret tty-ofill tty-ofdel #+linux tty-nldly)
#+bsd (def-enum ash 1 tty-opost tty-onlcr)
;; local modes
#-(or bsd linux) (def-enum ash 1 tty-isig tty-icanon tty-xcase tty-echo tty-echoe
tty-echok tty-echonl tty-noflsh #+irix tty-iexten
#+(or sunos linux) tty-tostop tty-echoctl tty-echoprt
tty-echoke #+(or sunos svr4) tty-defecho tty-flusho
#+linux nil tty-pendin #+irix tty-tostop
#+(or sunos linux) tty-iexten)
#+linux
(def-enum ash 1 tty-isig tty-icanon tty-xcase tty-echo tty-echoe
tty-echok tty-echonl tty-noflsh
tty-tostop tty-echoctl tty-echoprt
tty-echoke tty-flusho
tty-pendin tty-iexten)
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#+bsd (def-enum ash 1 tty-echoke tty-echoe tty-echok tty-echo tty-echonl
tty-echoprt tty-echoctl tty-isig tty-icanon nil
tty-iexten)
#+bsd (defconstant tty-tostop #x00400000)
#+bsd (defconstant tty-flusho #x00800000)
#+bsd (defconstant tty-pendin #x20000000)
#+bsd (defconstant tty-noflsh #x80000000)
#+hpux (defconstant tty-tostop #o10000000000)
#+hpux (defconstant tty-iexten #o20000000000)
;; control modes
(def-enum ash #-bsd #o100 #+bsd #x400 #+hpux nil tty-cstopb
tty-cread tty-parenb tty-parodd tty-hupcl tty-clocal
#+svr4 rcv1en #+svr4 xmt1en #+(or hpux svr4) tty-loblk)
;; special control characters
#+(or hpux svr4 linux) (def-enum + 0 vintr vquit verase vkill veof
#-linux veol #-linux veol2)
#+bsd (def-enum + 0 veof veol veol2 verase nil vkill nil nil vintr vquit)
#+linux (defconstant veol 11)
#+linux (defconstant veol2 16)
(defconstant tciflush 0)
(defconstant tcoflush 1)
(defconstant tcioflush 2))
#+bsd
(progn
(defconstant vmin 16)
(defconstant vtime 17)
(defconstant vsusp 10)
(defconstant vstart 12)
(defconstant vstop 13)
(defconstant vdsusp 11))
#+hpux
(progn
(defconstant vmin 11)
(defconstant vtime 12)
(defconstant vsusp 13)
(defconstant vstart 14)
(defconstant vstop 15)
(defconstant vdsusp 21))
#+(or hpux bsd linux)
(progn
(defconstant tcsanow 0)
(defconstant tcsadrain 1)
(defconstant tcsaflush 2))
#+(or linux svr4)
(progn
#-linux (defconstant vdsusp 11)
(defconstant vstart 8)
(defconstant vstop 9)
(defconstant vsusp 10)
(defconstant vmin #-linux 4 #+linux 6)
(defconstant vtime 5))
#+(or sunos svr4)
(progn
;; control modes
(defconstant tty-cbaud #o17)
(defconstant tty-csize #o60)
(defconstant tty-cs5 #o0)
(defconstant tty-cs6 #o20)
(defconstant tty-cs7 #o40)
(defconstant tty-cs8 #o60))
#+bsd
(progn
;; control modes
(defconstant tty-csize #x300)
(defconstant tty-cs5 #x000)