OSDN Git Service

00readme.autoconf: Added description for the -b switch which extract MacBinaries.
[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-2007  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 }
676
677 void
678 #if STDC_HEADERS
679 message(char *fmt, ...)
680 #else
681 message(fmt, va_alist)
682     char *fmt;
683     va_dcl
684 #endif
685 {
686     int errno_sv = errno;
687     va_list v;
688
689     fprintf(stderr, "LHa: ");
690
691     va_init(v, fmt);
692     vfprintf(stderr, fmt, v);
693     va_end(v);
694
695     fputs("\n", stderr);
696
697     errno =  errno_sv;
698 }
699
700 /* ------------------------------------------------------------------------ */
701 void
702 #if STDC_HEADERS
703 warning(char *fmt, ...)
704 #else
705 warning(fmt, va_alist)
706     char *fmt;
707     va_dcl
708 #endif
709 {
710     int errno_sv = errno;
711     va_list v;
712
713     fprintf(stderr, "LHa: Warning: ");
714
715     va_init(v, fmt);
716     vfprintf(stderr, fmt, v);
717     va_end(v);
718
719     fputs("\n", stderr);
720
721     errno =  errno_sv;
722 }
723
724 /* ------------------------------------------------------------------------ */
725 void
726 #if STDC_HEADERS
727 error(char *fmt, ...)
728 #else
729 error(fmt, va_alist)
730     char *fmt;
731     va_dcl
732 #endif
733 {
734     int errno_sv = errno;
735     va_list v;
736
737     fprintf(stderr, "LHa: Error: ");
738
739     va_init(v, fmt);
740     vfprintf(stderr, fmt, v);
741     va_end(v);
742
743     fputs("\n", stderr);
744
745     error_occurred = 1;
746
747     errno =  errno_sv;
748 }
749
750 void
751 #if STDC_HEADERS
752 fatal_error(char *fmt, ...)
753 #else
754 fatal_error(fmt, va_alist)
755     char *fmt;
756     va_dcl
757 #endif
758 {
759     int errno_sv = errno;
760     va_list v;
761
762     fprintf(stderr, "LHa: Fatal error: ");
763
764     va_init(v, fmt);
765     vfprintf(stderr, fmt, v);
766     va_end(v);
767
768     if (errno)
769         fprintf(stderr, ": %s\n", strerror(errno_sv));
770     else
771         fputs("\n", stderr);
772
773     exit(1);
774 }
775
776 void
777 cleanup()
778 {
779     if (temporary_fd != -1) {
780         close(temporary_fd);
781         temporary_fd = -1;
782         unlink(temporary_name);
783     }
784
785     if (recover_archive_when_interrupt) {
786         rename(backup_archive_name, archive_name);
787         recover_archive_when_interrupt = FALSE;
788     }
789     if (remove_extracting_file_when_interrupt) {
790         message("Removing: %s", writing_filename);
791         unlink(writing_filename);
792         remove_extracting_file_when_interrupt = FALSE;
793     }
794 }
795
796 RETSIGTYPE
797 interrupt(signo)
798     int signo;
799 {
800     message("Interrupted");
801
802     cleanup();
803
804     signal(SIGINT, SIG_DFL);
805 #ifdef SIGHUP
806     signal(SIGHUP, SIG_DFL);
807 #endif
808     kill(getpid(), signo);
809 }
810
811 /* ------------------------------------------------------------------------ */
812 /*                                                                          */
813 /* ------------------------------------------------------------------------ */
814 static int
815 sort_by_ascii(a, b)
816     char          **a, **b;
817 {
818     register char  *p, *q;
819     register int    c1, c2;
820
821     p = *a, q = *b;
822     if (generic_format) {
823         do {
824             c1 = *(unsigned char *) p++;
825             c2 = *(unsigned char *) q++;
826             if (!c1 || !c2)
827                 break;
828             if (islower(c1))
829                 c1 = toupper(c1);
830             if (islower(c2))
831                 c2 = toupper(c2);
832         }
833         while (c1 == c2);
834         return c1 - c2;
835     }
836     else {
837         while (*p == *q && *p != '\0')
838             p++, q++;
839         return *(unsigned char *) p - *(unsigned char *) q;
840     }
841 }
842
843 /* ------------------------------------------------------------------------ */
844 static void
845 sort_files()
846 {
847     if (cmd_filec > 1)
848         qsort(cmd_filev, cmd_filec, sizeof(char *), sort_by_ascii);
849 }
850
851 /* ------------------------------------------------------------------------ */
852 void *
853 xmalloc(size)
854     size_t size;
855 {
856     void *p = malloc(size);
857     if (!p)
858         fatal_error("Not enough memory");
859     return p;
860 }
861
862 /* ------------------------------------------------------------------------ */
863 void *
864 xrealloc(old, size)
865     void *old;
866     size_t size;
867 {
868     void *p = (char *) realloc(old, size);
869     if (!p)
870         fatal_error("Not enough memory");
871     return p;
872 }
873
874 char *
875 xstrdup(str)
876     char *str;
877 {
878     int len = strlen(str);
879     char *p = (char *)xmalloc(len + 1);
880     strcpy(p, str);             /* ok */
881     return p;
882 }
883
884 /* ------------------------------------------------------------------------ */
885 /*                              STRING POOL                                 */
886 /* ------------------------------------------------------------------------ */
887 /*
888   string pool :
889     +-------------+-------------+------+-------------+----------+
890     | N A M E 1 \0| N A M E 2 \0| .... | N A M E n \0|          |
891     +-------------+-------------+------+-------------+----------+
892       ^ ^        ^ buffer+0 buffer+used buffer+size
893
894   vector :
895     +---------------+---------------+------------- -----------------+
896     | pointer to    | pointer to    | pointer to   ...  pointer to  |
897     |  stringpool   |  N A M E 1    |  N A M E 2   ...   N A M E n  |
898     +---------------+---------------+-------------     -------------+
899     ^ malloc base      returned
900 */
901
902 /* ------------------------------------------------------------------------ */
903 void
904 init_sp(sp)
905     struct string_pool *sp;
906 {
907     sp->size = 1024 - 8;    /* any ( >=0 ) */
908     sp->used = 0;
909     sp->n = 0;
910     sp->buffer = (char *) xmalloc(sp->size * sizeof(char));
911 }
912
913 /* ------------------------------------------------------------------------ */
914 void
915 add_sp(sp, name, len)
916     struct string_pool *sp;
917     char           *name;   /* stored '\0' at tail */
918     int             len;    /* include '\0' */
919 {
920     while (sp->used + len > sp->size) {
921         sp->size *= 2;
922         sp->buffer = (char *) xrealloc(sp->buffer, sp->size * sizeof(char));
923     }
924     memmove(sp->buffer + sp->used, name, len);
925     sp->used += len;
926     sp->n++;
927 }
928
929 /* ------------------------------------------------------------------------ */
930 void
931 finish_sp(sp, v_count, v_vector)
932     register struct string_pool *sp;
933     int            *v_count;
934     char         ***v_vector;
935 {
936     int             i;
937     register char  *p;
938     char          **v;
939
940     v = (char **) xmalloc((sp->n + 1) * sizeof(char *));
941     *v++ = sp->buffer;
942     *v_vector = v;
943     *v_count = sp->n;
944     p = sp->buffer;
945     for (i = sp->n; i; i--) {
946         *v++ = p;
947         if (i - 1)
948             p += strlen(p) + 1;
949     }
950 }
951
952 /* ------------------------------------------------------------------------ */
953 void
954 free_sp(vector)
955     char          **vector;
956 {
957     vector--;
958     free(*vector);      /* free string pool */
959     free(vector);
960 }
961
962
963 /* ------------------------------------------------------------------------ */
964 /*                          READ DIRECTORY FILES                            */
965 /* ------------------------------------------------------------------------ */
966 static          boolean
967 include_path_p(path, name)
968     char           *path, *name;
969 {
970     char           *n = name;
971     while (*path)
972         if (*path++ != *n++)
973             return (path[-1] == '/' && *n == '\0');
974     return (*n == '/' || (n != name && path[-1] == '/' && n[-1] == '/'));
975 }
976
977 /* ------------------------------------------------------------------------ */
978 void
979 cleaning_files(v_filec, v_filev)
980     int            *v_filec;
981     char         ***v_filev;
982 {
983     char           *flags;
984     struct stat     stbuf;
985
986     register char **filev = *v_filev;
987     register int    filec = *v_filec;
988     register char  *p;
989     register int    i, j;
990
991     if (filec == 0)
992         return;
993
994     flags = xmalloc(filec * sizeof(char));
995
996     /* flags & 0x01 :   1: ignore */
997     /* flags & 0x02 :   1: directory, 0 : regular file */
998     /* flags & 0x04 :   1: need delete */
999
1000     for (i = 0; i < filec; i++)
1001         if (GETSTAT(filev[i], &stbuf) < 0) {
1002             flags[i] = 0x04;
1003             warning("Cannot access \"%s\" : %s; ignored.", filev[i],
1004                     strerror(errno));
1005         }
1006         else {
1007             if (is_regularfile(&stbuf))
1008                 flags[i] = 0x00;
1009             else if (is_directory(&stbuf))
1010                 flags[i] = 0x02;
1011 #ifdef S_IFLNK
1012             else if (is_symlink(&stbuf)) /* t.okamoto */
1013                 flags[i] = 0x00;
1014 #endif
1015             else {
1016                 flags[i] = 0x04;
1017                 warning("Cannot archive \"%s\", ignored.", filev[i]);
1018             }
1019         }
1020
1021     for (i = 0; i < filec; i++) {
1022         p = filev[i];
1023         if ((flags[i] & 0x07) == 0x00) {    /* regular file, not
1024                              * deleted/ignored */
1025             for (j = i + 1; j < filec; j++) {
1026                 if ((flags[j] & 0x07) == 0x00) {    /* regular file, not
1027                                      * deleted/ignored */
1028                     if (STREQU(p, filev[j]))
1029                         flags[j] = 0x04;    /* delete */
1030                 }
1031             }
1032         }
1033         else if ((flags[i] & 0x07) == 0x02) {   /* directory, not
1034                              * deleted/ignored */
1035             for (j = i + 1; j < filec; j++) {
1036                 if ((flags[j] & 0x07) == 0x00) {    /* regular file, not
1037                                      * deleted/ignored */
1038                     if (include_path_p(p, filev[j]))
1039                         flags[j] = 0x04;    /* delete */
1040                 }
1041                 else if ((flags[j] & 0x07) == 0x02) {   /* directory, not
1042                                      * deleted/ignored */
1043                     if (include_path_p(p, filev[j]))
1044                         flags[j] = 0x04;    /* delete */
1045                 }
1046             }
1047         }
1048     }
1049
1050     for (i = j = 0; i < filec; i++) {
1051         if ((flags[i] & 0x04) == 0) {
1052             if (i != j)
1053                 filev[j] = filev[i];
1054             j++;
1055         }
1056     }
1057     *v_filec = j;
1058
1059     free(flags);
1060 }
1061
1062 /* ------------------------------------------------------------------------ */
1063 boolean
1064 find_files(name, v_filec, v_filev)
1065     char           *name;
1066     int            *v_filec;
1067     char         ***v_filev;
1068 {
1069     struct string_pool sp;
1070     char            newname[FILENAME_LENGTH];
1071     int             len, n, i;
1072     DIR            *dirp;
1073     struct dirent  *dp;
1074     struct stat     tmp_stbuf, arc_stbuf, fil_stbuf;
1075     int exist_tmp = 1, exist_arc = 1;
1076
1077     len = str_safe_copy(newname, name, sizeof(newname));
1078     if (len > 0 && newname[len - 1] != '/') {
1079         if (len < sizeof(newname)-1)
1080             strcpy(&newname[len++], "/"); /* ok */
1081         else
1082             warning("the length of pathname \"%s\" is too long.", name);
1083     }
1084
1085     dirp = opendir(name);
1086     if (!dirp)
1087         return FALSE;
1088
1089     init_sp(&sp);
1090
1091     if (GETSTAT(temporary_name, &tmp_stbuf) == -1)
1092         exist_tmp = 0;
1093     if (GETSTAT(archive_name, &arc_stbuf) == -1)
1094         exist_arc = 0;
1095
1096     while ((dp = readdir(dirp)) != NULL) {
1097         n = NAMLEN(dp);
1098
1099         /* exclude '.' and '..' */
1100         if (strncmp(dp->d_name, ".", n) == 0
1101             || strncmp(dp->d_name, "..", n) == 0)
1102             continue;
1103
1104         /* exclude exclude_files supplied by user */
1105         for (i = 0; exclude_files && exclude_files[i]; i++) {
1106             if (fnmatch(exclude_files[i], dp->d_name,
1107                         FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD) == 0)
1108                 goto next;
1109         }
1110
1111         if (len + n >= sizeof(newname)) {
1112             warning("filename is too long");
1113             continue;
1114         }
1115
1116         strncpy(newname + len, dp->d_name, n);
1117         newname[len + n] = '\0';
1118         if (GETSTAT(newname, &fil_stbuf) < 0)
1119             continue;
1120
1121 #if defined(HAVE_STRUCT_STAT_ST_INO) && !__MINGW32__
1122         /* MinGW has meaningless st_ino */
1123
1124         /* exclude temporary file, archive file and these links */
1125         if (exist_tmp &&
1126             tmp_stbuf.st_dev == fil_stbuf.st_dev &&
1127             tmp_stbuf.st_ino == fil_stbuf.st_ino)
1128             continue;
1129
1130         if (exist_arc &&
1131             arc_stbuf.st_dev == fil_stbuf.st_dev &&
1132             arc_stbuf.st_ino == fil_stbuf.st_ino)
1133             continue;
1134 #endif
1135         add_sp(&sp, newname, len+n+1);
1136     next:
1137         ;
1138     }
1139     closedir(dirp);
1140     finish_sp(&sp, v_filec, v_filev);
1141     if (*v_filec > 1)
1142         qsort(*v_filev, *v_filec, sizeof(char *), sort_by_ascii);
1143     cleaning_files(v_filec, v_filev);
1144
1145     return TRUE;
1146 }
1147
1148 /* ------------------------------------------------------------------------ */
1149 void
1150 free_files(filec, filev)
1151     int             filec;
1152     char          **filev;
1153 {
1154     free_sp(filev);
1155 }
1156
1157 /* ------------------------------------------------------------------------ */
1158 /*                                                                          */
1159 /* ------------------------------------------------------------------------ */
1160 /* Build temporary file name and store to TEMPORARY_NAME */
1161 int
1162 build_temporary_name()
1163 {
1164 #ifdef TMP_FILENAME_TEMPLATE
1165     /* "/tmp/lhXXXXXX" etc. */
1166     if (extract_directory == NULL) {
1167         str_safe_copy(temporary_name, TMP_FILENAME_TEMPLATE,
1168                       sizeof(temporary_name));
1169     }
1170     else {
1171         xsnprintf(temporary_name, sizeof(temporary_name),
1172                   "%s/lhXXXXXX", extract_directory);
1173     }
1174 #else
1175     char *s;
1176
1177     str_safe_copy(temporary_name, archive_name, sizeof(temporary_name));
1178     s = strrchr(temporary_name, '/');
1179     if (s) {
1180         int len;
1181         len = s - temporary_name;
1182         if (len + strlen("lhXXXXXX") < sizeof(temporary_name))
1183             /* use directory at archive file */
1184             strcpy(s, "lhXXXXXX"); /* ok */
1185         else
1186             /* use current directory */
1187             str_safe_copy(temporary_name, "lhXXXXXX", sizeof(temporary_name));
1188     }
1189     else
1190         /* use current directory */
1191         str_safe_copy(temporary_name, "lhXXXXXX", sizeof(temporary_name));
1192 #endif
1193 #ifdef HAVE_MKSTEMP
1194     {
1195         int old_umask, fd;
1196
1197         old_umask = umask(077);
1198         fd = mkstemp(temporary_name);
1199         umask(old_umask);
1200         return fd;
1201     }
1202 #else
1203     {
1204         int flags;
1205
1206         mktemp(temporary_name);
1207         flags = O_CREAT|O_EXCL|O_RDWR;
1208 #ifdef O_BINARY
1209         flags |= O_BINARY;
1210 #endif
1211         return open(temporary_name, flags, 0600);
1212     }
1213 #endif
1214 }
1215
1216 /* ------------------------------------------------------------------------ */
1217 static void
1218 modify_filename_extention(buffer, ext, size)
1219     char           *buffer;
1220     char           *ext;
1221     size_t size;
1222 {
1223     register char  *p, *dot;
1224
1225     for (p = buffer, dot = (char *) 0; *p; p++) {
1226         if (*p == '.')
1227             dot = p;
1228         else if (*p == '/')
1229             dot = (char *) 0;
1230     }
1231
1232     if (dot)
1233         p = dot;
1234
1235     str_safe_copy(p, ext, size - (p - buffer));
1236 }
1237
1238 /* ------------------------------------------------------------------------ */
1239 /* build backup file name */
1240 void
1241 build_backup_name(buffer, original, size)
1242     char           *buffer;
1243     char           *original;
1244     size_t size;
1245 {
1246     str_safe_copy(buffer, original, size);
1247     modify_filename_extention(buffer, BACKUPNAME_EXTENTION, size);    /* ".bak" */
1248 }
1249
1250 /* ------------------------------------------------------------------------ */
1251 void
1252 build_standard_archive_name(buffer, original, size)
1253     char           *buffer;
1254     char           *original;
1255     size_t size;
1256 {
1257     str_safe_copy(buffer, original, size);
1258     modify_filename_extention(buffer, ARCHIVENAME_EXTENTION, size);   /* ".lzh" */
1259 }
1260
1261 /* ------------------------------------------------------------------------ */
1262 /*                                                                          */
1263 /* ------------------------------------------------------------------------ */
1264 boolean
1265 need_file(name)
1266     char           *name;
1267 {
1268     int             i;
1269
1270     if (cmd_filec == 0)
1271         return TRUE;
1272
1273     for (i = 0; i < cmd_filec; i++) {
1274         if (patmatch(cmd_filev[i], name, 0))
1275             return TRUE;
1276     }
1277
1278     return FALSE;
1279 }
1280
1281 FILE           *
1282 xfopen(name, mode)
1283     char           *name, *mode;
1284 {
1285     FILE           *fp;
1286
1287     if ((fp = fopen(name, mode)) == NULL)
1288         fatal_error("Cannot open file \"%s\"", name);
1289
1290     return fp;
1291 }
1292
1293 /* ------------------------------------------------------------------------ */
1294 /*                                                                          */
1295 /* ------------------------------------------------------------------------ */
1296 static          boolean
1297 open_old_archive_1(name, v_fp)
1298     char           *name;
1299     FILE          **v_fp;
1300 {
1301     FILE           *fp;
1302     struct stat     stbuf;
1303
1304     if (stat(name, &stbuf) >= 0 &&
1305         is_regularfile(&stbuf) &&
1306         (fp = fopen(name, READ_BINARY)) != NULL) {
1307         *v_fp = fp;
1308         archive_file_gid = stbuf.st_gid;
1309         archive_file_mode = stbuf.st_mode;
1310         return TRUE;
1311     }
1312
1313     *v_fp = NULL;
1314     archive_file_gid = -1;
1315     return FALSE;
1316 }
1317
1318 /* ------------------------------------------------------------------------ */
1319 FILE           *
1320 open_old_archive()
1321 {
1322     FILE           *fp;
1323     char           *p;
1324     static char expanded_archive_name[FILENAME_LENGTH];
1325
1326     if (!strcmp(archive_name, "-")) {
1327         if (cmd == CMD_EXTRACT || cmd == CMD_LIST) {
1328 #if defined(__MINGW32__) || defined(__DJGPP__)
1329             setmode(fileno(stdin), O_BINARY);
1330 #endif
1331             return stdin;
1332         }
1333         else
1334             return NULL;
1335     }
1336     p = strrchr(archive_name, '.');
1337     if (p) {
1338         if (strcasecmp(".LZH", p) == 0
1339             || strcasecmp(".LZS", p) == 0
1340             || strcasecmp(".COM", p) == 0  /* DOS SFX */
1341             || strcasecmp(".EXE", p) == 0
1342             || strcasecmp(".X", p) == 0    /* HUMAN SFX */
1343             || strcasecmp(".BAK", p) == 0) {   /* for BackUp */
1344             open_old_archive_1(archive_name, &fp);
1345             return fp;
1346         }
1347     }
1348
1349     if (open_old_archive_1(archive_name, &fp))
1350         return fp;
1351     xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
1352               "%s.lzh", archive_name);
1353     if (open_old_archive_1(expanded_archive_name, &fp)) {
1354         archive_name = expanded_archive_name;
1355         return fp;
1356     }
1357     /*
1358      * if ( (errno&0xffff)!=E_PNNF ) { archive_name =
1359      * expanded_archive_name; return NULL; }
1360      */
1361     xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
1362               "%s.lzs", archive_name);
1363     if (open_old_archive_1(expanded_archive_name, &fp)) {
1364         archive_name = expanded_archive_name;
1365         return fp;
1366     }
1367     /*
1368      * if ( (errno&0xffff)!=E_PNNF ) { archive_name =
1369      * expanded_archive_name; return NULL; }
1370      */
1371     /*
1372      * sprintf( expanded_archive_name , "%s.lzh",archive_name);
1373      * archive_name = expanded_archive_name;
1374      */
1375     return NULL;
1376 }
1377
1378 /* ------------------------------------------------------------------------ */
1379 int
1380 inquire(msg, name, selective)
1381     char           *msg, *name, *selective;
1382 {
1383     char            buffer[1024];
1384     char           *p;
1385
1386     for (;;) {
1387         fprintf(stderr, "%s %s ", name, msg);
1388         fflush(stderr);
1389
1390         fgets(buffer, 1024, stdin);
1391
1392         for (p = selective; *p; p++)
1393             if (buffer[0] == *p)
1394                 return p - selective;
1395     }
1396     /* NOTREACHED */
1397 }
1398
1399 /* ------------------------------------------------------------------------ */
1400 void
1401 write_archive_tail(nafp)
1402     FILE           *nafp;
1403 {
1404     putc(0x00, nafp);
1405 }
1406
1407 /* ------------------------------------------------------------------------ */
1408 void
1409 copy_old_one(oafp, nafp, hdr)
1410     FILE           *oafp, *nafp;
1411     LzHeader       *hdr;
1412 {
1413     if (noexec) {
1414         fseeko(oafp, hdr->header_size + hdr->packed_size, SEEK_CUR);
1415     }
1416     else {
1417         reading_filename = archive_name;
1418         writing_filename = temporary_name;
1419         copyfile(oafp, nafp, hdr->header_size + hdr->packed_size, 0, 0);
1420     }
1421 }
1422
1423 #undef exit
1424
1425 void
1426 lha_exit(status)
1427     int status;
1428 {
1429     cleanup();
1430     exit(status);
1431 }