- clean up lzma code a bit
[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 /* lzma code written by me in 2008 for rpm's rpmio.c */
83
84 typedef struct lzfile {
85   unsigned char buf[1 << 15];
86   lzma_stream strm;
87   FILE *file;
88   int encoding;
89   int eof;
90 } LZFILE;
91
92 static inline lzma_ret setup_alone_encoder(lzma_stream *strm, int level)
93 {
94   lzma_options_lzma options;
95   lzma_lzma_preset(&options, level);
96   return lzma_alone_encoder(strm, &options);
97 }
98
99 static lzma_stream stream_init = LZMA_STREAM_INIT;
100
101 static LZFILE *lzopen(const char *path, const char *mode, int fd, int isxz)
102 {
103   int level = 7;
104   int encoding = 0;
105   FILE *fp;
106   LZFILE *lzfile;
107   lzma_ret ret;
108
109   if (!path && fd < 0)
110     return 0;
111   for (; *mode; mode++)
112     {
113       if (*mode == 'w')
114         encoding = 1;
115       else if (*mode == 'r')
116         encoding = 0;
117       else if (*mode >= '1' && *mode <= '9')
118         level = *mode - '0';
119     }
120   if (fd != -1)
121     fp = fdopen(fd, encoding ? "w" : "r");
122   else
123     fp = fopen(path, encoding ? "w" : "r");
124   if (!fp)
125     return 0;
126   lzfile = calloc(1, sizeof(*lzfile));
127   if (!lzfile)
128     {
129       fclose(fp);
130       return 0;
131     }
132   lzfile->file = fp;
133   lzfile->encoding = encoding;
134   lzfile->eof = 0;
135   lzfile->strm = stream_init;
136   if (encoding)
137     {
138       if (isxz)
139         ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
140       else
141         ret = setup_alone_encoder(&lzfile->strm, level);
142     }
143   else
144     ret = lzma_auto_decoder(&lzfile->strm, 100 << 20, 0);
145   if (ret != LZMA_OK)
146     {
147       fclose(fp);
148       free(lzfile);
149       return 0;
150     }
151   return lzfile;
152 }
153
154 static int lzclose(void *cookie)
155 {
156   LZFILE *lzfile = cookie;
157   lzma_ret ret;
158   size_t n;
159   int rc;
160
161   if (!lzfile)
162     return -1;
163   if (lzfile->encoding)
164     {
165       for (;;)
166         {
167           lzfile->strm.avail_out = sizeof(lzfile->buf);
168           lzfile->strm.next_out = lzfile->buf;
169           ret = lzma_code(&lzfile->strm, LZMA_FINISH);
170           if (ret != LZMA_OK && ret != LZMA_STREAM_END)
171             return -1;
172           n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
173           if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
174             return -1;
175           if (ret == LZMA_STREAM_END)
176             break;
177         }
178     }
179   lzma_end(&lzfile->strm);
180   rc = fclose(lzfile->file);
181   free(lzfile);
182   return rc;
183 }
184
185 static ssize_t lzread(void *cookie, char *buf, size_t len)
186 {
187   LZFILE *lzfile = cookie;
188   lzma_ret ret;
189   int eof = 0;
190
191   if (!lzfile || lzfile->encoding)
192     return -1;
193   if (lzfile->eof)
194     return 0;
195   lzfile->strm.next_out = (unsigned char *)buf;
196   lzfile->strm.avail_out = len;
197   for (;;)
198     {
199       if (!lzfile->strm.avail_in)
200         {
201           lzfile->strm.next_in = lzfile->buf;
202           lzfile->strm.avail_in = fread(lzfile->buf, 1, sizeof(lzfile->buf), lzfile->file);
203           if (!lzfile->strm.avail_in)
204             eof = 1;
205         }
206       ret = lzma_code(&lzfile->strm, LZMA_RUN);
207       if (ret == LZMA_STREAM_END)
208         {
209           lzfile->eof = 1;
210           return len - lzfile->strm.avail_out;
211         }
212       if (ret != LZMA_OK)
213         return -1;
214       if (!lzfile->strm.avail_out)
215         return len;
216       if (eof)
217         return -1;
218     }
219 }
220
221 static ssize_t lzwrite(void *cookie, const char *buf, size_t len)
222 {
223   LZFILE *lzfile = cookie;
224   lzma_ret ret;
225   size_t n;
226   if (!lzfile || !lzfile->encoding)
227     return -1;
228   if (!len)
229     return 0;
230   lzfile->strm.next_in = (unsigned char *)buf;
231   lzfile->strm.avail_in = len;
232   for (;;)
233     {
234       lzfile->strm.next_out = lzfile->buf;
235       lzfile->strm.avail_out = sizeof(lzfile->buf);
236       ret = lzma_code(&lzfile->strm, LZMA_RUN);
237       if (ret != LZMA_OK)
238         return -1;
239       n = sizeof(lzfile->buf) - lzfile->strm.avail_out;
240       if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
241         return -1;
242       if (!lzfile->strm.avail_in)
243         return len;
244     }
245 }
246
247 static inline FILE *myxzfopen(const char *fn, const char *mode)
248 {
249   LZFILE *lzf = lzopen(fn, mode, -1, 1);
250   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
251 }
252
253 static inline FILE *myxzfdopen(int fd, const char *mode)
254 {
255   LZFILE *lzf = lzopen(0, mode, fd, 1);
256   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
257 }
258
259 static inline FILE *mylzfopen(const char *fn, const char *mode)
260 {
261   LZFILE *lzf = lzopen(fn, mode, -1, 0);
262   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
263 }
264
265 static inline FILE *mylzfdopen(int fd, const char *mode)
266 {
267   LZFILE *lzf = lzopen(0, mode, fd, 0);
268   return cookieopen(lzf, mode, lzread, lzwrite, lzclose);
269 }
270
271 #endif /* ENABLE_LZMA_COMPRESSION */
272
273
274 FILE *
275 solv_xfopen(const char *fn, const char *mode)
276 {
277   char *suf;
278
279   if (!fn)
280     return 0;
281   if (!mode)
282     mode = "r";
283   suf = strrchr(fn, '.');
284   if (suf && !strcmp(suf, ".gz"))
285     return mygzfopen(fn, mode);
286 #ifdef ENABLE_LZMA_COMPRESSION
287   if (suf && !strcmp(suf, ".xz"))
288     return myxzfopen(fn, mode);
289   if (suf && !strcmp(suf, ".lzma"))
290     return mylzfopen(fn, mode);
291 #endif
292   return fopen(fn, mode);
293 }
294
295 FILE *
296 solv_xfopen_fd(const char *fn, int fd, const char *mode)
297 {
298   const char *simplemode = mode;
299   char *suf;
300
301   suf = fn ? strrchr(fn, '.') : 0;
302   if (!mode)
303     {
304       int fl = fcntl(fd, F_GETFL, 0);
305       if (fl == -1)
306         return 0;
307       fl &= O_RDONLY|O_WRONLY|O_RDWR;
308       if (fl == O_WRONLY)
309         mode = simplemode = "w";
310       else if (fl == O_RDWR)
311         {
312           mode = "r+";
313           simplemode = "r";
314         }
315       else
316         mode = simplemode = "r";
317     }
318   if (suf && !strcmp(suf, ".gz"))
319     return mygzfdopen(fd, simplemode);
320 #ifdef ENABLE_LZMA_COMPRESSION
321   if (suf && !strcmp(suf, ".xz"))
322     return myxzfdopen(fd, simplemode);
323   if (suf && !strcmp(suf, ".lzma"))
324     return mylzfdopen(fd, simplemode);
325 #endif
326   return fdopen(fd, mode);
327 }
328