OSDN Git Service

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