OSDN Git Service

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