OSDN Git Service

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