xref: /haiku/src/libs/libsolv/ext/solv_xfopen.c (revision caed67a8cba83913b9c21ac2b06ebc6bd1cb3111)
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 }
492