OSDN Git Service

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