OSDN Git Service

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