gnu: magit: Fix rebase commands that require perl.
[jackhill/guix/guix.git] / guix / zlib.scm
CommitLineData
72153902
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2016 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix zlib)
20 #:use-module (rnrs bytevectors)
21 #:use-module (ice-9 binary-ports)
22 #:use-module (ice-9 match)
23 #:use-module (system foreign)
24 #:use-module (guix config)
25 #:export (zlib-available?
26 make-gzip-input-port
27 make-gzip-output-port
28 call-with-gzip-input-port
29 call-with-gzip-output-port
30 %default-buffer-size
31 %default-compression-level))
32
33;;; Commentary:
34;;;
35;;; Bindings to the gzip-related part of zlib's API. The main limitation of
36;;; this API is that it requires a file descriptor as the source or sink.
37;;;
38;;; Code:
39
40(define %zlib
41 ;; File name of zlib's shared library. When updating via 'guix pull',
42 ;; '%libz' might be undefined so protect against it.
43 (delay (dynamic-link (if (defined? '%libz)
44 %libz
45 "libz"))))
46
47(define (zlib-available?)
48 "Return true if zlib is available, #f otherwise."
49 (false-if-exception (force %zlib)))
50
51(define (zlib-procedure ret name parameters)
52 "Return a procedure corresponding to C function NAME in libz, or #f if
53either zlib or the function could not be found."
54 (match (false-if-exception (dynamic-func name (force %zlib)))
55 ((? pointer? ptr)
56 (pointer->procedure ret ptr parameters))
57 (#f
58 #f)))
59
60(define-wrapped-pointer-type <gzip-file>
61 ;; Scheme counterpart of the 'gzFile' opaque type.
62 gzip-file?
63 pointer->gzip-file
64 gzip-file->pointer
65 (lambda (obj port)
66 (format port "#<gzip-file ~a>"
67 (number->string (object-address obj) 16))))
68
69(define gzerror
70 (let ((proc (zlib-procedure '* "gzerror" '(* *))))
71 (lambda (gzfile)
72 (let* ((errnum* (make-bytevector (sizeof int)))
73 (ptr (proc (gzip-file->pointer gzfile)
74 (bytevector->pointer errnum*))))
75 (values (bytevector-sint-ref errnum* 0
76 (native-endianness) (sizeof int))
77 (pointer->string ptr))))))
78
79(define gzdopen
80 (let ((proc (zlib-procedure '* "gzdopen" (list int '*))))
81 (lambda (fd mode)
82 "Open file descriptor FD as a gzip stream with the given MODE. MODE must
83be a string denoting the how FD is to be opened, such as \"r\" for reading or
84\"w9\" for writing data compressed at level 9 to FD. Calling 'gzclose' also
85closes FD."
86 (let ((result (proc fd (string->pointer mode))))
87 (if (null-pointer? result)
88 (throw 'zlib-error 'gzdopen)
89 (pointer->gzip-file result))))))
90
91(define gzread!
92 (let ((proc (zlib-procedure int "gzread" (list '* '* unsigned-int))))
93 (lambda* (gzfile bv #:optional (start 0) (count (bytevector-length bv)))
94 "Read up to COUNT bytes from GZFILE into BV at offset START. Return the
95number of uncompressed bytes actually read."
96 (let ((ret (proc (gzip-file->pointer gzfile)
97 (bytevector->pointer bv start)
98 count)))
99 (if (< ret 0)
100 (throw 'zlib-error 'gzread! ret)
101 ret)))))
102
103(define gzwrite
104 (let ((proc (zlib-procedure int "gzwrite" (list '* '* unsigned-int))))
105 (lambda* (gzfile bv #:optional (start 0) (count (bytevector-length bv)))
106 "Write up to COUNT bytes from BV at offset START into GZFILE. Return
107the number of uncompressed bytes written, a strictly positive integer."
108 (let ((ret (proc (gzip-file->pointer gzfile)
109 (bytevector->pointer bv start)
110 count)))
111 (if (<= ret 0)
112 (throw 'zlib-error 'gzwrite ret)
113 ret)))))
114
115(define gzbuffer!
116 (let ((proc (zlib-procedure int "gzbuffer" (list '* unsigned-int))))
117 (lambda (gzfile size)
118 "Change the internal buffer size of GZFILE to SIZE bytes."
119 (let ((ret (proc (gzip-file->pointer gzfile) size)))
120 (unless (zero? ret)
121 (throw 'zlib-error 'gzbuffer! ret))))))
122
123(define gzeof?
124 (let ((proc (zlib-procedure int "gzeof" '(*))))
125 (lambda (gzfile)
126 "Return true if the end-of-file has been reached on GZFILE."
127 (not (zero? (proc (gzip-file->pointer gzfile)))))))
128
129(define gzclose
130 (let ((proc (zlib-procedure int "gzclose" '(*))))
131 (lambda (gzfile)
132 "Close GZFILE."
133 (let ((ret (proc (gzip-file->pointer gzfile))))
134 (unless (zero? ret)
135 (throw 'zlib-error 'gzclose ret (gzerror gzfile)))))))
136
137
138\f
139;;;
140;;; Port interface.
141;;;
142
143(define %default-buffer-size
144 ;; Default buffer size, as documented in <zlib.h>.
145 8192)
146
147(define %default-compression-level
148 ;; Z_DEFAULT_COMPRESSION.
149 -1)
150
151(define (close-procedure gzfile port)
152 "Return a procedure that closes GZFILE, ensuring its underlying PORT is
153closed even if closing GZFILE triggers an exception."
154 (lambda ()
155 (catch 'zlib-error
156 (lambda ()
157 ;; 'gzclose' closes the underlying file descriptor. 'close-port'
158 ;; calls close(2), gets EBADF, which is ignores.
159 (gzclose gzfile)
160 (close-port port))
161 (lambda args
162 ;; Make sure PORT is closed despite the zlib error.
163 (close-port port)
164 (apply throw args)))))
165
166(define* (make-gzip-input-port port #:key (buffer-size %default-buffer-size))
167 "Return an input port that decompresses data read from PORT, a file port.
168PORT is automatically closed when the resulting port is closed. BUFFER-SIZE
169is the size in bytes of the internal buffer, 8 KiB by default; using a larger
170buffer increases decompression speed."
171 (define gzfile
172 (gzdopen (fileno port) "r"))
173
174 (define (read! bv start count)
175 ;; XXX: Can 'gzread!' return zero even though we haven't reached the EOF?
176 (gzread! gzfile bv start count))
177
178 (unless (= buffer-size %default-buffer-size)
179 (gzbuffer! gzfile buffer-size))
180
181 (make-custom-binary-input-port "gzip-input" read! #f #f
182 (close-procedure gzfile port)))
183
184(define* (make-gzip-output-port port
185 #:key
186 (level %default-compression-level)
187 (buffer-size %default-buffer-size))
188 "Return an output port that compresses data at the given LEVEL, using PORT,
189a file port, as its sink. PORT is automatically closed when the resulting
190port is closed."
191 (define gzfile
192 (gzdopen (fileno port)
193 (string-append "w" (number->string level))))
194
195 (define (write! bv start count)
196 (gzwrite gzfile bv start count))
197
198 (unless (= buffer-size %default-buffer-size)
199 (gzbuffer! gzfile buffer-size))
200
201 (make-custom-binary-output-port "gzip-output" write! #f #f
202 (close-procedure gzfile port)))
203
204(define* (call-with-gzip-input-port port proc
205 #:key (buffer-size %default-buffer-size))
206 "Call PROC with a port that wraps PORT and decompresses data read from it.
207PORT is closed upon completion. The gzip internal buffer size is set to
208BUFFER-SIZE bytes."
209 (let ((gzip (make-gzip-input-port port #:buffer-size buffer-size)))
210 (dynamic-wind
211 (const #t)
212 (lambda ()
213 (proc gzip))
214 (lambda ()
215 (close-port gzip)))))
216
217(define* (call-with-gzip-output-port port proc
218 #:key
219 (level %default-compression-level)
220 (buffer-size %default-buffer-size))
221 "Call PROC with an output port that wraps PORT and compresses data. PORT is
222close upon completion. The gzip internal buffer size is set to BUFFER-SIZE
223bytes."
224 (let ((gzip (make-gzip-output-port port
225 #:level level
226 #:buffer-size buffer-size)))
227 (dynamic-wind
228 (const #t)
229 (lambda ()
230 (proc gzip))
231 (lambda ()
232 (close-port gzip)))))
233
234;;; zlib.scm ends here