Imported Upstream version 0.7.5
[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 #ifdef _WIN32
16   #include "fmemopen.c"
17 #endif
18
19 #include "solv_xfopen.h"
20 #include "util.h"
21
22 #ifndef WITHOUT_COOKIEOPEN
23
24 static FILE *cookieopen(void *cookie, const char *mode,
25         ssize_t (*cread)(void *, char *, size_t),
26         ssize_t (*cwrite)(void *, const char *, size_t),
27         int (*cclose)(void *))
28 {
29 #ifdef HAVE_FUNOPEN
30   if (!cookie)
31     return 0;
32   return funopen(cookie,
33       (int (*)(void *, char *, int))(*mode == 'r' ? cread : NULL),              /* readfn */
34       (int (*)(void *, const char *, int))(*mode == 'w' ? cwrite : NULL),       /* writefn */
35       (fpos_t (*)(void *, fpos_t, int))NULL,                                    /* seekfn */
36       cclose
37       );
38 #elif defined(HAVE_FOPENCOOKIE)
39   cookie_io_functions_t cio;
40
41   if (!cookie)
42     return 0;
43   memset(&cio, 0, sizeof(cio));
44   if (*mode == 'r')
45     cio.read = cread;
46   else if (*mode == 'w')
47     cio.write = cwrite;
48   cio.close = cclose;
49   return  fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
50 #else
51 # error Need to implement custom I/O
52 #endif
53 }
54
55
56 #ifdef ENABLE_ZLIB_COMPRESSION
57
58 /* gzip compression */
59
60 #include <zlib.h>
61
62 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
63 {
64   return gzread((gzFile)cookie, buf, nbytes);
65 }
66
67 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
68 {
69   return gzwrite((gzFile)cookie, buf, nbytes);
70 }
71
72 static int cookie_gzclose(void *cookie)
73 {
74   return gzclose((gzFile)cookie);
75 }
76
77 static inline FILE *mygzfopen(const char *fn, const char *mode)
78 {
79   gzFile gzf = gzopen(fn, mode);
80   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
81 }
82
83 static inline FILE *mygzfdopen(int fd, const char *mode)
84 {
85   gzFile gzf = gzdopen(fd, mode);
86   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
87 }
88
89 #endif
90
91
92 #ifdef ENABLE_BZIP2_COMPRESSION
93
94 /* bzip2 compression */
95
96 #include <bzlib.h>
97
98 static ssize_t cookie_bzread(void *cookie, char *buf, size_t nbytes)
99 {
100   return BZ2_bzread((BZFILE *)cookie, buf, nbytes);
101 }
102
103 static ssize_t cookie_bzwrite(void *cookie, const char *buf, size_t nbytes)
104 {
105   return BZ2_bzwrite((BZFILE *)cookie, (char *)buf, nbytes);
106 }
107
108 static int cookie_bzclose(void *cookie)
109 {
110   BZ2_bzclose((BZFILE *)cookie);
111   return 0;
112 }
113
114 static inline FILE *mybzfopen(const char *fn, const char *mode)
115 {
116   BZFILE *bzf = BZ2_bzopen(fn, mode);
117   return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
118 }
119
120 static inline FILE *mybzfdopen(int fd, const char *mode)
121 {
122   BZFILE *bzf = BZ2_bzdopen(fd, mode);
123   return cookieopen(bzf, mode, cookie_bzread, cookie_bzwrite, cookie_bzclose);
124 }
125
126 #endif
127
128
129 #ifdef ENABLE_LZMA_COMPRESSION
130
131 /* lzma code written by me in 2008 for rpm's rpmio.c */
132
133 #include <lzma.h>
134
135 typedef struct lzfile {
136   unsigned char buf[1 << 15];
137   lzma_stream strm;
138   FILE *file;
139   int encoding;
140   int eof;
141 } LZFILE;
142
143 static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
144 {
145   lzma_options_lzma options;
146   lzma_lzma_preset(&options, level);
147   return lzma_alone_encoder(strm, &options);
148 }
149
150 static lzma_stream stream_init = LZMA_STREAM_INIT;
151
152 static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
153 {
154   int level = 7;
155   int encoding = 0;
156   FILE *fp;
157   LZFILE *lzfile;
158   lzma_ret ret;
159
160   if (!path && fd < 0)
161     return 0;
162   for (; *mode; mode++)
163     {
164       if (*mode == 'w')
165         encoding = 1;
166       else if (*mode == 'r')
167         encoding = 0;
168       else if (*mode >= '1' && *mode <= '9')
169         level = *mode - '0';
170     }
171   if (fd != -1)
172     fp = fdopen(fd, encoding ? "w" : "r");
173   else
174     fp = fopen(path, encoding ? "w" : "r");
175   if (!fp)
176     return 0;
177   lzfile = calloc(1, sizeof(*lzfile));
178   if (!lzfile)
179     {
180       fclose(fp);
181       return 0;
182     }
183   lzfile->file = fp;
184   lzfile->encoding = encoding;
185   lzfile->eof = 0;
186   lzfile->strm = stream_init;
187   if (encoding)
188     {
189       if (isxz)
190         ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
191       else
192         ret = setup_alone_encoder(&lzfile->strm, level);
193     }
194   else
195     ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
196   if (ret != LZMA_OK)
197     {
198       fclose(fp);
199       free(lzfile);
200       return 0;
201     }
202   return lzfile;
203 }
204
205 static int lzclose(void *cookie)
206 {
207   LZFILE *lzfile = cookie;
208   lzma_ret ret;
209   size_t n;
210   int rc;
211
212   if (!lzfile)
213     return -1;
214   if (lzfile->encoding)
215     {
216       for (;;)
217         {
218           lzfile->strm.avail_out = sizeof(lzfile->buf);
219           lzfile->strm.next_out = lzfile->buf;
220           ret = lzma_code(&lzfile->strm, LZMA_FINISH);
221           if (ret != LZMA_OK && ret != LZMA_STREAM_END)
222             return -1;
223           n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
224           if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
225             return -1;
226           if (ret == LZMA_STREAM_END)
227             break;
228         }
229     }
230   lzma_end(&lzfile->strm);
231   rc = fclose(lzfile->file);
232   free(lzfile);
233   return rc;
234 }
235
236 static ssize_t lzread(void *cookie, char *buf, size_t len)
237 {
238   LZFILE *lzfile = cookie;
239   lzma_ret ret;
240   int eof = 0;
241
242   if (!lzfile || lzfile->encoding)
243     return -1;
244   if (lzfile->eof)
245     return 0;
246   lzfile->strm.next_out = (unsigned char *)buf;
247   lzfile->strm.avail_out = len;
248   for (;;)
249     {
250       if (!lzfile->strm.avail_in)
251         {
252           lzfile->strm.next_in = lzfile->buf;
253           lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
254           if (!lzfile->strm.avail_in)
255             eof = 1;
256         }
257       ret = lzma_code(&lzfile->strm, LZMA_RUN);
258       if (ret == LZMA_STREAM_END)
259         {
260           lzfile->eof = 1;
261           return len - lzfile->strm.avail_out;
262         }
263       if (ret != LZMA_OK)
264         return -1;
265       if (!lzfile->strm.avail_out)
266         return len;
267       if (eof)
268         return -1;
269     }
270 }
271
272 static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
273 {
274   LZFILE *lzfile = cookie;
275   lzma_ret ret;
276   size_t n;
277   if (!lzfile || !lzfile->encoding)
278     return -1;
279   if (!len)
280     return 0;
281   lzfile->strm.next_in = (unsigned char *)buf;
282   lzfile->strm.avail_in = len;
283   for (;;)
284     {
285       lzfile->strm.next_out = lzfile->buf;
286       lzfile->strm.avail_out = sizeof(lzfile->buf);
287       ret = lzma_code(&lzfile->strm, LZMA_RUN);
288       if (ret != LZMA_OK)
289         return -1;
290       n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
291       if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
292         return -1;
293       if (!lzfile->strm.avail_in)
294         return len;
295     }
296 }
297
298 static inline FILE *myxzfopen(const char *fn, const char *mode)
299 {
300   LZFILE *lzf = lzopen(fn, mode, -1, 1);
301   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
302 }
303
304 static inline FILE *myxzfdopen(int fd, const char *mode)
305 {
306   LZFILE *lzf = lzopen(0, mode, fd, 1);
307   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
308 }
309
310 static inline FILE *mylzfopen(const char *fn, const char *mode)
311 {
312   LZFILE *lzf = lzopen(fn, mode, -1, 0);
313   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
314 }
315
316 static inline FILE *mylzfdopen(int fd, const char *mode)
317 {
318   LZFILE *lzf = lzopen(0, mode, fd, 0);
319   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
320 }
321
322 #endif /* ENABLE_LZMA_COMPRESSION */
323
324 #ifdef ENABLE_ZSTD_COMPRESSION
325
326 #include <zstd.h>
327
328 typedef struct zstdfile {
329   ZSTD_CStream *cstream;
330   ZSTD_DStream *dstream;
331   FILE *file;
332   int encoding;
333   int eof;
334   ZSTD_inBuffer in;
335   ZSTD_outBuffer out;
336   unsigned char buf[1 << 15];
337 } ZSTDFILE;
338
339 static ZSTDFILE *zstdopen(const char *path, const char *mode, int fd)
340 {
341   int level = 7;
342   int encoding = 0;
343   FILE *fp;
344   ZSTDFILE *zstdfile;
345
346   if (!path && fd < 0)
347     return 0;
348   for (; *mode; mode++)
349     {
350       if (*mode == 'w')
351         encoding = 1;
352       else if (*mode == 'r')
353         encoding = 0;
354       else if (*mode >= '1' && *mode <= '9')
355         level = *mode - '0';
356     }
357   if (fd != -1)
358     fp = fdopen(fd, encoding ? "w" : "r");
359   else
360     fp = fopen(path, encoding ? "w" : "r");
361   if (!fp)
362     return 0;
363   zstdfile = solv_calloc(1, sizeof(*zstdfile));
364   zstdfile->encoding = encoding;
365   if (encoding)
366     {
367       zstdfile->cstream = ZSTD_createCStream();
368       zstdfile->encoding = 1;
369       if (!zstdfile->cstream)
370         {
371           solv_free(zstdfile);
372           fclose(fp);
373           return 0;
374         }
375       if (ZSTD_isError(ZSTD_initCStream(zstdfile->cstream, level)))
376         {
377           ZSTD_freeCStream(zstdfile->cstream);
378           solv_free(zstdfile);
379           fclose(fp);
380           return 0;
381         }
382       zstdfile->out.dst = zstdfile->buf;
383       zstdfile->out.pos = 0;
384       zstdfile->out.size = sizeof(zstdfile->buf);
385     }
386   else
387     {
388       zstdfile->dstream = ZSTD_createDStream();
389       if (ZSTD_isError(ZSTD_initDStream(zstdfile->dstream)))
390         {
391           ZSTD_freeDStream(zstdfile->dstream);
392           solv_free(zstdfile);
393           fclose(fp);
394           return 0;
395         }
396       zstdfile->in.src = zstdfile->buf;
397       zstdfile->in.pos = 0;
398       zstdfile->in.size = 0;
399     }
400   zstdfile->file = fp;
401   return zstdfile;
402 }
403
404 static int zstdclose(void *cookie)
405 {
406   ZSTDFILE *zstdfile = cookie;
407   int rc;
408
409   if (!zstdfile)
410     return -1;
411   if (zstdfile->encoding)
412     {
413       for (;;)
414         {
415           size_t ret;
416           zstdfile->out.pos = 0;
417           ret = ZSTD_endStream(zstdfile->cstream, &zstdfile->out);
418           if (ZSTD_isError(ret))
419             return -1;
420           if (zstdfile->out.pos && fwrite(zstdfile->buf, 1, zstdfile->out.pos, zstdfile->file) != zstdfile->out.pos)
421             return -1;
422           if (ret == 0)
423             break;
424         }
425       ZSTD_freeCStream(zstdfile->cstream);
426     }
427   else
428     {
429       ZSTD_freeDStream(zstdfile->dstream);
430     }
431   rc = fclose(zstdfile->file);
432   free(zstdfile);
433   return rc;
434 }
435
436 static ssize_t zstdread(void *cookie, char *buf, size_t len)
437 {
438   ZSTDFILE *zstdfile = cookie;
439   int eof = 0;
440   size_t ret = 0;
441   if (!zstdfile || zstdfile->encoding)
442     return -1;
443   if (zstdfile->eof)
444     return 0;
445   zstdfile->out.dst = buf;
446   zstdfile->out.pos = 0;
447   zstdfile->out.size = len;
448   for (;;)
449     {
450       if (!eof && zstdfile->in.pos == zstdfile->in.size)
451         {
452           zstdfile->in.pos = 0;
453           zstdfile->in.size = fread(zstdfile->buf, 1, sizeof(zstdfile->buf), zstdfile->file);
454           if (!zstdfile->in.size)
455             eof = 1;
456         }
457       if (ret || !eof)
458         ret = ZSTD_decompressStream(zstdfile->dstream, &zstdfile->out, &zstdfile->in);
459       if (ret == 0 && eof)
460         {
461           zstdfile->eof = 1;
462           return zstdfile->out.pos;
463         }
464       if (ZSTD_isError(ret))
465         return -1;
466       if (zstdfile->out.pos == len)
467         return len;
468     }
469 }
470
471 static ssize_t zstdwrite(void *cookie, const char *buf, size_t len)
472 {
473   ZSTDFILE *zstdfile = cookie;
474   if (!zstdfile || !zstdfile->encoding)
475     return -1;
476   if (!len)
477     return 0;
478   zstdfile->in.src = buf;
479   zstdfile->in.pos = 0;
480   zstdfile->in.size = len;
481
482   for (;;)
483     {
484       size_t ret;
485       zstdfile->out.pos = 0;
486       ret = ZSTD_compressStream(zstdfile->cstream, &zstdfile->out, &zstdfile->in);
487       if (ZSTD_isError(ret))
488         return -1;
489       if (zstdfile->out.pos && fwrite(zstdfile->buf, 1, zstdfile->out.pos, zstdfile->file) != zstdfile->out.pos)
490         return -1;
491       if (zstdfile->in.pos == len)
492         return len;
493     }
494 }
495
496 static inline FILE *myzstdfopen(const char *fn, const char *mode)
497 {
498   ZSTDFILE *zstdfile = zstdopen(fn, mode, -1);
499   return cookieopen(zstdfile, mode, zstdread, zstdwrite, zstdclose);
500 }
501
502 static inline FILE *myzstdfdopen(int fd, const char *mode)
503 {
504   ZSTDFILE *zstdfile = zstdopen(0, mode, fd);
505   return cookieopen(zstdfile, mode, zstdread, zstdwrite, zstdclose);
506 }
507
508 #endif
509
510 #ifdef ENABLE_ZCHUNK_COMPRESSION
511
512 #ifdef WITH_SYSTEM_ZCHUNK
513 /* use the system's zchunk library that supports reading and writing of zchunk files */
514
515 #include <zck.h>
516
517 static ssize_t cookie_zckread(void *cookie, char *buf, size_t nbytes)
518 {
519   return zck_read((zckCtx *)cookie, buf, nbytes);
520 }
521
522 static ssize_t cookie_zckwrite(void *cookie, const char *buf, size_t nbytes)
523 {
524   return zck_write((zckCtx *)cookie, buf, nbytes);
525 }
526
527 static int cookie_zckclose(void *cookie)
528 {
529   zckCtx *zck = (zckCtx *)cookie;
530   int fd = zck_get_fd(zck);
531   if (fd != -1)
532     close(fd);
533   zck_free(&zck);
534   return 0;
535 }
536
537 static void *zchunkopen(const char *path, const char *mode, int fd)
538 {
539   zckCtx *f;
540
541   if (!path && fd < 0)
542     return 0;
543   if (fd == -1)
544     {
545       if (*mode != 'w')
546         fd = open(path, O_RDONLY);
547       else
548         fd = open(path, O_WRONLY | O_CREAT, 0666);
549       if (fd == -1)
550         return 0;
551     }
552   f = zck_create();
553   if (!f)
554     {
555       close(fd);
556       return 0;
557     }
558   if (*mode != 'w')
559     {
560       if(!zck_init_read(f, fd))
561         return 0;
562     }
563    else
564     {
565       if(!zck_init_write(f, fd))
566         return 0;
567     }
568   return cookieopen(f, mode, cookie_zckread, cookie_zckwrite, cookie_zckclose);
569 }
570
571 #else
572
573 #include "solv_zchunk.h"
574 /* use the libsolv's limited zchunk implementation that only supports reading of zchunk files */
575
576 static void *zchunkopen(const char *path, const char *mode, int fd)
577 {
578   FILE *fp;
579   void *f;
580   if (!path && fd < 0)
581     return 0;
582   if (fd != -1)
583     fp = fdopen(fd, mode);
584   else
585     fp = fopen(path, mode);
586   if (!fp)
587     return 0;
588   if (strcmp(mode, "r") != 0)
589     return 0;
590   f = solv_zchunk_open(fp, 1);
591   if (!f)
592     fclose(fp);
593   return cookieopen(f, mode, (ssize_t (*)(void *, char *, size_t))solv_zchunk_read, 0, (int (*)(void *))solv_zchunk_close);
594 }
595
596 #endif
597
598 static inline FILE *myzchunkfopen(const char *fn, const char *mode)
599 {
600   return zchunkopen(fn, mode, -1);
601 }
602
603 static inline FILE *myzchunkfdopen(int fd, const char *mode)
604 {
605   return zchunkopen(0, mode, fd);
606 }
607
608 #endif /* ENABLE_ZCHUNK_COMPRESSION */
609
610 #else
611 /* no cookies no compression */
612 #undef ENABLE_ZLIB_COMPRESSION
613 #undef ENABLE_LZMA_COMPRESSION
614 #undef ENABLE_BZIP2_COMPRESSION
615 #undef ENABLE_ZSTD_COMPRESSION
616 #undef ENABLE_ZCHUNK_COMPRESSION
617 #endif
618
619
620
621 FILE *
622 solv_xfopen(const char *fn, const char *mode)
623 {
624   char *suf;
625
626   if (!fn)
627     return 0;
628   if (!mode)
629     mode = "r";
630   suf = strrchr(fn, '.');
631 #ifdef ENABLE_ZLIB_COMPRESSION
632   if (suf && !strcmp(suf, ".gz"))
633     return mygzfopen(fn, mode);
634 #else
635   if (suf && !strcmp(suf, ".gz"))
636     return 0;
637 #endif
638 #ifdef ENABLE_LZMA_COMPRESSION
639   if (suf && !strcmp(suf, ".xz"))
640     return myxzfopen(fn, mode);
641   if (suf && !strcmp(suf, ".lzma"))
642     return mylzfopen(fn, mode);
643 #else
644   if (suf && !strcmp(suf, ".xz"))
645     return 0;
646   if (suf && !strcmp(suf, ".lzma"))
647     return 0;
648 #endif
649 #ifdef ENABLE_BZIP2_COMPRESSION
650   if (suf && !strcmp(suf, ".bz2"))
651     return mybzfopen(fn, mode);
652 #else
653   if (suf && !strcmp(suf, ".bz2"))
654     return 0;
655 #endif
656 #ifdef ENABLE_ZSTD_COMPRESSION
657   if (suf && !strcmp(suf, ".zst"))
658     return myzstdfopen(fn, mode);
659 #else
660   if (suf && !strcmp(suf, ".zst"))
661     return 0;
662 #endif
663 #ifdef ENABLE_ZCHUNK_COMPRESSION
664   if (suf && !strcmp(suf, ".zck"))
665     return myzchunkfopen(fn, mode);
666 #else
667   if (suf && !strcmp(suf, ".zck"))
668     return 0;
669 #endif
670   return fopen(fn, mode);
671 }
672
673 FILE *
674 solv_xfopen_fd(const char *fn, int fd, const char *mode)
675 {
676   const char *simplemode = mode;
677   char *suf;
678
679   suf = fn ? strrchr(fn, '.') : 0;
680   if (!mode)
681     {
682       #ifndef _WIN32
683       int fl = fcntl(fd, F_GETFL, 0);
684       #else
685       HANDLE handle = (HANDLE) _get_osfhandle(fd);
686       BY_HANDLE_FILE_INFORMATION file_info;
687       if (!GetFileInformationByHandle(handle, &file_info))
688         return 0;
689       int fl = file_info.dwFileAttributes;
690       #endif
691       if (fl == -1)
692         return 0;
693       fl &= O_RDONLY|O_WRONLY|O_RDWR;
694       if (fl == O_WRONLY)
695         mode = simplemode = "w";
696       else if (fl == O_RDWR)
697         {
698           mode = "r+";
699           simplemode = "r";
700         }
701       else
702         mode = simplemode = "r";
703     }
704 #ifdef ENABLE_ZLIB_COMPRESSION
705   if (suf && !strcmp(suf, ".gz"))
706     return mygzfdopen(fd, simplemode);
707 #else
708   if (suf && !strcmp(suf, ".gz"))
709     return 0;
710 #endif
711 #ifdef ENABLE_LZMA_COMPRESSION
712   if (suf && !strcmp(suf, ".xz"))
713     return myxzfdopen(fd, simplemode);
714   if (suf && !strcmp(suf, ".lzma"))
715     return mylzfdopen(fd, simplemode);
716 #else
717   if (suf && !strcmp(suf, ".xz"))
718     return 0;
719   if (suf && !strcmp(suf, ".lzma"))
720     return 0;
721 #endif
722 #ifdef ENABLE_BZIP2_COMPRESSION
723   if (suf && !strcmp(suf, ".bz2"))
724     return mybzfdopen(fd, simplemode);
725 #else
726   if (suf && !strcmp(suf, ".bz2"))
727     return 0;
728 #endif
729 #ifdef ENABLE_ZSTD_COMPRESSION
730   if (suf && !strcmp(suf, ".zst"))
731     return myzstdfdopen(fd, simplemode);
732 #else
733   if (suf && !strcmp(suf, ".zst"))
734     return 0;
735 #endif
736 #ifdef ENABLE_ZCHUNK_COMPRESSION
737   if (suf && !strcmp(suf, ".zck"))
738     return myzchunkfdopen(fd, simplemode);
739 #else
740   if (suf && !strcmp(suf, ".zck"))
741     return 0;
742 #endif
743   return fdopen(fd, mode);
744 }
745
746 int
747 solv_xfopen_iscompressed(const char *fn)
748 {
749   const char *suf = fn ? strrchr(fn, '.') : 0;
750   if (!suf)
751     return 0;
752 #ifdef ENABLE_ZLIB_COMPRESSION
753   if (!strcmp(suf, ".gz"))
754     return 1;
755 #else
756     return -1;
757 #endif
758   if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
759 #ifdef ENABLE_LZMA_COMPRESSION
760     return 1;
761 #else
762     return -1;
763 #endif
764   if (!strcmp(suf, ".bz2"))
765 #ifdef ENABLE_BZIP2_COMPRESSION
766     return 1;
767 #else
768     return -1;
769 #endif
770   if (!strcmp(suf, ".zst"))
771 #ifdef ENABLE_ZSTD_COMPRESSION
772     return 1;
773 #else
774     return -1;
775 #endif
776   if (!strcmp(suf, ".zck"))
777 #ifdef ENABLE_ZCHUNK_COMPRESSION
778     return 1;
779 #else
780     return -1;
781 #endif
782   return 0;
783 }
784
785
786 #ifndef WITHOUT_COOKIEOPEN
787
788 struct bufcookie {
789   char **bufp;
790   size_t *buflp;
791   char *freemem;
792   size_t bufl_int;
793 };
794
795 static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
796 {
797   struct bufcookie *bc = cookie;
798   size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
799   if (n)
800     {
801       memcpy(buf, *bc->bufp, n);
802       *bc->bufp += n;
803       *bc->buflp -= n;
804     }
805   return n;
806 }
807
808 static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
809 {
810   struct bufcookie *bc = cookie;
811   int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
812   if (n)
813     {
814       *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
815       memcpy(*bc->bufp, buf, n);
816       (*bc->bufp)[n] = 0;       /* zero-terminate */
817       *bc->buflp += n;
818     }
819   return n;
820 }
821
822 static int cookie_bufclose(void *cookie)
823 {
824   struct bufcookie *bc = cookie;
825   if (bc->freemem)
826     solv_free(bc->freemem);
827   solv_free(bc);
828   return 0;
829 }
830
831 FILE *
832 solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
833 {
834   struct bufcookie *bc;
835   FILE *fp;
836   if (*mode != 'r' && *mode != 'w')
837     return 0;
838   bc = solv_calloc(1, sizeof(*bc));
839   bc->freemem = 0;
840   bc->bufp = bufp;
841   if (!buflp)
842     {
843       bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
844       buflp = &bc->bufl_int;
845     }
846   bc->buflp = buflp;
847   if (*mode == 'w')
848     {
849       *bc->bufp = solv_extend(0, 0, 1, 1, 4095);        /* always zero-terminate */
850       (*bc->bufp)[0] = 0;
851       *bc->buflp = 0;
852     }
853   fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
854   if (!strcmp(mode, "rf"))      /* auto-free */
855     bc->freemem = *bufp;
856   if (!fp)
857     {
858       if (*mode == 'w')
859         *bc->bufp = solv_free(*bc->bufp);
860       cookie_bufclose(bc);
861     }
862   return fp;
863 }
864
865 #else
866
867 FILE *
868 solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
869 {
870   FILE *fp;
871   size_t l;
872   if (*mode != 'r')
873     return 0;
874   l = buflp ? *buflp : strlen(*bufp);
875   if (!strcmp(mode, "rf"))
876     {
877       if (!(fp = fmemopen(0, l, "r+")))
878         return 0;
879       if (l && fwrite(*bufp, l, 1, fp) != 1)
880         {
881           fclose(fp);
882           return 0;
883         }
884       solv_free(*bufp);
885       rewind(fp);
886     }
887   else
888     fp = fmemopen(*bufp, l, "r");
889   return fp;
890 }
891
892 #endif
893