OSDN Git Service

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