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