Improve C89 compliance
[platform/upstream/libsolv.git] / ext / solv_xfopen.c
1 /*
2  * Copyright (c) 2011, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #define _GNU_SOURCE
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <zlib.h>
14 #include <fcntl.h>
15
16 #include "solv_xfopen.h"
17 #include "util.h"
18
19
20 static FILE *cookieopen(void *cookie, const char *mode,
21         ssize_t (*cread)(void *, char *, size_t), 
22         ssize_t (*cwrite)(void *, const char *, size_t), 
23         int (*cclose)(void *))
24 {
25   if (!cookie)
26     return 0;
27 #ifdef HAVE_FUNOPEN
28   return funopen(cookie, 
29       (int (*)(void *, char *, int))(*mode == 'r' ? cread: NULL),/* readfn */
30       (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL), /* writefn */
31       (fpos_t (*)(void *, fpos_t, int))NULL, /* seekfn */
32       cclose
33       );
34 #elif defined(HAVE_FOPENCOOKIE)
35   {
36     cookie_io_functions_t cio;
37     memset(&cio, 0, sizeof(cio));
38     if (*mode == 'r')
39       cio.read = cread;
40     else if (*mode == 'w')
41       cio.write = cwrite;
42     cio.close = cclose;
43     return  fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
44   }
45 #else
46 # error Need to implement custom I/O
47 #endif
48 }
49
50
51 /* gzip compression */
52
53 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
54 {
55   return gzread((gzFile)cookie, buf, nbytes);
56 }
57
58 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
59 {
60   return gzwrite((gzFile)cookie, buf, nbytes);
61 }
62
63 static int cookie_gzclose(void *cookie)
64 {
65   return gzclose((gzFile)cookie);
66 }
67
68 static inline FILE *mygzfopen(const char *fn, const char *mode)
69 {
70   gzFile gzf = gzopen(fn, mode);
71   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
72 }
73
74 static inline FILE *mygzfdopen(int fd, const char *mode)
75 {
76   gzFile gzf = gzdopen(fd, mode);
77   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
78 }
79
80 #ifdef ENABLE_BZIP2_COMPRESSION
81
82 #include <bzlib.h>
83
84 /* bzip2 compression */
85
86 static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes)
87 {
88   return BZ2_bzread((BZFILE *)cookie, buf, nbytes);
89 }
90
91 static ssize_t cookie_bzwrite(void *cookie, const char *buf, size_t nbytes)
92 {
93   return BZ2_bzwrite((BZFILE *)cookie, (char *)buf, nbytes);
94 }
95
96 static int cookie_bzclose(void *cookie)
97 {
98   BZ2_bzclose((BZFILE *)cookie);
99   return 0;
100 }
101
102 static inline FILE *mybzfopen(const char *fn, const char *mode)
103 {
104   BZFILE *bzf = BZ2_bzopen(fn, mode);
105   return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
106 }
107
108 static inline FILE *mybzfdopen(int fd, const char *mode)
109 {
110   BZFILE *bzf = BZ2_bzdopen(fd, mode);
111   return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
112 }
113
114 #endif
115
116
117 #ifdef ENABLE_LZMA_COMPRESSION
118
119 #include <lzma.h>
120
121 /* lzma code written by me in 2008 for rpm's rpmio.c */
122
123 typedef struct lzfile {
124   unsigned char buf[1 << 15];
125   lzma_stream strm;
126   FILE *file;
127   int encoding;
128   int eof;
129 } LZFILE;
130
131 static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
132 {
133   lzma_options_lzma options;
134   lzma_lzma_preset(&options, level);
135   return lzma_alone_encoder(strm, &options);
136 }
137
138 static lzma_stream stream_init = LZMA_STREAM_INIT;
139
140 static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
141 {
142   int level = 7;
143   int encoding = 0;
144   FILE *fp;
145   LZFILE *lzfile;
146   lzma_ret ret;
147
148   if (!path && fd < 0)
149     return 0;
150   for (; *mode; mode++)
151     {
152       if (*mode == 'w')
153         encoding = 1;
154       else if (*mode == 'r')
155         encoding = 0;
156       else if (*mode >= '1' && *mode <= '9')
157         level = *mode - '0';
158     }
159   if (fd != -1)
160     fp = fdopen(fd, encoding ? "w" : "r");
161   else
162     fp = fopen(path, encoding ? "w" : "r");
163   if (!fp)
164     return 0;
165   lzfile = calloc(1, sizeof(*lzfile));
166   if (!lzfile)
167     {
168       fclose(fp);
169       return 0;
170     }
171   lzfile->file = fp;
172   lzfile->encoding = encoding;
173   lzfile->eof = 0;
174   lzfile->strm = stream_init;
175   if (encoding)
176     {
177       if (isxz)
178         ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
179       else
180         ret = setup_alone_encoder(&lzfile->strm, level);
181     }
182   else
183     ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
184   if (ret != LZMA_OK)
185     {
186       fclose(fp);
187       free(lzfile);
188       return 0;
189     }
190   return lzfile;
191 }
192
193 static int lzclose(void *cookie)
194 {
195   LZFILE *lzfile = cookie;
196   lzma_ret ret;
197   size_t n;
198   int rc;
199
200   if (!lzfile)
201     return -1;
202   if (lzfile->encoding)
203     {
204       for (;;)
205         {
206           lzfile->strm.avail_out = sizeof(lzfile->buf);
207           lzfile->strm.next_out = lzfile->buf;
208           ret = lzma_code(&lzfile->strm, LZMA_FINISH);
209           if (ret != LZMA_OK && ret != LZMA_STREAM_END)
210             return -1;
211           n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
212           if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
213             return -1;
214           if (ret == LZMA_STREAM_END)
215             break;
216         }
217     }
218   lzma_end(&lzfile->strm);
219   rc = fclose(lzfile->file);
220   free(lzfile);
221   return rc;
222 }
223
224 static ssize_t lzread(void *cookie, char *buf, size_t len)
225 {
226   LZFILE *lzfile = cookie;
227   lzma_ret ret;
228   int eof = 0;
229
230   if (!lzfile || lzfile->encoding)
231     return -1;
232   if (lzfile->eof)
233     return 0;
234   lzfile->strm.next_out = (unsigned char *)buf;
235   lzfile->strm.avail_out = len;
236   for (;;)
237     {
238       if (!lzfile->strm.avail_in)
239         {
240           lzfile->strm.next_in = lzfile->buf;
241           lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
242           if (!lzfile->strm.avail_in)
243             eof = 1;
244         }
245       ret = lzma_code(&lzfile->strm, LZMA_RUN);
246       if (ret == LZMA_STREAM_END)
247         {
248           lzfile->eof = 1;
249           return len - lzfile->strm.avail_out;
250         }
251       if (ret != LZMA_OK)
252         return -1;
253       if (!lzfile->strm.avail_out)
254         return len;
255       if (eof)
256         return -1;
257     }
258 }
259
260 static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
261 {
262   LZFILE *lzfile = cookie;
263   lzma_ret ret;
264   size_t n;
265   if (!lzfile || !lzfile->encoding)
266     return -1;
267   if (!len)
268     return 0;
269   lzfile->strm.next_in = (unsigned char *)buf;
270   lzfile->strm.avail_in = len;
271   for (;;)
272     {
273       lzfile->strm.next_out = lzfile->buf;
274       lzfile->strm.avail_out = sizeof(lzfile->buf);
275       ret = lzma_code(&lzfile->strm, LZMA_RUN);
276       if (ret != LZMA_OK)
277         return -1;
278       n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
279       if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
280         return -1;
281       if (!lzfile->strm.avail_in)
282         return len;
283     }
284 }
285
286 static inline FILE *myxzfopen(const char *fn, const char *mode)
287 {
288   LZFILE *lzf = lzopen(fn, mode, -1, 1);
289   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
290 }
291
292 static inline FILE *myxzfdopen(int fd, const char *mode)
293 {
294   LZFILE *lzf = lzopen(0, mode, fd, 1);
295   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
296 }
297
298 static inline FILE *mylzfopen(const char *fn, const char *mode)
299 {
300   LZFILE *lzf = lzopen(fn, mode, -1, 0);
301   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
302 }
303
304 static inline FILE *mylzfdopen(int fd, const char *mode)
305 {
306   LZFILE *lzf = lzopen(0, mode, fd, 0);
307   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
308 }
309
310 #endif /* ENABLE_LZMA_COMPRESSION */
311
312
313 FILE *
314 solv_xfopen(const char *fn, const char *mode)
315 {
316   char *suf;
317
318   if (!fn)
319     return 0;
320   if (!mode)
321     mode = "r";
322   suf = strrchr(fn, '.');
323   if (suf && !strcmp(suf, ".gz"))
324     return mygzfopen(fn, mode);
325 #ifdef ENABLE_LZMA_COMPRESSION
326   if (suf && !strcmp(suf, ".xz"))
327     return myxzfopen(fn, mode);
328   if (suf && !strcmp(suf, ".lzma"))
329     return mylzfopen(fn, mode);
330 #else
331   if (suf && !strcmp(suf, ".xz"))
332     return 0;
333   if (suf && !strcmp(suf, ".lzma"))
334     return 0;
335 #endif
336 #ifdef ENABLE_BZIP2_COMPRESSION
337   if (suf && !strcmp(suf, ".bz2"))
338     return mybzfopen(fn, mode);
339 #else
340   if (suf && !strcmp(suf, ".bz2"))
341     return 0;
342 #endif
343   return fopen(fn, mode);
344 }
345
346 FILE *
347 solv_xfopen_fd(const char *fn, int fd, const char *mode)
348 {
349   const char *simplemode = mode;
350   char *suf;
351
352   suf = fn ? strrchr(fn, '.') : 0;
353   if (!mode)
354     {
355       int fl = fcntl(fd, F_GETFL, 0);
356       if (fl == -1)
357         return 0;
358       fl &= O_RDONLY|O_WRONLY|O_RDWR;
359       if (fl == O_WRONLY)
360         mode = simplemode = "w";
361       else if (fl == O_RDWR)
362         {
363           mode = "r+";
364           simplemode = "r";
365         }
366       else
367         mode = simplemode = "r";
368     }
369   if (suf && !strcmp(suf, ".gz"))
370     return mygzfdopen(fd, simplemode);
371 #ifdef ENABLE_LZMA_COMPRESSION
372   if (suf && !strcmp(suf, ".xz"))
373     return myxzfdopen(fd, simplemode);
374   if (suf && !strcmp(suf, ".lzma"))
375     return mylzfdopen(fd, simplemode);
376 #else
377   if (suf && !strcmp(suf, ".xz"))
378     return 0;
379   if (suf && !strcmp(suf, ".lzma"))
380     return 0;
381 #endif
382 #ifdef ENABLE_BZIP2_COMPRESSION
383   if (suf && !strcmp(suf, ".bz2"))
384     return mybzfdopen(fd, simplemode);
385 #else
386   if (suf && !strcmp(suf, ".bz2"))
387     return 0;
388 #endif
389   return fdopen(fd, mode);
390 }
391
392 int
393 solv_xfopen_iscompressed(const char *fn)
394 {
395   const char *suf = fn ? strrchr(fn, '.') : 0;
396   if (!suf)
397     return 0;
398   if (!strcmp(suf, ".gz"))
399     return 1;
400   if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
401 #ifdef ENABLE_LZMA_COMPRESSION
402     return 1;
403 #else
404     return -1;
405 #endif
406   if (!strcmp(suf, ".bz2"))
407 #ifdef ENABLE_BZIP2_COMPRESSION
408     return 1;
409 #else
410     return -1;
411 #endif
412   return 0;
413 }
414
415 struct bufcookie {
416   char **bufp;
417   size_t *buflp;
418   char *freemem;
419   size_t bufl_int;
420 };
421
422 static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
423 {
424   struct bufcookie *bc = cookie;
425   size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
426   if (n)
427     {
428       memcpy(buf, *bc->bufp, n);
429       *bc->bufp += n;
430       *bc->buflp -= n;
431     }
432   return n;
433 }
434
435 static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
436 {
437   struct bufcookie *bc = cookie;
438   int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
439   if (n)
440     {
441       *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
442       memcpy(*bc->bufp, buf, n);
443       (*bc->bufp)[n] = 0;       /* zero-terminate */
444       *bc->buflp += n;
445     }
446   return n;
447 }
448
449 static int cookie_bufclose(void *cookie)
450 {
451   struct bufcookie *bc = cookie;
452   if (bc->freemem)
453     solv_free(bc->freemem);
454   solv_free(bc);
455   return 0;
456 }
457
458 FILE *
459 solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
460 {
461   struct bufcookie *bc;
462   FILE *fp;
463   if (*mode != 'r' && *mode != 'w')
464     return 0;
465   bc = solv_calloc(1, sizeof(*bc));
466   bc->freemem = 0;
467   bc->bufp = bufp;
468   if (!buflp)
469     {
470       bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
471       buflp = &bc->bufl_int;
472     }
473   bc->buflp = buflp;
474   if (*mode == 'w')
475     {
476       *bc->bufp = solv_extend(0, 0, 1, 1, 4095);        /* always zero-terminate */
477       (*bc->bufp)[0] = 0;
478       *bc->buflp = 0;
479     }
480   fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
481   if (!strcmp(mode, "rf"))      /* auto-free */
482     bc->freemem = *bufp;
483   if (!fp)
484     {
485       if (*mode == 'w')
486         *bc->bufp = solv_free(*bc->bufp);
487       cookie_bufclose(bc);
488     }
489   return fp;
490 }