1 /**
2  * xzlib.c: front end for the transparent suport of lzma compression
3  *          at the I/O layer, based on an example file from lzma project
4  *
5  * See Copyright for the status of this software.
6  *
7  * Anders F Bjorklund <afb@users.sourceforge.net>
8  */
9 #define IN_LIBXML
10 #include "libxml.h"
11 #ifdef HAVE_LZMA_H
12 
13 #include <string.h>
14 #ifdef HAVE_ERRNO_H
15 #include <errno.h>
16 #endif
17 
18 
19 #ifdef HAVE_SYS_TYPES_H
20 #include <sys/types.h>
21 #endif
22 #ifdef HAVE_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #ifdef HAVE_FCNTL_H
26 #include <fcntl.h>
27 #endif
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_ZLIB_H
35 #include <zlib.h>
36 #endif
37 #include <lzma.h>
38 
39 #include "xzlib.h"
40 #include <libxml/xmlmemory.h>
41 
42 /* values for xz_state how */
43 #define LOOK 0                  /* look for a gzip/lzma header */
44 #define COPY 1                  /* copy input directly */
45 #define GZIP 2                  /* decompress a gzip stream */
46 #define LZMA 3                  /* decompress a lzma stream */
47 
48 /* internal lzma file state data structure */
49 typedef struct {
50     int mode;                   /* see lzma modes above */
51     int fd;                     /* file descriptor */
52     char *path;                 /* path or fd for error messages */
53     uint64_t pos;               /* current position in uncompressed data */
54     unsigned int size;          /* buffer size, zero if not allocated yet */
55     unsigned int want;          /* requested buffer size, default is BUFSIZ */
56     unsigned char *in;          /* input buffer */
57     unsigned char *out;         /* output buffer (double-sized when reading) */
58     unsigned char *next;        /* next output data to deliver or write */
59     unsigned int have;          /* amount of output data unused at next */
60     int eof;                    /* true if end of input file reached */
61     uint64_t start;             /* where the lzma data started, for rewinding */
62     uint64_t raw;               /* where the raw data started, for seeking */
63     int how;                    /* 0: get header, 1: copy, 2: decompress */
64     int direct;                 /* true if last read direct, false if lzma */
65     /* seek request */
66     uint64_t skip;              /* amount to skip (already rewound if backwards) */
67     int seek;                   /* true if seek request pending */
68     /* error information */
69     int err;                    /* error code */
70     char *msg;                  /* error message */
71     /* lzma stream */
72     int init;                   /* is the iniflate stream initialized */
73     lzma_stream strm;           /* stream structure in-place (not a pointer) */
74     char padding1[32];          /* padding allowing to cope with possible
75                                    extensions of above structure without
76 				   too much side effect */
77 #ifdef HAVE_ZLIB_H
78     /* zlib inflate or deflate stream */
79     z_stream zstrm;             /* stream structure in-place (not a pointer) */
80 #endif
81     char padding2[32];          /* padding allowing to cope with possible
82                                    extensions of above structure without
83 				   too much side effect */
84 } xz_state, *xz_statep;
85 
86 static void
xz_error(xz_statep state,int err,const char * msg)87 xz_error(xz_statep state, int err, const char *msg)
88 {
89     /* free previously allocated message and clear */
90     if (state->msg != NULL) {
91         if (state->err != LZMA_MEM_ERROR)
92             xmlFree(state->msg);
93         state->msg = NULL;
94     }
95 
96     /* set error code, and if no message, then done */
97     state->err = err;
98     if (msg == NULL)
99         return;
100 
101     /* for an out of memory error, save as static string */
102     if (err == LZMA_MEM_ERROR) {
103         state->msg = (char *) msg;
104         return;
105     }
106 
107     /* construct error message with path */
108     if ((state->msg =
109          xmlMalloc(strlen(state->path) + strlen(msg) + 3)) == NULL) {
110         state->err = LZMA_MEM_ERROR;
111         state->msg = (char *) "out of memory";
112         return;
113     }
114     strcpy(state->msg, state->path);
115     strcat(state->msg, ": ");
116     strcat(state->msg, msg);
117     return;
118 }
119 
120 static void
xz_reset(xz_statep state)121 xz_reset(xz_statep state)
122 {
123     state->have = 0;            /* no output data available */
124     state->eof = 0;             /* not at end of file */
125     state->how = LOOK;          /* look for gzip header */
126     state->direct = 1;          /* default for empty file */
127     state->seek = 0;            /* no seek request pending */
128     xz_error(state, LZMA_OK, NULL);     /* clear error */
129     state->pos = 0;             /* no uncompressed data yet */
130     state->strm.avail_in = 0;   /* no input data yet */
131 #ifdef HAVE_ZLIB_H
132     state->zstrm.avail_in = 0;  /* no input data yet */
133 #endif
134 }
135 
136 static xzFile
xz_open(const char * path,int fd,const char * mode ATTRIBUTE_UNUSED)137 xz_open(const char *path, int fd, const char *mode ATTRIBUTE_UNUSED)
138 {
139     xz_statep state;
140 
141     /* allocate xzFile structure to return */
142     state = xmlMalloc(sizeof(xz_state));
143     if (state == NULL)
144         return NULL;
145     state->size = 0;            /* no buffers allocated yet */
146     state->want = BUFSIZ;       /* requested buffer size */
147     state->msg = NULL;          /* no error message yet */
148     state->init = 0;            /* initialization of zlib data */
149 
150     /* save the path name for error messages */
151     state->path = xmlMalloc(strlen(path) + 1);
152     if (state->path == NULL) {
153         xmlFree(state);
154         return NULL;
155     }
156     strcpy(state->path, path);
157 
158     /* open the file with the appropriate mode (or just use fd) */
159     state->fd = fd != -1 ? fd : open(path,
160 #ifdef O_LARGEFILE
161                                      O_LARGEFILE |
162 #endif
163 #ifdef O_BINARY
164                                      O_BINARY |
165 #endif
166                                      O_RDONLY, 0666);
167     if (state->fd == -1) {
168         xmlFree(state->path);
169         xmlFree(state);
170         return NULL;
171     }
172 
173     /* save the current position for rewinding (only if reading) */
174     state->start = lseek(state->fd, 0, SEEK_CUR);
175     if (state->start == (uint64_t) - 1)
176         state->start = 0;
177 
178     /* initialize stream */
179     xz_reset(state);
180 
181     /* return stream */
182     return (xzFile) state;
183 }
184 
185 static int
xz_compressed(xzFile f)186 xz_compressed(xzFile f) {
187     xz_statep state;
188 
189     if (f == NULL)
190         return(-1);
191     state = (xz_statep) f;
192     if (state->init <= 0)
193         return(-1);
194 
195     switch (state->how) {
196         case COPY:
197 	    return(0);
198 	case GZIP:
199 	case LZMA:
200 	    return(1);
201     }
202     return(-1);
203 }
204 
205 xzFile
__libxml2_xzopen(const char * path,const char * mode)206 __libxml2_xzopen(const char *path, const char *mode)
207 {
208     return xz_open(path, -1, mode);
209 }
210 
211 int
__libxml2_xzcompressed(xzFile f)212 __libxml2_xzcompressed(xzFile f) {
213     return xz_compressed(f);
214 }
215 
216 xzFile
__libxml2_xzdopen(int fd,const char * mode)217 __libxml2_xzdopen(int fd, const char *mode)
218 {
219     char *path;                 /* identifier for error messages */
220     xzFile xz;
221 
222     if (fd == -1 || (path = xmlMalloc(7 + 3 * sizeof(int))) == NULL)
223         return NULL;
224     sprintf(path, "<fd:%d>", fd);       /* for debugging */
225     xz = xz_open(path, fd, mode);
226     xmlFree(path);
227     return xz;
228 }
229 
230 static int
xz_load(xz_statep state,unsigned char * buf,unsigned int len,unsigned int * have)231 xz_load(xz_statep state, unsigned char *buf, unsigned int len,
232         unsigned int *have)
233 {
234     int ret;
235 
236     *have = 0;
237     do {
238         ret = read(state->fd, buf + *have, len - *have);
239         if (ret <= 0)
240             break;
241         *have += ret;
242     } while (*have < len);
243     if (ret < 0) {
244         xz_error(state, -1, strerror(errno));
245         return -1;
246     }
247     if (ret == 0)
248         state->eof = 1;
249     return 0;
250 }
251 
252 static int
xz_avail(xz_statep state)253 xz_avail(xz_statep state)
254 {
255     lzma_stream *strm = &(state->strm);
256 
257     if (state->err != LZMA_OK)
258         return -1;
259     if (state->eof == 0) {
260         /* avail_in is size_t, which is not necessary sizeof(unsigned) */
261         unsigned tmp = strm->avail_in;
262 
263         if (xz_load(state, state->in, state->size, &tmp) == -1) {
264             strm->avail_in = tmp;
265             return -1;
266         }
267         strm->avail_in = tmp;
268         strm->next_in = state->in;
269     }
270     return 0;
271 }
272 
273 #ifdef HAVE_ZLIB_H
274 static int
xz_avail_zstrm(xz_statep state)275 xz_avail_zstrm(xz_statep state)
276 {
277     int ret;
278     state->strm.avail_in = state->zstrm.avail_in;
279     state->strm.next_in = state->zstrm.next_in;
280     ret = xz_avail(state);
281     state->zstrm.avail_in = (uInt) state->strm.avail_in;
282     state->zstrm.next_in = (Bytef *) state->strm.next_in;
283     return ret;
284 }
285 #endif
286 
287 static int
is_format_xz(xz_statep state)288 is_format_xz(xz_statep state)
289 {
290     lzma_stream *strm = &(state->strm);
291 
292     return strm->avail_in >= 6 && memcmp(state->in, "\3757zXZ", 6) == 0;
293 }
294 
295 static int
is_format_lzma(xz_statep state)296 is_format_lzma(xz_statep state)
297 {
298     lzma_stream *strm = &(state->strm);
299 
300     lzma_filter filter;
301     lzma_options_lzma *opt;
302     uint32_t dict_size;
303     uint64_t uncompressed_size;
304     size_t i;
305 
306     if (strm->avail_in < 13)
307         return 0;
308 
309     filter.id = LZMA_FILTER_LZMA1;
310     if (lzma_properties_decode(&filter, NULL, state->in, 5) != LZMA_OK)
311         return 0;
312 
313     opt = filter.options;
314     dict_size = opt->dict_size;
315     free(opt); /* we can't use xmlFree on a string returned by zlib */
316 
317     /* A hack to ditch tons of false positives: We allow only dictionary
318      * sizes that are 2^n or 2^n + 2^(n-1) or UINT32_MAX. LZMA_Alone
319      * created only files with 2^n, but accepts any dictionary size.
320      * If someone complains, this will be reconsidered.
321      */
322     if (dict_size != UINT32_MAX) {
323         uint32_t d = dict_size - 1;
324 
325         d |= d >> 2;
326         d |= d >> 3;
327         d |= d >> 4;
328         d |= d >> 8;
329         d |= d >> 16;
330         ++d;
331         if (d != dict_size || dict_size == 0)
332             return 0;
333     }
334 
335     /* Another hack to ditch false positives: Assume that if the
336      * uncompressed size is known, it must be less than 256 GiB.
337      * Again, if someone complains, this will be reconsidered.
338      */
339     uncompressed_size = 0;
340     for (i = 0; i < 8; ++i)
341         uncompressed_size |= (uint64_t) (state->in[5 + i]) << (i * 8);
342 
343     if (uncompressed_size != UINT64_MAX
344         && uncompressed_size > (UINT64_C(1) << 38))
345         return 0;
346 
347     return 1;
348 }
349 
350 #ifdef HAVE_ZLIB_H
351 
352 /* Get next byte from input, or -1 if end or error. */
353 #define NEXT() ((strm->avail_in == 0 && xz_avail(state) == -1) ? -1 : \
354                 (strm->avail_in == 0 ? -1 : \
355                  (strm->avail_in--, *(strm->next_in)++)))
356 /* Same thing, but from zstrm */
357 #define NEXTZ() ((strm->avail_in == 0 && xz_avail_zstrm(state) == -1) ? -1 : \
358                 (strm->avail_in == 0 ? -1 : \
359                  (strm->avail_in--, *(strm->next_in)++)))
360 
361 /* Get a four-byte little-endian integer and return 0 on success and the value
362    in *ret.  Otherwise -1 is returned and *ret is not modified. */
363 static int
gz_next4(xz_statep state,unsigned long * ret)364 gz_next4(xz_statep state, unsigned long *ret)
365 {
366     int ch;
367     unsigned long val;
368     z_streamp strm = &(state->zstrm);
369 
370     val = NEXTZ();
371     val += (unsigned) NEXTZ() << 8;
372     val += (unsigned long) NEXTZ() << 16;
373     ch = NEXTZ();
374     if (ch == -1)
375         return -1;
376     val += (unsigned long) ch << 24;
377     *ret = val;
378     return 0;
379 }
380 #endif
381 
382 static int
xz_head(xz_statep state)383 xz_head(xz_statep state)
384 {
385     lzma_stream *strm = &(state->strm);
386     lzma_stream init = LZMA_STREAM_INIT;
387     int flags;
388     unsigned len;
389 
390     /* allocate read buffers and inflate memory */
391     if (state->size == 0) {
392         /* allocate buffers */
393         state->in = xmlMalloc(state->want);
394         state->out = xmlMalloc(state->want << 1);
395         if (state->in == NULL || state->out == NULL) {
396             if (state->out != NULL)
397                 xmlFree(state->out);
398             if (state->in != NULL)
399                 xmlFree(state->in);
400             xz_error(state, LZMA_MEM_ERROR, "out of memory");
401             return -1;
402         }
403         state->size = state->want;
404 
405         /* allocate decoder memory */
406         state->strm = init;
407         state->strm.avail_in = 0;
408         state->strm.next_in = NULL;
409         if (lzma_auto_decoder(&state->strm, UINT64_MAX, 0) != LZMA_OK) {
410             xmlFree(state->out);
411             xmlFree(state->in);
412             state->size = 0;
413             xz_error(state, LZMA_MEM_ERROR, "out of memory");
414             return -1;
415         }
416 #ifdef HAVE_ZLIB_H
417         /* allocate inflate memory */
418         state->zstrm.zalloc = Z_NULL;
419         state->zstrm.zfree = Z_NULL;
420         state->zstrm.opaque = Z_NULL;
421         state->zstrm.avail_in = 0;
422         state->zstrm.next_in = Z_NULL;
423         if (state->init == 0) {
424             if (inflateInit2(&(state->zstrm), -15) != Z_OK) {/* raw inflate */
425                 xmlFree(state->out);
426                 xmlFree(state->in);
427                 state->size = 0;
428                 xz_error(state, LZMA_MEM_ERROR, "out of memory");
429                 return -1;
430             }
431             state->init = 1;
432         }
433 #endif
434     }
435 
436     /* get some data in the input buffer */
437     if (strm->avail_in == 0) {
438         if (xz_avail(state) == -1)
439             return -1;
440         if (strm->avail_in == 0)
441             return 0;
442     }
443 
444     /* look for the xz magic header bytes */
445     if (is_format_xz(state) || is_format_lzma(state)) {
446         state->how = LZMA;
447         state->direct = 0;
448         return 0;
449     }
450 #ifdef HAVE_ZLIB_H
451     /* look for the gzip magic header bytes 31 and 139 */
452     if (strm->next_in[0] == 31) {
453         strm->avail_in--;
454         strm->next_in++;
455         if (strm->avail_in == 0 && xz_avail(state) == -1)
456             return -1;
457         if (strm->avail_in && strm->next_in[0] == 139) {
458             /* we have a gzip header, woo hoo! */
459             strm->avail_in--;
460             strm->next_in++;
461 
462             /* skip rest of header */
463             if (NEXT() != 8) {  /* compression method */
464                 xz_error(state, LZMA_DATA_ERROR,
465                          "unknown compression method");
466                 return -1;
467             }
468             flags = NEXT();
469             if (flags & 0xe0) { /* reserved flag bits */
470                 xz_error(state, LZMA_DATA_ERROR,
471                          "unknown header flags set");
472                 return -1;
473             }
474             NEXT();             /* modification time */
475             NEXT();
476             NEXT();
477             NEXT();
478             NEXT();             /* extra flags */
479             NEXT();             /* operating system */
480             if (flags & 4) {    /* extra field */
481                 len = (unsigned) NEXT();
482                 len += (unsigned) NEXT() << 8;
483                 while (len--)
484                     if (NEXT() < 0)
485                         break;
486             }
487             if (flags & 8)      /* file name */
488                 while (NEXT() > 0) ;
489             if (flags & 16)     /* comment */
490                 while (NEXT() > 0) ;
491             if (flags & 2) {    /* header crc */
492                 NEXT();
493                 NEXT();
494             }
495             /* an unexpected end of file is not checked for here -- it will be
496              * noticed on the first request for uncompressed data */
497 
498             /* set up for decompression */
499             inflateReset(&state->zstrm);
500             state->zstrm.adler = crc32(0L, Z_NULL, 0);
501             state->how = GZIP;
502             state->direct = 0;
503             return 0;
504         } else {
505             /* not a gzip file -- save first byte (31) and fall to raw i/o */
506             state->out[0] = 31;
507             state->have = 1;
508         }
509     }
510 #endif
511 
512     /* doing raw i/o, save start of raw data for seeking, copy any leftover
513      * input to output -- this assumes that the output buffer is larger than
514      * the input buffer, which also assures space for gzungetc() */
515     state->raw = state->pos;
516     state->next = state->out;
517     if (strm->avail_in) {
518         memcpy(state->next + state->have, strm->next_in, strm->avail_in);
519         state->have += strm->avail_in;
520         strm->avail_in = 0;
521     }
522     state->how = COPY;
523     state->direct = 1;
524     return 0;
525 }
526 
527 static int
xz_decomp(xz_statep state)528 xz_decomp(xz_statep state)
529 {
530     int ret;
531     unsigned had;
532     unsigned long crc, len;
533     lzma_stream *strm = &(state->strm);
534 
535     lzma_action action = LZMA_RUN;
536 
537     /* fill output buffer up to end of deflate stream */
538     had = strm->avail_out;
539     do {
540         /* get more input for inflate() */
541         if (strm->avail_in == 0 && xz_avail(state) == -1)
542             return -1;
543         if (strm->avail_in == 0) {
544             xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
545             return -1;
546         }
547         if (state->eof)
548             action = LZMA_FINISH;
549 
550         /* decompress and handle errors */
551 #ifdef HAVE_ZLIB_H
552         if (state->how == GZIP) {
553             state->zstrm.avail_in = (uInt) state->strm.avail_in;
554             state->zstrm.next_in = (Bytef *) state->strm.next_in;
555             state->zstrm.avail_out = (uInt) state->strm.avail_out;
556             state->zstrm.next_out = (Bytef *) state->strm.next_out;
557             ret = inflate(&state->zstrm, Z_NO_FLUSH);
558             if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
559                 xz_error(state, Z_STREAM_ERROR,
560                          "internal error: inflate stream corrupt");
561                 return -1;
562             }
563             if (ret == Z_MEM_ERROR)
564                 ret = LZMA_MEM_ERROR;
565             if (ret == Z_DATA_ERROR)
566                 ret = LZMA_DATA_ERROR;
567             if (ret == Z_STREAM_END)
568                 ret = LZMA_STREAM_END;
569             state->strm.avail_in = state->zstrm.avail_in;
570             state->strm.next_in = state->zstrm.next_in;
571             state->strm.avail_out = state->zstrm.avail_out;
572             state->strm.next_out = state->zstrm.next_out;
573         } else                  /* state->how == LZMA */
574 #endif
575             ret = lzma_code(strm, action);
576         if (ret == LZMA_MEM_ERROR) {
577             xz_error(state, LZMA_MEM_ERROR, "out of memory");
578             return -1;
579         }
580         if (ret == LZMA_DATA_ERROR) {
581             xz_error(state, LZMA_DATA_ERROR, "compressed data error");
582             return -1;
583         }
584     } while (strm->avail_out && ret != LZMA_STREAM_END);
585 
586     /* update available output and crc check value */
587     state->have = had - strm->avail_out;
588     state->next = strm->next_out - state->have;
589 #ifdef HAVE_ZLIB_H
590     state->zstrm.adler =
591         crc32(state->zstrm.adler, state->next, state->have);
592 #endif
593 
594     if (ret == LZMA_STREAM_END) {
595 #ifdef HAVE_ZLIB_H
596         if (state->how == GZIP) {
597             if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
598                 xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
599                 return -1;
600             }
601             if (crc != state->zstrm.adler) {
602                 xz_error(state, LZMA_DATA_ERROR, "incorrect data check");
603                 return -1;
604             }
605             if (len != (state->zstrm.total_out & 0xffffffffL)) {
606                 xz_error(state, LZMA_DATA_ERROR, "incorrect length check");
607                 return -1;
608             }
609             state->strm.avail_in = 0;
610             state->strm.next_in = NULL;
611             state->strm.avail_out = 0;
612             state->strm.next_out = NULL;
613         } else
614 #endif
615         if (strm->avail_in != 0 || !state->eof) {
616             xz_error(state, LZMA_DATA_ERROR, "trailing garbage");
617             return -1;
618         }
619         state->how = LOOK;      /* ready for next stream, once have is 0 (leave
620                                  * state->direct unchanged to remember how) */
621     }
622 
623     /* good decompression */
624     return 0;
625 }
626 
627 static int
xz_make(xz_statep state)628 xz_make(xz_statep state)
629 {
630     lzma_stream *strm = &(state->strm);
631 
632     if (state->how == LOOK) {   /* look for lzma / gzip header */
633         if (xz_head(state) == -1)
634             return -1;
635         if (state->have)        /* got some data from xz_head() */
636             return 0;
637     }
638     if (state->how == COPY) {   /* straight copy */
639         if (xz_load(state, state->out, state->size << 1, &(state->have)) ==
640             -1)
641             return -1;
642         state->next = state->out;
643     } else if (state->how == LZMA || state->how == GZIP) {      /* decompress */
644         strm->avail_out = state->size << 1;
645         strm->next_out = state->out;
646         if (xz_decomp(state) == -1)
647             return -1;
648     }
649     return 0;
650 }
651 
652 static int
xz_skip(xz_statep state,uint64_t len)653 xz_skip(xz_statep state, uint64_t len)
654 {
655     unsigned n;
656 
657     /* skip over len bytes or reach end-of-file, whichever comes first */
658     while (len)
659         /* skip over whatever is in output buffer */
660         if (state->have) {
661             n = (uint64_t) state->have > len ?
662                 (unsigned) len : state->have;
663             state->have -= n;
664             state->next += n;
665             state->pos += n;
666             len -= n;
667         }
668 
669     /* output buffer empty -- return if we're at the end of the input */
670         else if (state->eof && state->strm.avail_in == 0)
671             break;
672 
673     /* need more data to skip -- load up output buffer */
674         else {
675             /* get more output, looking for header if required */
676             if (xz_make(state) == -1)
677                 return -1;
678         }
679     return 0;
680 }
681 
682 int
__libxml2_xzread(xzFile file,void * buf,unsigned len)683 __libxml2_xzread(xzFile file, void *buf, unsigned len)
684 {
685     unsigned got, n;
686     xz_statep state;
687     lzma_stream *strm;
688 
689     /* get internal structure */
690     if (file == NULL)
691         return -1;
692     state = (xz_statep) file;
693     strm = &(state->strm);
694 
695     /* check that we're reading and that there's no error */
696     if (state->err != LZMA_OK)
697         return -1;
698 
699     /* since an int is returned, make sure len fits in one, otherwise return
700      * with an error (this avoids the flaw in the interface) */
701     if ((int) len < 0) {
702         xz_error(state, LZMA_BUF_ERROR,
703                  "requested length does not fit in int");
704         return -1;
705     }
706 
707     /* if len is zero, avoid unnecessary operations */
708     if (len == 0)
709         return 0;
710 
711     /* process a skip request */
712     if (state->seek) {
713         state->seek = 0;
714         if (xz_skip(state, state->skip) == -1)
715             return -1;
716     }
717 
718     /* get len bytes to buf, or less than len if at the end */
719     got = 0;
720     do {
721         /* first just try copying data from the output buffer */
722         if (state->have) {
723             n = state->have > len ? len : state->have;
724             memcpy(buf, state->next, n);
725             state->next += n;
726             state->have -= n;
727         }
728 
729         /* output buffer empty -- return if we're at the end of the input */
730         else if (state->eof && strm->avail_in == 0)
731             break;
732 
733         /* need output data -- for small len or new stream load up our output
734          * buffer */
735         else if (state->how == LOOK || len < (state->size << 1)) {
736             /* get more output, looking for header if required */
737             if (xz_make(state) == -1)
738                 return -1;
739             continue;           /* no progress yet -- go back to memcpy() above */
740             /* the copy above assures that we will leave with space in the
741              * output buffer, allowing at least one gzungetc() to succeed */
742         }
743 
744         /* large len -- read directly into user buffer */
745         else if (state->how == COPY) {  /* read directly */
746             if (xz_load(state, buf, len, &n) == -1)
747                 return -1;
748         }
749 
750         /* large len -- decompress directly into user buffer */
751         else {                  /* state->how == LZMA */
752             strm->avail_out = len;
753             strm->next_out = buf;
754             if (xz_decomp(state) == -1)
755                 return -1;
756             n = state->have;
757             state->have = 0;
758         }
759 
760         /* update progress */
761         len -= n;
762         buf = (char *) buf + n;
763         got += n;
764         state->pos += n;
765     } while (len);
766 
767     /* return number of bytes read into user buffer (will fit in int) */
768     return (int) got;
769 }
770 
771 int
__libxml2_xzclose(xzFile file)772 __libxml2_xzclose(xzFile file)
773 {
774     int ret;
775     xz_statep state;
776 
777     /* get internal structure */
778     if (file == NULL)
779         return LZMA_DATA_ERROR;
780     state = (xz_statep) file;
781 
782     /* free memory and close file */
783     if (state->size) {
784         lzma_end(&(state->strm));
785 #ifdef HAVE_ZLIB_H
786         if (state->init == 1)
787             inflateEnd(&(state->zstrm));
788         state->init = 0;
789 #endif
790         xmlFree(state->out);
791         xmlFree(state->in);
792     }
793     xmlFree(state->path);
794     ret = close(state->fd);
795     xmlFree(state);
796     return ret ? ret : LZMA_OK;
797 }
798 #endif /* HAVE_LZMA_H */
799