1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <mspack.h>
9 #include <sys/stat.h>
10 
11 #include <error.h>
12 
13 #if HAVE_MKDIR
14 # if MKDIR_TAKES_ONE_ARG
15 #  define mkdir(a, b) mkdir(a)
16 # endif
17 #else
18 # if HAVE__MKDIR
19 #  define mkdir(a, b) _mkdir(a)
20 # else
21 #  error "Don't know how to create a directory on this system."
22 # endif
23 #endif
24 
25 mode_t user_umask;
26 
27 /**
28  * Ensures that all directory components in a filepath exist. New directory
29  * components are created, if necessary.
30  *
31  * @param path the filepath to check
32  * @return non-zero if all directory components in a filepath exist, zero
33  *         if components do not exist and cannot be created
34  */
ensure_filepath(char * path)35 static int ensure_filepath(char *path) {
36   struct stat st_buf;
37   char *p;
38   int ok;
39 
40   for (p = &path[1]; *p; p++) {
41     if (*p != '/') continue;
42     *p = '\0';
43     ok = (stat(path, &st_buf) == 0) && S_ISDIR(st_buf.st_mode);
44     if (!ok) ok = (mkdir(path, 0777 & ~user_umask) == 0);
45     *p = '/';
46     if (!ok) return 0;
47   }
48   return 1;
49 }
50 
create_output_name(char * fname)51 char *create_output_name(char *fname) {
52     char *out, *p;
53     if ((out = malloc(strlen(fname) + 1))) {
54         /* remove leading slashes */
55         while (*fname == '/' || *fname == '\\') fname++;
56         /* if that removes all characters, just call it "x" */
57         strcpy(out, (*fname) ? fname : "x");
58 
59         /* change "../" to "xx/" */
60         for (p = out; *p; p++) {
61             if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\\')) {
62                p[0] = p[1] = 'x';
63             }
64         }
65     }
66     return out;
67 }
68 
sortfunc(const void * a,const void * b)69 static int sortfunc(const void *a, const void *b) {
70   off_t diff =
71     ((* ((struct mschmd_file **) a))->offset) -
72     ((* ((struct mschmd_file **) b))->offset);
73   return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
74 }
75 
main(int argc,char * argv[])76 int main(int argc, char *argv[]) {
77   struct mschm_decompressor *chmd;
78   struct mschmd_header *chm;
79   struct mschmd_file *file, **f;
80   unsigned int numf, i;
81 
82   setbuf(stdout, NULL);
83   setbuf(stderr, NULL);
84   user_umask = umask(0); umask(user_umask);
85 
86   MSPACK_SYS_SELFTEST(i);
87   if (i) return 0;
88 
89   if ((chmd = mspack_create_chm_decompressor(NULL))) {
90     for (argv++; *argv; argv++) {
91       printf("%s\n", *argv);
92       if ((chm = chmd->open(chmd, *argv))) {
93 
94         /* build an ordered list of files for maximum extraction speed */
95         for (numf=0, file=chm->files; file; file = file->next) numf++;
96         if ((f = (struct mschmd_file **) calloc(numf, sizeof(struct mschmd_file *)))) {
97           for (i=0, file=chm->files; file; file = file->next) f[i++] = file;
98           qsort(f, numf, sizeof(struct mschmd_file *), &sortfunc);
99 
100           for (i = 0; i < numf; i++) {
101             char *outname = create_output_name(f[i]->filename);
102             printf("Extracting %s\n", outname);
103             ensure_filepath(outname);
104             if (chmd->extract(chmd, f[i], outname)) {
105               printf("%s: extract error on \"%s\": %s\n",
106                      *argv, f[i]->filename, ERROR(chmd));
107             }
108             free(outname);
109           }
110           free(f);
111         }
112         chmd->close(chmd, chm);
113       }
114       else {
115         printf("%s: can't open -- %s\n", *argv, ERROR(chmd));
116       }
117     }
118     mspack_destroy_chm_decompressor(chmd);
119   }
120   return 0;
121 }
122