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