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