OSDN Git Service

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