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

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

Large source code cleanup, getting rid of spaces, tabs, and svn keywords.

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