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

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

File size: 5.8 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2006-2007 Sam Hocevar <sam@zoy.org>
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
53caca_file_t *caca_file_open(char const *path, const char *mode)
54{
55#if defined __KERNEL__
56    seterrno(ENOSYS);
57    return NULL;
58#else
59    caca_file_t *fp = malloc(sizeof(*fp));
60
61    fp->readonly = !!strchr(mode, 'r');
62
63#   if defined HAVE_ZLIB_H
64    uint8_t buf[4];
65    unsigned int skip_size = 0;
66
67    fp->gz = gzopen(path, fp->readonly ? "rb" : "wb");
68    if(!fp->gz)
69    {
70        free(fp);
71        return NULL;
72    }
73
74    fp->eof = 0;
75    fp->zip = 0;
76    fp->total = 0;
77
78    if(fp->readonly)
79    {
80        /* Parse ZIP file and go to start of first file */
81        gzread(fp->gz, buf, 4);
82        if(memcmp(buf, "PK\3\4", 4))
83        {
84            gzseek(fp->gz, 0, SEEK_SET);
85            return fp;
86        }
87
88        fp->zip = 1;
89
90        gzseek(fp->gz, 22, SEEK_CUR);
91
92        gzread(fp->gz, buf, 2); /* Filename size */
93        skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
94        gzread(fp->gz, buf, 2); /* Extra field size */
95        skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
96
97        gzseek(fp->gz, skip_size, SEEK_CUR);
98
99        /* Initialise inflate stream */
100        fp->stream.total_out = 0;
101        fp->stream.zalloc = NULL;
102        fp->stream.zfree = NULL;
103        fp->stream.opaque = NULL;
104        fp->stream.next_in = NULL;
105        fp->stream.avail_in = 0;
106
107        if(inflateInit2(&fp->stream, -MAX_WBITS))
108        {
109            free(fp);
110            gzclose(fp->gz);
111            return NULL;
112        }
113    }
114#   else
115    fp->f = fopen(path, mode);
116
117    if(!fp->f)
118    {
119        free(fp);
120        return NULL;
121    }
122#   endif
123
124    return fp;
125#endif
126}
127
128int caca_file_close(caca_file_t *fp)
129{
130#if defined __KERNEL__
131    seterrno(ENOSYS);
132    return 0;
133#elif defined HAVE_ZLIB_H
134    gzFile gz = fp->gz;
135    if(fp->zip)
136        inflateEnd(&fp->stream);
137    free(fp);
138    return gzclose(gz);
139#else
140    FILE *f = fp->f;
141    free(fp);
142    return fclose(f);
143#endif
144}
145
146uint64_t caca_file_tell(caca_file_t *fp)
147{
148#if defined __KERNEL__
149    seterrno(ENOSYS);
150    return 0;
151#elif defined HAVE_ZLIB_H
152    if(fp->zip)
153        return fp->total;
154    return gztell(fp->gz);
155#else
156    return ftell(fp->f);
157#endif
158}
159
160size_t caca_file_read(caca_file_t *fp, void *ptr, size_t size)
161{
162#if defined __KERNEL__
163    seterrno(ENOSYS);
164    return 0;
165#elif defined HAVE_ZLIB_H
166    if(fp->zip)
167        return zipread(fp, ptr, size);
168    return gzread(fp->gz, ptr, size);
169#else
170    return fread(ptr, 1, size, fp->f);
171#endif
172}
173
174size_t caca_file_write(caca_file_t *fp, const void *ptr, size_t size)
175{
176#if defined __KERNEL__
177    seterrno(ENOSYS);
178    return 0;
179#else
180    if(fp->readonly)
181        return 0;
182
183#   if defined HAVE_ZLIB_H
184    if(fp->zip)
185    {
186        /* FIXME: zip files are not supported */
187        seterrno(ENOSYS);
188        return 0;
189    }
190    return gzwrite(fp->gz, ptr, size);
191#   else
192    return fwrite(ptr, 1, size, fp->f);
193#   endif
194#endif
195}
196
197char *caca_file_gets(caca_file_t *fp, char *s, int size)
198{
199#if defined __KERNEL__
200    seterrno(ENOSYS);
201    return NULL;
202#elif defined HAVE_ZLIB_H
203    if(fp->zip)
204    {
205        int i;
206
207        for(i = 0; i < size; i++)
208        {
209            int ret = zipread(fp, s + i, 1);
210
211            if(ret < 0)
212                return NULL;
213
214            if(ret == 0 || s[i] == '\n')
215            {
216                if(i + 1 < size)
217                    s[i + 1] = '\0';
218                return s;
219            }
220        }
221
222        return s;
223    }
224
225    return gzgets(fp->gz, s, size);
226#else
227    return fgets(s, size, fp->f);
228#endif
229}
230
231int caca_file_eof(caca_file_t *fp)
232{
233#if defined __KERNEL__
234    return 1;
235#elif defined HAVE_ZLIB_H
236    return fp->zip ? fp->eof : gzeof(fp->gz);
237#else
238    return feof(fp->f);
239#endif
240}
241
242#if !defined __KERNEL__ && defined HAVE_ZLIB_H
243static int zipread(caca_file_t *fp, void *buf, unsigned int len)
244{
245    unsigned int total_read = 0;
246
247    if(len == 0)
248        return 0;
249
250    fp->stream.next_out = buf;
251    fp->stream.avail_out = len;
252
253    while(fp->stream.avail_out > 0)
254    {
255        unsigned int tmp;
256        int ret = 0;
257
258        if(fp->stream.avail_in == 0 && !gzeof(fp->gz))
259        {
260            int bytes_read;
261
262            bytes_read = gzread(fp->gz, fp->read_buffer, READSIZE);
263            if(bytes_read < 0)
264                return -1;
265
266            fp->stream.next_in = fp->read_buffer;
267            fp->stream.avail_in = bytes_read;
268        }
269
270        tmp = fp->stream.total_out;
271        ret = inflate(&fp->stream, Z_SYNC_FLUSH);
272        total_read += fp->stream.total_out - tmp;
273
274        if(ret == Z_STREAM_END)
275        {
276            fp->eof = 1;
277            fp->total += total_read;
278            return total_read;
279        }
280
281        if(ret != Z_OK)
282            return ret;
283    }
284
285    fp->total += total_read;
286    return total_read;
287}
288#endif
289
Note: See TracBrowser for help on using the repository browser.