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

Last change on this file since 4333 was 4333, checked in by sam, 4 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.