File: file.c

package info (click to toggle)
libcaca 0.99.beta19-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 7,200 kB
  • ctags: 3,824
  • sloc: ansic: 22,346; python: 2,316; cs: 1,213; cpp: 1,114; java: 916; objc: 836; makefile: 606; sh: 457; ruby: 193; asm: 65
file content (372 lines) | stat: -rw-r--r-- 8,719 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/*
 *  libcaca       Colour ASCII-Art library
 *  Copyright (c) 2006-2012 Sam Hocevar <sam@hocevar.net>
 *                All Rights Reserved
 *
 *  This library is free software. It comes without any warranty, to
 *  the extent permitted by applicable law. You can redistribute it
 *  and/or modify it under the terms of the Do What the Fuck You Want
 *  to Public License, Version 2, as published by Sam Hocevar. See
 *  http://www.wtfpl.net/ for more details.
 */

/*
 *  This file contains functions for compressed file I/O.
 */

#include "config.h"

#if !defined __KERNEL__
#   include <stdio.h>
#   include <stdlib.h>
#   include <string.h>
#   if defined HAVE_ZLIB_H
#       include <zlib.h>
#       define READSIZE  128 /* Read buffer size */
#       define WRITESIZE 128 /* Inflate buffer size */
#   endif
#endif

#include "caca.h"
#include "caca_internals.h"

#if !defined __KERNEL__ && defined HAVE_ZLIB_H
static int zipread(caca_file_t *, void *, unsigned int);
#endif

#if !defined __KERNEL__
struct caca_file
{
#   if defined HAVE_ZLIB_H
    uint8_t read_buffer[READSIZE];
    z_stream stream;
    gzFile gz;
    int eof, zip, total;
#   endif
    FILE *f;
    int readonly;
};
#endif

/** \brief Open a file for reading or writing
 *
 *  Create a caca file handle for a file. If the file is zipped, it is
 *  decompressed on the fly.
 *
 *  If an error occurs, NULL is returned and \b errno is set accordingly:
 *  - \c ENOSTS Function not implemented.
 *  - \c EINVAL File not found or permission denied.
 *
 *  \param path The file path
 *  \param mode The file open mode
 *  \return A file handle to \e path.
 */
caca_file_t *caca_file_open(char const *path, const char *mode)
{
#if defined __KERNEL__
    seterrno(ENOSYS);
    return NULL;
#else
    caca_file_t *fp = malloc(sizeof(*fp));

    fp->readonly = !!strchr(mode, 'r');

#   if defined HAVE_ZLIB_H
    uint8_t buf[4];
    unsigned int skip_size = 0;

    fp->gz = gzopen(path, fp->readonly ? "rb" : "wb");
    if(!fp->gz)
    {
        free(fp);
        seterrno(EINVAL);
        return NULL;
    }

    fp->eof = 0;
    fp->zip = 0;
    fp->total = 0;

    if(fp->readonly)
    {
        /* Parse ZIP file and go to start of first file */
        gzread(fp->gz, buf, 4);
        if(memcmp(buf, "PK\3\4", 4))
        {
            gzseek(fp->gz, 0, SEEK_SET);
            return fp;
        }

        fp->zip = 1;

        gzseek(fp->gz, 22, SEEK_CUR);

        gzread(fp->gz, buf, 2); /* Filename size */
        skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
        gzread(fp->gz, buf, 2); /* Extra field size */
        skip_size += (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);

        gzseek(fp->gz, skip_size, SEEK_CUR);

        /* Initialise inflate stream */
        fp->stream.total_out = 0;
        fp->stream.zalloc = NULL;
        fp->stream.zfree = NULL;
        fp->stream.opaque = NULL;
        fp->stream.next_in = NULL;
        fp->stream.avail_in = 0;

        if(inflateInit2(&fp->stream, -MAX_WBITS))
        {
            gzclose(fp->gz);
            free(fp);
            seterrno(EINVAL);
            return NULL;
        }
    }
#   else
    fp->f = fopen(path, mode);

    if(!fp->f)
    {
        free(fp);
        seterrno(EINVAL);
        return NULL;
    }
#   endif

    return fp;
#endif
}

/** \brief Close a file handle
 *
 *  Close and destroy the resources associated with a caca file handle.
 *
 *  This function is a wrapper for fclose() or, if available, gzclose().
 *
 *  \param fp The file handle
 *  \return The return value of fclose() or gzclose().
 */
int caca_file_close(caca_file_t *fp)
{
#if defined __KERNEL__
    seterrno(ENOSYS);
    return 0;
#elif defined HAVE_ZLIB_H
    gzFile gz = fp->gz;
    if(fp->zip)
        inflateEnd(&fp->stream);
    free(fp);
    return gzclose(gz);
#else
    FILE *f = fp->f;
    free(fp);
    return fclose(f);
#endif
}

/** \brief Return the position in a file handle
 *
 *  Return the file handle position, in bytes.
 *
 *  \param fp The file handle
 *  \return The current offset in the file handle.
 */
