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