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