uint64_t caca_file_tell(caca_file_t *fp)
{
#if defined __KERNEL__
    seterrno(ENOSYS);
    return 0;
#elif defined HAVE_ZLIB_H
    if(fp->zip)
        return fp->total;
    return gztell(fp->gz);
#else
    return ftell(fp->f);
#endif
}

/** \brief Read data from a file handle
 *
 *  Read data from a file handle and copy them into the given buffer.
 *
 *  \param fp The file handle
 *  \param ptr The destination buffer
 *  \param size The number of bytes to read
 *  \return The number of bytes read
 */
size_t caca_file_read(caca_file_t *fp, void *ptr, size_t size)
{
#if defined __KERNEL__
    seterrno(ENOSYS);
    return 0;
#elif defined HAVE_ZLIB_H
    if(fp->zip)
        return zipread(fp, ptr, size);
    return gzread(fp->gz, ptr, size);
#else
    return fread(ptr, 1, size, fp->f);
#endif
}

/** \brief Write data to a file handle
 *
 *  Write the contents of the given buffer to the file handle.
 *
 *  \param fp The file handle
 *  \param ptr The source buffer
 *  \param size The number of bytes to write
 *  \return The number of bytes written
 */
size_t caca_file_write(caca_file_t *fp, const void *ptr, size_t size)
{
#if defined __KERNEL__
    seterrno(ENOSYS);
    return 0;
#else
    if(fp->readonly)
        return 0;

#   if defined HAVE_ZLIB_H
    if(fp->zip)
    {
        /* FIXME: zip files are not supported */
        seterrno(ENOSYS);
        return 0;
    }
    return gzwrite(fp->gz, ptr, size);
#   else
    return fwrite(ptr, 1, size, fp->f);
#   endif
#endif
}

/** \brief Read a line from a file handle
 *
 *  Read one line of data from a file handle, up to one less than the given
 *  number of bytes. A trailing zero is appended to the data.
 *
 *  \param fp The file handle
 *  \param s The destination buffer
 *  \param size The maximum number of bytes to read
 *  \return The number of bytes read, including the trailing zero
 */
char *caca_file_gets(caca_file_t *fp, char *s, int size)
{
#if defined __KERNEL__
    seterrno(ENOSYS);
    return NULL;
#elif defined HAVE_ZLIB_H
    if(fp->zip)
    {
        int i;

        for(i = 0; i < size; i++)
        {
            int ret = zipread(fp, s + i, 1);

            if(ret < 0)
                return NULL;

            if(ret == 0 || s[i] == '\n')
            {
                if(i + 1 < size)
                    s[i + 1] = '\0';
                return s;
            }
        }

        return s;
    }

    return gzgets(fp->gz, s, size);
#else
    return fgets(s, size, fp->f);
#endif
}

/** \brief Tell whether a file handle reached end of file
 *
 *  Return the end-of-file status of the file handle.
 *
 *  This function is a wrapper for feof() or, if available, gzeof().
 *
 *  \param fp The file handle
 *  \return 1 if EOF was reached, 0 otherwise
 */
int caca_file_eof(caca_file_t *fp)
{
#if defined __KERNEL__
    return 1;
#elif defined HAVE_ZLIB_H
    return fp->zip ? fp->eof : gzeof(fp->gz);
#else
    return feof(fp->f);
#endif
}

#if !defined __KERNEL__ && defined HAVE_ZLIB_H
static int zipread(caca_file_t *fp, void *buf, unsigned int len)
{
    unsigned int total_read = 0;

    if(len == 0)
        return 0;

    fp->stream.next_out = buf;
    fp->stream.avail_out = len;

    while(fp->stream.avail_out > 0)
    {
        unsigned int tmp;
        int ret = 0;

        if(fp->stream.avail_in == 0 && !gzeof(fp->gz))
        {
            int bytes_read;

            bytes_read = gzread(fp->gz, fp->read_buffer, READSIZE);
            if(bytes_read < 0)
                return -1;

            fp->stream.next_in = fp->read_buffer;
            fp->stream.avail_in = bytes_read;
        }

        tmp = fp->stream.total_out;
        ret = inflate(&fp->stream, Z_SYNC_FLUSH);
        total_read += fp->stream.total_out - tmp;

        if(ret == Z_STREAM_END)
        {
            fp->eof = 1;
            fp->total += total_read;
            return total_read;
        }

        if(ret != Z_OK)
            return ret;
    }

    fp->total += total_read;
    return total_read;
}
#endif

/*
 * XXX: The following functions are aliases.
 */

cucul_file_t *cucul_file_open(char const *, const char *)
         CACA_ALIAS(caca_file_open);
int cucul_file_close(cucul_file_t *) CACA_ALIAS(caca_file_close);
uint64_t cucul_file_tell(cucul_file_t *) CACA_ALIAS(caca_file_tell);
size_t cucul_file_read(cucul_file_t *, void *, size_t)
         CACA_ALIAS(caca_file_read);
size_t cucul_file_write(cucul_file_t *, const void *, size_t)
         CACA_ALIAS(caca_file_write);
char * cucul_file_gets(cucul_file_t *, char *, int)
         CACA_ALIAS(caca_file_gets);
int cucul_file_eof(cucul_file_t *) CACA_ALIAS(caca_file_eof);