Add ENABLE_COMPLEX_DEPS flag
[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) || (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   lzfile = solv_calloc(1, sizeof(*lzfile));
180   lzfile->encoding = encoding;
181   lzfile->eof = 0;
182   lzfile->strm = stream_init;
183   if (encoding)
184     {
185       if (isxz)
186         ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
187       else
188         ret = setup_alone_encoder(&lzfile->strm, level);
189     }
190   else
191     ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
192   if (ret != LZMA_OK)
193     {
194       solv_free(lzfile);
195       return 0;
196     }
197   if (!path)
198     fp = fdopen(fd, encoding ? "w" : "r");
199   else
200     fp = fopen(path, encoding ? "w" : "r");
201   if (!fp)
202     {
203       lzma_end(&lzfile->strm);
204       solv_free(lzfile);
205       return 0;
206     }
207   lzfile->file = fp;
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   solv_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 #ifdef ENABLE_ZSTD_COMPRESSION
331
332 #include <zstd.h>
333
334 typedef struct zstdfile {
335   ZSTD_CStream *cstream;
336   ZSTD_DStream *dstream;
337   FILE *file;
338   int encoding;
339   int eof;
340   ZSTD_inBuffer in;
341   ZSTD_outBuffer out;
342   unsigned char buf[1 << 15];
343 } ZSTDFILE;
344
345 static ZSTDFILE *zstdopen(const char *path, const char *mode, int fd)
346 {
347   int level = 7;
348   int encoding = 0;
349   FILE *fp;
350   ZSTDFILE *zstdfile;
351
352   if ((!path && fd < 0) || (path && fd >= 0))
353     return 0;
354   for (; *mode; mode++)
355     {
356       if (*mode == 'w')
357         encoding = 1;
358       else if (*mode == 'r')
359         encoding = 0;
360       else if (*mode >= '1' && *mode <= '9')
361         level = *mode - '0';
362     }
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           return 0;
373         }
374       if (ZSTD_isError(ZSTD_initCStream(zstdfile->cstream, level)))
375         {
376           ZSTD_freeCStream(zstdfile->cstream);
377           solv_free(zstdfile);
378           return 0;
379         }
380       zstdfile->out.dst = zstdfile->buf;
381       zstdfile->out.pos = 0;
382       zstdfile->out.size = sizeof(zstdfile->buf);
383     }
384   else
385     {
386       zstdfile->dstream = ZSTD_createDStream();
387       if (ZSTD_isError(ZSTD_initDStream(zstdfile->dstream)))
388         {
389           ZSTD_freeDStream(zstdfile->dstream);
390           solv_free(zstdfile);
391           return 0;
392         }
393       zstdfile->in.src = zstdfile->buf;
394       zstdfile->in.pos = 0;
395       zstdfile->in.size = 0;
396     }
397   if (!path)
398     fp = fdopen(fd, encoding ? "w" : "r");
399   else
400     fp = fopen(path, encoding ? "w" : "r");
401   if (!fp)
402     {
403       if (encoding)
404         ZSTD_freeCStream(zstdfile->cstream);
405       else
406         ZSTD_freeDStream(zstdfile->dstream);
407       solv_free(zstdfile);
408       return 0;
409     }
410   zstdfile->file = fp;
411   return zstdfile;
412 }
413
414 static int zstdclose(void *cookie)
415 {
416   ZSTDFILE *zstdfile = cookie;
417   int rc;
418
419   if (!zstdfile)
420     return -1;
421   if (zstdfile->encoding)
422     {
423       for (;;)
424         {
425           size_t ret;
426           zstdfile->out.pos = 0;
427           ret = ZSTD_endStream(zstdfile->cstream, &zstdfile->out);
428           if (ZSTD_isError(ret))
429             return -1;
430           if (zstdfile->out.pos && fwrite(zstdfile->buf, 1, zstdfile->out.pos, zstdfile->file) != zstdfile->out.pos)
431             return -1;
432           if (ret == 0)
433             break;
434         }
435       ZSTD_freeCStream(zstdfile->cstream);
436     }
437   else
438     {
439       ZSTD_freeDStream(zstdfile->dstream);
440     }
441   rc = fclose(zstdfile->file);
442   solv_free(zstdfile);
443   return rc;
444 }
445
446 static ssize_t zstdread(void *cookie, char *buf, size_t len)
447 {
448   ZSTDFILE *zstdfile = cookie;
449   int eof = 0;
450   size_t ret = 0;
451   if (!zstdfile || zstdfile->encoding)
452     return -1;
453   if (zstdfile->eof)
454     return 0;
455   zstdfile->out.dst = buf;
456   zstdfile->out.pos = 0;
457   zstdfile->out.size = len;
458   for (;;)
459     {
460       if (!eof && zstdfile->in.pos == zstdfile->in.size)
461         {
462           zstdfile->in.pos = 0;
463           zstdfile->in.size = fread(zstdfile->buf, 1, sizeof(zstdfile->buf), zstdfile->file);
464           if (!zstdfile->in.size)
465             eof = 1;
466         }
467       if (ret || !eof)
468         ret = ZSTD_decompressStream(zstdfile->dstream, &zstdfile->out, &zstdfile->in);
469       if (ret == 0 && eof)
470         {
471           zstdfile->eof = 1;
472           return zstdfile->out.pos;
473         }
474       if (ZSTD_isError(ret))
475         return -1;
476       if (zstdfile->out.pos == len)
477         return len;
478     }
479 }
480
481 static ssize_t zstdwrite(void *cookie, const char *buf, size_t len)
482 {
483   ZSTDFILE *zstdfile = cookie;
484   if (!zstdfile || !zstdfile->encoding)
485     return -1;
486   if (!len)
487     return 0;
488   zstdfile->in.src = buf;
489   zstdfile->in.pos = 0;
490   zstdfile->in.size = len;
491
492   for (;;)
493     {
494       size_t ret;
495       zstdfile->out.pos = 0;
496       ret = ZSTD_compressStream(zstdfile->cstream, &zstdfile->out, &zstdfile->in);
497       if (ZSTD_isError(ret))
498         return -1;
499       if (zstdfile->out.pos && fwrite(zstdfile->buf, 1, zstdfile->out.pos, zstdfile->file) != zstdfile->out.pos)
500         return -1;
501       if (zstdfile->in.pos == len)
502         return len;
503     }
504 }
505
506 static inline FILE *myzstdfopen(const char *fn, const char *mode)
507 {
508   ZSTDFILE *zstdfile = zstdopen(fn, mode, -1);
509   return cookieopen(zstdfile, mode, zstdread, zstdwrite, zstdclose);
510 }
511
512 static inline FILE *myzstdfdopen(int fd, const char *mode)
513 {
514   ZSTDFILE *zstdfile = zstdopen(0, mode, fd);
515   return cookieopen(zstdfile, mode, zstdread, zstdwrite, zstdclose);
516 }
517
518 #endif
519
520 #ifdef ENABLE_ZCHUNK_COMPRESSION
521
522 #ifdef WITH_SYSTEM_ZCHUNK
523 /* use the system's zchunk library that supports reading and writing of zchunk files */
524
525 #include <zck.h>
526
527 static ssize_t cookie_zckread(void *cookie, char *buf, size_t nbytes)
528 {
529   return zck_read((zckCtx *)cookie, buf, nbytes);
530 }
531
532 static ssize_t cookie_zckwrite(void *cookie, const char *buf, size_t nbytes)
533 {
534   return zck_write((zckCtx *)cookie, buf, nbytes);
535 }
536
537 static int cookie_zckclose(void *cookie)
538 {
539   zckCtx *zck = (zckCtx *)cookie;
540   int fd = zck_get_fd(zck);
541   if (fd != -1)
542     close(fd);
543   zck_free(&zck);
544   return 0;
545 }
546
547 static void *zchunkopen(const char *path, const char *mode, int fd)
548 {
549   zckCtx *f;
550
551   if ((!path && fd < 0) || (path && fd >= 0))
552     return 0;
553   if (path)
554     {
555       if (*mode != 'w')
556         fd = open(path, O_RDONLY);
557       else
558         fd = open(path, O_WRONLY | O_CREAT, 0666);
559       if (fd == -1)
560         return 0;
561     }
562   f = zck_create();
563   if (!f)
564     {
565       if (path)
566         close(fd);
567       return 0;
568     }
569   if (*mode != 'w')
570     {
571       if(!zck_init_read(f, fd))
572         {
573           zck_free(&f);
574           if (path)
575             close(fd);
576           return 0;
577         }
578     }
579    else
580     {
581       if(!zck_init_write(f, fd))
582         {
583           zck_free(&f);
584           if (path)
585             close(fd);
586           return 0;
587         }
588     }
589   return cookieopen(f, mode, cookie_zckread, cookie_zckwrite, cookie_zckclose);
590 }
591
592 #else
593
594 #include "solv_zchunk.h"
595 /* use the libsolv's limited zchunk implementation that only supports reading of zchunk files */
596
597 static void *zchunkopen(const char *path, const char *mode, int fd)
598 {
599   FILE *fp;
600   void *f;
601   if ((!path && fd < 0) || (path && fd >= 0))
602     return 0;
603   if (strcmp(mode, "r") != 0)
604     return 0;
605   if (!path)
606     fp = fdopen(fd, mode);
607   else
608     fp = fopen(path, mode);
609   if (!fp)
610     return 0;
611   f = solv_zchunk_open(fp, 1);
612   if (!f)
613     {
614       if (!path)
615         {
616           /* The fd passed by user must not be closed! */
617           /* Dup (save) the original fd to a temporary variable and then back. */
618           /* It is ugly and thread unsafe hack (non atomical sequence fclose dup2). */
619           int tmpfd = dup(fd);
620           fclose(fp);
621           dup2(tmpfd, fd);
622           close(tmpfd);
623         }
624       else
625         {
626           fclose(fp);
627         }
628     }
629   return cookieopen(f, mode, (ssize_t (*)(void *, char *, size_t))solv_zchunk_read, 0, (int (*)(void *))solv_zchunk_close);
630 }
631
632 #endif
633
634 static inline FILE *myzchunkfopen(const char *fn, const char *mode)
635 {
636   return zchunkopen(fn, mode, -1);
637 }
638
639 static inline FILE *myzchunkfdopen(int fd, const char *mode)
640 {
641   return zchunkopen(0, mode, fd);
642 }
643
644 #endif /* ENABLE_ZCHUNK_COMPRESSION */
645
646 #else
647 /* no cookies no compression */
648 #undef ENABLE_ZLIB_COMPRESSION
649 #undef ENABLE_LZMA_COMPRESSION
650 #undef ENABLE_BZIP2_COMPRESSION
651 #undef ENABLE_ZSTD_COMPRESSION
652 #undef ENABLE_ZCHUNK_COMPRESSION
653 #endif
654
655
656
657 FILE *
658 solv_xfopen(const char *fn, const char *mode)
659 {
660   char *suf;
661
662   if (!fn)
663     return 0;
664   if (!mode)
665     mode = "r";
666   suf = strrchr(fn, '.');
667 #ifdef ENABLE_ZLIB_COMPRESSION
668   if (suf && !strcmp(suf, ".gz"))
669     return mygzfopen(fn, mode);
670 #else
671   if (suf && !strcmp(suf, ".gz"))
672     return 0;
673 #endif
674 #ifdef ENABLE_LZMA_COMPRESSION
675   if (suf && !strcmp(suf, ".xz"))
676     return myxzfopen(fn, mode);
677   if (suf && !strcmp(suf, ".lzma"))
678     return mylzfopen(fn, mode);
679 #else
680   if (suf && !strcmp(suf, ".xz"))
681     return 0;
682   if (suf && !strcmp(suf, ".lzma"))
683     return 0;
684 #endif
685 #ifdef ENABLE_BZIP2_COMPRESSION
686   if (suf && !strcmp(suf, ".bz2"))
687     return mybzfopen(fn, mode);
688 #else
689   if (suf && !strcmp(suf, ".bz2"))
690     return 0;
691 #endif
692 #ifdef ENABLE_ZSTD_COMPRESSION
693   if (suf && !strcmp(suf, ".zst"))
694     return myzstdfopen(fn, mode);
695 #else
696   if (suf && !strcmp(suf, ".zst"))
697     return 0;
698 #endif
699 #ifdef ENABLE_ZCHUNK_COMPRESSION
700   if (suf && !strcmp(suf, ".zck"))
701     return myzchunkfopen(fn, mode);
702 #else
703   if (suf && !strcmp(suf, ".zck"))
704     return 0;
705 #endif
706   return fopen(fn, mode);
707 }
708
709 FILE *
710 solv_xfopen_fd(const char *fn, int fd, const char *mode)
711 {
712   const char *simplemode = mode;
713   char *suf;
714
715   suf = fn ? strrchr(fn, '.') : 0;
716   if (!mode)
717     {
718       #ifndef _WIN32
719       int fl = fcntl(fd, F_GETFL, 0);
720       #else
721       HANDLE handle = (HANDLE) _get_osfhandle(fd);
722       BY_HANDLE_FILE_INFORMATION file_info;
723       if (!GetFileInformationByHandle(handle, &file_info))
724         return 0;
725       int fl = file_info.dwFileAttributes;
726       #endif
727       if (fl == -1)
728         return 0;
729       fl &= O_RDONLY|O_WRONLY|O_RDWR;
730       if (fl == O_WRONLY)
731         mode = simplemode = "w";
732       else if (fl == O_RDWR)
733         {
734           mode = "r+";
735           simplemode = "r";
736         }
737       else
738         mode = simplemode = "r";
739     }
740 #ifdef ENABLE_ZLIB_COMPRESSION
741   if (suf && !strcmp(suf, ".gz"))
742     return mygzfdopen(fd, simplemode);
743 #else
744   if (suf && !strcmp(suf, ".gz"))
745     return 0;
746 #endif
747 #ifdef ENABLE_LZMA_COMPRESSION
748   if (suf && !strcmp(suf, ".xz"))
749     return myxzfdopen(fd, simplemode);
750   if (suf && !strcmp(suf, ".lzma"))
751     return mylzfdopen(fd, simplemode);
752 #else
753   if (suf && !strcmp(suf, ".xz"))
754     return 0;
755   if (suf && !strcmp(suf, ".lzma"))
756     return 0;
757 #endif
758 #ifdef ENABLE_BZIP2_COMPRESSION
759   if (suf && !strcmp(suf, ".bz2"))
760     return mybzfdopen(fd, simplemode);
761 #else
762   if (suf && !strcmp(suf, ".bz2"))
763     return 0;
764 #endif
765 #ifdef ENABLE_ZSTD_COMPRESSION
766   if (suf && !strcmp(suf, ".zst"))
767     return myzstdfdopen(fd, simplemode);
768 #else
769   if (suf && !strcmp(suf, ".zst"))
770     return 0;
771 #endif
772 #ifdef ENABLE_ZCHUNK_COMPRESSION
773   if (suf && !strcmp(suf, ".zck"))
774     return myzchunkfdopen(fd, simplemode);
775 #else
776   if (suf && !strcmp(suf, ".zck"))
777     return 0;
778 #endif
779   return fdopen(fd, mode);
780 }
781
782 int
783 solv_xfopen_iscompressed(const char *fn)
784 {
785   const char *suf = fn ? strrchr(fn, '.') : 0;
786   if (!suf)
787     return 0;
788 #ifdef ENABLE_ZLIB_COMPRESSION
789   if (!strcmp(suf, ".gz"))
790     return 1;
791 #else
792     return -1;
793 #endif
794   if (!strcmp(suf, ".xz") || !strcmp(suf, ".lzma"))
795 #ifdef ENABLE_LZMA_COMPRESSION
796     return 1;
797 #else
798     return -1;
799 #endif
800   if (!strcmp(suf, ".bz2"))
801 #ifdef ENABLE_BZIP2_COMPRESSION
802     return 1;
803 #else
804     return -1;
805 #endif
806   if (!strcmp(suf, ".zst"))
807 #ifdef ENABLE_ZSTD_COMPRESSION
808     return 1;
809 #else
810     return -1;
811 #endif
812   if (!strcmp(suf, ".zck"))
813 #ifdef ENABLE_ZCHUNK_COMPRESSION
814     return 1;
815 #else
816     return -1;
817 #endif
818   return 0;
819 }
820
821
822 #ifndef WITHOUT_COOKIEOPEN
823
824 struct bufcookie {
825   char **bufp;
826   size_t *buflp;
827   char *freemem;
828   size_t bufl_int;
829   char *buf_int;
830 };
831
832 static ssize_t cookie_bufread(void *cookie, char *buf, size_t nbytes)
833 {
834   struct bufcookie *bc = cookie;
835   size_t n = *bc->buflp > nbytes ? nbytes : *bc->buflp;
836   if (n)
837     {
838       memcpy(buf, *bc->bufp, n);
839       *bc->bufp += n;
840       *bc->buflp -= n;
841     }
842   return n;
843 }
844
845 static ssize_t cookie_bufwrite(void *cookie, const char *buf, size_t nbytes)
846 {
847   struct bufcookie *bc = cookie;
848   int n = nbytes > 0x40000000 ? 0x40000000 : nbytes;
849   if (n)
850     {
851       *bc->bufp = solv_extend(*bc->bufp, *bc->buflp, n + 1, 1, 4095);
852       memcpy(*bc->bufp, buf, n);
853       (*bc->bufp)[n] = 0;       /* zero-terminate */
854       *bc->buflp += n;
855     }
856   return n;
857 }
858
859 static int cookie_bufclose(void *cookie)
860 {
861   struct bufcookie *bc = cookie;
862   if (bc->freemem)
863     solv_free(bc->freemem);
864   solv_free(bc);
865   return 0;
866 }
867
868 FILE *
869 solv_xfopen_buf(const char *fn, char **bufp, size_t *buflp, const char *mode)
870 {
871   struct bufcookie *bc;
872   FILE *fp;
873   if (*mode != 'r' && *mode != 'w')
874     return 0;
875   bc = solv_calloc(1, sizeof(*bc));
876   bc->freemem = 0;
877   bc->bufp = bufp;
878   if (!buflp)
879     {
880       bc->bufl_int = *mode == 'w' ? 0 : strlen(*bufp);
881       buflp = &bc->bufl_int;
882     }
883   bc->buflp = buflp;
884   if (*mode == 'w')
885     {
886       *bc->bufp = solv_extend(0, 0, 1, 1, 4095);        /* always zero-terminate */
887       (*bc->bufp)[0] = 0;
888       *bc->buflp = 0;
889     }
890   fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
891   if (!strcmp(mode, "rf"))      /* auto-free */
892     bc->freemem = *bufp;
893   if (!fp)
894     {
895       if (*mode == 'w')
896         *bc->bufp = solv_free(*bc->bufp);
897       cookie_bufclose(bc);
898     }
899   return fp;
900 }
901
902 FILE *
903 solv_fmemopen(const char *buf, size_t bufl, const char *mode)
904 {
905   struct bufcookie *bc;
906   FILE *fp;
907   if (*mode != 'r')
908     return 0;
909   bc = solv_calloc(1, sizeof(*bc));
910   bc->buf_int = (char *)buf;
911   bc->bufl_int = bufl;
912   bc->bufp = &bc->buf_int;
913   bc->buflp = &bc->bufl_int;
914   fp = cookieopen(bc, mode, cookie_bufread, cookie_bufwrite, cookie_bufclose);
915   if (!strcmp(mode, "rf"))      /* auto-free */
916     bc->freemem = bc->buf_int;
917   if (!fp)
918     cookie_bufclose(bc);
919   return fp;
920 }
921
922 #else
923
924 FILE *
925 solv_fmemopen(const char *buf, size_t bufl, const char *mode)
926 {
927   FILE *fp;
928   if (*mode != 'r')
929     return 0;
930   if (!strcmp(mode, "rf"))
931     {
932       if (!(fp = fmemopen(0, bufl, "r+")))
933         return 0;
934       if (bufl && fwrite(buf, bufl, 1, fp) != 1)
935         {
936           fclose(fp);
937           return 0;
938         }
939       solv_free((char *)buf);
940       rewind(fp);
941     }
942   else
943     fp = fmemopen((char *)buf, bufl, "r");
944   return fp;
945 }
946
947 #endif
948