OSDN Git Service

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