Reimplement %allocate-instance in Scheme
[bpt/guile.git] / module / ice-9 / rdelim.scm
dissimilarity index 62%
index d21d45c..a406f4e 100644 (file)
-;;; installed-scm-file
-
-;;;; Copyright (C) 1997, 1999, 2000, 2001, 2006 Free Software Foundation, Inc.
-;;;; 
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 2.1 of the License, or (at your option) any later version.
-;;;; 
-;;;; This library is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;;;; Lesser General Public License for more details.
-;;;; 
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-;;;; 
-\f
-
-;;; This is the Scheme part of the module for delimited I/O.  It's
-;;; similar to (scsh rdelim) but somewhat incompatible.
-
-(define-module (ice-9 rdelim)
-  :export (read-line read-line! read-delimited read-delimited!
-          %read-delimited! %read-line write-line)  ; C
-  )
-
-(%init-rdelim-builtins)
-
-(define (read-line! string . maybe-port)
-  ;; corresponds to SCM_LINE_INCREMENTORS in libguile.
-  (define scm-line-incrementors "\n")
-
-  (let* ((port (if (pair? maybe-port)
-                  (car maybe-port)
-                  (current-input-port))))
-    (let* ((rv (%read-delimited! scm-line-incrementors
-                                string
-                                #t
-                                port))
-          (terminator (car rv))
-          (nchars (cdr rv)))
-      (cond ((and (= nchars 0)
-                 (eof-object? terminator))
-            terminator)
-           ((not terminator) #f)
-           (else nchars)))))
-
-(define (read-delimited! delims buf . args)
-  (let* ((num-args (length args))
-        (port (if (> num-args 0)
-                  (car args)
-                  (current-input-port)))
-        (handle-delim (if (> num-args 1)
-                          (cadr args)
-                          'trim))
-        (start (if (> num-args 2)
-                   (caddr args)
-                   0))
-        (end (if (> num-args 3)
-                 (cadddr args)
-                 (string-length buf))))
-    (let* ((rv (%read-delimited! delims
-                                buf
-                                (not (eq? handle-delim 'peek))
-                                port
-                                start
-                                end))
-          (terminator (car rv))
-          (nchars (cdr rv)))
-      (cond ((or (not terminator)      ; buffer filled
-                (eof-object? terminator))
-            (if (zero? nchars)
-                (if (eq? handle-delim 'split)
-                    (cons terminator terminator)
-                    terminator)
-                (if (eq? handle-delim 'split)
-                    (cons nchars terminator)
-                    nchars)))
-           (else
-            (case handle-delim
-              ((trim peek) nchars)
-              ((concat) (string-set! buf (+ nchars start) terminator)
-                        (+ nchars 1))
-              ((split) (cons nchars terminator))
-              (else (error "unexpected handle-delim value: " 
-                           handle-delim))))))))
-  
-(define (read-delimited delims . args)
-  (let* ((port (if (pair? args)
-                  (let ((pt (car args)))
-                    (set! args (cdr args))
-                    pt)
-                  (current-input-port)))
-        (handle-delim (if (pair? args)
-                          (car args)
-                          'trim)))
-    (let loop ((substrings '())
-              (total-chars 0)
-              (buf-size 100))          ; doubled each time through.
-      (let* ((buf (make-string buf-size))
-            (rv (%read-delimited! delims
-                                  buf
-                                  (not (eq? handle-delim 'peek))
-                                  port))
-            (terminator (car rv))
-            (nchars (cdr rv))
-            (join-substrings
-             (lambda ()
-               (apply string-append
-                      (reverse
-                       (cons (if (and (eq? handle-delim 'concat)
-                                      (not (eof-object? terminator)))
-                                 (string terminator)
-                                 "")
-                             (cons (substring buf 0 nchars)
-                                   substrings))))))
-            (new-total (+ total-chars nchars)))
-       (cond ((not terminator)
-              ;; buffer filled.
-              (loop (cons (substring buf 0 nchars) substrings)
-                    new-total
-                    (* buf-size 2)))
-             ((eof-object? terminator)
-              (if (zero? new-total)
-                  (if (eq? handle-delim 'split)
-                      (cons terminator terminator)
-                      terminator)
-                  (if (eq? handle-delim 'split)
-                      (cons (join-substrings) terminator)
-                      (join-substrings))))
-             (else
-              (case handle-delim
-                  ((trim peek concat) (join-substrings))
-                  ((split) (cons (join-substrings) terminator))
-
-
-                  (else (error "unexpected handle-delim value: "
-                               handle-delim)))))))))
-
-;;; read-line [PORT [HANDLE-DELIM]] reads a newline-terminated string
-;;; from PORT.  The return value depends on the value of HANDLE-DELIM,
-;;; which may be one of the symbols `trim', `concat', `peek' and
-;;; `split'.  If it is `trim' (the default), the trailing newline is
-;;; removed and the string is returned.  If `concat', the string is
-;;; returned with the trailing newline intact.  If `peek', the newline
-;;; is left in the input port buffer and the string is returned.  If
-;;; `split', the newline is split from the string and read-line
-;;; returns a pair consisting of the truncated string and the newline.
-
-(define (read-line . args)
-  (let* ((port         (if (null? args)
-                           (current-input-port)
-                           (car args)))
-        (handle-delim  (if (> (length args) 1)
-                           (cadr args)
-                           'trim))
-        (line/delim    (%read-line port))
-        (line          (car line/delim))
-        (delim         (cdr line/delim)))
-    (case handle-delim
-      ((trim) line)
-      ((split) line/delim)
-      ((concat) (if (and (string? line) (char? delim))
-                   (string-append line (string delim))
-                   line))
-      ((peek) (if (char? delim)
-                 (unread-char delim port))
-             line)
-      (else
-       (error "unexpected handle-delim value: " handle-delim)))))
+;;; installed-scm-file
+
+;;;; Copyright (C) 1997, 1999, 2000, 2001, 2006, 2010, 2013,
+;;;;   2014 Free Software Foundation, Inc.
+;;;;
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;; 
+;;;; This library is distributed in the hope that it will be useful,
+;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;;;; Lesser General Public License for more details.
+;;;; 
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;;;; 
+\f
+
+;;; This is the Scheme part of the module for delimited I/O.  It's
+;;; similar to (scsh rdelim) but somewhat incompatible.
+
+(define-module (ice-9 rdelim)
+  #:export (read-line
+            read-line!
+            read-delimited
+            read-delimited!
+            read-string
+            read-string!
+            %read-delimited!
+            %read-line
+            write-line))
+
+
+(%init-rdelim-builtins)
+
+(define* (read-line! string #:optional (port current-input-port))
+  ;; corresponds to SCM_LINE_INCREMENTORS in libguile.
+  (define scm-line-incrementors "\n")
+  (let* ((rv (%read-delimited! scm-line-incrementors
+                               string
+                               #t
+                               port))
+         (terminator (car rv))
+         (nchars (cdr rv)))
+    (cond ((and (= nchars 0)
+                (eof-object? terminator))
+           terminator)
+          ((not terminator) #f)
+          (else nchars))))
+
+(define* (read-delimited! delims buf #:optional
+                          (port (current-input-port)) (handle-delim 'trim)
+                          (start 0) (end (string-length buf)))
+  (let* ((rv (%read-delimited! delims
+                               buf
+                               (not (eq? handle-delim 'peek))
+                               port
+                               start
+                               end))
+         (terminator (car rv))
+         (nchars (cdr rv)))
+    (cond ((or (not terminator)         ; buffer filled
+               (eof-object? terminator))
+           (if (zero? nchars)
+               (if (eq? handle-delim 'split)
+                   (cons terminator terminator)
+                   terminator)
+               (if (eq? handle-delim 'split)
+                   (cons nchars terminator)
+                   nchars)))
+          (else
+           (case handle-delim
+             ((trim peek) nchars)
+             ((concat) (string-set! buf (+ nchars start) terminator)
+              (+ nchars 1))
+             ((split) (cons nchars terminator))
+             (else (error "unexpected handle-delim value: " 
+                          handle-delim)))))))
+  
+(define* (read-delimited delims #:optional (port (current-input-port))
+                         (handle-delim 'trim))
+  (let loop ((substrings '())
+             (total-chars 0)
+             (buf-size 100))           ; doubled each time through.
+    (let* ((buf (make-string buf-size))
+           (rv (%read-delimited! delims
+                                 buf
+                                 (not (eq? handle-delim 'peek))
+                                 port))
+           (terminator (car rv))
+           (nchars (cdr rv))
+           (new-total (+ total-chars nchars)))
+      (cond
+       ((not terminator)
+        ;; buffer filled.
+        (loop (cons (substring buf 0 nchars) substrings)
+              new-total
+              (* buf-size 2)))
+       ((and (eof-object? terminator) (zero? new-total))
+        (if (eq? handle-delim 'split)
+            (cons terminator terminator)
+            terminator))
+       (else
+        (let ((joined
+               (string-concatenate-reverse
+                (cons (substring buf 0 nchars) substrings))))
+          (case handle-delim
+            ((concat)
+             (if (eof-object? terminator)
+                 joined
+                 (string-append joined (string terminator))))
+            ((trim peek) joined)
+            ((split) (cons joined terminator))
+            (else (error "unexpected handle-delim value: "
+                         handle-delim)))))))))
+
+(define-syntax-rule (check-arg exp message arg ...)
+  (unless exp
+    (error message arg ...)))
+
+(define (index? n)
+  (and (integer? n) (exact? n) (>= n 0)))
+
+(define* (read-string! buf #:optional
+                       (port (current-input-port))
+                       (start 0) (end (string-length buf)))
+  "Read all of the characters out of PORT and write them to BUF.
+Returns the number of characters read.
+
+This function only reads out characters from PORT if it will be able to
+write them to BUF.  That is to say, if BUF is smaller than the number of
+available characters, then BUF will be filled, and characters will be
+left in the port."
+  (check-arg (string? buf) "not a string" buf)
+  (check-arg (index? start) "bad index" start)
+  (check-arg (index? end) "bad index" end)
+  (check-arg (<= start end) "start beyond end" start end)
+  (check-arg (<= end (string-length buf)) "end beyond string length" end)
+  (let lp ((n start))
+    (if (< n end)
+        (let ((c (read-char port)))
+          (if (eof-object? c)
+              (- n start)
+              (begin
+                (string-set! buf n c)
+                (lp (1+ n)))))
+        (- n start))))
+
+(define* read-string
+  (case-lambda*
+   "Read all of the characters out of PORT and return them as a string.
+If the COUNT argument is present, treat it as a limit to the number of
+characters to read.  By default, there is no limit."
+   ((#:optional (port (current-input-port)))
+    ;; Fast path.
+    ;; This creates more garbage than using 'string-set!' as in
+    ;; 'read-string!', but currently that is faster nonetheless.
+    (let loop ((chars '()))
+      (let ((char (read-char port)))
+        (if (eof-object? char)
+            (list->string (reverse! chars))
+            (loop (cons char chars))))))
+   ((port count)
+    ;; Slower path.
+    (let loop ((chars '())
+               (total 0))
+      (let ((char (read-char port)))
+        (if (or (eof-object? char) (>= total count))
+            (list->string (reverse chars))
+            (loop (cons char chars) (+ 1 total))))))))
+
+
+;;; read-line [PORT [HANDLE-DELIM]] reads a newline-terminated string
+;;; from PORT.  The return value depends on the value of HANDLE-DELIM,
+;;; which may be one of the symbols `trim', `concat', `peek' and
+;;; `split'.  If it is `trim' (the default), the trailing newline is
+;;; removed and the string is returned.  If `concat', the string is
+;;; returned with the trailing newline intact.  If `peek', the newline
+;;; is left in the input port buffer and the string is returned.  If
+;;; `split', the newline is split from the string and read-line
+;;; returns a pair consisting of the truncated string and the newline.
+
+(define* (read-line #:optional (port (current-input-port))
+                    (handle-delim 'trim))
+  (let* ((line/delim   (%read-line port))
+        (line          (car line/delim))
+        (delim         (cdr line/delim)))
+    (case handle-delim
+      ((trim) line)
+      ((split) line/delim)
+      ((concat) (if (and (string? line) (char? delim))
+                   (string-append line (string delim))
+                   line))
+      ((peek) (if (char? delim)
+                 (unread-char delim port))
+             line)
+      (else
+       (error "unexpected handle-delim value: " handle-delim)))))