OSDN Git Service

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