io.scm 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. ; Copyright (c) 1993-2007 by Richard Kelsey and Jonathan Rees. See file COPYING.
  2. ; Calls from Section 6 of POSIX.
  3. ; First some port manipulation routines.
  4. (define (fd-port? port)
  5. (if (port->channel port)
  6. #t
  7. #f))
  8. (define (port->fd port)
  9. (let ((channel (port->channel port)))
  10. (if channel
  11. (channel-os-index channel)
  12. #f)))
  13. ;----------------
  14. ; 4.7 Terminal Identification
  15. ; Out of place, but it makes more sense here.
  16. (define (port-is-a-terminal? port)
  17. (cond ((not (port? port))
  18. (call-error "not a port" port-is-a-terminal? port))
  19. ((port->channel port)
  20. => channel-is-a-terminal?)
  21. (else
  22. #f)))
  23. (define (port-terminal-name port)
  24. (cond ((not (port? port))
  25. (call-error "not a port" port-terminal-name port))
  26. ((port->channel port)
  27. => (lambda (channel)
  28. (byte-vector->os-string (channel-terminal-name channel))))
  29. (else
  30. #f)))
  31. (import-lambda-definition channel-is-a-terminal? (channel) "posix_is_a_tty")
  32. (import-lambda-definition channel-terminal-name (channel) "posix_tty_name")
  33. ;----------------
  34. ; 6.1 Pipes
  35. (define (open-pipe)
  36. (let ((in-out (call-imported-binding posix-pipe)))
  37. (values (input-channel->port (car in-out))
  38. (output-channel->port (cdr in-out)))))
  39. (import-definition posix-pipe)
  40. ;----------------
  41. ; 6.2 File descriptor manipulation.
  42. ; (DUP <fd-port>) -> fd-port
  43. ; (DUP2 <fd-port> <file descriptor>) -> fd-port
  44. ; (DUP-SWITCHING_MODE <fd-port>) -> fd-port
  45. ; (CHANNEL-DUP <channel>) -> channel
  46. ; (CHANNEL-DUP2 <channel> <file descriptor>) -> channel
  47. ; These change a ports file descriptor and return a new port (or channel)
  48. ; port that uses the old one's file descriptor. DUP uses the lowest unused
  49. ; file descriptor, DUP2 uses the one provided. If any existing channel
  50. ; uses the file descriptor passed to DUP2, that channel is closed.
  51. ; DUP-SWITCHING-MODE is the same as DUP except that the returned port has
  52. ; the opposite polarity.
  53. (define (dup port)
  54. (let ((channel (maybe-x->channel port)))
  55. (if channel
  56. ((if (input-port? port)
  57. input-channel->port
  58. output-channel->port)
  59. (channel-dup channel))
  60. (call-error "argument cannot be coerced to channel" dup port))))
  61. (define (channel-dup channel)
  62. (really-dup channel #f))
  63. (define (dup-switching-mode port)
  64. (let ((channel (maybe-x->channel port)))
  65. (if channel
  66. (if (input-port? port)
  67. (output-channel->port
  68. (really-dup channel (enum channel-status-option output)))
  69. (input-channel->port
  70. (really-dup channel (enum channel-status-option input))))
  71. (call-error "argument cannot be coerced to channel" dup-switching-mode port))))
  72. (define (dup2 port fd)
  73. (let ((channel (maybe-x->channel port)))
  74. (if channel
  75. ((if (input-port? port)
  76. input-channel->port
  77. output-channel->port)
  78. (channel-dup2 channel fd))
  79. (call-error "argument cannot be coerced to channel" dup2 port fd))))
  80. (import-lambda-definition really-dup (channel new-status) "posix_dup")
  81. (import-lambda-definition channel-dup2 (channel fd) "posix_dup2")
  82. ; A higher-level interface for DUP and DUP2.
  83. ;
  84. ; (remap-file-descriptors! . ports&channels)
  85. ;
  86. ; PORTS&CHANNELS gives the desired locations of the file descriptors associated
  87. ; with the ports and channels. (REMAP-FILE-DESCRIPTORS! P1 #F P2) moves P1's
  88. ; file descriptor to 0 and P2's to 2. All other channels are closed. The same
  89. ; file descriptor may be moved to multiple locations.
  90. ;
  91. ; It would be nice if this closed the port associated with a closed channel,
  92. ; but it doesn't.
  93. ;
  94. ; This is a classical parallel assignment problem. What we do is figure out a
  95. ; series of DUP()'s and DUP2()'s that produce the desired arrangement.
  96. ; FIND-TARGETS separates out the channels that must be moved to multiple file
  97. ; descriptors. We do the parallel assignment, and then do any duplications.
  98. ; Finally, any channels which were not mentioned in PORTS&CHANNELS are
  99. ; marked close-on-exec.
  100. (define (remap-file-descriptors! . ports&channels)
  101. (let ((channels (maybe-xs->channels ports&channels #t)))
  102. (if channels
  103. (call-with-values
  104. (lambda ()
  105. (find-targets channels))
  106. (lambda (targets extras)
  107. (do-dups targets)
  108. (for-each (lambda (pair)
  109. (channel-dup2 (cdr pair) (car pair)))
  110. extras)
  111. (let ((channels (list->vector channels)))
  112. (for-each (lambda (channel)
  113. (let ((index (channel-os-index channel)))
  114. (if (or (<= (vector-length channels) index)
  115. (not (vector-ref channels index)))
  116. (set-close-on-exec?! channel #t))))
  117. (open-channels-list)))))
  118. (apply call-error "not all arguments can be mapped to channels"
  119. remap-file-descriptors!
  120. ports&channels))))
  121. (define (close-all-but . ports&channels)
  122. (let ((channels (maybe-xs->channels ports&channels #f)))
  123. (if channels
  124. (for-each (lambda (channel)
  125. (if (not (memq channel channels))
  126. (close-channel channel)))
  127. (open-channels-list))
  128. (apply call-error "not all arguments can be mapped to channels"
  129. close-all-but
  130. ports&channels))))
  131. ; Coerce PORT-OR-CHANNEL to a channel, if possible.
  132. (define (maybe-x->channel port-or-channel)
  133. (cond ((channel? port-or-channel)
  134. port-or-channel)
  135. ((fd-port? port-or-channel)
  136. (port->channel port-or-channel))
  137. (else
  138. #f)))
  139. ; Coerce PORTS&CHANNELS to a list of channels, returning #F if any cannot
  140. ; be coerced. If FALSE-OKAY? is true, then any #F's in the list are just
  141. ; passed along.
  142. (define (maybe-xs->channels ports&channels false-okay?)
  143. (let loop ((todo ports&channels) (res '()))
  144. (cond ((null? todo)
  145. (reverse res))
  146. ((and false-okay?
  147. (not (car todo)))
  148. (loop (cdr todo)
  149. (cons #f res)))
  150. ((maybe-x->channel (car todo))
  151. => (lambda (channel)
  152. (loop (cdr todo)
  153. (cons channel res))))
  154. (else #f))))
  155. ; Returns two lists of pairs (<target-fd> . <channel>). No channel appears twice
  156. ; in the first list and every channel in the second list appears in the first.
  157. (define (find-targets channels)
  158. (call-with-values
  159. (lambda ()
  160. (fold->3 (lambda (channel i targets extras)
  161. (cond ((not channel)
  162. (values (+ i 1)
  163. targets
  164. extras))
  165. ((any (lambda (pair)
  166. (eq? channel (cdr pair)))
  167. targets)
  168. (values (+ i 1)
  169. targets
  170. `((,i . ,channel) . ,extras)))
  171. (else
  172. (values (+ i 1)
  173. `((,i . ,channel) . ,targets)
  174. extras))))
  175. channels
  176. 0
  177. '()
  178. '()))
  179. (lambda (i targets extras)
  180. (values targets extras))))
  181. ; TARGETS is a list of pairs (<wanted-fd> . <channel>). We loop down doing
  182. ; DUP-TO-TARGET, which is guarenteed to make progress, but not guarenteed to
  183. ; actually move the argument we give it.
  184. ;
  185. ; All this depends on DUP and DUP2 switching the original channel to the new
  186. ; file descriptor and returning a new channel with the original file descriptor.
  187. (define (do-dups targets)
  188. (if (not (null? targets))
  189. (let ((channel (cdar targets))
  190. (target-fd (caar targets))
  191. (rest (cdr targets)))
  192. (dup-to-target channel target-fd rest '())
  193. (do-dups (if (= (channel-os-index channel)
  194. target-fd)
  195. rest
  196. targets)))))
  197. ; Move CHANNEL to TARGET-FD. TARGETS is a list of yet-to-be-done
  198. ; (<fd> . <channel>) pairs. PENDING is a list of fd's we are waiting to move
  199. ; out of. If TARGET-FD is in PENDING, then we have a loop and use dup() to move
  200. ; from HAVE-FD so some other location, thus breaking the loop. If there is
  201. ; already someone in the location we want, we move them and then ourselves.
  202. (define (dup-to-target channel target-fd targets pending)
  203. (let ((have-fd (channel-os-index channel)))
  204. (cond ((= target-fd have-fd))
  205. ((memq target-fd pending)
  206. (channel-dup channel))
  207. (else
  208. (let ((occupant (find-occupant target-fd targets)))
  209. (if occupant
  210. (dup-to-target (cdr occupant)
  211. (car occupant)
  212. targets
  213. (cons have-fd pending)))
  214. (channel-dup2 channel target-fd))))))
  215. ; Return the (<wanted-fd> . <channel>) pair from TARGETS where <channel>
  216. ; currently has FD, if there is such.
  217. (define (find-occupant fd targets)
  218. (let loop ((targets targets))
  219. (cond ((null? targets)
  220. #f)
  221. ((= fd (channel-os-index (cdar targets)))
  222. (car targets))
  223. (else
  224. (loop (cdr targets))))))
  225. ;----------------
  226. ; 6.3 File Descriptor Reassignment
  227. ;
  228. ; int close(int fd) ; Use close-{input|output}-{port|channel}
  229. ;
  230. ; 6.4 Input and Output
  231. ;
  232. ; read() and write() ; Already available in various forms.
  233. ;----------------
  234. ; 6.5 Control Operations on Files
  235. ; fcntl(fd, F_DUPFD, target_fd) ; Use DUP instead.
  236. ; Descriptor flags
  237. ; fcntl(fd, F_GETFD)
  238. ; fcntl(fd, F_SETFD, flags)
  239. ;
  240. ; The only POSIX flag is FD_CLOEXEC, so that's all we do.
  241. (import-lambda-definition set-close-on-exec?! (channel bool)
  242. "posix_set_close_on_exec")
  243. (import-lambda-definition close-on-exec? (channel) "posix_close_on_exec_p")
  244. ; Status flags
  245. ; fcntl(fd, F_GETFL)
  246. ; fcntl(fd, F_SETFL, flags)
  247. (define (i/o-flags port-or-channel)
  248. (let ((channel (maybe-x->channel port-or-channel)))
  249. (if channel
  250. (call-imported-binding posix-io-flags channel #f)
  251. (call-error "argument cannot be coerced to channel" i/o-flags port-or-channel))))
  252. (define (set-i/o-flags! port-or-channel options)
  253. (let ((channel (maybe-x->channel port-or-channel)))
  254. (if (and channel
  255. (file-options? options))
  256. (call-imported-binding posix-io-flags channel options)
  257. (call-error "argument type error" set-i/o-flags! port-or-channel options))))
  258. (import-definition posix-io-flags)
  259. ; off_t lseek(int fd, off_t offset, int whence)
  260. ;----------------
  261. ; 6. File Synchronization
  262. ;
  263. ; int fsync(int fd) ; optional
  264. ; int fdatasync(int fd) ; optional
  265. ;
  266. ; 7. Asynchronous Input and Output
  267. ;
  268. ; All optional