OSDN Git Service

* src/lhext.c (cmd_extract): ftell(stdin) will return -1, so
[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                 error("Not a directory \"%s\"", path);
101                 return FALSE;
102         }
103
104         if (verbose)
105                 message("Making directory \"%s\".", path);
106
107 #if defined __MINGW32__
108     if (mkdir(path) >= 0)
109         return TRUE;
110 #else
111         if (mkdir(path, 0777) >= 0)     /* try */
112                 return TRUE;    /* successful done. */
113 #endif
114
115         if (!make_parent_path(path))
116                 return FALSE;
117
118 #if defined __MINGW32__
119     if (mkdir(path) < 0)
120                 error("Cannot make directory", path);
121         return FALSE;
122 #else
123         if (mkdir(path, 0777) < 0) {    /* try again */
124                 error("Cannot make directory", path);
125                 return FALSE;
126         }
127 #endif
128
129         return TRUE;
130 }
131
132 /* ------------------------------------------------------------------------ */
133 static FILE    *
134 open_with_make_path(name)
135         char           *name;
136 {
137         FILE           *fp;
138
139         if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
140                 if (!make_parent_path(name) ||
141                     (fp = fopen(name, WRITE_BINARY)) == NULL)
142                         error("Cannot extract a file \"%s\"", name);
143         }
144         return fp;
145 }
146
147 /* ------------------------------------------------------------------------ */
148 static void
149 adjust_info(name, hdr)
150         char           *name;
151         LzHeader       *hdr;
152 {
153         time_t          utimebuf[2];
154
155         /* adjust file stamp */
156         utimebuf[0] = utimebuf[1] = hdr->unix_last_modified_stamp;
157
158         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
159                 utime(name, utimebuf);
160
161         if (hdr->extend_type == EXTEND_UNIX
162             || hdr->extend_type == EXTEND_OS68K
163             || hdr->extend_type == EXTEND_XOSK) {
164 #ifdef NOT_COMPATIBLE_MODE
165                 Please need your modification in this space.
166 #else
167                 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
168                         chmod(name, hdr->unix_mode);
169 #endif
170                 if (!getuid()){
171             uid_t uid = hdr->unix_uid;
172             gid_t gid = hdr->unix_gid;
173
174 #if HAVE_GETPWNAM && HAVE_GETGRNAM
175             if (hdr->user[0]) {
176                 struct passwd *ent = getpwnam(hdr->user);
177                 if (ent) uid = ent->pw_uid;
178             }
179             if (hdr->group[0]) {
180                 struct group *ent = getgrnam(hdr->group);
181                 if (ent) gid = ent->gr_gid;
182             }
183 #endif
184
185 #if HAVE_LCHOWN
186                         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
187                                 lchown(name, uid, gid);
188                         else
189 #endif /* HAVE_LCHWON */
190                                 chown(name, uid, gid);
191                 }
192         }
193 }
194
195 /* ------------------------------------------------------------------------ */
196 static void
197 extract_one(afp, hdr)
198         FILE           *afp;    /* archive file */
199         LzHeader       *hdr;
200 {
201         FILE           *fp;     /* output file */
202         struct stat     stbuf;
203         char            name[257];
204         int             crc;
205         int             method;
206         boolean         save_quiet, save_verbose, up_flag;
207         char           *q = hdr->name, c;
208
209         if (ignore_directory && strrchr(hdr->name, '/')) {
210                 q = (char *) strrchr(hdr->name, '/') + 1;
211         }
212         else {
213                 if (*q == '/') {
214                         q++;
215                         /*
216                          * if OSK then strip device name
217                          */
218                         if (hdr->extend_type == EXTEND_OS68K
219                             || hdr->extend_type == EXTEND_XOSK) {
220                                 do
221                                         c = (*q++);
222                                 while (c && c != '/');
223                                 if (!c || !*q)
224                                         q = ".";        /* if device name only */
225                         }
226                 }
227         }
228
229         if (extract_directory)
230                 xsnprintf(name, sizeof(name), "%s/%s", extract_directory, q);
231         else
232                 strcpy(name, q);
233
234
235         /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
236         /* 1999.4.30 t.okamoto */
237         for (method = 0;; method++) {
238                 if (methods[method] == NULL) {
239                         error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
240                   5, hdr->method, name);
241                         return;
242                 }
243                 if (bcmp(hdr->method, methods[method], 5) == 0)
244                         break;
245         }
246
247         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
248                 && method != LZHDIRS_METHOD_NUM) {
249 #if 0
250                 for (method = 0;; method++) {
251                         if (methods[method] == NULL) {
252                 error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
253                       5, hdr->method, name);
254                         }
255                         if (bcmp(hdr->method, methods[method], 5) == 0)
256                                 break;
257                 }
258 #endif
259
260                 reading_filename = archive_name;
261                 writting_filename = name;
262                 if (output_to_stdout || verify_mode) {
263                         if (noexec) {
264                                 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
265                                 if (afp == stdin) {
266                                         int             i = hdr->packed_size;
267                                         while (i--)
268                                                 fgetc(afp);
269                                 }
270                                 return;
271                         }
272
273                         save_quiet = quiet;
274                         save_verbose = verbose;
275                         if (!quiet && output_to_stdout) {
276                                 printf("::::::::\n%s\n::::::::\n", name);
277                                 quiet = TRUE;
278                                 verbose = FALSE;
279                         }
280                         else if (verify_mode) {
281                                 quiet = FALSE;
282                                 verbose = TRUE;
283                         }
284
285 #if __MINGW32__
286             {
287                 int old_mode;
288                 fflush(stdout);
289                 old_mode = setmode(fileno(stdout), O_BINARY);
290 #endif
291
292                         crc = decode_lzhuf
293                                 (afp, stdout, hdr->original_size, hdr->packed_size, name, method);
294 #if __MINGW32__
295                 fflush(stdout);
296                 setmode(fileno(stdout), old_mode);
297             }
298 #endif
299                         quiet = save_quiet;
300                         verbose = save_verbose;
301                 }
302                 else {
303                         if (skip_flg == FALSE)  {
304                                 up_flag = inquire_extract(name);
305                                 if (up_flag == FALSE && force == FALSE) {
306                                         return;
307                                 }
308                         }
309
310                         if (skip_flg == TRUE) { /* if skip_flg */
311                                 if (stat(name, &stbuf) == 0 && force != TRUE) {
312                                         if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
313                                                 if (quiet != TRUE)
314                                                         printf("%s : Skipped...\n", name);
315                                                 return;
316                                         }
317                                 }
318                         }
319                         if (noexec) {
320                                 if (afp == stdin) {
321                                         int i = hdr->packed_size;
322                                         while (i--)
323                                                 fgetc(afp);
324                                 }
325                                 return;
326                         }
327
328                         signal(SIGINT, interrupt);
329 #ifdef SIGHUP
330                         signal(SIGHUP, interrupt);
331 #endif
332
333                         unlink(name);
334                         remove_extracting_file_when_interrupt = TRUE;
335
336                         if ((fp = open_with_make_path(name)) != NULL) {
337                                 crc = decode_lzhuf
338                                         (afp, fp, hdr->original_size, hdr->packed_size, name, method);
339                                 fclose(fp);
340                         }
341                         remove_extracting_file_when_interrupt = FALSE;
342                         signal(SIGINT, SIG_DFL);
343 #ifdef SIGHUP
344                         signal(SIGHUP, SIG_DFL);
345 #endif
346                         if (!fp)
347                                 return;
348                 }
349
350                 if (hdr->has_crc && crc != hdr->crc)
351                         error("CRC error: \"%s\"", name);
352         }
353         else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
354                          || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
355                          || method == LZHDIRS_METHOD_NUM) {
356                 /* ¢¬¤³¤ì¤Ç¡¢Symblic Link ¤Ï¡¢Âç¾æÉפ«¡© */
357                 if (!ignore_directory && !verify_mode) {
358                         if (noexec) {
359                                 if (quiet != TRUE)
360                                         printf("EXTRACT %s (directory)\n", name);
361                                 return;
362                         }
363                         /* NAME has trailing SLASH '/', (^_^) */
364                         if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
365                                 char            buf[256], *bb1, *bb2;
366                                 int             l_code;
367                                 strcpy(buf, name);
368                                 bb1 = strtok(buf, "|");
369                                 bb2 = strtok(NULL, "|");
370
371 #ifdef S_IFLNK
372                                 if (skip_flg == FALSE)  {
373                                         up_flag = inquire_extract(name);
374                                         if (up_flag == FALSE && force == FALSE) {
375                                                 return;
376                                         }
377                                 } else {
378                                         if (GETSTAT(bb1, &stbuf) == 0 && force != TRUE) {
379                                                 if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
380                                                         if (quiet != TRUE)
381                                                                 printf("%s : Skipped...\n", bb1);
382                                                         return;
383                                                 }
384                                         }
385                                 }
386
387                                 unlink(bb1);
388                                 l_code = symlink(bb2, bb1);
389                                 if (l_code < 0) {
390                                         if (quiet != TRUE)
391                                                 warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
392                                 bb1, bb2);
393                                 }
394                                 if (quiet != TRUE) {
395                                         message("Symbolic Link %s -> %s", bb1, bb2);
396                                 }
397                                 strcpy(name, bb1);      /* Symbolic's name set */
398 #else
399                                 warning("Can't make Symbolic Link %s -> %s", bb1, bb2);
400                                 return;
401 #endif
402                         } else { /* make directory */
403                                 if (!output_to_stdout && !make_parent_path(name))
404                                         return;
405                         }
406                 }
407         }
408         else {
409                 error("Unknown information: \"%s\"", name);
410         }
411
412         if (!output_to_stdout)
413                 adjust_info(name, hdr);
414 }
415
416 /* ------------------------------------------------------------------------ */
417 /* EXTRACT COMMAND MAIN                                                                                                         */
418 /* ------------------------------------------------------------------------ */
419 void
420 cmd_extract()
421 {
422         LzHeader        hdr;
423         long            pos;
424         FILE           *afp;
425
426         /* open archive file */
427         if ((afp = open_old_archive()) == NULL)
428                 fatal_error("Cannot open archive file \"%s\"", archive_name);
429
430         if (archive_is_msdos_sfx1(archive_name))
431                 skip_msdos_sfx1_code(afp);
432
433         /* extract each files */
434         while (get_header(afp, &hdr)) {
435                 if (need_file(hdr.name)) {
436                         pos = ftell(afp);
437                         extract_one(afp, &hdr);
438             /* when error occurred in extract_one(), should adjust
439                point of file stream */
440                         if (afp != stdin)
441                 fseek(afp, pos + hdr.packed_size, SEEK_SET);
442             else {
443                 /* FIXME: assume that the extract_one() has read
444                    packed_size bytes from stdin. */
445                 long i = 0;
446                                 while (i--) fgetc(afp);
447             }
448                 } else {
449                         if (afp != stdin)
450                                 fseek(afp, hdr.packed_size, SEEK_CUR);
451                         else {
452                                 int             i = hdr.packed_size;
453                                 while (i--)
454                                         fgetc(afp);
455                         }
456                 }
457         }
458
459         /* close archive file */
460         fclose(afp);
461
462         return;
463 }
464
465 /* Local Variables: */
466 /* mode:c */
467 /* tab-width:4 */
468 /* End: */
469 /* vi: set tabstop=4: */