~ chicken-core (chicken-5) /port.scm
Trap1;;; port.scm - Optional non-standard ports2;3; Copyright (c) 2008-2022, The CHICKEN Team4; Copyright (c) 2000-2007, Felix L. Winkelmann5; All rights reserved.6;7; Redistribution and use in source and binary forms, with or without8; modification, are permitted provided that the following conditions9; are met:10;11; Redistributions of source code must retain the above copyright12; notice, this list of conditions and the following disclaimer.13; Redistributions in binary form must reproduce the above copyright14; notice, this list of conditions and the following disclaimer in15; the documentation and/or other materials provided with the16; distribution.17; Neither the name of the author nor the names of its contributors18; may be used to endorse or promote products derived from this19; software without specific prior written permission.20;21; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS22; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT23; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS24; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE25; COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,26; INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES27; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR28; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)29; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,30; STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)31; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED32; OF THE POSSIBILITY OF SUCH DAMAGE.333435(declare36 (unit port)37 (uses extras))3839(module chicken.port40 (call-with-input-string41 call-with-output-string42 copy-port43 make-input-port44 make-output-port45 port-fold46 port-for-each47 port-map48 port-name49 port-position50 make-bidirectional-port51 make-broadcast-port52 make-concatenated-port53 set-buffering-mode!54 set-port-name!55 terminal-name56 terminal-port?57 terminal-size58 with-error-output-to-port59 with-input-from-port60 with-input-from-string61 with-output-to-port62 with-output-to-string63 with-error-output-to-string)6465(import scheme66 chicken.base67 chicken.fixnum68 chicken.foreign69 chicken.io)7071(include "common-declarations.scm")7273#>7475#if !defined(_WIN32)76# include <sys/ioctl.h>77# include <termios.h>78#endif7980#if !defined(__ANDROID__) && defined(TIOCGWINSZ)81static int get_tty_size(int fd, int *rows, int *cols)82{83 struct winsize tty_size;84 int r;8586 memset(&tty_size, 0, sizeof tty_size);8788 r = ioctl(fd, TIOCGWINSZ, &tty_size);89 if (r == 0) {90 *rows = tty_size.ws_row;91 *cols = tty_size.ws_col;92 }93 return r;94}95#else96static int get_tty_size(int fd, int *rows, int *cols)97{98 *rows = *cols = 0;99 errno = ENOSYS;100 return -1;101}102#endif103104#if defined(_WIN32) && !defined(__CYGWIN__)105char *ttyname(int fd) {106 errno = ENOSYS;107 return NULL;108}109#endif110111<#112113114(define-foreign-variable _iofbf int "_IOFBF")115(define-foreign-variable _iolbf int "_IOLBF")116(define-foreign-variable _ionbf int "_IONBF")117(define-foreign-variable _bufsiz int "BUFSIZ")118119(define (port-name #!optional (port ##sys#standard-input))120 (##sys#check-port port 'port-name)121 (##sys#slot port 3))122123(define (set-port-name! port name)124 (##sys#check-port port 'set-port-name!)125 (##sys#check-string name 'set-port-name!)126 (##sys#setslot port 3 name))127128(define (port-position #!optional (port ##sys#standard-input))129 (##sys#check-port port 'port-position)130 (if (##core#inline "C_input_portp" port)131 (##sys#values (##sys#slot port 4) (##sys#slot port 5))132 (##sys#error 'port-position "cannot compute position of port" port)))133134(define (set-buffering-mode! port mode . size)135 (##sys#check-port port 'set-buffering-mode!)136 (let ((size (if (pair? size) (car size) _bufsiz))137 (mode (case mode138 ((#:full) _iofbf)139 ((#:line) _iolbf)140 ((#:none) _ionbf)141 (else (##sys#error 'set-buffering-mode! "invalid buffering-mode" mode port)))))142 (##sys#check-fixnum size 'set-buffering-mode!)143 (when (fx< (if (eq? 'stream (##sys#slot port 7))144 ((foreign-lambda* int ((scheme-object p) (int m) (int s))145 "C_return(setvbuf(C_port_file(p), NULL, m, s));")146 port mode size)147 -1)148 0)149 (##sys#error 'set-buffering-mode! "cannot set buffering mode" port mode size))))150151;;;; Port-mapping (found in Gauche):152153(define (port-for-each fn thunk)154 (let loop ()155 (let ((x (thunk)))156 (unless (eof-object? x)157 (fn x)158 (loop) ) ) ) )159160(define port-map161 (lambda (fn thunk)162 (let loop ((xs '()))163 (let ((x (thunk)))164 (if (eof-object? x)165 (##sys#fast-reverse xs)166 (loop (cons (fn x) xs)))))))167168(define (port-fold fn acc thunk)169 (let loop ((acc acc))170 (let ((x (thunk)))171 (if (eof-object? x)172 acc173 (loop (fn x acc))) ) ) )174175(define-constant +buf-size+ 1024)176177(define copy-port178 (let ((read-char read-char)179 (write-char write-char))180 (define (read-buf port writer)181 (let ((buf (make-string +buf-size+)))182 (let loop ()183 (let ((n (read-string! +buf-size+ buf port)))184 (unless (eq? n 0)185 (writer buf n)186 (loop))))))187 (define (write-buf buf n port writer)188 (do ((i 0 (fx+ i 1)))189 ((fx>= i n))190 (writer (integer->char (##sys#byte buf i)) port)))191 (define (read-and-write reader writer)192 (let loop ()193 (let ((x (reader)))194 (unless (eof-object? x)195 (writer x)196 (loop)))))197 (define (read-and-write-buf src dest reader)198 (let ((buf (make-string +buf-size+)))199 (let loop ((n 0))200 (when (fx>= n +buf-size+)201 (write-string buf +buf-size+ dest)202 (set! n 0))203 (let ((c (reader src)))204 (cond ((eof-object? c)205 (when (fx>= n 0)206 (write-string buf n dest)))207 (else208 (##sys#setbyte buf n (char->integer c))209 (loop (fx+ n 1))))))))210 (lambda (src dest #!optional (read read-char) (write write-char))211 ;; does not check port args intentionally212 (cond ((eq? read read-char)213 (read-buf214 src215 (if (eq? write write-char)216 (lambda (buf n) (write-string buf n dest))217 (lambda (buf n) (write-buf buf n dest write)))))218 ((eq? write write-char)219 (read-and-write-buf src dest read))220 (else221 (read-and-write222 (lambda () (read src))223 (lambda (x) (write x dest))))))))224225226;;;; funky-ports227228(define (make-broadcast-port . ports)229 (make-output-port230 (lambda (s) (for-each (cut write-string s #f <>) ports))231 void232 (lambda () (for-each flush-output ports)) ) )233234(define (make-concatenated-port p1 . ports)235 (let ((ports (cons p1 ports)))236 ;;XXX should also forward other port-methods237 (make-input-port238 (lambda ()239 (let loop ()240 (if (null? ports)241 #!eof242 (let ((c (read-char (car ports))))243 (cond ((eof-object? c)244 (set! ports (cdr ports))245 (loop) )246 (else c) ) ) ) ) )247 (lambda ()248 (and (not (null? ports))249 (char-ready? (car ports))))250 void251 (lambda ()252 (let loop ()253 (if (null? ports)254 #!eof255 (let ((c (peek-char (car ports))))256 (cond ((eof-object? c)257 (set! ports (cdr ports))258 (loop) )259 (else c))))))260 (lambda (p n dest start)261 (let loop ((n n) (c 0))262 (cond ((null? ports) c)263 ((fx<= n 0) c)264 (else265 (let ((m (read-string! n dest (car ports) (fx+ start c))))266 (when (fx< m n)267 (set! ports (cdr ports)) )268 (loop (fx- n m) (fx+ c m))))))))))269270271;;; Redirect standard ports:272273(define (with-input-from-port port thunk)274 (##sys#check-input-port port #t 'with-input-from-port)275 (fluid-let ((##sys#standard-input port))276 (thunk) ) )277278(define (with-output-to-port port thunk)279 (##sys#check-output-port port #t 'with-output-to-port)280 (fluid-let ((##sys#standard-output port))281 (thunk) ) )282283(define (with-error-output-to-port port thunk)284 (##sys#check-output-port port #t 'with-error-output-to-port)285 (fluid-let ((##sys#standard-error port))286 (thunk) ) )287288;;; Extended string-port operations:289290(define call-with-input-string291 (lambda (str proc)292 (let ((in (open-input-string str)))293 (proc in) ) ) )294295(define call-with-output-string296 (lambda (proc)297 (let ((out (open-output-string)))298 (proc out)299 (get-output-string out) ) ) )300301(define with-input-from-string302 (lambda (str thunk)303 (fluid-let ([##sys#standard-input (open-input-string str)])304 (thunk) ) ) )305306(define with-output-to-string307 (lambda (thunk)308 (fluid-let ((##sys#standard-output (open-output-string)))309 (thunk)310 (get-output-string ##sys#standard-output) ) ) )311312(define with-error-output-to-string313 (lambda (thunk)314 (fluid-let ((##sys#standard-error (open-output-string)))315 (thunk)316 (get-output-string ##sys#standard-error) ) ) )317318;;; Custom ports:319;320; - Port-slots:321;322; 10: last323324(define make-input-port325 (lambda (read ready? close #!optional peek read-string read-line read-buffered)326 (let* ((class327 (vector328 (lambda (p) ; read-char329 (let ([last (##sys#slot p 10)])330 (cond [peek (read)]331 [last332 (##sys#setislot p 10 #f)333 last]334 [else (read)] ) ) )335 (lambda (p) ; peek-char336 (let ([last (##sys#slot p 10)])337 (cond [peek (peek)]338 [last last]339 [else340 (let ([last (read)])341 (##sys#setslot p 10 last)342 last) ] ) ) )343 #f ; write-char344 #f ; write-string345 (lambda (p d) ; close346 (close))347 #f ; flush-output348 (lambda (p) ; char-ready?349 (ready?) )350 read-string ; read-string!351 read-line ; read-line352 read-buffered))353 (data (vector #f))354 (port (##sys#make-port 1 class "(custom)" 'custom)))355 (##sys#set-port-data! port data)356 port) ) )357358(define make-output-port359 (lambda (write close #!optional flush)360 (let* ((class361 (vector362 #f ; read-char363 #f ; peek-char364 (lambda (p c) ; write-char365 (write (string c)) )366 (lambda (p s) ; write-string367 (write s) )368 (lambda (p d) ; close369 (close))370 (lambda (p) ; flush-output371 (when flush (flush)) )372 #f ; char-ready?373 #f ; read-string!374 #f) ) ; read-line375 (data (vector #f))376 (port (##sys#make-port 2 class "(custom)" 'custom)))377 (##sys#set-port-data! port data)378 port) ) )379380(define (make-bidirectional-port i o)381 (let* ((class (vector382 (lambda (_) ; read-char383 (read-char i))384 (lambda (_) ; peek-char385 (peek-char i))386 (lambda (_ c) ; write-char387 (write-char c o))388 (lambda (_ s) ; write-string389 (write-string s #f o))390 (lambda (_ d) ; close391 (case d392 ((1) (close-input-port i))393 ((2) (close-output-port o))))394 (lambda (_) ; flush-output395 (flush-output o))396 (lambda (_) ; char-ready?397 (char-ready? i))398 (lambda (_ n d s) ; read-string!399 (read-string! n d i s))400 (lambda (_ l) ; read-line401 (read-line i l))402 (lambda () ; read-buffered403 (read-buffered i))))404 (port (##sys#make-port 3 class "(bidirectional)" 'bidirectional)))405 (##sys#set-port-data! port (vector #f))406 port))407408;; Duplication from posix-common.scm409(define posix-error410 (let ((strerror (foreign-lambda c-string "strerror" int))411 (string-append string-append))412 (lambda (type loc msg . args)413 (let ((rn (##sys#update-errno)))414 (apply ##sys#signal-hook/errno415 type rn loc (string-append msg " - " (strerror rn)) args)))))416417;; Terminal ports418(define (terminal-port? port)419 (##sys#check-open-port port 'terminal-port?)420 (let ((fp (##sys#peek-unsigned-integer port 0)))421 (and (not (eq? 0 fp)) (##core#inline "C_tty_portp" port))))422423(define (check-terminal! caller port)424 (##sys#check-open-port port caller)425 (unless (and (eq? 'stream (##sys#slot port 7))426 (##core#inline "C_tty_portp" port))427 (##sys#error caller "port is not connected to a terminal" port)))428429(define terminal-name430 (let ((ttyname (foreign-lambda c-string "ttyname" int)))431 (lambda (port)432 (check-terminal! 'terminal-name port)433 (or (ttyname (##core#inline "C_port_fileno" port))434 (posix-error #:error 'terminal-name435 "cannot determine terminal name" port)))))436437(define terminal-size438 (let ((ttysize (foreign-lambda int "get_tty_size" int439 (nonnull-c-pointer int)440 (nonnull-c-pointer int))))441 (lambda (port)442 (check-terminal! 'terminal-size port)443 (let-location ((columns int)444 (rows int))445 (if (fx= 0 (ttysize (##core#inline "C_port_fileno" port)446 (location columns)447 (location rows)))448 (values columns rows)449 (posix-error #:error 'terminal-size450 "cannot determine terminal size" port))))))451452)