OSDN Git Service

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