OSDN Git Service

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