1 /* An implementation of the mspack_system interface which can access many
2 * things:
3 * - regular disk files
4 * - already opened stdio FILE* file pointers
5 * - open file descriptors
6 * - blocks of memory
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include <mspack.h>
18
19 /* definitions */
20
21 #define MTYPE_DISKFILE (0x01)
22 #define MTYPE_STDIOFH (0x02)
23 #define MTYPE_FILEDESC (0x03)
24 #define MTYPE_MEMORY (0x04)
25
26 struct m_filename {
27 unsigned char type; /* one of MTYPE_DISKFILE, STDIOFH, FILEDESC or MEMORY */
28 const char *filename; /* the user-friendly printable filename (may be NULL) */
29 union {
30 const char *diskfile; /* char *filename for MTYPE_DISKFILE */
31 FILE *stdiofh; /* FILE *existing_fh for MTYPE_STDIOFH */
32 int filedesc; /* int file_descriptor for MTYPE_FILEDESC */
33 struct {
34 unsigned char *data;
35 size_t length;
36 } memory;
37 } x;
38 };
39
40 struct m_file {
41 struct m_filename *file; /* pointer back to the m_filename data */
42 union {
43 FILE *fh; /* only used in DISKFILE, STDIOFH and FILEDESC types */
44 size_t position; /* only used in MEMORY types */
45 } x;
46 };
47
48 /* ------------------------------------------------------------------------ */
49 /* mspack_system implementation */
50
m_alloc(struct mspack_system * self,size_t bytes)51 static void *m_alloc(struct mspack_system *self, size_t bytes) {
52 return malloc(bytes);
53 }
54
m_free(void * buffer)55 static void m_free(void *buffer) {
56 free(buffer);
57 }
58
m_copy(void * src,void * dest,size_t bytes)59 static void m_copy(void *src, void *dest, size_t bytes) {
60 memcpy(dest, src, bytes);
61 }
62
63 /* A message printer that prints to stderr */
m_msg(struct m_file * file,const char * format,...)64 static void m_msg(struct m_file *file, const char *format, ...) {
65 va_list ap;
66 if (file && file->file && file->file->filename) {
67 fprintf(stderr, "%s: ", file->file->filename);
68 }
69 va_start(ap, format);
70 vfprintf(stderr, format, ap);
71 va_end(ap);
72 fputc((int) '\n', stderr);
73 fflush(stderr);
74 }
75
76
m_open_mem(struct mspack_system * self,struct m_filename * fn,int mode)77 static struct m_file *m_open_mem(struct mspack_system *self,
78 struct m_filename *fn, int mode)
79 {
80 struct m_file *fh;
81
82 /* validate arguments of the filename */
83 if (!fn->x.memory.data) return NULL;
84 if (!fn->x.memory.length) return NULL;
85
86 if ((fh = (struct m_file *) m_alloc(self, sizeof(struct m_file)))) {
87 fh->x.position = (mode == MSPACK_SYS_OPEN_APPEND) ?
88 fn->x.memory.length : 0;
89 fh->file = fn;
90 }
91 return fh;
92 }
93
m_open_file(struct mspack_system * self,struct m_filename * fn,int mode)94 static struct m_file *m_open_file(struct mspack_system *self,
95 struct m_filename *fn, int mode)
96 {
97 struct m_file *fh;
98 const char *fmode;
99 int fd;
100
101 switch (mode) {
102 case MSPACK_SYS_OPEN_READ: fmode = "rb"; break;
103 case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break;
104 case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break;
105 case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break;
106 default: return NULL;
107 }
108
109 /* validate the arguments in the provided filename */
110 switch (fn->type) {
111 case MTYPE_DISKFILE: if (!fn->x.diskfile) return NULL; break;
112 case MTYPE_STDIOFH: if (!fn->x.stdiofh) return NULL; break;
113 case MTYPE_FILEDESC: if (fn->x.filedesc < 0) return NULL; break;
114 }
115
116 /* allocate memory for the file handle */
117 if (!(fh = (struct m_file *) m_alloc(self, sizeof(struct m_file)))) return NULL;
118
119 /* open or duplicate the filehandle */
120 switch (fn->type) {
121 case MTYPE_DISKFILE:
122 fh->x.fh = fopen(fn->x.diskfile, fmode);
123 break;
124
125 case MTYPE_STDIOFH:
126 fd = fileno(fn->x.stdiofh);
127 fh->x.fh = (fd >= 0) ? fdopen(fd, fmode) : NULL;
128 break;
129
130 case MTYPE_FILEDESC:
131 fh->x.fh = fdopen(fn->x.filedesc, fmode);
132 break;
133 }
134
135 /* validate the new stdio filehandle */
136 if (fh->x.fh) {
137 fh->file = fn;
138 }
139 else {
140 free(fh);
141 fh = NULL;
142 }
143
144 return fh;
145 }
146
m_open(struct mspack_system * self,struct m_filename * fn,int mode)147 static struct m_file *m_open(struct mspack_system *self,
148 struct m_filename *fn, int mode)
149 {
150 if (!self || !fn) return NULL;
151
152 switch (fn->type) {
153 case MTYPE_DISKFILE:
154 case MTYPE_STDIOFH:
155 case MTYPE_FILEDESC:
156 return m_open_file(self, fn, mode);
157
158 case MTYPE_MEMORY:
159 return m_open_mem(self, fn, mode);
160 }
161 return NULL;
162 }
163
m_close(struct m_file * fh)164 static void m_close(struct m_file *fh) {
165 if (!fh || !fh->file) return;
166 if (fh->file->type != MTYPE_MEMORY) fclose(fh->x.fh);
167 m_free(fh);
168 }
169
170
m_read(struct m_file * fh,void * buffer,int bytes)171 static int m_read(struct m_file *fh, void *buffer, int bytes) {
172 if (!fh || !fh->file || !buffer || bytes < 0) return -1;
173
174 if (fh->file->type == MTYPE_MEMORY) {
175 int count = fh->file->x.memory.length - fh->x.position;
176 if (count > bytes) count = bytes;
177 if (count > 0) {
178 m_copy(&fh->file->x.memory.data[fh->x.position], buffer, (size_t) count);
179 }
180 fh->x.position += count;
181 return count;
182 }
183 else {
184 size_t count = fread(buffer, 1, (size_t) bytes, fh->x.fh);
185 if (!ferror(fh->x.fh)) return (int) count;
186 }
187 return -1;
188 }
189
190
m_write(struct m_file * fh,void * buffer,int bytes)191 static int m_write(struct m_file *fh, void *buffer, int bytes) {
192 if (!fh || !fh->file || !buffer || bytes < 0) return -1;
193
194 if (fh->file->type == MTYPE_MEMORY) {
195 int count = fh->file->x.memory.length - fh->x.position;
196 if (count > bytes) count = bytes;
197 if (count > 0) {
198 m_copy(buffer, &fh->file->x.memory.data[fh->x.position], (size_t) count);
199 }
200 fh->x.position += count;
201 return count;
202 }
203 else {
204 size_t count = fwrite(buffer, 1, (size_t) bytes, fh->x.fh);
205 if (!ferror(fh->x.fh)) return (int) count;
206 }
207 return -1;
208 }
209
210
m_seek(struct m_file * fh,off_t offset,int mode)211 static int m_seek(struct m_file *fh, off_t offset, int mode) {
212 if (!fh || !fh->file) return 1;
213
214 if (fh->file->type == MTYPE_MEMORY) {
215 switch (mode) {
216 case MSPACK_SYS_SEEK_START:
217 break;
218 case MSPACK_SYS_SEEK_CUR:
219 offset += (off_t) fh->x.position;
220 break;
221 case MSPACK_SYS_SEEK_END:
222 offset += (off_t) fh->file->x.memory.length;
223 break;
224 default:
225 return 1;
226 }
227
228 if (offset < 0) return 1;
229 if (offset > (off_t) fh->file->x.memory.length) return 1;
230 fh->x.position = (size_t) offset;
231 return 0;
232 }
233
234 /* file IO based method */
235 switch (mode) {
236 case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break;
237 case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break;
238 case MSPACK_SYS_SEEK_END: mode = SEEK_END; break;
239 default: return 1;
240 }
241 #if HAVE_FSEEKO
242 return fseeko(fh->x.fh, offset, mode);
243 #else
244 return fseek(fh->x.fh, offset, mode);
245 #endif
246 }
247
248
m_tell(struct m_file * fh)249 static off_t m_tell(struct m_file *fh) {
250 if (!fh || !fh->file) return -1;
251 if (fh->file->type == MTYPE_MEMORY) {
252 return (off_t) fh->x.position;
253 }
254 #if HAVE_FSEEKO
255 return (off_t) ftello(fh->x.fh);
256 #else
257 return (off_t) ftell(fh->x.fh);
258 #endif
259 }
260
261
262 static struct mspack_system multi_system = {
263 (struct mspack_file * (*)(struct mspack_system *, const char *, int)) &m_open,
264 (void (*)(struct mspack_file *)) &m_close,
265 (int (*)(struct mspack_file *, void *, int)) &m_read,
266 (int (*)(struct mspack_file *, void *, int)) &m_write,
267 (int (*)(struct mspack_file *, off_t, int)) &m_seek,
268 (off_t (*)(struct mspack_file *)) &m_tell,
269 (void (*)(struct mspack_file *, const char *, ...)) &m_msg,
270 &m_alloc,
271 &m_free,
272 &m_copy,
273 NULL
274 };
275
276 /* ------------------------------------------------------------------------ */
277 /* constructors and destructor */
278
create_filename(const char * filename)279 const char *create_filename(const char *filename) {
280 struct m_filename *fn;
281
282 if (!filename) return NULL; /* filename must not be null */
283
284 if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
285 fn->type = MTYPE_DISKFILE;
286 fn->filename = filename; /* pretty-printable filename */
287 fn->x.diskfile = filename;
288 }
289 return (const char *) fn;
290 }
291
create_filename_from_handle(FILE * fh)292 const char *create_filename_from_handle(FILE *fh) {
293 struct m_filename *fn;
294
295 if (!fh) return NULL; /* file handle must not be null */
296
297 if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
298 fn->type = MTYPE_STDIOFH;
299 fn->filename = NULL; /* pretty-printable filename */
300 fn->x.stdiofh = fh;
301 }
302 return (const char *) fn;
303 }
304
create_filename_from_descriptor(int fd)305 const char *create_filename_from_descriptor(int fd) {
306 struct m_filename *fn;
307
308 if (fd < 0) return NULL; /* file descriptor must be valid */
309
310 if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
311 fn->type = MTYPE_FILEDESC;
312 fn->filename = NULL; /* pretty-printable filename */
313 fn->x.filedesc = fd;
314 }
315 return (const char *) fn;
316 }
317
create_filename_from_memory(void * data,size_t length)318 const char *create_filename_from_memory(void *data, size_t length) {
319 struct m_filename *fn;
320
321 if (!data) return NULL; /* data pointer must not be NULL */
322 if (length == 0) return NULL; /* length must not be zero */
323
324 if ((fn = (struct m_filename *) malloc(sizeof(struct m_filename)))) {
325 fn->type = MTYPE_MEMORY;
326 fn->filename = NULL; /* pretty-printable filename */
327 fn->x.memory.data = (unsigned char *) data;
328 fn->x.memory.length = length;
329 }
330 return (const char *) fn;
331 }
332
set_filename_printable_name(const char * filename,const char * name)333 void set_filename_printable_name(const char *filename, const char *name) {
334 struct m_filename *fn = (struct m_filename *) filename;
335 if (!fn) return;
336 /* very basic validation of structure */
337 if ((fn->type < MTYPE_DISKFILE) || (fn->type > MTYPE_MEMORY)) return;
338 fn->filename = name;
339 }
340
free_filename(const char * filename)341 void free_filename(const char *filename) {
342 free((void *) filename);
343 }
344
345 /* ------------------------------------------------------------------------ */
346 /* example of usage with mscab_decompressor */
347
348 /* a simple cabinet */
349 static unsigned char memory_cab[] = {
350 0x4D,0x53,0x43,0x46,0x00,0x00,0x00,0x00,0xFD,0x00,0x00,0x00,0x00,0x00,0x00,
351 0x00,0x2C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x01,0x01,0x00,0x02,0x00,
352 0x00,0x00,0x22,0x06,0x00,0x00,0x5E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4D,
353 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6C,0x22,0xBA,0x59,0x20,0x00,
354 0x68,0x65,0x6C,0x6C,0x6F,0x2E,0x63,0x00,0x4A,0x00,0x00,0x00,0x4D,0x00,0x00,
355 0x00,0x00,0x00,0x6C,0x22,0xE7,0x59,0x20,0x00,0x77,0x65,0x6C,0x63,0x6F,0x6D,
356 0x65,0x2E,0x63,0x00,0xBD,0x5A,0xA6,0x30,0x97,0x00,0x97,0x00,0x23,0x69,0x6E,
357 0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x74,0x64,0x69,0x6F,0x2E,0x68,0x3E,
358 0x0D,0x0A,0x0D,0x0A,0x76,0x6F,0x69,0x64,0x20,0x6D,0x61,0x69,0x6E,0x28,0x76,
359 0x6F,0x69,0x64,0x29,0x0D,0x0A,0x7B,0x0D,0x0A,0x20,0x20,0x20,0x20,0x70,0x72,
360 0x69,0x6E,0x74,0x66,0x28,0x22,0x48,0x65,0x6C,0x6C,0x6F,0x2C,0x20,0x77,0x6F,
361 0x72,0x6C,0x64,0x21,0x5C,0x6E,0x22,0x29,0x3B,0x0D,0x0A,0x7D,0x0D,0x0A,0x23,
362 0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x73,0x74,0x64,0x69,0x6F,0x2E,
363 0x68,0x3E,0x0D,0x0A,0x0D,0x0A,0x76,0x6F,0x69,0x64,0x20,0x6D,0x61,0x69,0x6E,
364 0x28,0x76,0x6F,0x69,0x64,0x29,0x0D,0x0A,0x7B,0x0D,0x0A,0x20,0x20,0x20,0x20,
365 0x70,0x72,0x69,0x6E,0x74,0x66,0x28,0x22,0x57,0x65,0x6C,0x63,0x6F,0x6D,0x65,
366 0x21,0x5C,0x6E,0x22,0x29,0x3B,0x0D,0x0A,0x7D,0x0D,0x0A,0x0D,0x0A
367 };
368
main()369 int main() {
370 const char *mem_cab, *std_out, *std_err, *example;
371 struct mscab_decompressor *cabd;
372 struct mscabd_cabinet *cab;
373 struct mscabd_file *file;
374 int err;
375
376 mem_cab = create_filename_from_memory(&memory_cab[0], sizeof(memory_cab));
377 if (!mem_cab) exit(1);
378
379 std_out = create_filename_from_descriptor(1);
380 if (!std_out) exit(1);
381
382 std_err = create_filename_from_handle(stderr);
383 if (!std_err) exit(1);
384
385 example = create_filename("example.txt");
386 if (!example) exit(1);
387
388 set_filename_printable_name(mem_cab, "internal");
389 set_filename_printable_name(std_out, "stdout");
390 set_filename_printable_name(std_err, "stderr");
391
392 /* if self-test reveals an error */
393 MSPACK_SYS_SELFTEST(err);
394 if (err) exit(1);
395
396 /* create a cab decompressor using our custom mspack_system interface */
397 if ((cabd = mspack_create_cab_decompressor(&multi_system))) {
398
399 /* open a cab file direct from memory */
400 if ((cab = cabd->open(cabd, mem_cab))) {
401
402 /* first file in the cabinet: print it to stdout */
403 file = cab->files;
404 if (cabd->extract(cabd, file, std_out)) {
405 exit(1);
406 }
407
408 /* second file in the cabinet: print it to stderr */
409 file = file->next;
410 if (cabd->extract(cabd, file, std_err)) {
411 exit(1);
412 }
413 /* also write it to "example.txt" */
414 if (cabd->extract(cabd, file, example)) {
415 exit(1);
416 }
417 cabd->close(cabd, cab);
418 }
419 else {
420 fprintf(stderr, "can't open cabinet (%d)\n", cabd->last_error(cabd));
421 }
422 mspack_destroy_cab_decompressor(cabd);
423 }
424 else {
425 fprintf(stderr, "can't make decompressor\n");
426 }
427
428 free_filename(example);
429 free_filename(std_err);
430 free_filename(std_out);
431 free_filename(mem_cab);
432
433 return 0;
434
435 }
436