OSDN Git Service

* src/lhext.c (extract_one, cmd_extract): preserve directory
[lha/lha.git] / src / lhext.c
1 /* ------------------------------------------------------------------------ */
2 /* LHa for UNIX                                                             */
3 /*              lhext.c -- LHarc extract                                    */
4 /*                                                                          */
5 /*      Copyright (C) MCMLXXXIX Yooichi.Tagawa                              */
6 /*      Modified                Nobutaka Watazaki                           */
7 /*                                                                          */
8 /*  Ver. 0.00  Original                             1988.05.23  Y.Tagawa    */
9 /*  Ver. 1.00  Fixed                                1989.09.22  Y.Tagawa    */
10 /*  Ver. 0.03  LHa for UNIX                         1991.12.17  M.Oki       */
11 /*  Ver. 1.12  LHa for UNIX                         1993.10.01  N.Watazaki  */
12 /*  Ver. 1.13b Symbolic Link Update Bug Fix         1994.06.21  N.Watazaki  */
13 /*  Ver. 1.14  Source All chagned                   1995.01.14  N.Watazaki  */
14 /*  Ver. 1.14e bugfix                               1999.04.30  T.Okamoto   */
15 /* ------------------------------------------------------------------------ */
16 #include "lha.h"
17 /* ------------------------------------------------------------------------ */
18 static int      skip_flg = FALSE;   /* FALSE..No Skip , TRUE..Skip */
19 static char    *methods[] =
20 {
21     LZHUFF0_METHOD, LZHUFF1_METHOD, LZHUFF2_METHOD, LZHUFF3_METHOD,
22     LZHUFF4_METHOD, LZHUFF5_METHOD, LZHUFF6_METHOD, LZHUFF7_METHOD,
23     LARC_METHOD, LARC5_METHOD, LARC4_METHOD,
24     LZHDIRS_METHOD,
25     NULL
26 };
27
28 static void add_dirinfo(char* name, LzHeader* hdr);
29 static void adjust_dirinfo();
30
31 /* ------------------------------------------------------------------------ */
32 static          boolean
33 inquire_extract(name)
34     char           *name;
35 {
36     struct stat     stbuf;
37
38     skip_flg = FALSE;
39     if (stat(name, &stbuf) >= 0) {
40         if (!is_regularfile(&stbuf)) {
41             error("\"%s\" already exists (not a file)", name);
42             return FALSE;
43         }
44
45         if (noexec) {
46             printf("EXTRACT %s but file is exist.\n", name);
47             return FALSE;
48         }
49         else if (!force) {
50             if (!isatty(0)) {
51                 warning("skip to extract %s.", name);
52                 return FALSE;
53             }
54
55             switch (inquire("OverWrite ?(Yes/[No]/All/Skip)", name, "YyNnAaSs\n")) {
56             case 0:
57             case 1:/* Y/y */
58                 break;
59             case 2:
60             case 3:/* N/n */
61             case 8:/* Return */
62                 return FALSE;
63             case 4:
64             case 5:/* A/a */
65                 force = TRUE;
66                 break;
67             case 6:
68             case 7:/* S/s */
69                 skip_flg = TRUE;
70                 break;
71             }
72         }
73     }
74
75     if (noexec)
76         printf("EXTRACT %s\n", name);
77
78     return TRUE;
79 }
80
81 /* ------------------------------------------------------------------------ */
82 static          boolean
83 make_parent_path(name)
84     char           *name;
85 {
86     char            path[FILENAME_LENGTH];
87     struct stat     stbuf;
88     register char  *p;
89
90     /* make parent directory name into PATH for recursive call */
91     strcpy(path, name);
92     for (p = path + strlen(path); p > path; p--)
93         if (p[-1] == '/') {
94             *--p = '\0';
95             break;
96         }
97
98     if (p == path) {
99         message("invalid path name \"%s\"", name);
100         return FALSE;   /* no more parent. */
101     }
102
103     if (GETSTAT(path, &stbuf) >= 0) {
104         if (is_directory(&stbuf))
105             return TRUE;
106     }
107
108     if (verbose)
109         message("Making directory \"%s\".", path);
110
111 #if defined __MINGW32__
112     if (mkdir(path) >= 0)
113         return TRUE;
114 #else
115     if (mkdir(path, 0777) >= 0) /* try */
116         return TRUE;    /* successful done. */
117 #endif
118
119     if (!make_parent_path(path))
120         return FALSE;
121
122 #if defined __MINGW32__
123     if (mkdir(path) < 0) {      /* try again */
124         error("Cannot make directory \"%s\"", path);
125         return FALSE;
126     }
127 #else
128     if (mkdir(path, 0777) < 0) {    /* try again */
129         error("Cannot make directory \"%s\"", path);
130         return FALSE;
131     }
132 #endif
133
134     return TRUE;
135 }
136
137 /* ------------------------------------------------------------------------ */
138 static FILE    *
139 open_with_make_path(name)
140     char           *name;
141 {
142     FILE           *fp;
143
144     if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
145         if (!make_parent_path(name) ||
146             (fp = fopen(name, WRITE_BINARY)) == NULL)
147             error("Cannot extract a file \"%s\"", name);
148     }
149     return fp;
150 }
151
152 /* ------------------------------------------------------------------------ */
153 static void
154 adjust_info(name, hdr)
155     char           *name;
156     LzHeader       *hdr;
157 {
158     struct utimbuf utimebuf;
159
160     /* adjust file stamp */
161     utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
162
163     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
164         utime(name, &utimebuf);
165
166     if (hdr->extend_type == EXTEND_UNIX
167         || hdr->extend_type == EXTEND_OS68K
168         || hdr->extend_type == EXTEND_XOSK) {
169 #ifdef NOT_COMPATIBLE_MODE
170         Please need your modification in this space.
171 #else
172         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
173             chmod(name, hdr->unix_mode);
174 #endif
175         if (!getuid()){
176             uid_t uid = hdr->unix_uid;
177             gid_t gid = hdr->unix_gid;
178
179 #if HAVE_GETPWNAM && HAVE_GETGRNAM
180             if (hdr->user[0]) {
181                 struct passwd *ent = getpwnam(hdr->user);
182                 if (ent) uid = ent->pw_uid;
183             }
184             if (hdr->group[0]) {
185                 struct group *ent = getgrnam(hdr->group);
186                 if (ent) gid = ent->gr_gid;
187             }
188 #endif
189
190 #if HAVE_LCHOWN
191             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
192                 lchown(name, uid, gid);
193             else
194 #endif /* HAVE_LCHWON */
195                 chown(name, uid, gid);
196         }
197     }
198 #if __CYGWIN__
199     else {
200         /* On Cygwin, execute permission should be set for .exe or .dll. */
201         mode_t m;
202
203         umask(m = umask(0));    /* get current umask */
204         chmod(name, 0777 & ~m);
205     }
206 #endif
207 }
208
209 /* ------------------------------------------------------------------------ */
210 static size_t
211 extract_one(afp, hdr)
212     FILE           *afp;    /* archive file */
213     LzHeader       *hdr;
214 {
215     FILE           *fp; /* output file */
216     struct stat     stbuf;
217     char            name[FILENAME_LENGTH];
218     unsigned int crc;
219     int             method;
220     boolean         save_quiet, save_verbose, up_flag;
221     char           *q = hdr->name, c;
222     size_t read_size = 0;
223
224     if (ignore_directory && strrchr(hdr->name, '/')) {
225         q = (char *) strrchr(hdr->name, '/') + 1;
226     }
227     else {
228         if (*q == '/') {
229             q++;
230             /*
231              * if OSK then strip device name
232              */
233             if (hdr->extend_type == EXTEND_OS68K
234                 || hdr->extend_type == EXTEND_XOSK) {
235                 do
236                     c = (*q++);
237                 while (c && c != '/');
238                 if (!c || !*q)
239                     q = ".";    /* if device name only */
240             }
241         }
242     }
243
244     if (extract_directory)
245         xsnprintf(name, sizeof(name), "%s/%s", extract_directory, q);
246     else
247         strcpy(name, q);
248
249
250     /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
251     /* 1999.4.30 t.okamoto */
252     for (method = 0;; method++) {
253         if (methods[method] == NULL) {
254             error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
255                   5, hdr->method, name);
256             return read_size;
257         }
258         if (memcmp(hdr->method, methods[method], 5) == 0)
259             break;
260     }
261
262     if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
263         && method != LZHDIRS_METHOD_NUM) {
264     extract_regular:
265 #if 0
266         for (method = 0;; method++) {
267             if (methods[method] == NULL) {
268                 error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
269                       5, hdr->method, name);
270                 return read_size;
271             }
272             if (memcmp(hdr->method, methods[method], 5) == 0)
273                 break;
274         }
275 #endif
276
277         reading_filename = archive_name;
278         writing_filename = name;
279         if (output_to_stdout || verify_mode) {
280             if (noexec) {
281                 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
282                 return read_size;
283             }
284
285             save_quiet = quiet;
286             save_verbose = verbose;
287             if (!quiet && output_to_stdout) {
288                 printf("::::::::\n%s\n::::::::\n", name);
289                 quiet = TRUE;
290                 verbose = FALSE;
291             }
292             else if (verify_mode) {
293                 quiet = FALSE;
294                 verbose = TRUE;
295             }
296
297 #if __MINGW32__
298             {
299                 int old_mode;
300                 fflush(stdout);
301                 old_mode = setmode(fileno(stdout), O_BINARY);
302 #endif
303
304             crc = decode_lzhuf(afp, stdout,
305                                hdr->original_size, hdr->packed_size,
306                                name, method, &read_size);
307 #if __MINGW32__
308                 fflush(stdout);
309                 setmode(fileno(stdout), old_mode);
310             }
311 #endif
312             quiet = save_quiet;
313             verbose = save_verbose;
314         }
315         else {
316             if (skip_flg == FALSE)  {
317                 up_flag = inquire_extract(name);
318                 if (up_flag == FALSE && force == FALSE) {
319                     return read_size;
320                 }
321             }
322
323             if (skip_flg == TRUE) { /* if skip_flg */
324                 if (stat(name, &stbuf) == 0 && force != TRUE) {
325                     if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
326                         if (quiet != TRUE)
327                             printf("%s : Skipped...\n", name);
328                         return read_size;
329                     }
330                 }
331             }
332             if (noexec) {
333                 return read_size;
334             }
335
336             signal(SIGINT, interrupt);
337 #ifdef SIGHUP
338             signal(SIGHUP, interrupt);
339 #endif
340
341             unlink(name);
342             remove_extracting_file_when_interrupt = TRUE;
343
344             if ((fp = open_with_make_path(name)) != NULL) {
345                 crc = decode_lzhuf(afp, fp,
346                                    hdr->original_size, hdr->packed_size,
347                                    name, method, &read_size);
348                 fclose(fp);
349             }
350             remove_extracting_file_when_interrupt = FALSE;
351             signal(SIGINT, SIG_DFL);
352 #ifdef SIGHUP
353             signal(SIGHUP, SIG_DFL);
354 #endif
355             if (!fp)
356                 return read_size;
357         }
358
359         if (hdr->has_crc && crc != hdr->crc)
360             error("CRC error: \"%s\"", name);
361     }
362     else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
363              || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
364              || method == LZHDIRS_METHOD_NUM) {
365         /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */
366         if (!ignore_directory && !verify_mode) {
367             if (noexec) {
368                 if (quiet != TRUE)
369                     printf("EXTRACT %s (directory)\n", name);
370                 return read_size;
371             }
372             /* NAME has trailing SLASH '/', (^_^) */
373             if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
374                 int             l_code;
375
376 #ifdef S_IFLNK
377                 if (skip_flg == FALSE)  {
378                     up_flag = inquire_extract(name);
379                     if (up_flag == FALSE && force == FALSE) {
380                         return read_size;
381                     }
382                 } else {
383                     if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
384                         if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
385                             if (quiet != TRUE)
386                                 printf("%s : Skipped...\n", name);
387                             return read_size;
388                         }
389                     }
390                 }
391
392                 unlink(name);
393                 make_parent_path(name);
394                 l_code = symlink(hdr->realname, name);
395                 if (l_code < 0) {
396                     if (quiet != TRUE)
397                         warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
398                                 hdr->realname, name);
399                 }
400                 if (quiet != TRUE) {
401                     message("Symbolic Link %s -> %s",
402                             hdr->realname, name);
403                 }
404 #else
405                 warning("Can't make Symbolic Link %s -> %s",
406                         hdr->realname, name);
407                 return read_size;
408 #endif
409             } else { /* make directory */
410                 if (!output_to_stdout && !make_parent_path(name))
411                     return read_size;
412                 /* save directory information */
413                 add_dirinfo(name, hdr);
414             }
415         }
416     }
417     else {
418         if (force)              /* force extract */
419             goto extract_regular;
420         else
421             error("Unknown file type: \"%s\". use `f' option to force extract.", name);
422     }
423
424     if (!output_to_stdout)
425         adjust_info(name, hdr);
426
427     return read_size;
428 }
429
430 /* ------------------------------------------------------------------------ */
431 /* EXTRACT COMMAND MAIN                                                     */
432 /* ------------------------------------------------------------------------ */
433 void
434 cmd_extract()
435 {
436     LzHeader        hdr;
437     off_t           pos;
438     FILE           *afp;
439     size_t read_size;
440
441     /* open archive file */
442     if ((afp = open_old_archive()) == NULL)
443         fatal_error("Cannot open archive file \"%s\"", archive_name);
444
445     if (archive_is_msdos_sfx1(archive_name))
446         seek_lha_header(afp);
447
448     /* extract each files */
449     while (get_header(afp, &hdr)) {
450         if (need_file(hdr.name)) {
451             pos = ftello(afp);
452             read_size = extract_one(afp, &hdr);
453             if (read_size != hdr.packed_size) {
454                 /* when error occurred in extract_one(), should adjust
455                    point of file stream */
456                 if (pos != -1 && afp != stdin)
457                     fseeko(afp, pos + hdr.packed_size - read_size, SEEK_SET);
458                 else {
459                     size_t i = hdr.packed_size - read_size;
460                     while (i--) fgetc(afp);
461                 }
462             }
463         } else {
464             if (afp != stdin)
465                 fseeko(afp, hdr.packed_size, SEEK_CUR);
466             else {
467                 size_t i = hdr.packed_size;
468                 while (i--) fgetc(afp);
469             }
470         }
471     }
472
473     /* close archive file */
474     fclose(afp);
475
476     /* adjust directory information */
477     adjust_dirinfo();
478
479     return;
480 }
481
482 /*
483  * restore directory information (time stamp).
484  * added by A.Iriyama  2003.12.12
485  */
486
487 typedef struct lhdDirectoryInfo_t {
488     struct lhdDirectoryInfo_t *next;
489     LzHeader hdr;
490 } LzHeaderList;
491
492 static LzHeaderList *dirinfo;
493
494 static void add_dirinfo(char *name, LzHeader *hdr)
495 {
496     LzHeaderList *p;
497
498     if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
499         return;
500
501     p = xmalloc(sizeof(LzHeaderList));
502
503     memcpy(&p->hdr, hdr, sizeof(LzHeader));
504     strncpy(p->hdr.name, name, sizeof(p->hdr.name));
505     p->hdr.name[sizeof(p->hdr.name)-1] = 0;
506
507     {
508         LzHeaderList *tmp = dirinfo;
509         dirinfo = p;
510         dirinfo->next = tmp;
511     }
512 }
513
514 static void adjust_dirinfo()
515 {
516     while (dirinfo) {
517         adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
518
519         {
520             LzHeaderList *tmp = dirinfo;
521             dirinfo = dirinfo->next;
522             free(tmp);
523         }
524     }
525 }