This file is indexed.

/usr/share/scheme48-1.9/posix/io.scm is in scheme48 1.9-5.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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
259
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
; Part of Scheme 48 1.9.  See file COPYING for notices and license.

; Authors: Richard Kelsey, Jonathan Rees, Marcus Crestani, Mike Sperber


; Calls from Section 6 of POSIX.

(import-dynamic-externals "=scheme48external/posix")

; First some port manipulation routines.

(define (fd-port? port)
  (if (port->channel port)
      #t
      #f))

(define (port->fd port)
  (let ((channel (port->channel port)))
    (if channel
	(channel-os-index channel)
	#f)))

;----------------
; 4.7 Terminal Identification
; Out of place, but it makes more sense here.

(define (port-is-a-terminal? port)
  (cond ((not (port? port))
	 (assertion-violation 'port-is-a-terminal? "not a port" port))
	((port->channel port)
	 => channel-is-a-terminal?)
	(else
	 #f)))

(define (port-terminal-name port)
  (cond ((not (port? port))
	 (assertion-violation 'port-terminal-name "not a port" port))
	((port->channel port)
	 => (lambda (channel)
	      (byte-vector->os-string (channel-terminal-name channel))))
	(else
	 #f)))

(import-lambda-definition-2 channel-is-a-terminal? (channel) "posix_is_a_tty")
(import-lambda-definition-2 channel-terminal-name (channel) "posix_tty_name")

;----------------
; 6.1 Pipes

(define (open-pipe)
  (let ((in-out (call-imported-binding-2 posix-pipe)))
    (values (input-channel->port (car in-out))
            (output-channel->port (cdr in-out)))))

(import-definition posix-pipe)

;----------------
; 6.2 File descriptor manipulation.

; (DUP <fd-port>)                    -> fd-port
; (DUP2 <fd-port> <file descriptor>) -> fd-port
; (DUP-SWITCHING_MODE <fd-port>)     -> fd-port
; (CHANNEL-DUP <channel>)                    -> channel
; (CHANNEL-DUP2 <channel> <file descriptor>) -> channel
; These change a ports file descriptor and return a new port (or channel)
; port that uses the old one's file descriptor.  DUP uses the lowest unused
; file descriptor, DUP2 uses the one provided.  If any existing channel
; uses the file descriptor passed to DUP2, that channel is closed.
; DUP-SWITCHING-MODE is the same as DUP except that the returned port has
; the opposite polarity.

(define (dup port)
  (let ((channel (maybe-x->channel port)))
    (if channel
	((if (input-port? port)
	     input-channel->port
	     output-channel->port)
	 (channel-dup channel))
	(assertion-violation 'dup "argument cannot be coerced to channel" port))))

(define (channel-dup channel)
  (really-dup channel #f))

(define (dup-switching-mode port)
  (let ((channel (maybe-x->channel port)))
    (if channel
	(if (input-port? port)
	    (output-channel->port
	     (really-dup channel (enum channel-status-option output)))
	    (input-channel->port
	     (really-dup channel (enum channel-status-option input))))
	(assertion-violation 'dup-switching-mode "argument cannot be coerced to channel" port))))

(define (dup2 port fd)
  (let ((channel (maybe-x->channel port)))
    (if channel
	((if (input-port? port)
	     input-channel->port
	     output-channel->port)
	 (channel-dup2 channel fd))
	(assertion-violation 'dup2 "argument cannot be coerced to channel" port fd))))

(import-lambda-definition-2 really-dup (channel new-status) "posix_dup")
(import-lambda-definition-2 channel-dup2 (channel fd) "posix_dup2")

; A higher-level interface for DUP and DUP2.
;
; (remap-file-descriptors! . ports&channels)
;
; PORTS&CHANNELS gives the desired locations of the file descriptors associated
; with the ports and channels.  (REMAP-FILE-DESCRIPTORS! P1 #F P2) moves P1's
; file descriptor to 0 and P2's to 2.  All other channels are closed.  The same
; file descriptor may be moved to multiple locations.
;
; It would be nice if this closed the port associated with a closed channel,
; but it doesn't.
;
; This is a classical parallel assignment problem.  What we do is figure out a
; series of DUP()'s and DUP2()'s that produce the desired arrangement.
; FIND-TARGETS separates out the channels that must be moved to multiple file
; descriptors.  We do the parallel assignment, and then do any duplications.
; Finally, any channels which were not mentioned in PORTS&CHANNELS are
; marked close-on-exec.

(define (remap-file-descriptors! . ports&channels)
  (let ((channels (maybe-xs->channels ports&channels #t)))
    (if channels
	(call-with-values    
	 (lambda ()
	   (find-targets channels))
	 (lambda (targets extras)
	   (do-dups targets)
	   (for-each (lambda (pair)
		       (channel-dup2 (cdr pair) (car pair)))
		     extras)
	   (let ((channels (list->vector channels)))
	     (for-each (lambda (channel)
			 (let ((index (channel-os-index channel)))
			   (if (or (<= (vector-length channels) index)
				   (not (vector-ref channels index)))
			       (set-close-on-exec?! channel #t))))
		       (open-channels-list)))))
	(apply assertion-violation 'remap-file-descriptors!
	       "not all arguments can be mapped to channels"
	       ports&channels))))

(define (close-all-but . ports&channels)
  (let ((channels (maybe-xs->channels ports&channels #f)))
    (if channels
	(for-each (lambda (channel)
		    (if (not (memq channel channels))
			(close-channel channel)))
		  (open-channels-list))
	(apply assertion-violation 'close-all-but 
	       "not all arguments can be mapped to channels"
	       ports&channels))))

; Coerce PORT-OR-CHANNEL to a channel, if possible.

(define (maybe-x->channel port-or-channel)
  (cond ((channel? port-or-channel)
	 port-or-channel)
	((fd-port? port-or-channel)
	 (port->channel port-or-channel))
	(else
	 #f)))

; Coerce PORTS&CHANNELS to a list of channels, returning #F if any cannot
; be coerced.  If FALSE-OKAY? is true, then any #F's in the list are just
; passed along.

(define (maybe-xs->channels ports&channels false-okay?)
  (let loop ((todo ports&channels) (res '()))
    (cond ((null? todo)
	   (reverse res))
	  ((and false-okay?
		(not (car todo)))
	   (loop (cdr todo)
		 (cons #f res)))
	  ((maybe-x->channel (car todo))
	   => (lambda (channel)
		(loop (cdr todo)
		      (cons channel res))))
	  (else #f))))

; Returns two lists of pairs (<target-fd> . <channel>).  No channel appears twice
; in the first list and every channel in the second list appears in the first.

(define (find-targets channels)
  (call-with-values
   (lambda () 
     (fold->3 (lambda (channel i targets extras)
		(cond ((not channel)
		       (values (+ i 1)
			       targets
			       extras))
		      ((any (lambda (pair)
			      (eq? channel (cdr pair)))
			    targets)
		       (values (+ i 1)
			       targets
			       `((,i . ,channel) . ,extras)))
		      (else
		       (values (+ i 1)
			       `((,i . ,channel) . ,targets)
			       extras))))
	      channels
	      0
	      '()
	      '()))
   (lambda (i targets extras)
     (values targets extras))))

; TARGETS is a list of pairs (<wanted-fd> . <channel>).  We loop down doing
; DUP-TO-TARGET, which is guarenteed to make progress, but not guarenteed to
; actually move the argument we give it.
;
; All this depends on DUP and DUP2 switching the original channel to the new
; file descriptor and returning a new channel with the original file descriptor.

(define (do-dups targets)
  (if (not (null? targets))
      (let ((channel (cdar targets))
	    (target-fd (caar targets))
	    (rest (cdr targets)))
	(dup-to-target channel target-fd rest '())
	(do-dups (if (= (channel-os-index channel)
			target-fd)
		     rest
		     targets)))))

; Move CHANNEL to TARGET-FD.  TARGETS is a list of yet-to-be-done
; (<fd> . <channel>) pairs.  PENDING is a list of fd's we are waiting to move
; out of.  If TARGET-FD is in PENDING, then we have a loop and use dup() to move
; from HAVE-FD so some other location, thus breaking the loop.  If there is
; already someone in the location we want, we move them and then ourselves.

(define (dup-to-target channel target-fd targets pending)
  (let ((have-fd (channel-os-index channel)))
    (cond ((= target-fd have-fd))
	  ((memq target-fd pending)
	   (channel-dup channel))
	  (else
	   (let ((occupant (find-occupant target-fd targets)))
	     (if occupant
		 (dup-to-target (cdr occupant)
				(car occupant)
				targets
				(cons have-fd pending)))
	     (channel-dup2 channel target-fd))))))

; Return the (<wanted-fd> . <channel>) pair from TARGETS where <channel>
; currently has FD, if there is such.

(define (find-occupant fd targets)
  (let loop ((targets targets))
    (cond ((null? targets)
	   #f)
	  ((= fd (channel-os-index (cdar targets)))
	   (car targets))
	  (else
	   (loop (cdr targets))))))

;----------------
; 6.3 File Descriptor Reassignment
;
; int close(int fd)     ; Use close-{input|output}-{port|channel}
;
; 6.4 Input and Output
;
; read() and write()    ; Already available in various forms.
;----------------
; 6.5 Control Operations on Files

; fcntl(fd, F_DUPFD, target_fd)            ; Use DUP instead.

; Descriptor flags
; fcntl(fd, F_GETFD)
; fcntl(fd, F_SETFD, flags)
;
; The only POSIX flag is FD_CLOEXEC, so that's all we do.

(import-lambda-definition-2 set-close-on-exec?! (channel bool)
			  "posix_set_close_on_exec")
(import-lambda-definition-2 close-on-exec? (channel) "posix_close_on_exec_p")

; Status flags
; fcntl(fd, F_GETFL)
; fcntl(fd, F_SETFL, flags)

(define (i/o-flags port-or-channel)
  (let ((channel (maybe-x->channel port-or-channel)))
    (if channel
	(call-imported-binding-2 posix-io-flags channel #f)
	(assertion-violation 'i/o-flags "argument cannot be coerced to channel" port-or-channel))))

(define (set-i/o-flags! port-or-channel options)
  (let ((channel (maybe-x->channel port-or-channel)))
    (if (and channel
	     (file-options? options))
	(call-imported-binding-2 posix-io-flags channel options)
	(assertion-violation 'set-i/o-flags! "argument type error" port-or-channel options))))

(import-definition posix-io-flags)

; off_t lseek(int fd, off_t offset, int whence)

;----------------
; 6. File Synchronization
;
; int fsync(int fd)              ; optional
; int fdatasync(int fd)          ; optional
;
; 7. Asynchronous Input and Output
;
; All optional