OSDN Git Service

Fix warning message on extracting symlink.
[lha/lha.git] / src / lharc.c
1 /* ------------------------------------------------------------------------ */
2 /* LHa for UNIX                                                             */
3 /*              lharc.c -- append to archive                                */
4 /*                                                                          */
5 /*      Copyright (C) MCMLXXXIX Yooichi.Tagawa                              */
6 /*      Modified                Nobutaka Watazaki                           */
7 /*                          Thanks to H.Yoshizaki. (MS-DOS LHarc)           */
8 /*                                                                          */
9 /*  Ver. 0.00  Original                         1988.05.23  Y.Tagawa        */
10 /*  Ver. 0.01  Alpha Version (for 4.2BSD)       1989.05.28  Y.Tagawa        */
11 /*  Ver. 0.02  Alpha Version Rel.2              1989.05.29  Y.Tagawa        */
12 /*  Ver. 0.03  Release #3  Beta Version         1989.07.02  Y.Tagawa        */
13 /*  Ver. 0.03a Debug                            1989.07.03  Y.Tagawa        */
14 /*  Ver. 0.03b Modified                         1989.07.13  Y.Tagawa        */
15 /*  Ver. 0.03c Debug (Thanks to void@rena.dit.junet)                        */
16 /*                                              1989.08.09  Y.Tagawa        */
17 /*  Ver. 0.03d Modified (quiet and verbose)     1989.09.14  Y.Tagawa        */
18 /*  V1.00  Fixed                                1989.09.22  Y.Tagawa        */
19 /*  V1.01  Bug Fixed                            1989.12.25  Y.Tagawa        */
20 /*                                                                          */
21 /*  DOS-Version Original LHx V C2.01        (C) H.Yohizaki                  */
22 /*                                                                          */
23 /*  V2.00  UNIX Lharc + DOS LHx -> OSK LHx      1990.11.01  Momozou         */
24 /*  V2.01  Minor Modified                       1990.11.24  Momozou         */
25 /*                                                                          */
26 /*  Ver. 0.02  LHx for UNIX                     1991.11.18  M.Oki           */
27 /*  Ver. 0.03  LHa for UNIX                     1991.12.17  M.Oki           */
28 /*  Ver. 0.04  LHa for UNIX beta version        1992.01.20  M.Oki           */
29 /*  Ver. 1.00  LHa for UNIX Fixed               1992.03.19  M.Oki           */
30 /*                                                                          */
31 /*  Ver. 1.10  for Symbolic Link                1993.06.25  N.Watazaki      */
32 /*  Ver. 1.11  for Symbolic Link Bug Fixed      1993.08.18  N.Watazaki      */
33 /*  Ver. 1.12  for File Date Check              1993.10.28  N.Watazaki      */
34 /*  Ver. 1.13  Bug Fixed (Idicator calcurate)   1994.02.21  N.Watazaki      */
35 /*  Ver. 1.13a Bug Fixed (Sym. Link delete)     1994.03.11  N.Watazaki      */
36 /*  Ver. 1.13b Bug Fixed (Sym. Link delete)     1994.07.29  N.Watazaki      */
37 /*  Ver. 1.14  Source All chagned               1995.01.14  N.Watazaki      */
38 /*  Ver. 1.14b,c  Bug Fixed                     1996.03.07  t.okamoto       */
39 /*  Ver. 1.14d Version up                       1997.01.12  t.okamoto       */
40 /*  Ver. 1.14g Bug Fixed                        2000.05.06  t.okamoto       */
41 /*  Ver. 1.14i Modified                         2000.10.06  t.okamoto       */
42 /* ------------------------------------------------------------------------ */
43 #define LHA_MAIN_SRC
44
45 #include "lha.h"
46
47 static int      cmd = CMD_UNKNOWN;
48 static int error_occurred;
49
50 /* static functions */
51 static void     sort_files();
52 static void     print_version();
53
54 extern int optional_archive_kanji_code;
55 extern int optional_system_kanji_code;
56
57 /* ------------------------------------------------------------------------ */
58 static void
59 init_variable()     /* Added N.Watazaki */
60 {
61 /* options */
62     quiet           = FALSE;
63     text_mode       = FALSE;
64     verbose         = 0;
65     noexec          = FALSE;    /* debugging option */
66     force           = FALSE;
67     timestamp_archive = FALSE;
68
69     compress_method = DEFAULT_LZHUFF_METHOD; /* defined in config.h */
70
71     header_level    = 2;        /* level 2 */
72     quiet_mode      = 0;
73
74 #ifdef EUC
75     euc_mode        = FALSE;
76 #endif
77
78 /* view command flags */
79     verbose_listing = FALSE;
80
81 /* extract command flags */
82     output_to_stdout = FALSE;
83
84 /* append command flags */
85     new_archive         = FALSE;
86     update_if_newer     = FALSE;
87     delete_after_append = FALSE;
88     generic_format      = FALSE;
89
90     recover_archive_when_interrupt          = FALSE;
91     remove_extracting_file_when_interrupt   = FALSE;
92     get_filename_from_stdin                 = FALSE;
93     ignore_directory                        = FALSE;
94     exclude_files                           = NULL;
95     verify_mode                             = FALSE;
96
97     convertcase                             = FALSE;
98
99     extract_directory = NULL;
100     temporary_fd = -1;
101
102 #if BACKUP_OLD_ARCHIVE
103     backup_old_archive = TRUE;
104 #else
105     backup_old_archive = FALSE;
106 #endif
107
108     extract_broken_archive = FALSE;
109     decode_macbinary_contents = FALSE;
110     sort_contents = TRUE;
111     recursive_archiving = TRUE;
112     dump_lzss = FALSE;
113 }
114
115 /* ------------------------------------------------------------------------ */
116 /* NOTES :          Text File Format                                        */
117 /* GENERATOR        NewLine                                                 */
118 /* [generic]        0D 0A                                                   */
119 /* [MS-DOS]         0D 0A                                                   */
120 /* [OS9][MacOS]     0D                                                      */
121 /* [UNIX]           0A                                                      */
122 /* ------------------------------------------------------------------------ */
123 static void
124 print_tiny_usage()
125 {
126     fprintf(stdout, "\
127 usage: lha [-]<commands>[<options>] [-<options> ...] archive_file [file...]\n\
128   commands:  [axelvudmcpt]\n\
129   options:   [q[012]vnfto[567]dizg012%s%s[w=<dir>|x=<pattern>]]\n\
130   long options: --system-kanji-code={euc,sjis,utf8,cap}\n\
131                 --archive-kanji-code={euc,sjis,utf8,cap}\n\
132                 --extract-broken-archive\n\
133                 --convert-filename-case\n\
134                 --ignore-mac-files\n\
135                 --timestamp-archive\n\
136                 --traditional\n\
137                 --help\n\
138                 --version\n"
139 #ifdef EUC
140             ,"e"
141 #else
142             ,""
143 #endif
144 #if HAVE_LIBAPPLEFILE
145             ,"b"                 /* decode_macbinary_contents */
146 #else
147             ,""
148 #endif
149             );
150 }
151
152 static void
153 print_usage()
154 {
155     fprintf(stdout, "\
156 LHarc    for UNIX  V 1.02  Copyright(C) 1989  Y.Tagawa\n\
157 LHx      for MSDOS V C2.01 Copyright(C) 1990  H.Yoshizaki\n\
158 LHx(arc) for OSK   V 2.01  Modified     1990  Momozou\n\
159 LHa      for UNIX  V 1.00  Copyright(C) 1992  Masaru Oki\n\
160 LHa      for UNIX  V 1.14  Modified     1995  Nobutaka Watazaki\n\
161 LHa      for UNIX  V 1.14i Modified     2000  Tsugio Okamoto\n\
162 LHA-PMA  for UNIX  V 2     PMA added    2000  Maarten ter Huurne\n\
163                    Autoconfiscated 2001-2008  Koji Arai\n\
164 ");
165
166     print_tiny_usage();
167
168     fprintf(stdout, "\
169 commands:                           options:\n\
170  a   Add(or replace) to archive      q{num} quiet (num:quiet mode)\n\
171  x,e EXtract from archive            v  verbose\n\
172  l,v List / Verbose List             n  not execute\n\
173  u   Update newer files to archive   f  force (over write at extract)\n\
174  d   Delete from archive             t  FILES are TEXT file\n");
175 #ifdef SUPPORT_LH7
176     fprintf(stdout, "\
177  m   Move to archive (means 'ad')    o[567] compression method (a/u/c)\n\
178 ");
179 #endif
180 #ifndef SUPPORT_LH7
181     fprintf(stdout, "\
182  m   Move to archive (means 'ad')    o  use LHarc compatible method (a/u/c)\n\
183 ");
184 #endif
185     fprintf(stdout, "\
186  c   re-Construct new archive        d  delete FILES after (a/u/c)\n\
187  p   Print to STDOUT from archive    i  ignore directory path (x/e)\n\
188  t   Test file CRC in archive        z  files not compress (a/u/c)\n\
189                                      g  Generic format (for compatibility)\n\
190                                      0/1/2 header level (a/u/c)\n\
191 ");
192 #ifdef EUC
193     fprintf(stdout, "\
194                                      e  TEXT code convert from/to EUC\n\
195 ");
196 #endif
197 #if HAVE_LIBAPPLEFILE
198     fprintf(stdout, "\
199                                      b  decode MacBinary (x/e)\n\
200 ");
201 #endif
202     fprintf(stdout, "\
203                                      w=<dir> specify extract directory (x/e)\n\
204                                      x=<pattern>  eXclude files (a/u/c)\n\
205 ");
206 }
207
208 #include "getopt_long.h"
209
210 /*
211   Parse LHA options
212 */
213 static int
214 parse_suboption(int argc, char **argv)
215 {
216     enum {
217         HELP_OPTION = 256,
218         VERSION_OPTION,
219         SYSTEM_KANJI_CODE_OPTION,
220         ARCHIVE_KANJI_CODE_OPTION,
221         TRADITIONAL_BEHAVIOR,
222         IGNORE_MAC_FILES,
223         DEBUG_OPTION,
224     };
225
226     struct option long_options[] = {
227         /* These options set a flag. */
228         {"help",    no_argument,       0, HELP_OPTION},
229         {"version", no_argument,       0, VERSION_OPTION},
230
231         {"system-kanji-code", required_argument, 0, SYSTEM_KANJI_CODE_OPTION},
232         {"archive-kanji-code", required_argument, 0, ARCHIVE_KANJI_CODE_OPTION},
233         {"extract-broken-archive", no_argument, &extract_broken_archive, 1},
234         {"convert-filename-case", no_argument, &convertcase, TRUE},
235         {"traditional", no_argument, 0, TRADITIONAL_BEHAVIOR},
236         {"ignore-mac-files", no_argument, 0, IGNORE_MAC_FILES},
237         {"timestamp-archive", no_argument, &timestamp_archive, 1},
238         {"debug", required_argument, 0, DEBUG_OPTION},
239         {0, 0, 0, 0}
240     };
241     int i;
242
243     char short_options[256] = "q[012]vnfto[567]dizg012ew:x:";
244     /* "[...]" means optional 1 byte argument (original extention) */
245
246 #if HAVE_LIBAPPLEFILE
247     strncat(short_options, "b", sizeof(short_options)-strlen(short_options)-1);
248 #endif
249
250     /* parse option */
251     while (1) {
252         int option_index = 0;
253         int c = getopt_long(argc, argv,
254                             short_options, long_options,
255                             &option_index);
256
257         if (c == -1) break;     /* end of options */
258
259         switch (c) {
260         case 0:
261             /* Already set a flag variable by the definition of the
262                long_options. */
263             break;
264         case '?':
265             /* Invalid option */
266             print_tiny_usage();
267             exit(2);
268         case HELP_OPTION:
269             print_usage();
270             exit(0);
271         case VERSION_OPTION:
272             print_version();
273             exit(0);
274         case 'q':
275             if (!optarg) {
276                 /* In quiet mode, no confirm to overwrite */
277                 force = TRUE;
278                 quiet = TRUE;
279                 break;
280             }
281
282             switch (*optarg) {
283             case '0':           /* no quiet */
284             case '1':           /* no use the incremental indicator */
285                 quiet_mode = *optarg - '0';
286                 break;
287             case '2':           /* no output */
288                 /* fall through */
289             default:
290                 force = TRUE;
291                 quiet = TRUE;
292                 break;
293             }
294             break;
295         case 'f':
296             force = TRUE;
297             break;
298         case 'v':
299             verbose++;
300             break;
301         case 't':
302             text_mode = TRUE;
303             break;
304 #ifdef EUC
305         case 'e':
306             text_mode = TRUE;
307             euc_mode = TRUE;
308             break;
309 #endif
310 #if HAVE_LIBAPPLEFILE
311         case 'b':
312             decode_macbinary_contents = TRUE;
313             break;
314 #endif
315         case 'n':
316             noexec = TRUE;
317             break;
318         case 'g':
319             generic_format = TRUE;
320             header_level = 0;
321             break;
322         case 'd':
323             delete_after_append = TRUE;
324             break;
325         case 'o':
326             if (!optarg) {
327                 compress_method = LZHUFF1_METHOD_NUM;
328                 header_level = 0;
329                 break;
330             }
331             switch (*optarg) {
332             case '5':
333                 compress_method = LZHUFF5_METHOD_NUM;
334                 break;
335 #ifdef SUPPORT_LH7
336             case '6':
337                 compress_method = LZHUFF6_METHOD_NUM;
338                 break;
339             case '7':
340                 compress_method = LZHUFF7_METHOD_NUM;
341                 break;
342 #endif
343             default:
344                 error("invalid compression method 'o%c'", *optarg);
345                 return -1;
346             }
347             break;
348         case 'z':
349             compress_method = LZHUFF0_METHOD_NUM;   /* Changed N.Watazaki */
350             break;
351         case 'i':
352             ignore_directory = TRUE;
353             break;
354         case 'x':
355             if (!optarg) {
356                 error("exclude files does not specified for `-x'");
357                 exit(2);
358             }
359
360             for (i = 0; exclude_files && exclude_files[i]; i++)
361                 ;
362             exclude_files = (char**)xrealloc(exclude_files,
363                                              sizeof(char*) * (i+2));
364
365             if (*optarg == '=')
366                 optarg++;
367             exclude_files[i] = optarg;
368             exclude_files[i+1] = 0;
369
370             break;
371         case 'w':
372             if (!optarg) {
373                 error("working directory does not specified for `-w'");
374                 exit(2);
375             }
376             if (*optarg == '=')
377                 optarg++;
378
379             extract_directory = optarg;
380             break;
381         case '0':
382             header_level = 0;
383             break;
384         case '1':
385             header_level = 1;
386             break;
387         case '2':
388             header_level = 2;
389             break;
390         case SYSTEM_KANJI_CODE_OPTION:
391             if (!optarg) {
392                 error("kanji code not specified for --%s",
393                       long_options[option_index].name);
394                 return -1;
395             }
396             if (strcmp(optarg, "euc") == 0) {
397                 optional_system_kanji_code = CODE_EUC;
398             }
399             else if (strcmp(optarg, "sjis") == 0) {
400                 optional_system_kanji_code = CODE_SJIS;
401             }
402             else if (strcmp(optarg, "utf8") == 0) {
403                 optional_system_kanji_code = CODE_UTF8;
404             }
405             else if (strcmp(optarg, "cap") == 0) {
406                 optional_system_kanji_code = CODE_CAP;
407             }
408             else {
409                 error("unknown kanji code \"%s\"", optarg);
410                 return -1;
411             }
412             break;
413
414         case ARCHIVE_KANJI_CODE_OPTION:
415             if (!optarg) {
416                 error("kanji code not specified for --%s",
417                       long_options[option_index].name);
418                 return -1;
419             }
420             if (strcmp(optarg, "euc") == 0) {
421                 optional_archive_kanji_code = CODE_EUC;
422             }
423             else if (strcmp(optarg, "sjis") == 0) {
424                 optional_archive_kanji_code = CODE_SJIS;
425             }
426             else if (strcmp(optarg, "utf8") == 0) {
427                 optional_archive_kanji_code = CODE_UTF8;
428             }
429             else if (strcmp(optarg, "cap") == 0) {
430                 optional_archive_kanji_code = CODE_CAP;
431             }
432             else {
433                 error("unknown kanji code \"%s\"", optarg);
434                 return -1;
435             }
436             break;
437
438         case TRADITIONAL_BEHAVIOR:
439             convertcase = TRUE;
440             break;
441
442         case IGNORE_MAC_FILES:
443             /* ignore Mac specific files (._*, .DS_Store and Icon\r)
444                when archiving */
445             for (i = 0; exclude_files && exclude_files[i]; i++)
446                 ;
447             exclude_files = (char**)xrealloc(exclude_files,
448                                              sizeof(char*) * (i+4));
449
450             exclude_files[i] = xstrdup("._*");
451             exclude_files[i+1] = xstrdup(".DS_Store");
452             exclude_files[i+2] = xstrdup("Icon\r");
453             exclude_files[i+3] = 0;
454             break;
455
456         case DEBUG_OPTION:
457             if (!optarg) {
458                 error("debugging item is not specified for --%s",
459                       long_options[option_index].name);
460                 return -1;
461             }
462             if (strcmp(optarg, "nosort") == 0) {
463                 sort_contents = FALSE;
464             }
465             else if (strcmp(optarg, "norecursion") == 0) {
466                 recursive_archiving = FALSE;
467             }
468             else if (strcmp(optarg, "dumplzss") == 0) {
469                 dump_lzss = TRUE;
470                 quiet = TRUE;
471             }
472             else {
473                 error("unknown debugging item \"%s\" for --%s",
474                       optarg, long_options[option_index].name);
475                 return -1;
476             }
477             break;
478
479         default:
480             error("unknown option `-%c'.", c);
481             return -1;
482         }
483     }
484
485     argc -= optind;
486     argv += optind;
487
488     if (!archive_name) {
489         archive_name = *argv++;
490         argc--;
491     }
492
493     cmd_filec = argc;
494     cmd_filev = argv;
495
496     return 0;
497 }
498
499 /*
500   Parse LHA command and options.
501 */
502 static int
503 parse_option(int argc, char **argv)
504 {
505     char *cmd_char;
506
507     if (argv[1] == NULL || strcmp(argv[1], "--help") == 0) {
508         print_usage();
509         exit(0);
510     }
511
512     if (strcmp(argv[1], "--version") == 0) {
513         print_version();
514         exit(0);
515     }
516
517     if (argc == 2 && *argv[1] != '-') {
518         archive_name = argv[1];
519         cmd = CMD_LIST;
520         cmd_filec = 0;
521         cmd_filev = 0;
522         return 0;
523     }
524
525     cmd_char = argv[1];
526
527     if (cmd_char[0] == '-')
528         cmd_char++;
529
530     /* parse commands */
531     switch (*cmd_char) {
532     case 'x':
533     case 'e':
534         cmd = CMD_EXTRACT;
535         break;
536
537     case 'p':
538         output_to_stdout = TRUE;
539         cmd = CMD_EXTRACT;
540         break;
541
542     case 'c':
543         new_archive = TRUE;
544         cmd = CMD_ADD;
545         break;
546
547     case 'a':
548         cmd = CMD_ADD;
549         break;
550
551     case 'd':
552         cmd = CMD_DELETE;
553         break;
554
555     case 'u':
556         update_if_newer = TRUE;
557         cmd = CMD_ADD;
558         break;
559
560     case 'm':
561         delete_after_append = TRUE;
562         cmd = CMD_ADD;
563         break;
564
565     case 'v':
566         verbose_listing = TRUE;
567         cmd = CMD_LIST;
568         break;
569
570     case 'l':
571         cmd = CMD_LIST;
572         break;
573
574     case 't':
575         cmd = CMD_EXTRACT;
576         verify_mode = TRUE;
577         break;
578
579     default:
580         error("unknown command `-%c'", *cmd_char);
581         return -1;
582     }
583
584     if (cmd_char[1] == '\0') {
585         /* argv[1] is command name */
586         argv[1] = argv[0];
587         argv++;
588         argc--;
589     }
590     else {
591         /* Eliminate command character
592            e.g.) lha  cv foo.lzh -> lha -v foo.lzh
593                  lha -cv foo.lzh -> lha -v foo.lzh
594         */
595         cmd_char[0] = '-';
596         argv[1] = cmd_char;
597     }
598
599     return parse_suboption(argc, argv);
600 }
601
602 /* ------------------------------------------------------------------------ */
603 int
604 main(argc, argv)
605     int             argc;
606     char           *argv[];
607 {
608     char           *p;
609
610     int i;
611
612     init_variable();        /* Added N.Watazaki */
613
614     if (parse_option(argc, argv) == -1) {
615         fputs("\n", stderr);
616         print_tiny_usage();
617         exit(2);
618     }
619
620     if (!archive_name) {
621         error("archive file does not specified");
622         fputs("\n", stderr);
623         print_tiny_usage();
624         exit(2);
625     }
626
627     if (!strcmp(archive_name, "-")) {
628         if (!isatty(1) && cmd == CMD_ADD)
629             quiet = TRUE;
630     }
631 #if 0 /* Comment out; IMHO, this feature is useless. by Koji Arai */
632     else {
633         if (argc == 3 && !isatty(0)) { /* 1999.7.18 */
634             /* Bug(?) on MinGW, isatty() return 0 on Cygwin console.
635                mingw-runtime-1.3-2 and Cygwin 1.3.10(0.51/3/2) on Win2000 */
636             get_filename_from_stdin = TRUE;
637         }
638     }
639 #endif
640
641     /* target file name */
642     if (get_filename_from_stdin) {
643         char inpbuf[4096];
644         char **xfilev;
645         int xfilec = 257;
646
647         cmd_filec = 0;
648         xfilev = (char **)xmalloc(sizeof(char *) * xfilec);
649         while (fgets(inpbuf, sizeof(inpbuf), stdin)) {
650             /* delete \n if it exist */
651             i=0; p=inpbuf;
652             while (i < sizeof(inpbuf) && p != 0) {
653                 if (*p == '\n') {
654                     *p = 0;
655                     break;
656                 }
657                 p++; i++;
658             }
659
660             if (cmd_filec >= xfilec) {
661                 xfilec += 256;
662                 xfilev = (char **) xrealloc(xfilev,
663                            sizeof(char *) * xfilec);
664             }
665             if (strlen(inpbuf) < 1)
666                 continue;
667             xfilev[cmd_filec++] = xstrdup(inpbuf);
668         }
669         xfilev[cmd_filec] = NULL;
670         cmd_filev = xfilev;
671     }
672
673     sort_files();
674
675     /* make crc table */
676     make_crctable();
677
678     switch (cmd) {
679     case CMD_EXTRACT:
680         cmd_extract();
681         break;
682     case CMD_ADD:
683         cmd_add();
684         break;
685     case CMD_LIST:
686         cmd_list();
687         break;
688     case CMD_DELETE:
689         cmd_delete();
690         break;
691     }
692
693     if (error_occurred)
694         return 1;
695     return 0;
696 }
697
698
699 /* ------------------------------------------------------------------------ */
700 static void
701 print_version()
702 {
703     /* macro PACKAGE_NAME, PACKAGE_VERSION and PLATFORM are
704        defined in config.h by configure script */
705     fprintf(stderr, "%s version %s (%s)\n",
706             PACKAGE_NAME, PACKAGE_VERSION, PLATFORM);
707     fprintf(stderr, "  configure options: %s\n", LHA_CONFIGURE_OPTIONS);
708 }
709
710 void
711 #if STDC_HEADERS
712 message(char *fmt, ...)
713 #else
714 message(fmt, va_alist)
715     char *fmt;
716     va_dcl
717 #endif
718 {
719     int errno_sv = errno;
720     va_list v;
721
722     fprintf(stderr, "LHa: ");
723
724     va_init(v, fmt);
725     vfprintf(stderr, fmt, v);
726     va_end(v);
727
728     fputs("\n", stderr);
729
730     errno =  errno_sv;
731 }
732
733 /* ------------------------------------------------------------------------ */
734 void
735 #if STDC_HEADERS
736 warning(char *fmt, ...)
737 #else
738 warning(fmt, va_alist)
739     char *fmt;
740     va_dcl
741 #endif
742 {
743     int errno_sv = errno;
744     va_list v;
745
746     fprintf(stderr, "LHa: Warning: ");
747
748     va_init(v, fmt);
749     vfprintf(stderr, fmt, v);
750     va_end(v);
751
752     fputs("\n", stderr);
753
754     errno =  errno_sv;
755 }
756
757 /* ------------------------------------------------------------------------ */
758 void
759 #if STDC_HEADERS
760 error(char *fmt, ...)
761 #else
762 error(fmt, va_alist)
763     char *fmt;
764     va_dcl
765 #endif
766 {
767     int errno_sv = errno;
768     va_list v;
769
770     fprintf(stderr, "LHa: Error: ");
771
772     va_init(v, fmt);
773     vfprintf(stderr, fmt, v);
774     va_end(v);
775
776     fputs("\n", stderr);
777
778     error_occurred = 1;
779
780     errno =  errno_sv;
781 }
782
783 void
784 #if STDC_HEADERS
785 fatal_error(char *fmt, ...)
786 #else
787 fatal_error(fmt, va_alist)
788     char *fmt;
789     va_dcl
790 #endif
791 {
792     int errno_sv = errno;
793     va_list v;
794
795     fprintf(stderr, "LHa: Fatal error: ");
796
797     va_init(v, fmt);
798     vfprintf(stderr, fmt, v);
799     va_end(v);
800
801     if (errno)
802         fprintf(stderr, ": %s\n", strerror(errno_sv));
803     else
804         fputs("\n", stderr);
805
806     exit(1);
807 }
808
809 void
810 cleanup()
811 {
812     if (temporary_fd != -1) {
813         close(temporary_fd);
814         temporary_fd = -1;
815         unlink(temporary_name);
816     }
817
818     if (recover_archive_when_interrupt) {
819         rename(backup_archive_name, archive_name);
820         recover_archive_when_interrupt = FALSE;
821     }
822     if (remove_extracting_file_when_interrupt) {
823         message("Removing: %s", writing_filename);
824         unlink(writing_filename);
825         remove_extracting_file_when_interrupt = FALSE;
826     }
827 }
828
829 RETSIGTYPE
830 interrupt(signo)
831     int signo;
832 {
833     message("Interrupted");
834
835     cleanup();
836
837     signal(SIGINT, SIG_DFL);
838 #ifdef SIGHUP
839     signal(SIGHUP, SIG_DFL);
840 #endif
841     kill(getpid(), signo);
842 }
843
844 /* ------------------------------------------------------------------------ */
845 /*                                                                          */
846 /* ------------------------------------------------------------------------ */
847 static int
848 sort_by_ascii(a, b)
849     char          **a, **b;
850 {
851     register char  *p, *q;
852     register int    c1, c2;
853
854     p = *a, q = *b;
855     if (generic_format) {
856         do {
857             c1 = *(unsigned char *) p++;
858             c2 = *(unsigned char *) q++;
859             if (!c1 || !c2)
860                 break;
861             if (islower(c1))
862                 c1 = toupper(c1);
863             if (islower(c2))
864                 c2 = toupper(c2);
865         }
866         while (c1 == c2);
867         return c1 - c2;
868     }
869     else {
870         while (*p == *q && *p != '\0')
871             p++, q++;
872         return *(unsigned char *) p - *(unsigned char *) q;
873     }
874 }
875
876 /* ------------------------------------------------------------------------ */
877 static void
878 sort_files()
879 {
880     if (cmd_filec > 1 && sort_contents)
881         qsort(cmd_filev, cmd_filec, sizeof(char *), sort_by_ascii);
882 }
883
884 /* ------------------------------------------------------------------------ */
885 void *
886 xmalloc(size)
887     size_t size;
888 {
889     void *p = malloc(size);
890     if (!p)
891         fatal_error("Not enough memory");
892     return p;
893 }
894
895 /* ------------------------------------------------------------------------ */
896 void *
897 xrealloc(old, size)
898     void *old;
899     size_t size;
900 {
901     void *p = (char *) realloc(old, size);
902     if (!p)
903         fatal_error("Not enough memory");
904     return p;
905 }
906
907 char *
908 xstrdup(str)
909     char *str;
910 {
911     int len = strlen(str);
912     char *p = (char *)xmalloc(len + 1);
913     strcpy(p, str);             /* ok */
914     return p;
915 }
916
917 /* ------------------------------------------------------------------------ */
918 /*                              STRING POOL                                 */
919 /* ------------------------------------------------------------------------ */
920 /*
921   string pool :
922     +-------------+-------------+------+-------------+----------+
923     | N A M E 1 \0| N A M E 2 \0| .... | N A M E n \0|          |
924     +-------------+-------------+------+-------------+----------+
925       ^ ^        ^ buffer+0 buffer+used buffer+size
926
927   vector :
928     +---------------+---------------+------------- -----------------+
929     | pointer to    | pointer to    | pointer to   ...  pointer to  |
930     |  stringpool   |  N A M E 1    |  N A M E 2   ...   N A M E n  |
931     +---------------+---------------+-------------     -------------+
932     ^ malloc base      returned
933 */
934
935 /* ------------------------------------------------------------------------ */
936 void
937 init_sp(sp)
938     struct string_pool *sp;
939 {
940     sp->size = 1024 - 8;    /* any ( >=0 ) */
941     sp->used = 0;
942     sp->n = 0;
943     sp->buffer = (char *) xmalloc(sp->size * sizeof(char));
944 }
945
946 /* ------------------------------------------------------------------------ */
947 void
948 add_sp(sp, name, len)
949     struct string_pool *sp;
950     char           *name;   /* stored '\0' at tail */
951     int             len;    /* include '\0' */
952 {
953     while (sp->used + len > sp->size) {
954         sp->size *= 2;
955         sp->buffer = (char *) xrealloc(sp->buffer, sp->size * sizeof(char));
956     }
957     memmove(sp->buffer + sp->used, name, len);
958     sp->used += len;
959     sp->n++;
960 }
961
962 /* ------------------------------------------------------------------------ */
963 void
964 finish_sp(sp, v_count, v_vector)
965     register struct string_pool *sp;
966     int            *v_count;
967     char         ***v_vector;
968 {
969     int             i;
970     register char  *p;
971     char          **v;
972
973     v = (char **) xmalloc((sp->n + 1) * sizeof(char *));
974     *v++ = sp->buffer;
975     *v_vector = v;
976     *v_count = sp->n;
977     p = sp->buffer;
978     for (i = sp->n; i; i--) {
979         *v++ = p;
980         if (i - 1)
981             p += strlen(p) + 1;
982     }
983 }
984
985 /* ------------------------------------------------------------------------ */
986 void
987 free_sp(vector)
988     char          **vector;
989 {
990     vector--;
991     free(*vector);      /* free string pool */
992     free(vector);
993 }
994
995
996 /* ------------------------------------------------------------------------ */
997 /*                          READ DIRECTORY FILES                            */
998 /* ------------------------------------------------------------------------ */
999 static          boolean
1000 include_path_p(path, name)
1001     char           *path, *name;
1002 {
1003     char           *n = name;
1004     while (*path)
1005         if (*path++ != *n++)
1006             return (path[-1] == '/' && *n == '\0');
1007     return (*n == '/' || (n != name && path[-1] == '/' && n[-1] == '/'));
1008 }
1009
1010 /* ------------------------------------------------------------------------ */
1011 void
1012 cleaning_files(v_filec, v_filev)
1013     int            *v_filec;
1014     char         ***v_filev;
1015 {
1016     char           *flags;
1017     struct stat     stbuf;
1018
1019     register char **filev = *v_filev;
1020     register int    filec = *v_filec;
1021     register char  *p;
1022     register int    i, j;
1023
1024     if (filec == 0)
1025         return;
1026
1027     flags = xmalloc(filec * sizeof(char));
1028
1029     /* flags & 0x01 :   1: ignore */
1030     /* flags & 0x02 :   1: directory, 0 : regular file */
1031     /* flags & 0x04 :   1: need delete */
1032
1033     for (i = 0; i < filec; i++)
1034         if (GETSTAT(filev[i], &stbuf) < 0) {
1035             flags[i] = 0x04;
1036             warning("Cannot access \"%s\" : %s; ignored.", filev[i],
1037                     strerror(errno));
1038         }
1039         else {
1040             if (is_regularfile(&stbuf))
1041                 flags[i] = 0x00;
1042             else if (is_directory(&stbuf))
1043                 flags[i] = 0x02;
1044 #ifdef S_IFLNK
1045             else if (is_symlink(&stbuf)) /* t.okamoto */
1046                 flags[i] = 0x00;
1047 #endif
1048             else {
1049                 flags[i] = 0x04;
1050                 warning("Cannot archive \"%s\", ignored.", filev[i]);
1051             }
1052         }
1053
1054     for (i = 0; i < filec; i++) {
1055         p = filev[i];
1056         if ((flags[i] & 0x07) == 0x00) {    /* regular file, not
1057                              * deleted/ignored */
1058             for (j = i + 1; j < filec; j++) {
1059                 if ((flags[j] & 0x07) == 0x00) {    /* regular file, not
1060                                      * deleted/ignored */
1061                     if (STREQU(p, filev[j]))
1062                         flags[j] = 0x04;    /* delete */
1063                 }
1064             }
1065         }
1066         else if ((flags[i] & 0x07) == 0x02) {   /* directory, not
1067                              * deleted/ignored */
1068             for (j = i + 1; j < filec; j++) {
1069                 if ((flags[j] & 0x07) == 0x00) {    /* regular file, not
1070                                      * deleted/ignored */
1071                     if (include_path_p(p, filev[j]))
1072                         flags[j] = 0x04;    /* delete */
1073                 }
1074                 else if ((flags[j] & 0x07) == 0x02) {   /* directory, not
1075                                      * deleted/ignored */
1076                     if (include_path_p(p, filev[j]))
1077                         flags[j] = 0x04;    /* delete */
1078                 }
1079             }
1080         }
1081     }
1082
1083     for (i = j = 0; i < filec; i++) {
1084         if ((flags[i] & 0x04) == 0) {
1085             if (i != j)
1086                 filev[j] = filev[i];
1087             j++;
1088         }
1089     }
1090     *v_filec = j;
1091
1092     free(flags);
1093 }
1094
1095 /* ------------------------------------------------------------------------ */
1096 boolean
1097 find_files(name, v_filec, v_filev)
1098     char           *name;
1099     int            *v_filec;
1100     char         ***v_filev;
1101 {
1102     struct string_pool sp;
1103     char            newname[FILENAME_LENGTH];
1104     int             len, n, i;
1105     DIR            *dirp;
1106     struct dirent  *dp;
1107     struct stat     tmp_stbuf, arc_stbuf, fil_stbuf;
1108     int exist_tmp = 1, exist_arc = 1;
1109
1110     len = str_safe_copy(newname, name, sizeof(newname));
1111     if (len > 0 && newname[len - 1] != '/') {
1112         if (len < sizeof(newname)-1)
1113             strcpy(&newname[len++], "/"); /* ok */
1114         else
1115             warning("the length of pathname \"%s\" is too long.", name);
1116     }
1117
1118     dirp = opendir(name);
1119     if (!dirp)
1120         return FALSE;
1121
1122     init_sp(&sp);
1123
1124     if (GETSTAT(temporary_name, &tmp_stbuf) == -1)
1125         exist_tmp = 0;
1126     if (GETSTAT(archive_name, &arc_stbuf) == -1)
1127         exist_arc = 0;
1128
1129     while ((dp = readdir(dirp)) != NULL) {
1130         n = NAMLEN(dp);
1131
1132         /* exclude '.' and '..' */
1133         if (strncmp(dp->d_name, ".", n) == 0
1134             || strncmp(dp->d_name, "..", n) == 0)
1135             continue;
1136
1137         /* exclude exclude_files supplied by user */
1138         for (i = 0; exclude_files && exclude_files[i]; i++) {
1139             if (fnmatch(exclude_files[i], dp->d_name,
1140                         FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD) == 0)
1141                 goto next;
1142         }
1143
1144         if (len + n >= sizeof(newname)) {
1145             warning("filename is too long");
1146             continue;
1147         }
1148
1149         strncpy(newname + len, dp->d_name, n);
1150         newname[len + n] = '\0';
1151         if (GETSTAT(newname, &fil_stbuf) < 0)
1152             continue;
1153
1154 #if defined(HAVE_STRUCT_STAT_ST_INO) && !__MINGW32__
1155         /* MinGW has meaningless st_ino */
1156
1157         /* exclude temporary file, archive file and these links */
1158         if (exist_tmp &&
1159             tmp_stbuf.st_dev == fil_stbuf.st_dev &&
1160             tmp_stbuf.st_ino == fil_stbuf.st_ino)
1161             continue;
1162
1163         if (exist_arc &&
1164             arc_stbuf.st_dev == fil_stbuf.st_dev &&
1165             arc_stbuf.st_ino == fil_stbuf.st_ino)
1166             continue;
1167 #endif
1168         add_sp(&sp, newname, len+n+1);
1169     next:
1170         ;
1171     }
1172     closedir(dirp);
1173     finish_sp(&sp, v_filec, v_filev);
1174     if (*v_filec > 1 && sort_contents)
1175         qsort(*v_filev, *v_filec, sizeof(char *), sort_by_ascii);
1176     cleaning_files(v_filec, v_filev);
1177
1178     return TRUE;
1179 }
1180
1181 /* ------------------------------------------------------------------------ */
1182 void
1183 free_files(filec, filev)
1184     int             filec;
1185     char          **filev;
1186 {
1187     free_sp(filev);
1188 }
1189
1190 /* ------------------------------------------------------------------------ */
1191 /*                                                                          */
1192 /* ------------------------------------------------------------------------ */
1193 /* Build temporary file name and store to TEMPORARY_NAME */
1194 int
1195 build_temporary_name()
1196 {
1197 #ifdef TMP_FILENAME_TEMPLATE
1198     /* "/tmp/lhXXXXXX" etc. */
1199     if (extract_directory == NULL) {
1200         str_safe_copy(temporary_name, TMP_FILENAME_TEMPLATE,
1201                       sizeof(temporary_name));
1202     }
1203     else {
1204         xsnprintf(temporary_name, sizeof(temporary_name),
1205                   "%s/lhXXXXXX", extract_directory);
1206     }
1207 #else
1208     char *s;
1209
1210     str_safe_copy(temporary_name, archive_name, sizeof(temporary_name));
1211     s = strrchr(temporary_name, '/');
1212     if (s) {
1213         int len;
1214         len = s - temporary_name;
1215         if (len + strlen("lhXXXXXX") < sizeof(temporary_name))
1216             /* use directory at archive file */
1217             strcpy(s, "lhXXXXXX"); /* ok */
1218         else
1219             /* use current directory */
1220             str_safe_copy(temporary_name, "lhXXXXXX", sizeof(temporary_name));
1221     }
1222     else
1223         /* use current directory */
1224         str_safe_copy(temporary_name, "lhXXXXXX", sizeof(temporary_name));
1225 #endif
1226 #ifdef HAVE_MKSTEMP
1227     {
1228         int old_umask, fd;
1229
1230         old_umask = umask(077);
1231         fd = mkstemp(temporary_name);
1232         umask(old_umask);
1233         return fd;
1234     }
1235 #else
1236     {
1237         int flags;
1238
1239         mktemp(temporary_name);
1240         flags = O_CREAT|O_EXCL|O_RDWR;
1241 #ifdef O_BINARY
1242         flags |= O_BINARY;
1243 #endif
1244         return open(temporary_name, flags, 0600);
1245     }
1246 #endif
1247 }
1248
1249 /* ------------------------------------------------------------------------ */
1250 static void
1251 modify_filename_extention(buffer, ext, size)
1252     char           *buffer;
1253     char           *ext;
1254     size_t size;
1255 {
1256     register char  *p, *dot;
1257
1258     for (p = buffer, dot = (char *) 0; *p; p++) {
1259         if (*p == '.')
1260             dot = p;
1261         else if (*p == '/')
1262             dot = (char *) 0;
1263     }
1264
1265     if (dot)
1266         p = dot;
1267
1268     str_safe_copy(p, ext, size - (p - buffer));
1269 }
1270
1271 /* ------------------------------------------------------------------------ */
1272 /* build backup file name */
1273 void
1274 build_backup_name(buffer, original, size)
1275     char           *buffer;
1276     char           *original;
1277     size_t size;
1278 {
1279     str_safe_copy(buffer, original, size);
1280     modify_filename_extention(buffer, BACKUPNAME_EXTENTION, size);    /* ".bak" */
1281 }
1282
1283 /* ------------------------------------------------------------------------ */
1284 void
1285 build_standard_archive_name(buffer, original, size)
1286     char           *buffer;
1287     char           *original;
1288     size_t size;
1289 {
1290     str_safe_copy(buffer, original, size);
1291     modify_filename_extention(buffer, ARCHIVENAME_EXTENTION, size);   /* ".lzh" */
1292 }
1293
1294 /* ------------------------------------------------------------------------ */
1295 /*                                                                          */
1296 /* ------------------------------------------------------------------------ */
1297 boolean
1298 need_file(name)
1299     char           *name;
1300 {
1301     int             i;
1302
1303     if (cmd_filec == 0)
1304         return TRUE;
1305
1306     for (i = 0; i < cmd_filec; i++) {
1307         if (patmatch(cmd_filev[i], name, 0))
1308             return TRUE;
1309     }
1310
1311     return FALSE;
1312 }
1313
1314 FILE           *
1315 xfopen(name, mode)
1316     char           *name, *mode;
1317 {
1318     FILE           *fp;
1319
1320     if ((fp = fopen(name, mode)) == NULL)
1321         fatal_error("Cannot open file \"%s\"", name);
1322
1323     return fp;
1324 }
1325
1326 /* ------------------------------------------------------------------------ */
1327 /*                                                                          */
1328 /* ------------------------------------------------------------------------ */
1329 static          boolean
1330 open_old_archive_1(name, v_fp)
1331     char           *name;
1332     FILE          **v_fp;
1333 {
1334     FILE           *fp;
1335     struct stat     stbuf;
1336
1337     if (stat(name, &stbuf) >= 0 &&
1338         is_regularfile(&stbuf) &&
1339         (fp = fopen(name, READ_BINARY)) != NULL) {
1340         *v_fp = fp;
1341         archive_file_gid = stbuf.st_gid;
1342         archive_file_mode = stbuf.st_mode;
1343         return TRUE;
1344     }
1345
1346     *v_fp = NULL;
1347     archive_file_gid = -1;
1348     return FALSE;
1349 }
1350
1351 /* ------------------------------------------------------------------------ */
1352 FILE           *
1353 open_old_archive()
1354 {
1355     FILE           *fp = NULL;
1356     char           *p = NULL, *ext, *ext2;
1357     static char expanded_archive_name[FILENAME_LENGTH];
1358
1359     if (!strcmp(archive_name, "-")) {
1360         if (cmd == CMD_EXTRACT || cmd == CMD_LIST) {
1361 #if defined(__MINGW32__) || defined(__DJGPP__)
1362             setmode(fileno(stdin), O_BINARY);
1363 #endif
1364             return stdin;
1365         }
1366         else
1367             return NULL;
1368     }
1369
1370     ext2 = strrchr(archive_name, '.');
1371     if (ext2) {
1372         ext2++;
1373
1374         /* .com: DOS SFX
1375            .exe: DOS SFX
1376            .x:   HUMAN SFX
1377            .bak: Backup file
1378            .lha: Amiga(?) */
1379         p = xstrdup("lzh," ADDITIONAL_SUFFIXES);
1380         for (ext = strtok(p, ",");
1381              ext;
1382              ext = strtok(NULL, ",")) {
1383
1384             if (*ext == 0) continue;
1385
1386             if (strcasecmp(ext, ext2)) {
1387                 /* Try to open file just specified filename
1388                    with usual suffixes.
1389                    Return NULL if the file is not exist. */
1390
1391                 open_old_archive_1(archive_name, &fp);
1392                 goto ret;       /* found or not */
1393             }
1394         }
1395         free(p);
1396         p = NULL;
1397     }
1398
1399     /* Try to open file just specified filename */
1400     if (open_old_archive_1(archive_name, &fp))
1401         goto ret;               /* found */
1402
1403     /* Try to open file with implicit suffixes */
1404     p = xstrdup("lzh," ADDITIONAL_SUFFIXES);
1405     for (ext = strtok(p, ",");
1406          ext;
1407          ext = strtok(NULL, ",")) {
1408
1409         if (*ext == 0) continue;
1410
1411         xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
1412                   "%s.%s", archive_name, ext);
1413
1414         if (open_old_archive_1(expanded_archive_name, &fp)) {
1415             archive_name = expanded_archive_name;
1416             goto ret;           /* found */
1417         }
1418     }
1419
1420 ret:
1421     if (p) free(p);
1422     return fp;
1423 }
1424
1425 /* ------------------------------------------------------------------------ */
1426 int
1427 inquire(msg, name, selective)
1428     char           *msg, *name, *selective;
1429 {
1430     char            buffer[1024];
1431     char           *p;
1432
1433     for (;;) {
1434         fprintf(stderr, "%s %s ", name, msg);
1435         fflush(stderr);
1436
1437         fgets(buffer, 1024, stdin);
1438
1439         for (p = selective; *p; p++)
1440             if (buffer[0] == *p)
1441                 return p - selective;
1442     }
1443     /* NOTREACHED */
1444 }
1445
1446 /* ------------------------------------------------------------------------ */
1447 void
1448 write_archive_tail(nafp)
1449     FILE           *nafp;
1450 {
1451     putc(0x00, nafp);
1452 }
1453
1454 /* ------------------------------------------------------------------------ */
1455 #undef exit
1456
1457 void
1458 lha_exit(status)
1459     int status;
1460 {
1461     cleanup();
1462     exit(status);
1463 }