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