io.scm 9.8 KB

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