1 /* ------------------------------------------------------------------------ */
3 /* lhext.c -- LHarc extract */
5 /* Copyright (C) MCMLXXXIX Yooichi.Tagawa */
6 /* Modified Nobutaka Watazaki */
8 /* Ver. 0.00 Original 1988.05.23 Y.Tagawa */
9 /* Ver. 1.00 Fixed 1989.09.22 Y.Tagawa */
10 /* Ver. 0.03 LHa for UNIX 1991.12.17 M.Oki */
11 /* Ver. 1.12 LHa for UNIX 1993.10.01 N.Watazaki */
12 /* Ver. 1.13b Symbolic Link Update Bug Fix 1994.06.21 N.Watazaki */
13 /* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */
14 /* Ver. 1.14e bugfix 1999.04.30 T.Okamoto */
15 /* ------------------------------------------------------------------------ */
17 /* ------------------------------------------------------------------------ */
18 static int skip_flg = FALSE; /* FALSE..No Skip , TRUE..Skip */
19 static char *methods[] =
21 LZHUFF0_METHOD, LZHUFF1_METHOD, LZHUFF2_METHOD, LZHUFF3_METHOD,
22 LZHUFF4_METHOD, LZHUFF5_METHOD, LZHUFF6_METHOD, LZHUFF7_METHOD,
23 LARC_METHOD, LARC5_METHOD, LARC4_METHOD,
25 PMARC0_METHOD, PMARC2_METHOD,
29 static void add_dirinfo(char* name, LzHeader* hdr);
30 static void adjust_dirinfo();
32 #ifdef HAVE_LIBAPPLEFILE
33 static boolean decode_macbinary(FILE *ofp, off_t size, const char *outPath);
36 /* ------------------------------------------------------------------------ */
44 if (stat(name, &stbuf) >= 0) {
45 if (!is_regularfile(&stbuf)) {
46 error("\"%s\" already exists (not a file)", name);
51 printf("EXTRACT %s but file is exist.\n", name);
56 warning("skip to extract %s.", name);
60 switch (inquire("OverWrite ?(Yes/[No]/All/Skip)", name, "YyNnAaSs\n")) {
81 printf("EXTRACT %s\n", name);
87 make_name_with_pathcheck(char *name, size_t namesz, const char *q)
94 if (extract_directory) {
95 sz = xsnprintf(name, namesz, "%s/", extract_directory);
103 while ((p = strchr(q, '/')) != NULL) {
104 if (namesz - offset < (p - q) + 2) {
107 memcpy(name + offset, q, (p - q));
108 name[offset + (p - q)] = 0;
113 if (lstat(name, &stbuf) < 0) {
114 name[offset++] = '/';
117 if (is_symlink(&stbuf)) {
120 name[offset++] = '/';
124 str_safe_copy(name + offset, q, namesz - offset);
129 /* ------------------------------------------------------------------------ */
131 make_parent_path(name)
134 char path[FILENAME_LENGTH];
138 /* make parent directory name into PATH for recursive call */
139 str_safe_copy(path, name, sizeof(path));
140 for (p = path + strlen(path); p > path; p--)
147 message("invalid path name \"%s\"", name);
148 return FALSE; /* no more parent. */
151 if (GETSTAT(path, &stbuf) >= 0) {
152 if (is_directory(&stbuf))
157 message("Making directory \"%s\".", path);
159 #if defined __MINGW32__
160 if (mkdir(path) >= 0)
163 if (mkdir(path, 0777) >= 0) /* try */
164 return TRUE; /* successful done. */
167 if (!make_parent_path(path))
170 #if defined __MINGW32__
171 if (mkdir(path) < 0) { /* try again */
172 error("Cannot make directory \"%s\"", path);
176 if (mkdir(path, 0777) < 0) { /* try again */
177 error("Cannot make directory \"%s\"", path);
185 /* ------------------------------------------------------------------------ */
187 open_with_make_path(name)
192 if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
193 if (!make_parent_path(name) ||
194 (fp = fopen(name, WRITE_BINARY)) == NULL)
195 error("Cannot extract a file \"%s\"", name);
200 /* ------------------------------------------------------------------------ */
202 symlink_with_make_path(realname, name)
203 const char *realname;
208 l_code = symlink(realname, name);
210 make_parent_path(name);
211 l_code = symlink(realname, name);
217 /* ------------------------------------------------------------------------ */
219 adjust_info(name, hdr)
223 struct utimbuf utimebuf;
225 /* adjust file stamp */
226 utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
228 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
229 utime(name, &utimebuf);
231 if (hdr->extend_type == EXTEND_UNIX
232 || hdr->extend_type == EXTEND_OS68K
233 || hdr->extend_type == EXTEND_XOSK) {
235 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK) {
236 chmod(name, hdr->unix_mode);
240 uid_t uid = hdr->unix_uid;
241 gid_t gid = hdr->unix_gid;
243 #if HAVE_GETPWNAM && HAVE_GETGRNAM
245 struct passwd *ent = getpwnam(hdr->user);
246 if (ent) uid = ent->pw_uid;
249 struct group *ent = getgrnam(hdr->group);
250 if (ent) gid = ent->gr_gid;
255 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
256 lchown(name, uid, gid);
258 #endif /* HAVE_LCHWON */
259 chown(name, uid, gid);
264 /* On Cygwin, execute permission should be set for .exe or .dll. */
267 umask(m = umask(0)); /* get current umask */
268 chmod(name, 0777 & ~m);
273 /* ------------------------------------------------------------------------ */
275 extract_one(afp, hdr)
276 FILE *afp; /* archive file */
279 FILE *fp; /* output file */
280 #if HAVE_LIBAPPLEFILE
281 FILE *tfp; /* temporary output file */
284 char name[FILENAME_LENGTH];
287 boolean save_quiet, save_verbose, up_flag;
288 char *q = hdr->name, c;
291 if (ignore_directory && strrchr(hdr->name, '/')) {
292 q = (char *) strrchr(hdr->name, '/') + 1;
295 if (is_directory_traversal(q)) {
296 error("Possible directory traversal hack attempt in %s", q);
301 while (*q == '/') { q++; }
304 * if OSK then strip device name
306 if (hdr->extend_type == EXTEND_OS68K
307 || hdr->extend_type == EXTEND_XOSK) {
310 while (c && c != '/');
312 q = "."; /* if device name only */
317 if (!make_name_with_pathcheck(name, sizeof(name), q)) {
318 error("Possible symlink traversal hack attempt in %s", q);
322 /* LZHDIRS_METHODを持つヘッダをチェックする */
323 /* 1999.4.30 t.okamoto */
324 for (method = 0;; method++) {
325 if (methods[method] == NULL) {
326 error("Unknown method \"%.*s\"; \"%s\" will be skipped ...",
327 5, hdr->method, name);
330 if (memcmp(hdr->method, methods[method], 5) == 0)
334 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
335 && method != LZHDIRS_METHOD_NUM) {
338 for (method = 0;; method++) {
339 if (methods[method] == NULL) {
340 error("Unknown method \"%.*s\"; \"%s\" will be skipped ...",
341 5, hdr->method, name);
344 if (memcmp(hdr->method, methods[method], 5) == 0)
349 reading_filename = archive_name;
350 writing_filename = name;
351 if (output_to_stdout || verify_mode) {
352 /* "Icon\r" should be a resource fork file encoded in MacBinary
353 format, so that it should be skipped. */
354 if (hdr->extend_type == EXTEND_MACOS
355 && strcmp(basename(name), "Icon\r") == 0
356 && decode_macbinary_contents) {
361 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
366 save_verbose = verbose;
367 if (!quiet && output_to_stdout) {
368 printf("::::::::\n%s\n::::::::\n", name);
372 else if (verify_mode) {
377 #if defined(__MINGW32__) || defined(__DJGPP__)
381 old_mode = setmode(fileno(stdout), O_BINARY);
384 #if HAVE_LIBAPPLEFILE
385 /* On default, MacLHA encodes into MacBinary. */
386 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
387 /* build temporary file */
388 tfp = NULL; /* avoid compiler warnings `uninitialized' */
389 tfp = build_temporary_file();
391 crc = decode_lzhuf(afp, tfp,
392 hdr->original_size, hdr->packed_size,
393 name, method, &read_size);
395 decode_macbinary(stdout, hdr->original_size, name);
396 unlink(temporary_name);
398 crc = decode_lzhuf(afp, stdout,
399 hdr->original_size, hdr->packed_size,
400 name, method, &read_size);
403 crc = decode_lzhuf(afp, stdout,
404 hdr->original_size, hdr->packed_size,
405 name, method, &read_size);
406 #endif /* HAVE_LIBAPPLEFILE */
407 #if defined(__MINGW32__) || defined(__DJGPP__)
409 setmode(fileno(stdout), old_mode);
413 verbose = save_verbose;
417 /* "Icon\r" should be a resource fork of parent folder's icon,
418 so that it can be skipped when system is not Mac OS X. */
419 if (hdr->extend_type == EXTEND_MACOS
420 && strcmp(basename(name), "Icon\r") == 0
421 && decode_macbinary_contents) {
422 make_parent_path(name); /* create directory only */
425 #endif /* __APPLE__ */
426 if (skip_flg == FALSE) {
427 up_flag = inquire_extract(name);
428 if (up_flag == FALSE && force == FALSE) {
433 if (skip_flg == TRUE) { /* if skip_flg */
434 if (stat(name, &stbuf) == 0 && force != TRUE) {
435 if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
437 printf("%s : Skipped...\n", name);
446 signal(SIGINT, interrupt);
448 signal(SIGHUP, interrupt);
452 remove_extracting_file_when_interrupt = TRUE;
454 if ((fp = open_with_make_path(name)) != NULL) {
455 #if HAVE_LIBAPPLEFILE
456 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
457 /* build temporary file */
458 tfp = NULL; /* avoid compiler warnings `uninitialized' */
459 tfp = build_temporary_file();
461 crc = decode_lzhuf(afp, tfp,
462 hdr->original_size, hdr->packed_size,
463 name, method, &read_size);
465 decode_macbinary(fp, hdr->original_size, name);
467 /* TODO: set resource fork */
468 /* after processing, "Icon\r" is not needed. */
469 if (strcmp(basename(name), "Icon\r") == 0) {
472 #endif /* __APPLE__ */
473 unlink(temporary_name);
475 crc = decode_lzhuf(afp, fp,
476 hdr->original_size, hdr->packed_size,
477 name, method, &read_size);
479 #else /* HAVE_LIBAPPLEFILE */
480 crc = decode_lzhuf(afp, fp,
481 hdr->original_size, hdr->packed_size,
482 name, method, &read_size);
483 #endif /* HAVE_LIBAPPLEFILE */
486 remove_extracting_file_when_interrupt = FALSE;
487 signal(SIGINT, SIG_DFL);
489 signal(SIGHUP, SIG_DFL);
495 if (hdr->has_crc && crc != hdr->crc)
496 error("CRC error: \"%s\"", name);
498 else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
499 || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
500 || method == LZHDIRS_METHOD_NUM) {
501 /* ↑これで、Symbolic Link は、大丈夫か? */
502 if (!ignore_directory && !verify_mode && !output_to_stdout) {
505 printf("EXTRACT %s (directory)\n", name);
508 /* NAME has trailing SLASH '/', (^_^) */
509 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
513 if (skip_flg == FALSE) {
514 up_flag = inquire_extract(name);
515 if (up_flag == FALSE && force == FALSE) {
519 if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
520 if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
522 printf("%s : Skipped...\n", name);
529 l_code = symlink_with_make_path(hdr->realname, name);
532 warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
533 name, hdr->realname);
536 message("Symbolic Link %s -> %s",
537 name, hdr->realname);
540 warning("Can't make Symbolic Link %s -> %s",
541 name, hdr->realname);
545 else { /* make directory */
546 if (!make_parent_path(name))
548 /* save directory information */
549 add_dirinfo(name, hdr);
554 if (force) /* force extract */
555 goto extract_regular;
557 error("Unknown file type: \"%s\". use `f' option to force extract.", name);
560 if (!output_to_stdout && !verify_mode) {
561 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_DIRECTORY)
562 adjust_info(name, hdr);
569 skip_to_nextpos(FILE *fp, off_t pos, off_t off, off_t read_size)
572 if (fseeko(fp, pos + off, SEEK_SET) != 0) {
577 off_t i = off - read_size;
579 if (fgetc(fp) == EOF) {
587 /* ------------------------------------------------------------------------ */
588 /* EXTRACT COMMAND MAIN */
589 /* ------------------------------------------------------------------------ */
598 /* open archive file */
599 if ((afp = open_old_archive()) == NULL)
600 fatal_error("Cannot open archive file \"%s\"", archive_name);
602 if (archive_is_msdos_sfx1(archive_name))
603 seek_lha_header(afp);
605 /* extract each files */
606 while (get_header(afp, &hdr)) {
608 if (need_file(hdr.name)) {
609 read_size = extract_one(afp, &hdr);
610 if (read_size != hdr.packed_size) {
611 /* when error occurred in extract_one(), should adjust
612 point of file stream */
613 if (skip_to_nextpos(afp, pos, hdr.packed_size, read_size) == -1) {
614 fatal_error("Cannot seek to next header position from \"%s\"", hdr.name);
618 if (skip_to_nextpos(afp, pos, hdr.packed_size, 0) == -1) {
619 fatal_error("Cannot seek to next header position from \"%s\"", hdr.name);
624 /* close archive file */
627 /* adjust directory information */
634 is_directory_traversal(char *path)
638 for (; *path; path++) {
641 if (*path == '.') state = 1;
645 if (*path == '.') state = 2;
646 else if (*path == '/') state = 0;
650 if (*path == '/') return 1;
654 if (*path == '/') state = 0;
663 * restore directory information (timestamp, permission and uid/gid).
664 * added by A.Iriyama 2003.12.12
667 typedef struct LzHeaderList_t {
668 struct LzHeaderList_t *next;
672 static LzHeaderList *dirinfo;
674 static void add_dirinfo(char *name, LzHeader *hdr)
676 LzHeaderList *p, *tmp, top;
678 if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
681 p = xmalloc(sizeof(LzHeaderList));
683 memcpy(&p->hdr, hdr, sizeof(LzHeader));
684 strncpy(p->hdr.name, name, sizeof(p->hdr.name));
685 p->hdr.name[sizeof(p->hdr.name)-1] = 0;
697 reverse sorted by pathname order
701 dirinfo->hdr.name = "a/b/d"
702 dirinfo->next->hdr.name = "a/b/c"
703 dirinfo->next->next->hdr.name = "a/b"
707 dirinfo->hdr.name = "a/b/d"
708 dirinfo->next->hdr.name = "a/b/c"
709 dirinfo->next->next->hdr.name = "a/b"
710 dirinfo->next->next->next->hdr.name = "a"
715 for (tmp = ⊤ tmp->next; tmp = tmp->next) {
716 if (strcmp(p->hdr.name, tmp->next->hdr.name) > 0) {
722 if (tmp->next == NULL) {
731 static void adjust_dirinfo()
734 /* message("adjusting [%s]", dirinfo->hdr.name); */
735 adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
738 LzHeaderList *tmp = dirinfo;
739 dirinfo = dirinfo->next;
745 #if HAVE_LIBAPPLEFILE
747 decode_macbinary(ofp, size, outPath)
752 af_file_t *afp = NULL;
754 unsigned char *datap;
757 if ((afp = af_open(temporary_name)) != NULL) {
759 datap = af_data(afp, &dlen);
760 fwrite(datap, sizeof(unsigned char), dlen, ofp);
763 } else { /* it may be not encoded in MacBinary */
765 if ((ifp = fopen(temporary_name, READ_BINARY)) == NULL) {
766 error("Cannot open a temporary file \"%s\"", temporary_name);
769 copyfile(ifp, ofp, size, 0, 0);
776 #endif /* HAVE_LIBAPPLEFILE */