source: libcaca/trunk/caca/file.c @ 4318

Last change on this file since 4318 was 4318, checked in by Sam Hocevar, 10 years ago

Document file functions and fix a few issues here and there.

File size: 8.5 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2006-2007 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id$
7 *
8 *  This library is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  This file contains functions for compressed file I/O.
17 */
18
19#include "config.h"
20
21#if !defined __KERNEL__
22#   include <stdio.h>
23#   include <stdlib.h>
24#   include <string.h>
25#   if defined HAVE_ZLIB_H
26#       include <zlib.h>
27#       define READSIZE  128 /* Read buffer size */
28#       define WRITESIZE 128 /* Inflate buffer size */
29#   endif
30#endif
31
32#include "caca.h"
33#include "caca_internals.h"
34
35#if !defined __KERNEL__ && defined HAVE_ZLIB_H
36static int zipread(caca_file_t *, void *, unsigned int);
37#endif
38
39#if !defined __KERNEL__
40struct caca_file
41{
42#   if defined HAVE_ZLIB_H
43    uint8_t read_buffer[READSIZE];
44    z_stream stream;
45    gzFile gz;
46    int eof, zip, total;
47#   endif
48    FILE *f;
49    int readonly;
50};
51#endif
52
53/** \brief Open a file for reading or writing
54 *
55 *  Create a caca file handle for a file. If the file is zipped, it is
56 *  decompressed on the fly.
57 *
58 *  If an error occurs, NULL is returned and \b errno is set accordingly:
59 *  - \c ENOSTS Function not implemented.
60 *  - \c EINVAL File not found or permission denied.
61 *
62 *  \param path The file path
63 *  \param mode The file open mode
64 *  \return A file handle to \e path.
65 */
66caca_file_t *caca_file_open(char const *path, const char *mode)
67{
68#if defined __KERNEL__
69    seterrno(ENOSYS);
70    return NULL;
71#else
72    caca_file_t *fp = malloc(sizeof(*fp));
73
74    fp->readonly = !!strchr(mode, 'r');
75
76#   if defined HAVE_ZLIB_H
77    uint8_t buf[4];
78    unsigned int skip_size = 0;
79
80    fp->gz = gzopen(path, fp->readonly ? "rb" : "wb");
81    if(!fp->gz)
82    {
83        free(fp);
84        seterrno(EINVAL);
85        return NULL;
86    }
87
88    fp->eof = 0;
89    fp->zip = 0;
90    fp->total = 0;
91
92    if(fp->readonly)
93    {
94        /* Parse ZIP file and go to start of first file */
95        gzread(fp->gz, buf, 4);
96        if(memcmp(buf, "PK\3\4", 4))
97        {
98            gzseek(fp->gz, 0, SEEK_SET);
99            return fp;
100        }
101
102        fp->zip = 1;
103
104        gzseek(fp->gz, 22, SEEK_CUR);
105
106        gzread(fp->gz, buf, 2); /* Filename size */
107        skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
108        gzread(fp->gz, buf, 2); /* Extra field size */
109        skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
110
111        gzseek(fp->gz, skip_size, SEEK_CUR);
112
113        /* Initialise inflate stream */
114        fp->stream.total_out = 0;
115        fp->stream.zalloc = NULL;
116        fp->stream.zfree = NULL;
117        fp->stream.opaque = NULL;
118        fp->stream.next_in = NULL;
119        fp->stream.avail_in = 0;
120
121        if(inflateInit2(&fp->stream, -MAX_WBITS))
122        {
123            free(fp);
124            gzclose(fp->gz);
125            seterrno(EINVAL);
126            return NULL;
127        }
128    }
129#   else
130    fp->f = fopen(path, mode);
131
132    if(!fp->f)
133    {
134        free(fp);
135        seterrno(EINVAL);
136        return NULL;
137    }
138#   endif
139
140    return fp;
141#endif
142}
143
144/** \brief Close a file handle
145 *
146 *  Close and destroy the resources associated with a caca file handle.
147 *
148 *  This function is a wrapper for fclose() or, if available, gzclose().
149 *
150 *  \param fp The file handle
151 *  \return The return value of fclose() or gzclose().
152 */
153int caca_file_close(caca_file_t *fp)
154{
155#if defined __KERNEL__
156    seterrno(ENOSYS);
157    return 0;
158#elif defined HAVE_ZLIB_H
159    gzFile gz = fp->gz;
160    if(fp->zip)
161        inflateEnd(&fp->stream);
162    free(fp);
163    return gzclose(gz);
164#else
165    FILE *f = fp->f;
166    free(fp);
167    return fclose(f);
168#endif
169}
170
171/** \brief Return the position in a file handle
172 *
173 *  Return the file handle position, in bytes.
174 *
175 *  \param fp The file handle
176 *  \return The current offset in the file handle.
177 */
178uint64_t caca_file_tell(caca_file_t *fp)
179{
180#if defined __KERNEL__
181    seterrno(ENOSYS);
182    return 0;
183#elif defined HAVE_ZLIB_H
184    if(fp->zip)
185        return fp->total;
186    return gztell(fp->gz);
187#else
188    return ftell(fp->f);
189#endif
190}
191
192/** \brief Read data from a file handle
193 *
194 *  Read data from a file handle and copy them into the given buffer.
195 *
196 *  \param fp The file handle
197 *  \param ptr The destination buffer
198 *  \param size The number of bytes to read
199 *  \return The number of bytes read
200 */
201size_t caca_file_read(caca_file_t *fp, void *ptr, size_t size)
202{
203#if defined __KERNEL__
204    seterrno(ENOSYS);
205    return 0;
206#elif defined HAVE_ZLIB_H
207    if(fp->zip)
208        return zipread(fp, ptr, size);
209    return gzread(fp->gz, ptr, size);
210#else
211    return fread(ptr, 1, size, fp->f);
212#endif
213}
214
215/** \brief Write data to a file handle
216 *
217 *  Write the contents of the given buffer to the file handle.
218 *
219 *  \param fp The file handle
220 *  \param ptr The source buffer
221 *  \param size The number of bytes to write
222 *  \return The number of bytes written
223 */
224size_t caca_file_write(caca_file_t *fp, const void *ptr, size_t size)
225{
226#if defined __KERNEL__
227    seterrno(ENOSYS);
228    return 0;
229#else
230    if(fp->readonly)
231        return 0;
232
233#   if defined HAVE_ZLIB_H
234    if(fp->zip)
235    {
236        /* FIXME: zip files are not supported */
237        seterrno(ENOSYS);
238        return 0;
239    }
240    return gzwrite(fp->gz, ptr, size);
241#   else
242    return fwrite(ptr, 1, size, fp->f);
243#   endif
244#endif
245}
246
247/** \brief Read a line from a file handle
248 *
249 *  Read one line of data from a file handle, up to one less than the given
250 *  number of bytes. A trailing zero is appended to the data.
251 *
252 *  \param fp The file handle
253 *  \param s The destination buffer
254 *  \param size The maximum number of bytes to read
255 *  \return The number of bytes read, including the trailing zero
256 */
257char *caca_file_gets(caca_file_t *fp, char *s, int size)
258{
259#if defined __KERNEL__
260    seterrno(ENOSYS);
261    return NULL;
262#elif defined HAVE_ZLIB_H
263    if(fp->zip)
264    {
265        int i;
266
267        for(i = 0; i < size; i++)
268        {
269            int ret = zipread(fp, s + i, 1);
270
271            if(ret < 0)
272                return NULL;
273
274            if(ret == 0 || s[i] == '\n')
275            {
276                if(i + 1 < size)
277                    s[i + 1] = '\0';
278                return s;
279            }
280        }
281
282        return s;
283    }
284
285    return gzgets(fp->gz, s, size);
286#else
287    return fgets(s, size, fp->f);
288#endif
289}
290
291/** \brief Tell whether a file handle reached end of file
292 *
293 *  Return the end-of-file status of the file handle.
294 *
295 *  This function is a wrapper for feof() or, if available, gzeof().
296 *
297 *  \param fp The file handle
298 *  \return 1 if EOF was reached, 0 otherwise
299 */
300int caca_file_eof(caca_file_t *fp)
301{
302#if defined __KERNEL__
303    return 1;
304#elif defined HAVE_ZLIB_H
305    return fp->zip ? fp->eof : gzeof(fp->gz);
306#else
307    return feof(fp->f);
308#endif
309}
310
311#if !defined __KERNEL__ && defined HAVE_ZLIB_H
312static int zipread(caca_file_t *fp, void *buf, unsigned int len)
313{
314    unsigned int total_read = 0;
315
316    if(len == 0)
317        return 0;
318
319    fp->stream.next_out = buf;
320    fp->stream.avail_out = len;
321
322    while(fp->stream.avail_out > 0)
323    {
324        unsigned int tmp;
325        int ret = 0;
326
327        if(fp->stream.avail_in == 0 && !gzeof(fp->gz))
328        {
329            int bytes_read;
330
331            bytes_read = gzread(fp->gz, fp->read_buffer, READSIZE);
332            if(bytes_read < 0)
333                return -1;
334
335            fp->stream.next_in = fp->read_buffer;
336            fp->stream.avail_in = bytes_read;
337        }
338
339        tmp = fp->stream.total_out;
340        ret = inflate(&fp->stream, Z_SYNC_FLUSH);
341        total_read += fp->stream.total_out - tmp;
342
343        if(ret == Z_STREAM_END)
344        {
345            fp->eof = 1;
346            fp->total += total_read;
347            return total_read;
348        }
349
350        if(ret != Z_OK)
351            return ret;
352    }
353
354    fp->total += total_read;
355    return total_read;
356}
357#endif
358
359/*
360 * XXX: The following functions are aliases.
361 */
362
363cucul_file_t *cucul_file_open(char const *, const char *)
364         CACA_ALIAS(caca_file_open);
365int cucul_file_close(cucul_file_t *) CACA_ALIAS(caca_file_close);
366uint64_t cucul_file_tell(cucul_file_t *) CACA_ALIAS(caca_file_tell);
367size_t cucul_file_read(cucul_file_t *, void *, size_t)
368         CACA_ALIAS(caca_file_read);
369size_t cucul_file_write(cucul_file_t *, const void *, size_t)
370         CACA_ALIAS(caca_file_write);
371char * cucul_file_gets(cucul_file_t *, char *, int)
372         CACA_ALIAS(caca_file_gets);
373int cucul_file_eof(cucul_file_t *) CACA_ALIAS(caca_file_eof);
374
Note: See TracBrowser for help on using the repository browser.