- support writing of gzipped files
[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 <string.h>
12 #include <zlib.h>
13 #include <fcntl.h>
14
15 #include "solv_xfopen.h"
16
17 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
18 {
19   return gzread((gzFile *)cookie, buf, nbytes);
20 }
21
22 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
23 {
24   return gzwrite((gzFile *)cookie, buf, nbytes);
25 }
26
27 static int
28 cookie_gzclose(void *cookie)
29 {
30   return gzclose((gzFile *)cookie);
31 }
32
33 static FILE *mygzfopen(gzFile* gzf, const char *mode)
34 {
35 #ifdef HAVE_FUNOPEN
36   return funopen(
37       gzf, (int (*)(void *, char *, int))(*mode == 'r' ? cookie_gzread : NULL), /* readfn */
38       (int (*)(void *, const char *, int))(*mode == 'w' ? cookie_gzwrite : NULL), /* writefn */
39       (fpos_t (*)(void *, fpos_t, int))NULL, /* seekfn */
40       cookie_gzclose
41       );
42 #elif defined(HAVE_FOPENCOOKIE)
43   cookie_io_functions_t cio;
44   memset(&cio, 0, sizeof(cio));
45   if (*mode == 'r')
46     cio.read = cookie_gzread;
47   else if (*mode == 'w')
48     cio.write = cookie_gzwrite;
49   cio.close = cookie_gzclose;
50   return  fopencookie(gzf, *mode == 'w' ? "w" : "r", cio);
51 #else
52 # error Need to implement custom I/O
53 #endif
54 }
55
56 FILE *
57 solv_xfopen(const char *fn, const char *mode)
58 {
59   char *suf;
60   gzFile *gzf;
61
62   if (!fn)
63     return 0;
64   if (!mode)
65     mode = "r";
66   suf = strrchr(fn, '.');
67   if (!suf || strcmp(suf, ".gz") != 0)
68     return fopen(fn, mode);
69   gzf = gzopen(fn, mode);
70   if (!gzf)
71     return 0;
72   return mygzfopen(gzf, mode);
73 }
74
75 FILE *
76 solv_xfopen_fd(const char *fn, int fd, const char *mode)
77 {
78   char *suf;
79   gzFile *gzf;
80
81   suf = fn ? strrchr(fn, '.') : 0;
82   if (!mode)
83     {
84       int fl = fcntl(fd, F_GETFL, 0);
85       if (fl == -1)
86         return 0;
87       fl &= O_RDONLY|O_WRONLY|O_RDWR;
88       if (fl == O_WRONLY)
89         mode = "w";
90       else if (fl == O_RDWR)
91         {
92           if (!suf || strcmp(suf, ".gz") != 0)
93             mode = "r+";
94           else
95             mode = "r";
96         }
97       else
98         mode = "r";
99     }
100   if (!suf || strcmp(suf, ".gz") != 0)
101     return fdopen(fd, mode);
102   gzf = gzdopen(fd, mode);
103   if (!gzf)
104     return 0;
105   return mygzfopen(gzf, mode);
106 }
107