OSDN Git Service

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