1 /* ------------------------------------------------------------------------ */
3 /* lharc.c -- append to archive */
5 /* Copyright (C) MCMLXXXIX Yooichi.Tagawa */
6 /* Modified Nobutaka Watazaki */
7 /* Thanks to H.Yoshizaki. (MS-DOS LHarc) */
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 */
21 /* DOS-Version Original LHx V C2.01 (C) H.Yohizaki */
23 /* V2.00 UNIX Lharc + DOS LHx -> OSK LHx 1990.11.01 Momozou */
24 /* V2.01 Minor Modified 1990.11.24 Momozou */
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 */
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 /* ------------------------------------------------------------------------ */
47 static int cmd = CMD_UNKNOWN;
48 static int error_occurred;
50 /* static functions */
51 static void sort_files();
52 static void print_version();
54 extern int optional_archive_kanji_code;
55 extern int optional_system_kanji_code;
57 /* ------------------------------------------------------------------------ */
59 init_variable() /* Added N.Watazaki */
65 noexec = FALSE; /* debugging option */
69 compress_method = DEFAULT_LZHUFF_METHOD; /* defined in config.h */
71 header_level = 2; /* level 2 */
78 /* view command flags */
79 verbose_listing = FALSE;
81 /* extract command flags */
82 output_to_stdout = FALSE;
84 /* append command flags */
86 update_if_newer = FALSE;
87 delete_after_append = FALSE;
88 generic_format = FALSE;
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;
98 noconvertcase = FALSE;
100 extract_directory = NULL;
103 #if BACKUP_OLD_ARCHIVE
104 backup_old_archive = TRUE;
106 backup_old_archive = FALSE;
110 /* ------------------------------------------------------------------------ */
111 /* NOTES : Text File Format */
112 /* GENERATOR NewLine */
113 /* [generic] 0D 0A */
115 /* [OS9][MacOS] 0D */
117 /* ------------------------------------------------------------------------ */
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\
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");
142 m Move to archive (means 'ad') o[567] compression method (a/u/c)\n\
147 m Move to archive (means 'ad') o use LHarc compatible method (a/u/c)\n\
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\
160 e TEXT code convert from/to EUC\n\
164 w=<dir> specify extract directory (x/e)\n\
165 x=<pattern> eXclude files (a/u/c)\n\
167 #if IGNORE_DOT_FILES /* experimental feature */
169 X ignore dot files (a/u/c)\n\
175 parse_option(int argc, char **argv)
180 argv++; argc--; /* exclude command name */
187 if (strcmp(*argv, "--help") == 0) {
191 if (strcmp(*argv, "--version") == 0) {
197 archive_name = *argv++; argc--;
204 opt = *argv++; argc--;
217 output_to_stdout = TRUE;
235 update_if_newer = TRUE;
240 delete_after_append = TRUE;
245 verbose_listing = TRUE;
271 case '0': /* no quiet */
272 case '1': /* no use the incremental indicator */
273 quiet_mode = *p - '0';
276 case '2': /* no output */
280 /* In quiet mode, no confirm to overwrite */
308 generic_format = TRUE;
309 noconvertcase = TRUE;
313 delete_after_append = TRUE;
318 compress_method = LZHUFF1_METHOD_NUM;
322 compress_method = LZHUFF5_METHOD_NUM;
327 compress_method = LZHUFF6_METHOD_NUM;
331 compress_method = LZHUFF7_METHOD_NUM;
336 error("invalid compression method 'o%c'", *p);
341 compress_method = LZHUFF0_METHOD_NUM; /* Changed N.Watazaki */
344 ignore_directory = TRUE;
350 for (i = 0; exclude_files && exclude_files[i]; i++)
352 exclude_files = (char**)xrealloc(exclude_files,
353 sizeof(char*) * (i+2));
360 exclude_files[i] = *argv++; argc--;
361 exclude_files[i+1] = 0;
365 exclude_files[i] = p;
366 exclude_files[i+1] = 0;
370 #if IGNORE_DOT_FILES /* experimental feature */
372 for (i = 0; exclude_files && exclude_files[i]; i++)
374 exclude_files = (char**)xrealloc(exclude_files,
375 sizeof(char*) * (i+2));
377 exclude_files[i] = xstrdup(".*");
378 exclude_files[i+1] = 0;
389 extract_directory = *argv++; argc--;
393 extract_directory = p;
407 error("Unknown option '%c'.", p[-1]);
414 if (!opt || opt[0] != '-')
417 /* special archive name */
418 if (strcmp(opt, "-") == 0)
421 /* GNU style long options */
422 if (opt[0] == '-' && opt[1] == '-') {
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;
431 else if (strcmp(opt, "sjis") == 0) {
432 optional_system_kanji_code = CODE_SJIS;
434 else if (strcmp(opt, "utf8") == 0) {
435 optional_system_kanji_code = CODE_UTF8;
437 else if (strcmp(opt, "cap") == 0) {
438 optional_system_kanji_code = CODE_CAP;
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;
451 else if (strcmp(opt, "sjis") == 0) {
452 optional_archive_kanji_code = CODE_SJIS;
454 else if (strcmp(opt, "utf8") == 0) {
455 optional_archive_kanji_code = CODE_UTF8;
457 else if (strcmp(opt, "cap") == 0) {
458 optional_archive_kanji_code = CODE_CAP;
472 archive_name = *argv++; argc--;
478 /* ------------------------------------------------------------------------ */
488 init_variable(); /* Added N.Watazaki */
490 parse_option(argc, argv);
497 if (!strcmp(archive_name, "-")) {
498 if (!isatty(1) && cmd == CMD_ADD)
501 #if 0 /* Comment out; IMHO, this feature is useless. by Koji Arai */
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;
511 /* target file name */
512 if (get_filename_from_stdin) {
518 xfilev = (char **)xmalloc(sizeof(char *) * xfilec);
519 while (fgets(inpbuf, sizeof(inpbuf), stdin)) {
520 /* delete \n if it exist */
522 while (i < sizeof(inpbuf) && p != 0) {
530 if (cmd_filec >= xfilec) {
532 xfilev = (char **) xrealloc(xfilev,
533 sizeof(char *) * xfilec);
535 if (strlen(inpbuf) < 1)
537 xfilev[cmd_filec++] = xstrdup(inpbuf);
539 xfilev[cmd_filec] = NULL;
574 /* ------------------------------------------------------------------------ */
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);
586 message(char *fmt, ...)
588 message(fmt, va_alist)
593 int errno_sv = errno;
596 fprintf(stderr, "LHa: ");
599 vfprintf(stderr, fmt, v);
607 /* ------------------------------------------------------------------------ */
610 warning(char *fmt, ...)
612 warning(fmt, va_alist)
617 int errno_sv = errno;
620 fprintf(stderr, "LHa: Warning: ");
623 vfprintf(stderr, fmt, v);
631 /* ------------------------------------------------------------------------ */
634 error(char *fmt, ...)
641 int errno_sv = errno;
644 fprintf(stderr, "LHa: Error: ");
647 vfprintf(stderr, fmt, v);
659 fatal_error(char *fmt, ...)
661 fatal_error(fmt, va_alist)
666 int errno_sv = errno;
669 fprintf(stderr, "LHa: Fatal error: ");
672 vfprintf(stderr, fmt, v);
676 fprintf(stderr, ": %s\n", strerror(errno_sv));
680 if (remove_temporary_at_error) {
681 if (temporary_fd != -1)
683 unlink(temporary_name);
689 /* ------------------------------------------------------------------------ */
694 message("Interrupted");
696 if (temporary_fd != -1)
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);
705 signal(SIGINT, SIG_DFL);
707 signal(SIGHUP, SIG_DFL);
709 kill(getpid(), signo);
712 /* ------------------------------------------------------------------------ */
714 /* ------------------------------------------------------------------------ */
719 register char *p, *q;
723 if (generic_format) {
725 c1 = *(unsigned char *) p++;
726 c2 = *(unsigned char *) q++;
738 while (*p == *q && *p != '\0')
740 return *(unsigned char *) p - *(unsigned char *) q;
744 /* ------------------------------------------------------------------------ */
749 qsort(cmd_filev, cmd_filec, sizeof(char *), sort_by_ascii);
752 /* ------------------------------------------------------------------------ */
757 void *p = malloc(size);
759 fatal_error("Not enough memory");
763 /* ------------------------------------------------------------------------ */
769 void *p = (char *) realloc(old, size);
771 fatal_error("Not enough memory");
779 int len = strlen(str);
780 char *p = (char *)xmalloc(len + 1);
785 /* ------------------------------------------------------------------------ */
787 /* ------------------------------------------------------------------------ */
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
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
803 /* ------------------------------------------------------------------------ */
806 struct string_pool *sp;
808 sp->size = 1024 - 8; /* any ( >=0 ) */
811 sp->buffer = (char *) xmalloc(sp->size * sizeof(char));
814 /* ------------------------------------------------------------------------ */
816 add_sp(sp, name, len)
817 struct string_pool *sp;
818 char *name; /* stored '\0' at tail */
819 int len; /* include '\0' */
821 while (sp->used + len > sp->size) {
823 sp->buffer = (char *) xrealloc(sp->buffer, sp->size * sizeof(char));
825 memmove(sp->buffer + sp->used, name, len);
830 /* ------------------------------------------------------------------------ */
832 finish_sp(sp, v_count, v_vector)
833 register struct string_pool *sp;
841 v = (char **) xmalloc((sp->n + 1) * sizeof(char *));
846 for (i = sp->n; i; i--) {
853 /* ------------------------------------------------------------------------ */
859 free(*vector); /* free string pool */
864 /* ------------------------------------------------------------------------ */
865 /* READ DIRECTORY FILES */
866 /* ------------------------------------------------------------------------ */
868 include_path_p(path, name)
874 return (path[-1] == '/' && *n == '\0');
875 return (*n == '/' || (n != name && path[-1] == '/' && n[-1] == '/'));
878 /* ------------------------------------------------------------------------ */
880 cleaning_files(v_filec, v_filev)
887 register char **filev = *v_filev;
888 register int filec = *v_filec;
895 flags = xmalloc(filec * sizeof(char));
897 /* flags & 0x01 : 1: ignore */
898 /* flags & 0x02 : 1: directory, 0 : regular file */
899 /* flags & 0x04 : 1: need delete */
901 for (i = 0; i < filec; i++)
902 if (GETSTAT(filev[i], &stbuf) < 0) {
904 warning("Cannot access \"%s\" : %s; ignored.", filev[i],
908 if (is_regularfile(&stbuf))
910 else if (is_directory(&stbuf))
913 else if (is_symlink(&stbuf)) /* t.okamoto */
918 warning("Cannot archive \"%s\", ignored.", filev[i]);
922 for (i = 0; i < filec; i++) {
924 if ((flags[i] & 0x07) == 0x00) { /* regular file, not
926 for (j = i + 1; j < filec; j++) {
927 if ((flags[j] & 0x07) == 0x00) { /* regular file, not
929 if (STREQU(p, filev[j]))
930 flags[j] = 0x04; /* delete */
934 else if ((flags[i] & 0x07) == 0x02) { /* directory, not
936 for (j = i + 1; j < filec; j++) {
937 if ((flags[j] & 0x07) == 0x00) { /* regular file, not
939 if (include_path_p(p, filev[j]))
940 flags[j] = 0x04; /* delete */
942 else if ((flags[j] & 0x07) == 0x02) { /* directory, not
944 if (include_path_p(p, filev[j]))
945 flags[j] = 0x04; /* delete */
951 for (i = j = 0; i < filec; i++) {
952 if ((flags[i] & 0x04) == 0) {
963 /* ------------------------------------------------------------------------ */
965 find_files(name, v_filec, v_filev)
970 struct string_pool sp;
971 char newname[FILENAME_LENGTH];
975 struct stat tmp_stbuf, arc_stbuf, fil_stbuf;
977 strcpy(newname, name);
979 if (len > 0 && newname[len - 1] != '/')
980 newname[len++] = '/';
982 dirp = opendir(name);
988 GETSTAT(temporary_name, &tmp_stbuf);
989 GETSTAT(archive_name, &arc_stbuf);
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)
999 strncpy(newname + len, dp->d_name, n);
1000 newname[len + n] = '\0';
1001 if (GETSTAT(newname, &fil_stbuf) < 0)
1003 #if !defined(HAVE_STRUCT_STAT_ST_INO) || __MINGW32__
1004 if ( dp->d_name[0] != '.' ||
1006 (dp->d_name[1] != '.' ||
1008 add_sp(&sp, newname, len+n+1);
1011 if ((dp->d_ino != 0) &&
1012 /* exclude '.' and '..' */
1013 ((dp->d_name[0] != '.') ||
1015 ((dp->d_name[1] != '.') ||
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);
1028 finish_sp(&sp, v_filec, v_filev);
1030 qsort(*v_filev, *v_filec, sizeof(char *), sort_by_ascii);
1031 cleaning_files(v_filec, v_filev);
1036 /* ------------------------------------------------------------------------ */
1038 free_files(filec, filev)
1045 /* ------------------------------------------------------------------------ */
1047 /* ------------------------------------------------------------------------ */
1048 /* Build temporary file name and store to TEMPORARY_NAME */
1050 build_temporary_name()
1052 #ifdef TMP_FILENAME_TEMPLATE
1053 /* "/tmp/lhXXXXXX" etc. */
1054 if (extract_directory == NULL) {
1055 strcpy(temporary_name, TMP_FILENAME_TEMPLATE);
1058 xsnprintf(temporary_name, sizeof(temporary_name),
1059 "%s/lhXXXXXX", extract_directory);
1064 strcpy(temporary_name, archive_name);
1065 for (p = temporary_name, s = (char *) 0; *p; p++)
1068 strcpy((s ? s + 1 : temporary_name), "lhXXXXXX");
1074 old_umask = umask(077);
1075 fd = mkstemp(temporary_name);
1083 mktemp(temporary_name);
1084 flags = O_CREAT|O_EXCL|O_RDWR;
1088 return open(temporary_name, flags, 0600);
1093 /* ------------------------------------------------------------------------ */
1095 modify_filename_extention(buffer, ext)
1099 register char *p, *dot;
1101 for (p = buffer, dot = (char *) 0; *p; p++) {
1114 /* ------------------------------------------------------------------------ */
1115 /* build backup file name */
1117 build_backup_name(buffer, original)
1121 strcpy(buffer, original);
1122 modify_filename_extention(buffer, BACKUPNAME_EXTENTION); /* ".bak" */
1125 /* ------------------------------------------------------------------------ */
1127 build_standard_archive_name(buffer, orginal)
1131 strcpy(buffer, orginal);
1132 modify_filename_extention(buffer, ARCHIVENAME_EXTENTION); /* ".lzh" */
1135 /* ------------------------------------------------------------------------ */
1137 /* ------------------------------------------------------------------------ */
1147 for (i = 0; i < cmd_filec; i++) {
1148 if (patmatch(cmd_filev[i], name, 0))
1161 if ((fp = fopen(name, mode)) == NULL)
1162 fatal_error("Cannot open file \"%s\"", name);
1167 /* ------------------------------------------------------------------------ */
1169 /* ------------------------------------------------------------------------ */
1171 open_old_archive_1(name, v_fp)
1178 if (stat(name, &stbuf) >= 0 &&
1179 is_regularfile(&stbuf) &&
1180 (fp = fopen(name, READ_BINARY)) != NULL) {
1182 archive_file_gid = stbuf.st_gid;
1183 archive_file_mode = stbuf.st_mode;
1188 archive_file_gid = -1;
1192 /* ------------------------------------------------------------------------ */
1198 static char expanded_archive_name[FILENAME_LENGTH];
1200 if (!strcmp(archive_name, "-")) {
1201 if (cmd == CMD_EXTRACT || cmd == CMD_LIST) {
1203 setmode(fileno(stdin), O_BINARY);
1210 p = strrchr(archive_name, '.');
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);
1223 if (open_old_archive_1(archive_name, &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;
1232 * if ( (errno&0xffff)!=E_PNNF ) { archive_name =
1233 * expanded_archive_name; return NULL; }
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;
1242 * if ( (errno&0xffff)!=E_PNNF ) { archive_name =
1243 * expanded_archive_name; return NULL; }
1246 * sprintf( expanded_archive_name , "%s.lzh",archive_name);
1247 * archive_name = expanded_archive_name;
1252 /* ------------------------------------------------------------------------ */
1254 inquire(msg, name, selective)
1255 char *msg, *name, *selective;
1261 fprintf(stderr, "%s %s ", name, msg);
1264 fgets(buffer, 1024, stdin);
1266 for (p = selective; *p; p++)
1267 if (buffer[0] == *p)
1268 return p - selective;
1273 /* ------------------------------------------------------------------------ */
1275 write_archive_tail(nafp)
1281 /* ------------------------------------------------------------------------ */
1283 copy_old_one(oafp, nafp, hdr)
1288 fseeko(oafp, hdr->header_size + hdr->packed_size, SEEK_CUR);
1291 reading_filename = archive_name;
1292 writing_filename = temporary_name;
1293 copyfile(oafp, nafp, hdr->header_size + hdr->packed_size, 0, 0);