]> pd.if.org Git - zpackage/blob - lib/uncompress.c
switch to blake2
[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 zpm_uncompress_cb(void *buf, size_t bufsize, void *cbdata,
16                 int (*cb)(void *ud, void *buf, size_t bufsize)) {
17         lzma_stream s = LZMA_STREAM_INIT;
18         lzma_stream *strm;
19         ssize_t bytes = 0;
20         
21         uint8_t outbuf[BUFSIZ];
22
23         int ret;
24
25         strm = &s;
26
27         ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
28         /* The only reasonable error here is LZMA_MEM_ERROR. */
29         if (ret != LZMA_OK) {
30                 fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
31                                 : "Internal error (bug)");
32                 return -1;
33         }
34
35         strm->avail_in = bufsize;
36         strm->next_in = buf;
37         strm->avail_out = BUFSIZ;
38         strm->next_out = outbuf;
39
40         lzma_action action = LZMA_RUN;
41
42         while (1) {
43                 ret = lzma_code(strm, action);
44
45                 // Write and check write error before checking decoder error.
46                 // This way as much data as possible gets written to output
47                 // even if decoder detected an error.
48                 if (strm->avail_out == 0 || ret != LZMA_OK) {
49                         size_t avail = BUFSIZ - strm->avail_out;
50                         ssize_t written = 0;
51                         uint8_t *start;
52
53                         start = outbuf;
54
55                         while (avail > 0) {
56                                 written = cb(cbdata, outbuf, avail);
57                                 if (written == -1) {
58                                         /* Wouldn't be a surprise if writing to
59                                          * stderr would fail too but at least
60                                          * try to show an error message.
61                                          */
62                                         return -1;
63                                 }
64                                 avail -= written;
65                                 start += written;
66                                 bytes += written;
67                         }
68
69                         strm->next_out = outbuf;
70                         strm->avail_out = BUFSIZ;
71                 }
72
73                 if (ret != LZMA_OK) {
74                         if (ret == LZMA_STREAM_END) {
75                                 // lzma_stream_decoder() already guarantees
76                                 // that there's no trailing garbage.
77                                 assert(strm->avail_in == 0);
78                                 //assert(action == LZMA_FINISH);
79                                 lzma_end(strm);
80                                 return bytes;
81                         }
82
83                         lzma_end(strm);
84                         const char *msg;
85                         switch (ret) {
86                                 case LZMA_MEM_ERROR:
87                                         msg = strerror(ENOMEM);
88                                         break;
89
90                                 case LZMA_FORMAT_ERROR:
91                                         msg = "File format not recognized";
92                                         break;
93
94                                 case LZMA_OPTIONS_ERROR:
95                                         // FIXME: Better message?
96                                         msg = "Unsupported compression options";
97                                         break;
98
99                                 case LZMA_DATA_ERROR:
100                                         msg = "File is corrupt";
101                                         break;
102
103                                 case LZMA_BUF_ERROR:
104                                         msg = "Unexpected end of input";
105                                         break;
106
107                                 default:
108                                         msg = "Internal error (bug)";
109                                         break;
110                         }
111
112                         fprintf(stderr, "zpmuncompress: %s\n", msg);
113                         return -1;
114                 }
115         }
116
117 }
118
119 ssize_t uncompresslzma(void *buf, size_t bufsize, int out) {
120         lzma_stream s = LZMA_STREAM_INIT;
121         lzma_stream *strm;
122         ssize_t bytes = 0;
123         
124         uint8_t outbuf[BUFSIZ];
125
126         int ret;
127
128         strm = &s;
129
130         ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
131         /* The only reasonable error here is LZMA_MEM_ERROR. */
132         if (ret != LZMA_OK) {
133                 fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
134                                 : "Internal error (bug)");
135                 return -1;
136         }
137
138         strm->avail_in = bufsize;
139         strm->next_in = buf;
140         strm->avail_out = BUFSIZ;
141         strm->next_out = outbuf;
142
143         lzma_action action = LZMA_RUN;
144
145         while (1) {
146                 ret = lzma_code(strm, action);
147
148                 // Write and check write error before checking decoder error.
149                 // This way as much data as possible gets written to output
150                 // even if decoder detected an error.
151                 if (strm->avail_out == 0 || ret != LZMA_OK) {
152                         size_t avail = BUFSIZ - strm->avail_out;
153                         ssize_t written = 0;
154                         uint8_t *start;
155
156                         start = outbuf;
157
158                         while (avail > 0) {
159                                 written = write(out, outbuf, avail);
160                                 if (written == -1) {
161                                         /* Wouldn't be a surprise if writing to
162                                          * stderr would fail too but at least
163                                          * try to show an error message.
164                                          */
165                                         fprintf(stderr, "Cannot write to output"
166                                                         " file stream: %s",
167                                                         strerror(errno));
168                                         return -1;
169                                 }
170                                 avail -= written;
171                                 start += written;
172                                 bytes += written;
173                         }
174
175                         strm->next_out = outbuf;
176                         strm->avail_out = BUFSIZ;
177                 }
178
179                 if (ret != LZMA_OK) {
180                         if (ret == LZMA_STREAM_END) {
181                                 // lzma_stream_decoder() already guarantees
182                                 // that there's no trailing garbage.
183                                 assert(strm->avail_in == 0);
184                                 //assert(action == LZMA_FINISH);
185                                 lzma_end(strm);
186                                 return bytes;
187                         }
188
189                         lzma_end(strm);
190                         const char *msg;
191                         switch (ret) {
192                                 case LZMA_MEM_ERROR:
193                                         msg = strerror(ENOMEM);
194                                         break;
195
196                                 case LZMA_FORMAT_ERROR:
197                                         msg = "File format not recognized";
198                                         break;
199
200                                 case LZMA_OPTIONS_ERROR:
201                                         // FIXME: Better message?
202                                         msg = "Unsupported compression options";
203                                         break;
204
205                                 case LZMA_DATA_ERROR:
206                                         msg = "File is corrupt";
207                                         break;
208
209                                 case LZMA_BUF_ERROR:
210                                         msg = "Unexpected end of input";
211                                         break;
212
213                                 default:
214                                         msg = "Internal error (bug)";
215                                         break;
216                         }
217
218                         fprintf(stderr, "zpmuncompress: %s\n", msg);
219                         return -1;
220                 }
221         }
222 }