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