io.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       io.lisp (8280B)
       ---
            1 ;; Copyright (c) 2002-2006, Edward Marco Baringer
            2 ;; All rights reserved.
            3 
            4 (in-package :alexandria)
            5 
            6 (defmacro with-open-file* ((stream filespec &key direction element-type
            7                                    if-exists if-does-not-exist external-format)
            8                            &body body)
            9   "Just like WITH-OPEN-FILE, but NIL values in the keyword arguments mean to use
           10 the default value specified for OPEN."
           11   (once-only (direction element-type if-exists if-does-not-exist external-format)
           12     `(with-open-stream
           13          (,stream (apply #'open ,filespec
           14                          (append
           15                           (when ,direction
           16                             (list :direction ,direction))
           17                           (when ,element-type
           18                             (list :element-type ,element-type))
           19                           (when ,if-exists
           20                             (list :if-exists ,if-exists))
           21                           (when ,if-does-not-exist
           22                             (list :if-does-not-exist ,if-does-not-exist))
           23                           (when ,external-format
           24                             (list :external-format ,external-format)))))
           25        ,@body)))
           26 
           27 (defmacro with-input-from-file ((stream-name file-name &rest args
           28                                              &key (direction nil direction-p)
           29                                              &allow-other-keys)
           30                                 &body body)
           31   "Evaluate BODY with STREAM-NAME to an input stream on the file
           32 FILE-NAME. ARGS is sent as is to the call to OPEN except EXTERNAL-FORMAT,
           33 which is only sent to WITH-OPEN-FILE when it's not NIL."
           34   (declare (ignore direction))
           35   (when direction-p
           36     (error "Can't specify :DIRECTION for WITH-INPUT-FROM-FILE."))
           37   `(with-open-file* (,stream-name ,file-name :direction :input ,@args)
           38      ,@body))
           39 
           40 (defmacro with-output-to-file ((stream-name file-name &rest args
           41                                             &key (direction nil direction-p)
           42                                             &allow-other-keys)
           43                                &body body)
           44   "Evaluate BODY with STREAM-NAME to an output stream on the file
           45 FILE-NAME. ARGS is sent as is to the call to OPEN except EXTERNAL-FORMAT,
           46 which is only sent to WITH-OPEN-FILE when it's not NIL."
           47   (declare (ignore direction))
           48   (when direction-p
           49     (error "Can't specify :DIRECTION for WITH-OUTPUT-TO-FILE."))
           50   `(with-open-file* (,stream-name ,file-name :direction :output ,@args)
           51      ,@body))
           52 
           53 (defun read-stream-content-into-string (stream &key (buffer-size 4096))
           54   "Return the \"content\" of STREAM as a fresh string."
           55   (check-type buffer-size positive-integer)
           56   (let ((*print-pretty* nil))
           57     (with-output-to-string (datum)
           58       (let ((buffer (make-array buffer-size :element-type 'character)))
           59         (loop
           60           :for bytes-read = (read-sequence buffer stream)
           61           :do (write-sequence buffer datum :start 0 :end bytes-read)
           62           :while (= bytes-read buffer-size))))))
           63 
           64 (defun read-file-into-string (pathname &key (buffer-size 4096) external-format)
           65   "Return the contents of the file denoted by PATHNAME as a fresh string.
           66 
           67 The EXTERNAL-FORMAT parameter will be passed directly to WITH-OPEN-FILE
           68 unless it's NIL, which means the system default."
           69   (with-input-from-file
           70       (file-stream pathname :external-format external-format)
           71     (read-stream-content-into-string file-stream :buffer-size buffer-size)))
           72 
           73 (defun write-string-into-file (string pathname &key (if-exists :error)
           74                                                     if-does-not-exist
           75                                                     external-format)
           76   "Write STRING to PATHNAME.
           77 
           78 The EXTERNAL-FORMAT parameter will be passed directly to WITH-OPEN-FILE
           79 unless it's NIL, which means the system default."
           80   (with-output-to-file (file-stream pathname :if-exists if-exists
           81                                     :if-does-not-exist if-does-not-exist
           82                                     :external-format external-format)
           83     (write-sequence string file-stream)))
           84 
           85 (defun read-stream-content-into-byte-vector (stream &key ((%length length))
           86                                                          (initial-size 4096))
           87   "Return \"content\" of STREAM as freshly allocated (unsigned-byte 8) vector."
           88   (check-type length (or null non-negative-integer))
           89   (check-type initial-size positive-integer)
           90   (do ((buffer (make-array (or length initial-size)
           91                            :element-type '(unsigned-byte 8)))
           92        (offset 0)
           93        (offset-wanted 0))
           94       ((or (/= offset-wanted offset)
           95            (and length (>= offset length)))
           96        (if (= offset (length buffer))
           97            buffer
           98            (subseq buffer 0 offset)))
           99     (unless (zerop offset)
          100       (let ((new-buffer (make-array (* 2 (length buffer))
          101                                     :element-type '(unsigned-byte 8))))
          102         (replace new-buffer buffer)
          103         (setf buffer new-buffer)))
          104     (setf offset-wanted (length buffer)
          105           offset (read-sequence buffer stream :start offset))))
          106 
          107 (defun read-file-into-byte-vector (pathname)
          108   "Read PATHNAME into a freshly allocated (unsigned-byte 8) vector."
          109   (with-input-from-file (stream pathname :element-type '(unsigned-byte 8))
          110     (read-stream-content-into-byte-vector stream '%length (file-length stream))))
          111 
          112 (defun write-byte-vector-into-file (bytes pathname &key (if-exists :error)
          113                                                        if-does-not-exist)
          114   "Write BYTES to PATHNAME."
          115   (check-type bytes (vector (unsigned-byte 8)))
          116   (with-output-to-file (stream pathname :if-exists if-exists
          117                                :if-does-not-exist if-does-not-exist
          118                                :element-type '(unsigned-byte 8))
          119     (write-sequence bytes stream)))
          120 
          121 (defun copy-file (from to &key (if-to-exists :supersede)
          122                                (element-type '(unsigned-byte 8)) finish-output)
          123   (with-input-from-file (input from :element-type element-type)
          124     (with-output-to-file (output to :element-type element-type
          125                                     :if-exists if-to-exists)
          126       (copy-stream input output
          127                    :element-type element-type
          128                    :finish-output finish-output))))
          129 
          130 (defun copy-stream (input output &key (element-type (stream-element-type input))
          131                     (buffer-size 4096)
          132                     (buffer (make-array buffer-size :element-type element-type))
          133                     (start 0) end
          134                     finish-output)
          135   "Reads data from INPUT and writes it to OUTPUT. Both INPUT and OUTPUT must
          136 be streams, they will be passed to READ-SEQUENCE and WRITE-SEQUENCE and must have
          137 compatible element-types."
          138   (check-type start non-negative-integer)
          139   (check-type end (or null non-negative-integer))
          140   (check-type buffer-size positive-integer)
          141   (when (and end
          142              (< end start))
          143     (error "END is smaller than START in ~S" 'copy-stream))
          144   (let ((output-position 0)
          145         (input-position 0))
          146     (unless (zerop start)
          147       ;; FIXME add platform specific optimization to skip seekable streams
          148       (loop while (< input-position start)
          149             do (let ((n (read-sequence buffer input
          150                                        :end (min (length buffer)
          151                                                  (- start input-position)))))
          152                  (when (zerop n)
          153                    (error "~@<Could not read enough bytes from the input to fulfill ~
          154                            the :START ~S requirement in ~S.~:@>" 'copy-stream start))
          155                  (incf input-position n))))
          156     (assert (= input-position start))
          157     (loop while (or (null end) (< input-position end))
          158           do (let ((n (read-sequence buffer input
          159                                      :end (when end
          160                                             (min (length buffer)
          161                                                  (- end input-position))))))
          162                (when (zerop n)
          163                  (if end
          164                      (error "~@<Could not read enough bytes from the input to fulfill ~
          165                           the :END ~S requirement in ~S.~:@>" 'copy-stream end)
          166                      (return)))
          167                (incf input-position n)
          168                (write-sequence buffer output :end n)
          169                (incf output-position n)))
          170     (when finish-output
          171       (finish-output output))
          172     output-position))