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