custom-ports.scm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. ;;; custom-ports.scm --- Defining new ports in Scheme
  2. ;;; Copyright (C) 2023 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software: you can redistribute it and/or modify
  5. ;;; it under the terms of the GNU Lesser General Public License as
  6. ;;; published by the Free Software Foundation, either version 3 of the
  7. ;;; License, or (at your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful, but
  10. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;; Lesser General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public
  15. ;;; License along with this program. If not, see
  16. ;;; <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;;;
  19. ;;; Code:
  20. (define-module (ice-9 custom-ports)
  21. #:use-module (ice-9 match)
  22. #:use-module (ice-9 textual-ports)
  23. #:use-module (srfi srfi-9)
  24. #:declarative? #f ; Because of extension.
  25. #:export (make-custom-port))
  26. ;; Replaced by extension; here just to suppress warnings.
  27. (define %make-custom-port error)
  28. (define %custom-port-data error)
  29. (define-record-type <custom-port-data>
  30. (make-custom-port-data print read write read-wait-fd write-wait-fd
  31. seek close get-natural-buffer-sizes
  32. random-access? input-waiting? truncate)
  33. custom-port-data?
  34. (print custom-port-data-print)
  35. (read custom-port-data-read)
  36. (write custom-port-data-write)
  37. (read-wait-fd custom-port-data-read-wait-fd)
  38. (write-wait-fd custom-port-data-write-wait-fd)
  39. (seek custom-port-data-seek)
  40. (close custom-port-data-close)
  41. (get-natural-buffer-sizes custom-port-data-get-natural-buffer-sizes)
  42. (random-access? custom-port-data-random-access?)
  43. (input-waiting? custom-port-data-input-waiting?)
  44. (truncate custom-port-data-truncate))
  45. (define-syntax define-custom-port-dispatcher
  46. (lambda (stx)
  47. (define (prefixed-name prefix suffix)
  48. (datum->syntax suffix (symbol-append prefix (syntax->datum suffix))))
  49. (syntax-case stx ()
  50. ((_ stem arg ...)
  51. (with-syntax ((accessor (prefixed-name 'custom-port-data- #'stem))
  52. (dispatcher (prefixed-name 'custom-port- #'stem)))
  53. #'(define (dispatcher port data arg ...)
  54. ((accessor data) port arg ...)))))))
  55. ;; These bindings are captured by the extension.
  56. (define (custom-port-read port bv start count)
  57. ((custom-port-data-read (%custom-port-data port)) port bv start count))
  58. (define (custom-port-write port bv start count)
  59. ((custom-port-data-write (%custom-port-data port)) port bv start count))
  60. (define-custom-port-dispatcher print out-port)
  61. (define-custom-port-dispatcher read-wait-fd)
  62. (define-custom-port-dispatcher write-wait-fd)
  63. (define-custom-port-dispatcher seek offset whence)
  64. (define-custom-port-dispatcher close)
  65. (define-custom-port-dispatcher get-natural-buffer-sizes read-size write-size)
  66. (define-custom-port-dispatcher random-access?)
  67. (define-custom-port-dispatcher input-waiting?)
  68. (define-custom-port-dispatcher truncate length)
  69. (eval-when (load)
  70. (load-extension (string-append "libguile-" (effective-version))
  71. "scm_init_custom_ports"))
  72. (define* (make-default-print #:key (id "custom-port"))
  73. (lambda (port out-port)
  74. (define mode
  75. (cond
  76. ((port-closed? port) "closed:")
  77. ((input-port? port) (if (output-port? port) "input-output:" "input:"))
  78. ((output-port? port) "output:")
  79. (else "bogus:")))
  80. (put-string out-port "#<")
  81. (put-string out-port mode)
  82. (put-string out-port id)
  83. (put-string out-port " ")
  84. (put-string out-port (number->string (object-address port) 16))
  85. (put-string out-port ">")))
  86. (define (default-read-wait-fd port) #f)
  87. (define (default-write-wait-fd port) #f)
  88. (define (default-seek port offset whence)
  89. (error "custom port did not define a seek method" port))
  90. (define (default-close port) (values))
  91. (define (default-get-natural-buffer-sizes port read-buf-size write-buf-size)
  92. (values read-buf-size write-buf-size))
  93. (define (make-default-random-access? seek)
  94. (if seek
  95. (lambda (port) #t)
  96. (lambda (port) #f)))
  97. (define (default-input-waiting? port) #t)
  98. (define (default-truncate port length)
  99. (error "custom port did not define a truncate method" port))
  100. (define* (make-custom-port
  101. #:key
  102. read
  103. write
  104. (read-wait-fd default-read-wait-fd)
  105. (input-waiting? (and read default-input-waiting?))
  106. (write-wait-fd default-write-wait-fd)
  107. (seek #f)
  108. (random-access? #f)
  109. (close #f)
  110. (get-natural-buffer-sizes default-get-natural-buffer-sizes)
  111. (id "custom-port")
  112. (print (make-default-print #:id id))
  113. (truncate default-truncate)
  114. (encoding (string->symbol (fluid-ref %default-port-encoding)))
  115. (conversion-strategy (fluid-ref %default-port-conversion-strategy))
  116. (close-on-gc? #f))
  117. "Create a custom port whose behavior is determined by the methods passed
  118. as keyword arguments. Supplying a @code{#:read} method will make an input
  119. port, passing @code{#:write} will make an output port, and passing them
  120. both will make an input/output port.
  121. See the manual for full documentation on the semantics of these
  122. methods."
  123. (define (canonicalize-encoding encoding)
  124. (match encoding
  125. (#f 'ISO-8859-1)
  126. ((or 'ISO-8859-1 'UTF-8
  127. 'UTF-16 'UTF-16LE 'UTF-16BE
  128. 'UTF-32 'UTF-32LE 'UTF-32BE) encoding)
  129. ((? symbol?)
  130. (string->symbol (string-upcase (symbol->string encoding))))))
  131. (define (canonicalize-conversion-strategy conversion-strategy)
  132. (match encoding
  133. ('escape 'escape)
  134. ('substitute 'substitute)
  135. (_ 'error)))
  136. (let ((seek (or seek default-seek))
  137. (close (or close default-close))
  138. (random-access? (or random-access?
  139. (if seek (lambda (_) #t) (lambda (_) #f))))
  140. (close-on-gc? (and close close-on-gc?)))
  141. (define data
  142. (make-custom-port-data print read write read-wait-fd write-wait-fd
  143. seek close get-natural-buffer-sizes
  144. random-access? input-waiting? truncate))
  145. (unless (or read write)
  146. (error "Must have at least one I/O method (#:read and #:write)"))
  147. (%make-custom-port (->bool read) (->bool write) data
  148. (canonicalize-encoding encoding)
  149. (canonicalize-conversion-strategy conversion-strategy)
  150. close-on-gc?)))