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, size_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);
86 /* ------------------------------------------------------------------------ */
88 make_parent_path(name)
91 char path[FILENAME_LENGTH];
95 /* make parent directory name into PATH for recursive call */
96 str_safe_copy(path, name, sizeof(path));
97 for (p = path + strlen(path); p > path; p--)
104 message("invalid path name \"%s\"", name);
105 return FALSE; /* no more parent. */
108 if (GETSTAT(path, &stbuf) >= 0) {
109 if (is_directory(&stbuf))
114 message("Making directory \"%s\".", path);
116 #if defined __MINGW32__
117 if (mkdir(path) >= 0)
120 if (mkdir(path, 0777) >= 0) /* try */
121 return TRUE; /* successful done. */
124 if (!make_parent_path(path))
127 #if defined __MINGW32__
128 if (mkdir(path) < 0) { /* try again */
129 error("Cannot make directory \"%s\"", path);
133 if (mkdir(path, 0777) < 0) { /* try again */
134 error("Cannot make directory \"%s\"", path);
142 /* ------------------------------------------------------------------------ */
144 open_with_make_path(name)
149 if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
150 if (!make_parent_path(name) ||
151 (fp = fopen(name, WRITE_BINARY)) == NULL)
152 error("Cannot extract a file \"%s\"", name);
157 /* ------------------------------------------------------------------------ */
159 adjust_info(name, hdr)
163 struct utimbuf utimebuf;
165 /* adjust file stamp */
166 utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
168 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
169 utime(name, &utimebuf);
171 if (hdr->extend_type == EXTEND_UNIX
172 || hdr->extend_type == EXTEND_OS68K
173 || hdr->extend_type == EXTEND_XOSK) {
175 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK) {
176 chmod(name, hdr->unix_mode);
180 uid_t uid = hdr->unix_uid;
181 gid_t gid = hdr->unix_gid;
183 #if HAVE_GETPWNAM && HAVE_GETGRNAM
185 struct passwd *ent = getpwnam(hdr->user);
186 if (ent) uid = ent->pw_uid;
189 struct group *ent = getgrnam(hdr->group);
190 if (ent) gid = ent->gr_gid;
195 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
196 lchown(name, uid, gid);
198 #endif /* HAVE_LCHWON */
199 chown(name, uid, gid);
204 /* On Cygwin, execute permission should be set for .exe or .dll. */
207 umask(m = umask(0)); /* get current umask */
208 chmod(name, 0777 & ~m);
213 /* ------------------------------------------------------------------------ */
215 extract_one(afp, hdr)
216 FILE *afp; /* archive file */
219 FILE *fp; /* output file */
220 #if HAVE_LIBAPPLEFILE
221 FILE *tfp; /* temporary output file */
224 char name[FILENAME_LENGTH];
227 boolean save_quiet, save_verbose, up_flag;
228 char *q = hdr->name, c;
231 if (ignore_directory && strrchr(hdr->name, '/')) {
232 q = (char *) strrchr(hdr->name, '/') + 1;
235 if (is_directory_traversal(q)) {
236 fprintf(stderr, "Possible directory traversal hack attempt in %s\n", q);
241 while (*q == '/') { q++; }
244 * if OSK then strip device name
246 if (hdr->extend_type == EXTEND_OS68K
247 || hdr->extend_type == EXTEND_XOSK) {
250 while (c && c != '/');
252 q = "."; /* if device name only */
257 if (extract_directory)
258 xsnprintf(name, sizeof(name), "%s/%s", extract_directory, q);
260 str_safe_copy(name, q, sizeof(name));
262 /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
263 /* 1999.4.30 t.okamoto */
264 for (method = 0;; method++) {
265 if (methods[method] == NULL) {
266 error("Unknown method \"%.*s\"; \"%s\" will be skipped ...",
267 5, hdr->method, name);
270 if (memcmp(hdr->method, methods[method], 5) == 0)
274 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
275 && method != LZHDIRS_METHOD_NUM) {
278 for (method = 0;; method++) {
279 if (methods[method] == NULL) {
280 error("Unknown method \"%.*s\"; \"%s\" will be skipped ...",
281 5, hdr->method, name);
284 if (memcmp(hdr->method, methods[method], 5) == 0)
289 reading_filename = archive_name;
290 writing_filename = name;
291 if (output_to_stdout || verify_mode) {
292 /* "Icon\r" should be a resource fork file encoded in MacBinary
293 format, so that it should be skipped. */
294 if (hdr->extend_type == EXTEND_MACOS
295 && strcmp(basename(name), "Icon\r") == 0
296 && decode_macbinary_contents) {
301 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
306 save_verbose = verbose;
307 if (!quiet && output_to_stdout) {
308 printf("::::::::\n%s\n::::::::\n", name);
312 else if (verify_mode) {
317 #if defined(__MINGW32__) || defined(__DJGPP__)
321 old_mode = setmode(fileno(stdout), O_BINARY);
324 #if HAVE_LIBAPPLEFILE
325 /* On default, MacLHA encodes into MacBinary. */
326 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
327 /* build temporary file */
328 tfp = NULL; /* avoid compiler warnings `uninitialized' */
329 tfp = build_temporary_file();
331 crc = decode_lzhuf(afp, tfp,
332 hdr->original_size, hdr->packed_size,
333 name, method, &read_size);
335 decode_macbinary(stdout, hdr->original_size, name);
336 unlink(temporary_name);
338 crc = decode_lzhuf(afp, stdout,
339 hdr->original_size, hdr->packed_size,
340 name, method, &read_size);
343 crc = decode_lzhuf(afp, stdout,
344 hdr->original_size, hdr->packed_size,
345 name, method, &read_size);
346 #endif /* HAVE_LIBAPPLEFILE */
347 #if defined(__MINGW32__) || defined(__DJGPP__)
349 setmode(fileno(stdout), old_mode);
353 verbose = save_verbose;
357 /* "Icon\r" should be a resource fork of parent folder's icon,
358 so that it can be skipped when system is not Mac OS X. */
359 if (hdr->extend_type == EXTEND_MACOS
360 && strcmp(basename(name), "Icon\r") == 0
361 && decode_macbinary_contents) {
362 make_parent_path(name); /* create directory only */
365 #endif /* __APPLE__ */
366 if (skip_flg == FALSE) {
367 up_flag = inquire_extract(name);
368 if (up_flag == FALSE && force == FALSE) {
373 if (skip_flg == TRUE) { /* if skip_flg */
374 if (stat(name, &stbuf) == 0 && force != TRUE) {
375 if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
377 printf("%s : Skipped...\n", name);
386 signal(SIGINT, interrupt);
388 signal(SIGHUP, interrupt);
392 remove_extracting_file_when_interrupt = TRUE;
394 if ((fp = open_with_make_path(name)) != NULL) {
395 #if HAVE_LIBAPPLEFILE
396 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
397 /* build temporary file */
398 tfp = NULL; /* avoid compiler warnings `uninitialized' */
399 tfp = build_temporary_file();
401 crc = decode_lzhuf(afp, tfp,
402 hdr->original_size, hdr->packed_size,
403 name, method, &read_size);
405 decode_macbinary(fp, hdr->original_size, name);
407 /* TODO: set resource fork */
408 /* after processing, "Icon\r" is not needed. */
409 if (strcmp(basename(name), "Icon\r") == 0) {
412 #endif /* __APPLE__ */
413 unlink(temporary_name);
415 crc = decode_lzhuf(afp, fp,
416 hdr->original_size, hdr->packed_size,
417 name, method, &read_size);
419 #else /* HAVE_LIBAPPLEFILE */
420 crc = decode_lzhuf(afp, fp,
421 hdr->original_size, hdr->packed_size,
422 name, method, &read_size);
423 #endif /* HAVE_LIBAPPLEFILE */
426 remove_extracting_file_when_interrupt = FALSE;
427 signal(SIGINT, SIG_DFL);
429 signal(SIGHUP, SIG_DFL);
435 if (hdr->has_crc && crc != hdr->crc)
436 error("CRC error: \"%s\"", name);
438 else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
439 || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
440 || method == LZHDIRS_METHOD_NUM) {
441 /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */
442 if (!ignore_directory && !verify_mode) {
445 printf("EXTRACT %s (directory)\n", name);
448 /* NAME has trailing SLASH '/', (^_^) */
449 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
453 if (skip_flg == FALSE) {
454 up_flag = inquire_extract(name);
455 if (up_flag == FALSE && force == FALSE) {
459 if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
460 if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
462 printf("%s : Skipped...\n", name);
469 make_parent_path(name);
470 l_code = symlink(hdr->realname, name);
473 warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
474 name, hdr->realname);
477 message("Symbolic Link %s -> %s",
478 name, hdr->realname);
481 warning("Can't make Symbolic Link %s -> %s",
482 name, hdr->realname);
486 else { /* make directory */
487 if (!output_to_stdout && !make_parent_path(name))
489 /* save directory information */
490 add_dirinfo(name, hdr);
495 if (force) /* force extract */
496 goto extract_regular;
498 error("Unknown file type: \"%s\". use `f' option to force extract.", name);
501 if (!output_to_stdout && !verify_mode) {
502 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_DIRECTORY)
503 adjust_info(name, hdr);
509 /* ------------------------------------------------------------------------ */
510 /* EXTRACT COMMAND MAIN */
511 /* ------------------------------------------------------------------------ */
520 /* open archive file */
521 if ((afp = open_old_archive()) == NULL)
522 fatal_error("Cannot open archive file \"%s\"", archive_name);
524 if (archive_is_msdos_sfx1(archive_name))
525 seek_lha_header(afp);
527 /* extract each files */
528 while (get_header(afp, &hdr)) {
529 if (need_file(hdr.name)) {
531 read_size = extract_one(afp, &hdr);
532 if (read_size != hdr.packed_size) {
533 /* when error occurred in extract_one(), should adjust
534 point of file stream */
535 if (pos != -1 && afp != stdin)
536 fseeko(afp, pos + hdr.packed_size, SEEK_SET);
538 off_t i = hdr.packed_size - read_size;
539 while (i--) fgetc(afp);
544 fseeko(afp, hdr.packed_size, SEEK_CUR);
546 off_t i = hdr.packed_size;
547 while (i--) fgetc(afp);
552 /* close archive file */
555 /* adjust directory information */
562 is_directory_traversal(char *path)
566 for (; *path; path++) {
569 if (*path == '.') state = 1;
573 if (*path == '.') state = 2;
574 else if (*path == '/') state = 0;
578 if (*path == '/') return 1;
582 if (*path == '/') state = 0;
591 * restore directory information (timestamp, permission and uid/gid).
592 * added by A.Iriyama 2003.12.12
595 typedef struct LzHeaderList_t {
596 struct LzHeaderList_t *next;
600 static LzHeaderList *dirinfo;
602 static void add_dirinfo(char *name, LzHeader *hdr)
604 LzHeaderList *p, *tmp, top;
606 if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
609 p = xmalloc(sizeof(LzHeaderList));
611 memcpy(&p->hdr, hdr, sizeof(LzHeader));
612 strncpy(p->hdr.name, name, sizeof(p->hdr.name));
613 p->hdr.name[sizeof(p->hdr.name)-1] = 0;
625 reverse sorted by pathname order
629 dirinfo->hdr.name = "a/b/d"
630 dirinfo->next->hdr.name = "a/b/c"
631 dirinfo->next->next->hdr.name = "a/b"
635 dirinfo->hdr.name = "a/b/d"
636 dirinfo->next->hdr.name = "a/b/c"
637 dirinfo->next->next->hdr.name = "a/b"
638 dirinfo->next->next->next->hdr.name = "a"
643 for (tmp = ⊤ tmp->next; tmp = tmp->next) {
644 if (strcmp(p->hdr.name, tmp->next->hdr.name) > 0) {
650 if (tmp->next == NULL) {
659 static void adjust_dirinfo()
662 /* message("adjusting [%s]", dirinfo->hdr.name); */
663 adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
666 LzHeaderList *tmp = dirinfo;
667 dirinfo = dirinfo->next;
673 #if HAVE_LIBAPPLEFILE
675 decode_macbinary(ofp, size, outPath)
680 af_file_t *afp = NULL;
682 unsigned char *datap;
685 if ((afp = af_open(temporary_name)) != NULL) {
687 datap = af_data(afp, &dlen);
688 fwrite(datap, sizeof(unsigned char), dlen, ofp);
691 } else { /* it may be not encoded in MacBinary */
693 if ((ifp = fopen(temporary_name, READ_BINARY)) == NULL) {
694 error("Cannot open a temporary file \"%s\"", temporary_name);
697 copyfile(ifp, ofp, size, 0, 0);
704 #endif /* HAVE_LIBAPPLEFILE */