]> pd.if.org Git - zpackage/blob - lib/uncompress.c
bbc1adbadfcfb3087fbbb67f0f608b8b7257dd61
[zpackage] / lib / uncompress.c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <assert.h>
10
11 #include <sys/mman.h>
12
13 #include "lzma.h"
14
15 ssize_t uncompresslzma(void *buf, size_t bufsize, int out) {
16         lzma_stream s = LZMA_STREAM_INIT;
17         lzma_stream *strm;
18         ssize_t bytes = 0;
19         
20         uint8_t outbuf[BUFSIZ];
21
22         int ret;
23
24         strm = &s;
25
26         ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
27         /* The only reasonable error here is LZMA_MEM_ERROR. */
28         if (ret != LZMA_OK) {
29                 fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
30                                 : "Internal error (bug)");
31                 return -1;
32         }
33
34         strm->avail_in = bufsize;
35         strm->next_in = buf;
36         strm->avail_out = BUFSIZ;
37         strm->next_out = outbuf;
38
39         lzma_action action = LZMA_RUN;
40
41         while (1) {
42                 ret = lzma_code(strm, action);
43
44                 // Write and check write error before checking decoder error.
45                 // This way as much data as possible gets written to output
46                 // even if decoder detected an error.
47                 if (strm->avail_out == 0 || ret != LZMA_OK) {
48                         size_t avail = BUFSIZ - strm->avail_out;
49                         ssize_t written = 0;
50                         uint8_t *start;
51
52                         start = outbuf;
53
54                         while (avail > 0) {
55                                 written = write(out, outbuf, avail);
56                                 if (written == -1) {
57                                         /* Wouldn't be a surprise if writing to
58                                          * stderr would fail too but at least
59                                          * try to show an error message.
60                                          */
61                                         fprintf(stderr, "Cannot write to output"
62                                                         " file stream: %s",
63                                                         strerror(errno));
64                                         return -1;
65                                 }
66                                 avail -= written;
67                                 start += written;
68                                 bytes += written;
69                         }
70
71                         strm->next_out = outbuf;
72                         strm->avail_out = BUFSIZ;
73                 }
74
75                 if (ret != LZMA_OK) {
76                         if (ret == LZMA_STREAM_END) {
77                                 // lzma_stream_decoder() already guarantees
78                                 // that there's no trailing garbage.
79                                 assert(strm->avail_in == 0);
80                                 //assert(action == LZMA_FINISH);
81                                 lzma_end(strm);
82                                 return bytes;
83                         }
84
85                         lzma_end(strm);
86                         const char *msg;
87                         switch (ret) {
88                                 case LZMA_MEM_ERROR:
89                                         msg = strerror(ENOMEM);
90                                         break;
91
92                                 case LZMA_FORMAT_ERROR:
93                                         msg = "File format not recognized";
94                                         break;
95
96                                 case LZMA_OPTIONS_ERROR:
97                                         // FIXME: Better message?
98                                         msg = "Unsupported compression options";
99                                         break;
100
101                                 case LZMA_DATA_ERROR:
102                                         msg = "File is corrupt";
103                                         break;
104
105                                 case LZMA_BUF_ERROR:
106                                         msg = "Unexpected end of input";
107                                         break;
108
109                                 default:
110                                         msg = "Internal error (bug)";
111                                         break;
112                         }
113
114                         fprintf(stderr, "zpmuncompress: %s\n", msg);
115                         return -1;
116                 }
117         }
118 }