OSDN Git Service

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