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