2006-04-29 17:23:16 -04:00
|
|
|
! Copyright (C) 2004, 2006 Slava Pestov.
|
|
|
|
! See http://factorcode.org/license.txt for BSD license.
|
2005-03-29 19:58:22 -05:00
|
|
|
IN: io-internals
|
2005-12-08 18:14:49 -05:00
|
|
|
USING: alien arrays errors generic hashtables io kernel
|
2006-06-17 02:29:46 -04:00
|
|
|
kernel-internals math parser sequences strings threads
|
|
|
|
unix-internals vectors words ;
|
2005-04-17 18:34:09 -04:00
|
|
|
|
2005-04-22 20:09:46 -04:00
|
|
|
! We want namespaces::bind to shadow the bind system call from
|
2005-04-17 18:34:09 -04:00
|
|
|
! unix-internals
|
|
|
|
USING: namespaces ;
|
2005-03-29 19:58:22 -05:00
|
|
|
|
2005-06-13 01:42:16 -04:00
|
|
|
! This will go elsewhere soon
|
|
|
|
: byte-bit ( n alien -- byte bit )
|
2005-06-15 16:34:16 -04:00
|
|
|
over -5 shift alien-unsigned-4 swap 31 bitand ;
|
2005-06-13 01:42:16 -04:00
|
|
|
|
|
|
|
: bit-nth ( n alien -- ? )
|
|
|
|
byte-bit 1 swap shift bitand 0 > ;
|
|
|
|
|
|
|
|
: set-bit ( ? byte bit -- byte )
|
2005-09-24 15:21:17 -04:00
|
|
|
1 swap shift rot [ bitor ] [ bitnot bitand ] if ;
|
2005-06-13 01:42:16 -04:00
|
|
|
|
|
|
|
: set-bit-nth ( ? n alien -- )
|
|
|
|
[ byte-bit set-bit ] 2keep
|
2005-06-15 16:34:16 -04:00
|
|
|
swap -5 shift set-alien-unsigned-4 ;
|
2005-06-13 01:42:16 -04:00
|
|
|
|
2005-06-18 21:15:07 -04:00
|
|
|
: clear-bits ( alien len -- )
|
2005-12-25 17:46:21 -05:00
|
|
|
[ 0 -rot set-alien-unsigned-1 ] each-with ;
|
2005-06-18 21:15:07 -04:00
|
|
|
|
2005-06-13 01:42:16 -04:00
|
|
|
! Global variables
|
|
|
|
SYMBOL: read-fdset
|
|
|
|
SYMBOL: read-tasks
|
|
|
|
SYMBOL: write-fdset
|
|
|
|
SYMBOL: write-tasks
|
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
! Some general stuff
|
|
|
|
: file-mode OCT: 0600 ;
|
|
|
|
|
2005-06-13 01:42:16 -04:00
|
|
|
: (io-error) err_no strerror throw ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
2006-01-28 15:49:31 -05:00
|
|
|
: check-null ( n -- ) zero? [ (io-error) ] when ;
|
2005-06-09 21:00:00 -04:00
|
|
|
|
|
|
|
: io-error ( n -- ) 0 < [ (io-error) ] when ;
|
|
|
|
|
2006-07-19 17:15:13 -04:00
|
|
|
: init-handle ( fd -- )
|
|
|
|
#! We drop the error code rather than calling io-error,
|
|
|
|
#! since on OS X 10.3, this operation fails from init-io
|
|
|
|
#! when running the Factor.app (presumably because fd 0 and
|
|
|
|
#! 1 are closed).
|
|
|
|
F_SETFL O_NONBLOCK fcntl drop ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
! Common delegate of native stream readers and writers
|
2005-09-18 23:22:58 -04:00
|
|
|
SYMBOL: input
|
|
|
|
SYMBOL: output
|
|
|
|
SYMBOL: closed
|
|
|
|
|
|
|
|
TUPLE: port handle error timeout cutoff type sbuf eof? ;
|
|
|
|
|
2006-08-05 19:01:59 -04:00
|
|
|
PREDICATE: port input-port port-type input eq? ;
|
|
|
|
PREDICATE: port output-port port-type output eq? ;
|
2005-05-23 20:56:38 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
C: port ( handle buffer -- port )
|
2006-02-24 22:40:36 -05:00
|
|
|
[ set-delegate ] keep
|
|
|
|
[ >r dup init-handle r> set-port-handle ] keep
|
2005-05-23 20:56:38 -04:00
|
|
|
[ 0 swap set-port-timeout ] keep
|
|
|
|
[ 0 swap set-port-cutoff ] keep
|
2005-07-20 18:33:32 -04:00
|
|
|
80 <sbuf> over set-port-sbuf ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
2005-05-23 20:56:38 -04:00
|
|
|
: touch-port ( port -- )
|
2006-04-27 03:20:02 -04:00
|
|
|
dup port-timeout dup zero?
|
2005-09-24 15:21:17 -04:00
|
|
|
[ 2drop ] [ millis + swap set-port-cutoff ] if ;
|
2005-05-23 20:56:38 -04:00
|
|
|
|
|
|
|
M: port set-timeout ( timeout port -- )
|
|
|
|
[ set-port-timeout ] keep touch-port ;
|
|
|
|
|
2006-08-05 19:01:59 -04:00
|
|
|
: buffered-port 32768 <buffer> <port> ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
: >port< dup port-handle swap delegate ;
|
|
|
|
|
2005-06-18 21:15:07 -04:00
|
|
|
: pending-error ( port -- )
|
2005-09-17 22:25:18 -04:00
|
|
|
dup port-error f rot set-port-error [ throw ] when* ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
2005-07-22 22:18:47 -04:00
|
|
|
: report-error ( error port -- )
|
2005-08-31 21:06:13 -04:00
|
|
|
[ "Error on fd " % dup port-handle # ": " % swap % ] "" make
|
|
|
|
swap set-port-error ;
|
2005-07-22 22:18:47 -04:00
|
|
|
|
2005-06-18 21:15:07 -04:00
|
|
|
: defer-error ( port -- ? )
|
|
|
|
#! Return t if it is an unrecoverable error.
|
2005-07-22 22:18:47 -04:00
|
|
|
err_no dup EAGAIN = over EINTR = or
|
2005-09-24 15:21:17 -04:00
|
|
|
[ 2drop f ] [ strerror swap report-error t ] if ;
|
2005-04-15 22:28:37 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
! Associates a port with a list of continuations waiting on the
|
|
|
|
! port to finish I/O
|
2005-04-14 01:32:06 -04:00
|
|
|
TUPLE: io-task port callbacks ;
|
2006-02-04 02:19:45 -05:00
|
|
|
C: io-task ( port -- )
|
|
|
|
[ set-io-task-port ] keep
|
2006-06-17 02:29:46 -04:00
|
|
|
V{ } clone over set-io-task-callbacks ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
! Multiplexer
|
2005-04-12 18:31:50 -04:00
|
|
|
GENERIC: do-io-task ( task -- ? )
|
2005-06-13 01:42:16 -04:00
|
|
|
GENERIC: task-container ( task -- vector )
|
2005-05-23 20:56:38 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: io-task-fd io-task-port port-handle ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: add-io-task ( callback task -- )
|
2006-06-17 02:29:46 -04:00
|
|
|
[ io-task-callbacks push ] keep
|
2005-06-13 01:42:16 -04:00
|
|
|
dup io-task-fd over task-container 2dup hash [
|
2006-05-09 21:37:07 -04:00
|
|
|
"Cannot perform multiple reads from the same port" throw
|
2005-04-14 19:37:13 -04:00
|
|
|
] when set-hash ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: remove-io-task ( task -- )
|
2005-06-13 01:42:16 -04:00
|
|
|
dup io-task-fd swap task-container remove-hash ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2006-06-17 02:29:46 -04:00
|
|
|
: pop-callbacks ( task -- )
|
|
|
|
dup io-task-callbacks swap remove-io-task
|
|
|
|
[ schedule-thread ] each ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-06-13 01:42:16 -04:00
|
|
|
: handle-fd ( task -- )
|
2006-06-17 02:29:46 -04:00
|
|
|
dup io-task-port touch-port
|
|
|
|
dup do-io-task [ pop-callbacks ] [ drop ] if ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-05-23 20:56:38 -04:00
|
|
|
: timeout? ( port -- ? )
|
2006-01-28 15:49:31 -05:00
|
|
|
port-cutoff dup zero? not swap millis < and ;
|
2005-05-23 20:56:38 -04:00
|
|
|
|
2005-06-13 01:42:16 -04:00
|
|
|
: handle-fdset ( fdset tasks -- )
|
2005-04-14 19:37:13 -04:00
|
|
|
[
|
2005-11-27 17:45:48 -05:00
|
|
|
nip dup io-task-port timeout? [
|
2005-07-22 22:18:47 -04:00
|
|
|
dup io-task-port "Timeout" swap report-error
|
2006-06-17 02:29:46 -04:00
|
|
|
nip pop-callbacks
|
2005-07-22 22:18:47 -04:00
|
|
|
] [
|
|
|
|
tuck io-task-fd swap bit-nth
|
2005-09-24 15:21:17 -04:00
|
|
|
[ handle-fd ] [ drop ] if
|
|
|
|
] if
|
2005-06-13 01:42:16 -04:00
|
|
|
] hash-each-with ;
|
2005-05-23 20:56:38 -04:00
|
|
|
|
2005-06-13 01:42:16 -04:00
|
|
|
: init-fdset ( fdset tasks -- )
|
2006-05-11 01:46:32 -04:00
|
|
|
>r dup dup FD_SETSIZE clear-bits r>
|
2005-11-27 17:45:48 -05:00
|
|
|
[ drop t swap rot set-bit-nth ] hash-each-with ;
|
2005-05-23 20:56:38 -04:00
|
|
|
|
2006-05-11 01:46:32 -04:00
|
|
|
: read-fdset/tasks
|
|
|
|
read-fdset get-global read-tasks get-global ;
|
|
|
|
|
|
|
|
: write-fdset/tasks
|
|
|
|
write-fdset get-global write-tasks get-global ;
|
|
|
|
|
2005-06-13 01:42:16 -04:00
|
|
|
: init-fdsets ( -- read write except )
|
2006-05-11 01:46:32 -04:00
|
|
|
read-fdset/tasks init-fdset
|
|
|
|
write-fdset/tasks init-fdset f ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
2005-04-30 17:17:10 -04:00
|
|
|
: io-multiplex ( timeout -- )
|
2006-04-27 03:20:02 -04:00
|
|
|
>r FD_SETSIZE init-fdsets r> make-timeval select io-error
|
2006-05-11 01:46:32 -04:00
|
|
|
read-fdset/tasks handle-fdset
|
|
|
|
write-fdset/tasks handle-fdset ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
! Readers
|
|
|
|
|
2005-06-19 17:50:35 -04:00
|
|
|
: <reader> ( fd -- stream )
|
2005-09-18 23:22:58 -04:00
|
|
|
buffered-port input over set-port-type <line-reader> ;
|
2005-06-19 17:50:35 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: open-read ( path -- fd )
|
2005-04-17 18:34:09 -04:00
|
|
|
O_RDONLY file-mode open dup io-error ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-03 16:55:56 -04:00
|
|
|
: reader-eof ( reader -- )
|
2005-09-24 15:21:17 -04:00
|
|
|
dup port-sbuf empty? [ t swap set-port-eof? ] [ drop ] if ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-06-18 16:42:49 -04:00
|
|
|
: (refill) ( port -- n )
|
2005-06-18 21:15:07 -04:00
|
|
|
>port< dup buffer-end swap buffer-capacity read ;
|
2005-06-18 16:42:49 -04:00
|
|
|
|
2005-06-18 21:15:07 -04:00
|
|
|
: refill ( port -- ? )
|
|
|
|
#! Return f if there is a recoverable error
|
2006-08-05 20:14:14 -04:00
|
|
|
dup buffer-empty? [
|
2005-06-18 21:15:07 -04:00
|
|
|
dup (refill) dup 0 >= [
|
|
|
|
swap n>buffer t
|
|
|
|
] [
|
|
|
|
drop defer-error
|
2005-09-24 15:21:17 -04:00
|
|
|
] if
|
2005-04-27 01:40:09 -04:00
|
|
|
] [
|
2005-06-18 21:15:07 -04:00
|
|
|
drop t
|
2005-09-24 15:21:17 -04:00
|
|
|
] if ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2006-08-05 20:14:14 -04:00
|
|
|
! Reading a single character
|
|
|
|
TUPLE: read1-task ;
|
|
|
|
|
|
|
|
C: read1-task ( port -- task )
|
|
|
|
[ >r <io-task> r> set-delegate ] keep ;
|
|
|
|
|
|
|
|
M: read1-task do-io-task ( task -- ? )
|
|
|
|
io-task-port dup refill [
|
|
|
|
[
|
|
|
|
dup buffer-empty?
|
|
|
|
[ t over set-port-eof? ] when
|
|
|
|
] when drop
|
|
|
|
] keep ;
|
|
|
|
|
|
|
|
M: read1-task task-container drop read-tasks get-global ;
|
|
|
|
|
|
|
|
: wait-to-read1 ( port -- )
|
|
|
|
dup buffer-empty? [
|
|
|
|
[ swap <read1-task> add-io-task stop ] callcc0
|
|
|
|
] when pending-error ;
|
|
|
|
|
|
|
|
M: input-port stream-read1 ( stream -- char/f )
|
|
|
|
dup wait-to-read1 dup port-eof? [ drop f ] [ buffer-pop ] if ;
|
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
! Reading character counts
|
2005-06-19 00:23:01 -04:00
|
|
|
: read-step ( count reader -- ? )
|
2005-06-19 17:50:35 -04:00
|
|
|
dup port-sbuf -rot >r over length - ( remaining) r>
|
2005-04-27 01:40:09 -04:00
|
|
|
2dup buffer-length <= [
|
2005-04-25 03:33:33 -04:00
|
|
|
buffer> nappend t
|
2005-04-03 18:28:55 -04:00
|
|
|
] [
|
2005-04-25 03:33:33 -04:00
|
|
|
buffer>> nip nappend f
|
2005-09-24 15:21:17 -04:00
|
|
|
] if ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
|
|
|
: can-read-count? ( count reader -- ? )
|
2006-08-05 20:14:14 -04:00
|
|
|
0 over port-sbuf set-length read-step ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-04-12 18:31:50 -04:00
|
|
|
TUPLE: read-task count ;
|
|
|
|
|
2005-04-25 03:33:33 -04:00
|
|
|
C: read-task ( count port -- task )
|
|
|
|
[ >r <io-task> r> set-delegate ] keep
|
|
|
|
[ set-read-task-count ] keep ;
|
2005-04-12 18:31:50 -04:00
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
: >read-task< dup read-task-count swap io-task-port ;
|
|
|
|
|
2005-04-17 18:34:09 -04:00
|
|
|
M: read-task do-io-task ( task -- ? )
|
2005-06-18 21:15:07 -04:00
|
|
|
>read-task< dup refill [
|
2005-07-20 18:33:32 -04:00
|
|
|
dup buffer-empty? [
|
2005-06-19 00:23:01 -04:00
|
|
|
reader-eof drop t
|
2005-06-18 21:15:07 -04:00
|
|
|
] [
|
|
|
|
read-step
|
2005-09-24 15:21:17 -04:00
|
|
|
] if
|
2005-04-12 18:31:50 -04:00
|
|
|
] [
|
2005-06-18 21:15:07 -04:00
|
|
|
2drop f
|
2005-09-24 15:21:17 -04:00
|
|
|
] if ;
|
2005-04-12 18:31:50 -04:00
|
|
|
|
2006-04-27 21:36:29 -04:00
|
|
|
M: read-task task-container drop read-tasks get-global ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: wait-to-read ( count port -- )
|
|
|
|
2dup can-read-count? [
|
2005-09-18 01:37:28 -04:00
|
|
|
[ -rot <read-task> add-io-task stop ] callcc0
|
2006-05-11 01:46:32 -04:00
|
|
|
] unless pending-error drop ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
2006-08-05 19:01:59 -04:00
|
|
|
M: input-port stream-read ( count stream -- string )
|
2005-07-20 18:33:32 -04:00
|
|
|
[ wait-to-read ] keep dup port-eof?
|
2005-09-24 15:21:17 -04:00
|
|
|
[ drop f ] [ port-sbuf >string ] if ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
! Writers
|
|
|
|
|
|
|
|
: open-write ( path -- fd )
|
2005-04-17 18:34:09 -04:00
|
|
|
O_WRONLY O_CREAT bitor O_TRUNC bitor file-mode open
|
2005-04-14 19:37:13 -04:00
|
|
|
dup io-error ;
|
|
|
|
|
2005-06-19 17:50:35 -04:00
|
|
|
: <writer> ( fd -- writer )
|
2005-12-16 21:12:35 -05:00
|
|
|
buffered-port output over set-port-type <plain-writer> ;
|
2005-04-07 20:02:59 -04:00
|
|
|
|
2005-06-18 21:15:07 -04:00
|
|
|
: write-step ( port -- )
|
|
|
|
dup >port< dup buffer@ swap buffer-length write dup 0 >= [
|
2005-04-15 22:28:37 -04:00
|
|
|
swap buffer-consume
|
|
|
|
] [
|
2005-06-18 21:15:07 -04:00
|
|
|
drop defer-error drop
|
2005-09-24 15:21:17 -04:00
|
|
|
] if ;
|
2005-04-03 19:02:50 -04:00
|
|
|
|
2005-04-07 20:02:59 -04:00
|
|
|
: can-write? ( len writer -- ? )
|
|
|
|
#! If the buffer is empty and the string is too long,
|
|
|
|
#! extend the buffer.
|
2005-07-20 18:33:32 -04:00
|
|
|
dup buffer-empty? [
|
2005-05-04 01:14:45 -04:00
|
|
|
2drop t
|
2005-04-07 20:02:59 -04:00
|
|
|
] [
|
|
|
|
[ buffer-fill + ] keep buffer-capacity <=
|
2005-09-24 15:21:17 -04:00
|
|
|
] if ;
|
2005-04-03 19:02:50 -04:00
|
|
|
|
2005-04-12 18:31:50 -04:00
|
|
|
TUPLE: write-task ;
|
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
C: write-task ( port -- task )
|
2005-04-12 18:31:50 -04:00
|
|
|
[ >r <io-task> r> set-delegate ] keep ;
|
|
|
|
|
|
|
|
M: write-task do-io-task
|
2006-04-28 00:03:48 -04:00
|
|
|
io-task-port dup buffer-length zero? over port-error or
|
|
|
|
[ 0 swap buffer-reset t ] [ write-step f ] if ;
|
2005-04-12 18:31:50 -04:00
|
|
|
|
2006-04-27 21:36:29 -04:00
|
|
|
M: write-task task-container drop write-tasks get-global ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: add-write-io-task ( callback task -- )
|
2006-04-29 17:22:42 -04:00
|
|
|
dup io-task-fd write-tasks get-global hash
|
2006-06-17 02:29:46 -04:00
|
|
|
[ io-task-callbacks push ] [ add-io-task ] ?if ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2006-02-04 02:19:45 -05:00
|
|
|
: port-flush ( port -- )
|
2005-09-18 23:22:58 -04:00
|
|
|
[ swap <write-task> add-write-io-task stop ] callcc0 drop ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2006-08-05 19:01:59 -04:00
|
|
|
M: output-port stream-flush ( stream -- )
|
|
|
|
dup port-flush pending-error ;
|
2006-02-04 02:19:45 -05:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: wait-to-write ( len port -- )
|
2006-08-05 19:01:59 -04:00
|
|
|
tuck can-write? [ drop ] [ stream-flush ] if ;
|
2005-07-17 14:48:55 -04:00
|
|
|
|
2006-08-05 19:01:59 -04:00
|
|
|
M: output-port stream-write1 ( char writer -- )
|
|
|
|
1 over wait-to-write ch>buffer ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2006-08-05 19:01:59 -04:00
|
|
|
M: output-port stream-write ( string writer -- )
|
2005-12-16 21:12:35 -05:00
|
|
|
over length over wait-to-write >buffer ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-06-19 17:50:35 -04:00
|
|
|
M: port stream-close ( stream -- )
|
2005-09-18 23:22:58 -04:00
|
|
|
dup port-type closed eq? [
|
2006-04-28 00:03:48 -04:00
|
|
|
dup port-type >r closed over set-port-type r>
|
|
|
|
output eq? [ dup port-flush ] when dup port-handle close
|
2006-02-04 02:19:45 -05:00
|
|
|
dup delegate [ buffer-free ] when* f over set-delegate
|
2005-09-18 23:22:58 -04:00
|
|
|
] unless drop ;
|
2005-04-17 18:34:09 -04:00
|
|
|
|
|
|
|
! Make a duplex stream for reading/writing a pair of fds
|
2006-07-21 16:31:39 -04:00
|
|
|
: open-r/w ( path -- fd ) O_RDWR file-mode open dup io-error ;
|
2005-06-19 17:50:35 -04:00
|
|
|
|
2005-12-16 21:12:35 -05:00
|
|
|
: <fd-stream> ( infd outfd -- stream )
|
|
|
|
>r <reader> r> <writer> <duplex-stream> ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-06-19 17:50:35 -04:00
|
|
|
USE: io
|
2005-04-24 23:02:19 -04:00
|
|
|
|
2005-04-17 18:34:09 -04:00
|
|
|
: init-io ( -- )
|
|
|
|
#! Should only be called on startup. Calling this at any
|
|
|
|
#! other time can have unintended consequences.
|
|
|
|
global [
|
2005-10-29 23:25:38 -04:00
|
|
|
H{ } clone read-tasks set
|
2005-12-25 17:46:21 -05:00
|
|
|
FD_SETSIZE <byte-array> read-fdset set
|
2005-10-29 23:25:38 -04:00
|
|
|
H{ } clone write-tasks set
|
2005-12-25 17:46:21 -05:00
|
|
|
FD_SETSIZE <byte-array> write-fdset set
|
2005-12-16 21:12:35 -05:00
|
|
|
0 1 <fd-stream> stdio set
|
2005-08-23 15:50:32 -04:00
|
|
|
] bind ;
|