- adapt to liobsolv coding style
[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 <zlib.h>
14 #include <fcntl.h>
15
16 #include "solv_xfopen.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   if (!cookie)
25     return 0;
26 #ifdef HAVE_FUNOPEN
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   memset(&cio, 0, sizeof(cio));
36   if (*mode == 'r')
37     cio.read = cread;
38   else if (*mode == 'w')
39     cio.write = cwrite;
40   cio.close = cclose;
41   return  fopencookie(cookie, *mode == 'w' ? "w" : "r", cio);
42 #else
43 # error Need to implement custom I/O
44 #endif
45 }
46
47
48 /* gzip compression */
49
50 static ssize_t cookie_gzread(void *cookie, char *buf, size_t nbytes)
51 {
52   return gzread((gzFile *)cookie, buf, nbytes);
53 }
54
55 static ssize_t cookie_gzwrite(void *cookie, const char *buf, size_t nbytes)
56 {
57   return gzwrite((gzFile *)cookie, buf, nbytes);
58 }
59
60 static int cookie_gzclose(void *cookie)
61 {
62   return gzclose((gzFile *)cookie);
63 }
64
65 static inline FILE *mygzfopen(const char *fn, const char *mode)
66 {
67   gzFile *gzf = gzopen(fn, mode);
68   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
69 }
70
71 static inline FILE *mygzfdopen(int fd, const char *mode)
72 {
73   gzFile *gzf = gzdopen(fd, mode);
74   return cookieopen(gzf, mode, cookie_gzread, cookie_gzwrite, cookie_gzclose);
75 }
76
77
78 #ifdef ENABLE_LZMA_COMPRESSION
79
80 #include <lzma.h>
81
82 typedef struct lzfile {
83   unsigned char buf[1 << 15];
84   lzma_stream strm;
85   FILE *file;
86   int encoding;
87   int eof;
88 } LZFILE;
89
90 static LZFILE *lzopen(const char *path, const char *mode, int fd, int xz)
91 {
92   int level = 7;
93   int encoding = 0;
94   FILE *fp;
95   LZFILE *lzfile;
96   lzma_ret ret;
97   lzma_stream init_strm = LZMA_STREAM_INIT;
98
99   if (!path && fd < 0)
100     return 0;
101   for (; *mode; mode++)
102     {
103       if (*mode == 'w')
104         encoding = 1;
105       else if (*mode == 'r')
106         encoding = 0;
107       else if (*mode >= '1' && *mode <= '9')
108         level = *mode - '0';
109     }
110   if (fd != -1)
111     fp = fdopen(fd, encoding ? "w" : "r");
112   else
113     fp = fopen(path, encoding ? "w" : "r");
114   if (!fp)
115     return 0;
116   lzfile = calloc(1, sizeof(*lzfile));
117   if (!lzfile)
118     {
119       fclose(fp);
120       return 0;
121     }
122   lzfile->file = fp;
123   lzfile->encoding = encoding;
124   lzfile->eof = 0;
125   lzfile->strm = init_strm;
126   if (encoding)
127     {
128       if (xz)
129         ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
130       else
131         {
132           lzma_options_lzma options;
133           lzma_lzma_preset(&options, level);
134           ret = lzma_alone_encoder(&lzfile->strm, &options);
135         }
136     }
137   else
138     {
139       /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
140       ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
141     }
142   if (ret != LZMA_OK)
143     {
144       fclose(fp);
145       free(lzfile);
146       return 0;
147     }
148   return lzfile;
149 }
150
151 static int lzclose(void *cookie)
152 {
153   LZFILE *lzfile = cookie;
154   lzma_ret ret;
155   size_t n;
156   int rc;
157
158   if (!lzfile)
159     return -1;
160   if (lzfile->encoding)
161     {
162       for (;;)
163         {
164           lzfile->strm.avail_out = sizeof(lzfile->buf);
165           lzfile->strm.next_out = lzfile->buf;
166           ret = lzma_code(&lzfile->strm, LZMA_FINISH);
167           if (ret != LZMA_OK && ret != LZMA_STREAM_END)
168             return -1;
169           n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
170           if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
171             return -1;
172           if (ret == LZMA_STREAM_END)
173             break;
174         }
175     }
176   lzma_end(&lzfile->strm);
177   rc = fclose(lzfile->file);
178   free(lzfile);
179   return rc;
180 }
181
182 static ssize_t lzread(void *cookie, char *buf, size_t len)
183 {
184   LZFILE *lzfile = cookie;
185   lzma_ret ret;
186   int eof = 0;
187
188   if (!lzfile || lzfile->encoding)
189     return -1;
190   if (lzfile->eof)
191     return 0;
192   lzfile->strm.next_out = (unsigned char *)buf;
193   lzfile->strm.avail_out = len;
194   for (;;)
195     {
196       if (!lzfile->strm.avail_in)
197         {
198           lzfile->strm.next_in = lzfile->buf;
199           lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
200           if (!lzfile->strm.avail_in)
201             eof = 1;
202         }
203       ret = lzma_code(&lzfile->strm, LZMA_RUN);
204       if (ret == LZMA_STREAM_END)
205         {
206           lzfile->eof = 1;
207           return len - lzfile->strm.avail_out;
208         }
209       if (ret != LZMA_OK)
210         return -1;
211       if (!lzfile->strm.avail_out)
212         return len;
213       if (eof)
214         return -1;
215     }
216 }
217
218 static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
219 {
220   LZFILE *lzfile = cookie;
221   lzma_ret ret;
222   size_t n;
223   if (!lzfile || !lzfile->encoding)
224     return -1;
225   if (!len)
226     return 0;
227   lzfile->strm.next_in = (unsigned char *)buf;
228   lzfile->strm.avail_in = len;
229   for (;;)
230     {
231       lzfile->strm.next_out = lzfile->buf;
232       lzfile->strm.avail_out = sizeof(lzfile->buf);
233       ret = lzma_code(&lzfile->strm, LZMA_RUN);
234       if (ret != LZMA_OK)
235         return -1;
236       n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
237       if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
238         return -1;
239       if (!lzfile->strm.avail_in)
240         return len;
241     }
242 }
243
244 static inline FILE *myxzfopen(const char *fn, const char *mode)
245 {
246   LZFILE *lzf = lzopen(fn, mode, -1, 1);
247   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
248 }
249
250 static inline FILE *myxzfdopen(int fd, const char *mode)
251 {
252   LZFILE *lzf = lzopen(0, mode, fd, 1);
253   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
254 }
255
256 static inline FILE *mylzfopen(const char *fn, const char *mode)
257 {
258   LZFILE *lzf = lzopen(fn, mode, -1, 0);
259   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
260 }
261
262 static inline FILE *mylzfdopen(int fd, const char *mode)
263 {
264   LZFILE *lzf = lzopen(0, mode, fd, 0);
265   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
266 }
267
268 #endif /* ENABLE_LZMA_COMPRESSION */
269
270
271 FILE *
272 solv_xfopen(const char *fn, const char *mode)
273 {
274   char *suf;
275
276   if (!fn)
277     return 0;
278   if (!mode)
279     mode = "r";
280   suf = strrchr(fn, '.');
281   if (suf && !strcmp(suf, ".gz"))
282     return mygzfopen(fn, mode);
283 #ifdef ENABLE_LZMA_COMPRESSION
284   if (suf && !strcmp(suf, ".xz"))
285     return myxzfopen(fn, mode);
286   if (suf && !strcmp(suf, ".lzma"))
287     return mylzfopen(fn, mode);
288 #endif
289   return fopen(fn, mode);
290 }
291
292 FILE *
293 solv_xfopen_fd(const char *fn, int fd, const char *mode)
294 {
295   char *suf;
296
297   suf = fn ? strrchr(fn, '.') : 0;
298   if (!mode)
299     {
300       int fl = fcntl(fd, F_GETFL, 0);
301       if (fl == -1)
302         return 0;
303       fl &= O_RDONLY|O_WRONLY|O_RDWR;
304       if (fl == O_WRONLY)
305         mode = "w";
306       else if (fl == O_RDWR)
307         {
308           if (!suf || strcmp(suf, ".gz") != 0)
309             mode = "r+";
310           else
311             mode = "r";
312         }
313       else
314         mode = "r";
315     }
316   if (suf && !strcmp(suf, ".gz"))
317     return mygzfdopen(fd, mode);
318 #ifdef ENABLE_LZMA_COMPRESSION
319   if (suf && !strcmp(suf, ".xz"))
320     return myxzfdopen(fd, mode);
321   if (suf && !strcmp(suf, ".lzma"))
322     return mylzfdopen(fd, mode);
323 #endif
324   return fdopen(fd, mode);
325 }
326