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