prim-io.scm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
  3. ; I/O primitives
  4. ; See doc/io.txt for a description of Scheme 48's I/O system.
  5. ; Argument specs
  6. (define open-input-port->
  7. (input-type (lambda (x)
  8. (and (port? x)
  9. (port-has-status? x (enum port-status-options
  10. open-for-input))))
  11. no-coercion))
  12. (define open-output-port->
  13. (input-type (lambda (x)
  14. (and (port? x)
  15. (port-has-status? x (enum port-status-options
  16. open-for-output))))
  17. no-coercion))
  18. (define channel-> (input-type channel? no-coercion))
  19. (define (port-has-status? port status)
  20. (not (= 0 (bitwise-and (extract-fixnum (port-status port))
  21. (shift-left 1 status)))))
  22. ;; Must be a C-level string, as a byte vector
  23. (define (extract-filename filename)
  24. (extract-low-string filename))
  25. ; Check SPEC type and then call OPEN-CHANNEL.
  26. (define-consing-primitive open-channel (any-> any-> fixnum-> any->)
  27. (lambda (ignore) channel-size)
  28. (lambda (spec id mode close-silently? key)
  29. (let* ((lose (lambda (reason)
  30. (raise-exception* reason 0 spec (enter-fixnum mode))))
  31. (win (lambda (index)
  32. (receive (channel reason)
  33. (make-registered-channel mode id index close-silently? key)
  34. (cond ((false? channel)
  35. (if (code-vector? spec)
  36. (close-channel-index! index spec mode))
  37. (lose reason))
  38. (else
  39. (goto return channel)))))))
  40. (cond ((not (open-channel-status? mode))
  41. (lose (enum exception wrong-type-argument)))
  42. ((fixnum? spec)
  43. (if (<= 0 (extract-fixnum spec))
  44. (win (extract-fixnum spec))
  45. (lose (enum exception wrong-type-argument))))
  46. ((code-vector? spec)
  47. (receive (channel status)
  48. (let ((filename (extract-filename spec)))
  49. (if (or (= mode (enum channel-status-option input))
  50. (= mode (enum channel-status-option special-input)))
  51. (open-input-file-channel filename)
  52. (open-output-file-channel filename)))
  53. (cond ((eq? status (enum errors no-errors))
  54. (win channel))
  55. (else
  56. (raise-exception os-error 0
  57. spec
  58. (enter-fixnum mode)
  59. (enter-fixnum status))))))
  60. (else
  61. (lose (enum exception wrong-type-argument)))))))
  62. (define (open-channel-status? mode)
  63. (or (= mode (enum channel-status-option input))
  64. (= mode (enum channel-status-option output))
  65. (= mode (enum channel-status-option special-input))
  66. (= mode (enum channel-status-option special-output))))
  67. (define-primitive close-channel (channel->)
  68. (lambda (channel)
  69. (if (open? channel)
  70. (let ((status (close-channel! channel)))
  71. (if (error? status)
  72. (raise-exception os-error 0 channel status)
  73. (goto no-result)))
  74. (raise-exception wrong-type-argument 0 channel))))
  75. (define-primitive channel-ready? (channel->)
  76. (lambda (channel)
  77. (if (open? channel)
  78. (receive (ready? status)
  79. (channel-ready? (extract-channel channel)
  80. (input-channel? channel))
  81. (if (error? status)
  82. (raise-exception os-error 0 channel status)
  83. (goto return-boolean ready?)))
  84. (raise-exception wrong-type-argument 0 channel))))
  85. ;----------------
  86. ; Reading from and writing to channels.
  87. ;
  88. ; This is a wrapper around CHANNEL-READ-BLOCK. We check argument
  89. ; types and interpret the return value. We either return a
  90. ; number---the number of bytes read---or a cell containing the OS
  91. ; error code in the case of an I/O error.
  92. (define-consing-primitive channel-maybe-read
  93. (channel-> any-> fixnum-> fixnum-> boolean->)
  94. (lambda (ignore) cell-size)
  95. (lambda (channel buffer start count wait? key)
  96. (if (and (input-channel? channel)
  97. (buffer? buffer)
  98. (not (immutable? buffer))
  99. (<= (+ start count)
  100. (buffer-length buffer)))
  101. (receive (got eof? pending? status)
  102. (channel-read-block (extract-channel channel)
  103. (address+ (address-after-header buffer)
  104. start)
  105. count
  106. wait?)
  107. (goto return
  108. (cond ((error? status)
  109. (make-cell (enter-fixnum status) key))
  110. (eof?
  111. ;; possible on Windows
  112. (if pending?
  113. (set-channel-os-status! channel true))
  114. vm-eof-object)
  115. (pending?
  116. (set-channel-os-status! channel true)
  117. false)
  118. (else
  119. (enter-fixnum got)))))
  120. (raise-exception wrong-type-argument 0
  121. channel
  122. buffer
  123. (enter-fixnum start)
  124. (enter-fixnum count)
  125. (enter-boolean wait?)))))
  126. ; This is a wrapper around CHANNEL-WRITE-BLOCK. We check argument
  127. ; types and interpret the return value. We either return a
  128. ; number---the number of bytes written---or a cell containing the OS
  129. ; error code in the case of an I/O error.
  130. (define-consing-primitive channel-maybe-write
  131. (channel-> any-> fixnum-> fixnum->)
  132. (lambda (ignore) cell-size)
  133. (lambda (channel buffer start count key)
  134. (if (and (output-channel? channel)
  135. (buffer? buffer)
  136. (<= (+ start count)
  137. (buffer-length buffer)))
  138. (receive (got pending? status)
  139. (channel-write-block (extract-channel channel)
  140. (address+ (address-after-header buffer)
  141. start)
  142. count)
  143. (goto return
  144. (cond
  145. ((error? status)
  146. (make-cell (enter-fixnum status) key))
  147. (pending?
  148. (set-channel-os-status! channel true)
  149. false)
  150. (else
  151. (enter-fixnum got)))))
  152. (raise-exception wrong-type-argument 0
  153. channel
  154. buffer
  155. (enter-fixnum start)
  156. (enter-fixnum count)))))
  157. ;----------------
  158. ; Utilities for the above two opcodes.
  159. (define (buffer? thing)
  160. (code-vector? thing))
  161. (define (buffer-length buffer)
  162. (code-vector-length buffer))
  163. (define (extract-channel channel)
  164. (extract-fixnum (channel-os-index channel)))
  165. ;----------------
  166. ;; random stuff
  167. (define-primitive channel-parameter (fixnum->)
  168. (lambda (param)
  169. (enum-case channel-parameter-option param
  170. ((buffer-size)
  171. (goto return-fixnum (channel-buffer-size)))
  172. ((crlf?)
  173. (goto return-boolean (channel-crlf?)))
  174. (else
  175. (raise-exception bad-option 0 (enter-fixnum param))))))
  176. (define-primitive channel-abort (channel->)
  177. (lambda (channel)
  178. (goto return (vm-channel-abort channel))))
  179. (define-primitive open-channels-list ()
  180. (lambda ()
  181. (goto return (open-channels-list))))
  182. ;----------------------------------------------------------------
  183. ; Port instructions.
  184. ;
  185. ; These are only for speed. If no port was supplied by the user they have
  186. ; to look up the appropriate port in the current dynamic environments.
  187. ; This is a complete hack, also done for speed. See rts/current-port.scm
  188. ; for the other end.
  189. (define (read-or-peek-byte read?)
  190. (lambda ()
  191. (let ((port (if (= (code-byte 0) 0)
  192. (val)
  193. (get-current-port
  194. (enter-fixnum
  195. (enum current-port-marker current-input-port))))))
  196. (if (and (port? port)
  197. (port-has-status? port
  198. (enum port-status-options open-for-input)))
  199. (let ((b (port-buffer port)))
  200. (if (false? b)
  201. (raise-exception buffer-full/empty 1 port)
  202. (let ((i (extract-fixnum (port-index port)))
  203. (l (extract-fixnum (port-limit port))))
  204. (cond ((= i l)
  205. (raise-exception buffer-full/empty 1 port))
  206. (else
  207. (if read?
  208. (set-port-index! port (enter-fixnum (+ i 1))))
  209. (goto continue-with-value
  210. (enter-fixnum (code-vector-ref b i))
  211. 1))))))
  212. (raise-exception wrong-type-argument 1 port)))))
  213. (let ((do-it (read-or-peek-byte #t)))
  214. (define-primitive read-byte () do-it))
  215. (let ((do-it (read-or-peek-byte #f)))
  216. (define-primitive peek-byte () do-it))
  217. (define (read-or-peek-char read?)
  218. (lambda ()
  219. (let ((port (if (= (code-byte 0) 0)
  220. (val)
  221. (get-current-port
  222. (enter-fixnum
  223. (enum current-port-marker current-input-port))))))
  224. (if (and (port? port)
  225. (port-has-status? port
  226. (enum port-status-options open-for-input)))
  227. (let ((b (port-buffer port)))
  228. (if (false? b)
  229. (raise-exception buffer-full/empty 1 port)
  230. (let loop ((i (extract-fixnum (port-index port))))
  231. (let ((l (extract-fixnum (port-limit port)))
  232. (codec (port-text-codec-spec port))
  233. (lose
  234. (lambda ()
  235. ;; we may have gotten out of synch because of CR/LF conversion
  236. (if read?
  237. (set-port-index! port (enter-fixnum i)))
  238. (raise-exception buffer-full/empty 1 port))))
  239. (cond ((= i l)
  240. (lose))
  241. ((not (fixnum? codec))
  242. (lose))
  243. (else
  244. (call-with-values
  245. (lambda ()
  246. (decode-scalar-value (extract-fixnum codec)
  247. (address+ (address-after-header b) i)
  248. (- l i)))
  249. (lambda (encoding-ok? ok? incomplete? value count)
  250. (define (deliver)
  251. (if read?
  252. (begin
  253. (set-port-pending-cr?! port false)
  254. (set-port-index! port (enter-fixnum (+ i count)))))
  255. (goto continue-with-value
  256. (scalar-value->vm-char value)
  257. 1))
  258. (cond
  259. ((not encoding-ok?)
  260. (raise-exception wrong-type-argument 1 port))
  261. ((or (not ok?) incomplete?)
  262. (lose))
  263. ((not (false? (port-crlf? port)))
  264. ;; CR/LF handling. Great.
  265. (cond
  266. ((= value cr-code)
  267. (if read?
  268. (begin
  269. (set-port-pending-cr?! port true)
  270. (set-port-index! port (enter-fixnum (+ i count)))))
  271. (goto continue-with-value (scalar-value->vm-char lf-code) 1))
  272. ((and (= value lf-code)
  273. (not (false? (port-pending-cr? port))))
  274. (if read?
  275. (set-port-pending-cr?! port false))
  276. (loop (+ i count)))
  277. (else
  278. (deliver))))
  279. (else
  280. (deliver)))))))))))
  281. (raise-exception wrong-type-argument 1 port)))))
  282. (let ((do-it (read-or-peek-char #t)))
  283. (define-primitive read-char () do-it))
  284. (let ((do-it (read-or-peek-char #f)))
  285. (define-primitive peek-char () do-it))
  286. (define-primitive write-byte ()
  287. (lambda ()
  288. (receive (byte port)
  289. (if (= (code-byte 0) 0)
  290. (values (pop)
  291. (val))
  292. (values (val)
  293. (get-current-port (enter-fixnum
  294. (enum current-port-marker
  295. current-output-port)))))
  296. (cond
  297. ((not (and (fixnum? byte)
  298. (port? port)
  299. (port-has-status? port
  300. (enum port-status-options open-for-output))))
  301. (raise-exception wrong-type-argument 1 byte port))
  302. ((false? (port-limit port)) ; unbuffered
  303. (raise-exception buffer-full/empty 1 byte port))
  304. (else
  305. (let ((b (port-buffer port))
  306. (i (extract-fixnum (port-index port))))
  307. (cond ((= i (code-vector-length b))
  308. (raise-exception buffer-full/empty 1 byte port))
  309. (else
  310. (set-port-index! port (enter-fixnum (+ i 1)))
  311. (code-vector-set! b i (extract-fixnum byte))
  312. (goto continue-with-value unspecific-value 1)))))))))
  313. (define cr-code 13)
  314. (define lf-code 10)
  315. (define-primitive write-char ()
  316. (lambda ()
  317. (receive (char port)
  318. (if (= (code-byte 0) 0)
  319. (values (pop)
  320. (val))
  321. (values (val)
  322. (get-current-port (enter-fixnum
  323. (enum current-port-marker
  324. current-output-port)))))
  325. (cond
  326. ((not (and (vm-char? char)
  327. (port? port)
  328. (port-has-status? port
  329. (enum port-status-options open-for-output))))
  330. (raise-exception wrong-type-argument 1 char port))
  331. ((false? (port-limit port)) ; unbuffered
  332. (raise-exception buffer-full/empty 1 char port))
  333. (else
  334. (let ((codec (port-text-codec-spec port))
  335. (lose
  336. ;; #### this isn't really the right exception
  337. (lambda () (raise-exception buffer-full/empty 1 char port))))
  338. (if (not (fixnum? codec))
  339. (lose)
  340. (let* ((b (port-buffer port))
  341. (i (extract-fixnum (port-index port)))
  342. (l (code-vector-length b)))
  343. (cond
  344. ((= i l) (lose))
  345. ;; CR/LF handling is atrocious
  346. ((and (not (false? (port-crlf? port)))
  347. (= (vm-char->scalar-value char) lf-code))
  348. (call-with-values
  349. (lambda ()
  350. (encode-scalar-value (extract-fixnum codec) cr-code
  351. (address+ (address-after-header b) i)
  352. (- l i)))
  353. (lambda (codec-ok? encoding-ok? out-of-space? count)
  354. (cond
  355. ((not codec-ok?)
  356. (raise-exception wrong-type-argument 1 char port))
  357. ((or (not encoding-ok?) out-of-space?)
  358. (lose))
  359. (else
  360. (let ((i (+ i count)))
  361. (if (= i l)
  362. (lose)
  363. (call-with-values
  364. (lambda ()
  365. (encode-scalar-value (extract-fixnum codec) lf-code
  366. (address+ (address-after-header b) i)
  367. (- l i)))
  368. (lambda (codec-ok? encoding-ok? out-of-space? count)
  369. (cond
  370. ;; the codec is the same as before, so it must be OK
  371. ((or (not encoding-ok?) out-of-space?)
  372. (lose))
  373. (else
  374. (set-port-index! port (enter-fixnum (+ i count)))
  375. (goto continue-with-value unspecific-value 1))))))))))))
  376. (else
  377. (call-with-values
  378. (lambda ()
  379. (encode-scalar-value (extract-fixnum codec) (vm-char->scalar-value char)
  380. (address+ (address-after-header b) i)
  381. (- l i)))
  382. (lambda (codec-ok? encoding-ok? out-of-space? count)
  383. (cond
  384. ((not codec-ok?)
  385. (raise-exception wrong-type-argument 1 char port))
  386. ((or (not encoding-ok?) out-of-space?)
  387. (lose))
  388. (else
  389. (set-port-index! port (enter-fixnum (+ i count)))
  390. (goto continue-with-value unspecific-value 1)))))))))))))))
  391. ; Do an ASSQ-like walk up the current dynamic environment, looking for
  392. ; MARKER.
  393. (define (get-current-port marker)
  394. (let ((thread (current-thread)))
  395. (if (and (record? thread)
  396. (< 1 (record-length thread)))
  397. (let loop ((env (record-ref thread 1)))
  398. (cond ((not (and (vm-pair? env)
  399. (vm-pair? (vm-car env))))
  400. (if (vm-eq? env null)
  401. (error (if (eq? (extract-fixnum marker)
  402. (enum current-port-marker
  403. current-output-port))
  404. "dynamic environment doesn't have current-output-port"
  405. "dynamic environment doesn't have current-input-port"))
  406. (error "dynamic environment is not a proper list")))
  407. ((vm-eq? marker (vm-car (vm-car env)))
  408. (vm-cdr (vm-car env)))
  409. (else
  410. (loop (vm-cdr env)))))
  411. (error "current thread is not a record"))))
  412. (define-primitive os-error-message (fixnum->)
  413. (lambda (status)
  414. (let* ((raw (error-string status))
  415. (len (+ (string-length raw) 1))
  416. (vector (maybe-make-b-vector+gc (enum stob byte-vector) len)))
  417. (if (false? vector)
  418. (raise-exception heap-overflow
  419. 0
  420. (enter-fixnum len))
  421. (do ((i 0 (+ 1 i)))
  422. ((>= i len)
  423. (goto return vector))
  424. (code-vector-set! vector i (char->ascii (string-ref raw i))))))))
  425. ;----------------
  426. ; A poor man's WRITE for use in debugging.
  427. (define-primitive message (any->)
  428. (lambda (stuff)
  429. (let ((out (current-error-port)))
  430. (let loop ((stuff stuff))
  431. (if (vm-pair? stuff)
  432. (begin
  433. (message-element (vm-car stuff) out)
  434. (loop (vm-cdr stuff)))))
  435. (newline out)))
  436. return-unspecific)
  437. (define (message-element thing out)
  438. (cond ((fixnum? thing)
  439. (write-integer (extract-fixnum thing) out))
  440. ((vm-char? thing)
  441. (write-string "#\\" out)
  442. (write-char (ascii->char (vm-char->scalar-value thing)) out)) ; ####
  443. ((typed-record? thing)
  444. (write-string "#{" out)
  445. (write-vm-string (record-type-name thing) out)
  446. (write-char #\} out))
  447. ((vm-string? thing)
  448. (write-vm-string thing out))
  449. ((vm-symbol? thing)
  450. (write-vm-string (vm-symbol->string thing) out))
  451. (else
  452. (write-string (cond ((vm-boolean? thing)
  453. (if (extract-boolean thing) "#t" "#f"))
  454. ((vm-eq? thing null)
  455. "()")
  456. ((vm-pair? thing)
  457. "(...)")
  458. ((vm-vector? thing)
  459. "#(...)")
  460. ((closure? thing)
  461. "#{procedure}")
  462. ((template? thing)
  463. "#{template}")
  464. ((location? thing)
  465. "#{location}")
  466. ((code-vector? thing)
  467. "#{code-vector}")
  468. ((continuation? thing)
  469. "#{continuation}")
  470. (else
  471. "???"))
  472. out))))
  473. ;----------------------------------------------------------------
  474. ; RESUME-PROC is called when the image is resumed.
  475. ; This does a garbage collection rooting from RESUME-PROC, writes the heap
  476. ; into a file, and then aborts the garbage collection (which didn't modify
  477. ; any VM registers or the stack).
  478. ; Bug: finalizers for things in the image are ignored.
  479. (define-primitive write-image-low (code-vector-> any-> code-vector-> vector->)
  480. (lambda (filename resume-proc comment-string undumpables)
  481. (let* ((lose (lambda (status)
  482. (raise-exception* (enum exception os-error) 0
  483. (enter-fixnum status)
  484. filename
  485. resume-proc comment-string)))
  486. (port-lose (lambda (status port)
  487. (let ((close-status (close-output-port port)))
  488. (if (error? close-status)
  489. (lose close-status)
  490. (lose status))))))
  491. (receive (port status)
  492. (open-output-file (extract-filename filename))
  493. (if (error? status)
  494. (lose status)
  495. (let ((status (write-string (extract-low-string comment-string) port)))
  496. (if (error? status)
  497. (port-lose status port)
  498. (let ((status (s48-write-image resume-proc
  499. undumpables
  500. port)))
  501. (if (error? status)
  502. (port-lose status port)
  503. (let ((status (close-output-port port)))
  504. (if (error? status)
  505. (lose status)
  506. (goto no-result))))))))))))
  507. ; READ-IMAGE needs to protect some values against GCs (this can't be with
  508. ; READ-IMAGE as that is compiled separately.)
  509. (add-gc-root! s48-initializing-gc-root)