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,
28 static void add_dirinfo(char* name, LzHeader* hdr);
29 static void adjust_dirinfo();
31 #ifdef HAVE_LIBAPPLEFILE
32 static boolean decode_macbinary(FILE *ofp, size_t size, const char *outPath);
35 /* ------------------------------------------------------------------------ */
43 if (stat(name, &stbuf) >= 0) {
44 if (!is_regularfile(&stbuf)) {
45 error("\"%s\" already exists (not a file)", name);
50 printf("EXTRACT %s but file is exist.\n", name);
55 warning("skip to extract %s.", name);
59 switch (inquire("OverWrite ?(Yes/[No]/All/Skip)", name, "YyNnAaSs\n")) {
80 printf("EXTRACT %s\n", name);
85 /* ------------------------------------------------------------------------ */
87 make_parent_path(name)
90 char path[FILENAME_LENGTH];
94 /* make parent directory name into PATH for recursive call */
95 str_safe_copy(path, name, sizeof(path));
96 for (p = path + strlen(path); p > path; p--)
103 message("invalid path name \"%s\"", name);
104 return FALSE; /* no more parent. */
107 if (GETSTAT(path, &stbuf) >= 0) {
108 if (is_directory(&stbuf))
113 message("Making directory \"%s\".", path);
115 #if defined __MINGW32__
116 if (mkdir(path) >= 0)
119 if (mkdir(path, 0777) >= 0) /* try */
120 return TRUE; /* successful done. */
123 if (!make_parent_path(path))
126 #if defined __MINGW32__
127 if (mkdir(path) < 0) { /* try again */
128 error("Cannot make directory \"%s\"", path);
132 if (mkdir(path, 0777) < 0) { /* try again */
133 error("Cannot make directory \"%s\"", path);
141 /* ------------------------------------------------------------------------ */
143 open_with_make_path(name)
148 if ((fp = fopen(name, WRITE_BINARY)) == NULL) {
149 if (!make_parent_path(name) ||
150 (fp = fopen(name, WRITE_BINARY)) == NULL)
151 error("Cannot extract a file \"%s\"", name);
156 /* ------------------------------------------------------------------------ */
158 adjust_info(name, hdr)
162 struct utimbuf utimebuf;
164 /* adjust file stamp */
165 utimebuf.actime = utimebuf.modtime = hdr->unix_last_modified_stamp;
167 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
168 utime(name, &utimebuf);
170 if (hdr->extend_type == EXTEND_UNIX
171 || hdr->extend_type == EXTEND_OS68K
172 || hdr->extend_type == EXTEND_XOSK) {
173 #ifdef NOT_COMPATIBLE_MODE
174 /* Please need your modification in this space. */
176 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
177 chmod(name, hdr->unix_mode);
178 #endif /* __DJGPP__ */
180 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) != UNIX_FILE_SYMLINK)
181 chmod(name, hdr->unix_mode);
184 uid_t uid = hdr->unix_uid;
185 gid_t gid = hdr->unix_gid;
187 #if HAVE_GETPWNAM && HAVE_GETGRNAM
189 struct passwd *ent = getpwnam(hdr->user);
190 if (ent) uid = ent->pw_uid;
193 struct group *ent = getgrnam(hdr->group);
194 if (ent) gid = ent->gr_gid;
199 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK)
200 lchown(name, uid, gid);
202 #endif /* HAVE_LCHWON */
203 chown(name, uid, gid);
208 /* On Cygwin, execute permission should be set for .exe or .dll. */
211 umask(m = umask(0)); /* get current umask */
212 chmod(name, 0777 & ~m);
217 /* ------------------------------------------------------------------------ */
219 extract_one(afp, hdr)
220 FILE *afp; /* archive file */
223 FILE *fp; /* output file */
224 #if HAVE_LIBAPPLEFILE
225 FILE *tfp; /* temporary output file */
228 char name[FILENAME_LENGTH];
231 boolean save_quiet, save_verbose, up_flag;
232 char *q = hdr->name, c;
233 size_t read_size = 0;
235 if (ignore_directory && strrchr(hdr->name, '/')) {
236 q = (char *) strrchr(hdr->name, '/') + 1;
239 if (is_directory_traversal(q)) {
240 fprintf(stderr, "Possible directory traversal hack attempt in %s\n", q);
245 while (*q == '/') { q++; }
248 * if OSK then strip device name
250 if (hdr->extend_type == EXTEND_OS68K
251 || hdr->extend_type == EXTEND_XOSK) {
254 while (c && c != '/');
256 q = "."; /* if device name only */
261 if (extract_directory)
262 xsnprintf(name, sizeof(name), "%s/%s", extract_directory, q);
264 str_safe_copy(name, q, sizeof(name));
266 /* LZHDIRS_METHOD¤ò»ý¤Ä¥Ø¥Ã¥À¤ò¥Á¥§¥Ã¥¯¤¹¤ë */
267 /* 1999.4.30 t.okamoto */
268 for (method = 0;; method++) {
269 if (methods[method] == NULL) {
270 error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
271 5, hdr->method, name);
274 if (memcmp(hdr->method, methods[method], 5) == 0)
278 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_REGULAR
279 && method != LZHDIRS_METHOD_NUM) {
282 for (method = 0;; method++) {
283 if (methods[method] == NULL) {
284 error("Unknown method \"%.*s\"; \"%s\" will be skiped ...",
285 5, hdr->method, name);
288 if (memcmp(hdr->method, methods[method], 5) == 0)
293 reading_filename = archive_name;
294 writing_filename = name;
295 if (output_to_stdout || verify_mode) {
296 /* "Icon\r" should be a resource fork file encoded in MacBinary
297 format, so that it should be skipped. */
298 if (hdr->extend_type == EXTEND_MACOS
299 && strcmp(basename(name), "Icon\r") == 0
300 && decode_macbinary_contents) {
305 printf("%s %s\n", verify_mode ? "VERIFY" : "EXTRACT", name);
310 save_verbose = verbose;
311 if (!quiet && output_to_stdout) {
312 printf("::::::::\n%s\n::::::::\n", name);
316 else if (verify_mode) {
321 #if defined(__MINGW32__) || defined(__DJGPP__)
325 old_mode = setmode(fileno(stdout), O_BINARY);
328 #if HAVE_LIBAPPLEFILE
329 /* On default, MacLHA encodes into MacBinary. */
330 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
331 /* build temporary file */
332 tfp = NULL; /* avoid compiler warnings `uninitialized' */
333 tfp = build_temporary_file();
335 crc = decode_lzhuf(afp, tfp,
336 hdr->original_size, hdr->packed_size,
337 name, method, &read_size);
339 decode_macbinary(stdout, hdr->original_size, name);
340 unlink(temporary_name);
342 crc = decode_lzhuf(afp, stdout,
343 hdr->original_size, hdr->packed_size,
344 name, method, &read_size);
347 crc = decode_lzhuf(afp, stdout,
348 hdr->original_size, hdr->packed_size,
349 name, method, &read_size);
350 #endif /* HAVE_LIBAPPLEFILE */
351 #if defined(__MINGW32__) || defined(__DJGPP__)
353 setmode(fileno(stdout), old_mode);
357 verbose = save_verbose;
361 /* "Icon\r" should be a resource fork of parent folder's icon,
362 so that it can be skipped when system is not Mac OS X. */
363 if (hdr->extend_type == EXTEND_MACOS
364 && strcmp(basename(name), "Icon\r") == 0
365 && decode_macbinary_contents) {
366 make_parent_path(name); /* create directory only */
369 #endif /* __APPLE__ */
370 if (skip_flg == FALSE) {
371 up_flag = inquire_extract(name);
372 if (up_flag == FALSE && force == FALSE) {
377 if (skip_flg == TRUE) { /* if skip_flg */
378 if (stat(name, &stbuf) == 0 && force != TRUE) {
379 if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
381 printf("%s : Skipped...\n", name);
390 signal(SIGINT, interrupt);
392 signal(SIGHUP, interrupt);
396 remove_extracting_file_when_interrupt = TRUE;
398 if ((fp = open_with_make_path(name)) != NULL) {
399 #if HAVE_LIBAPPLEFILE
400 if (hdr->extend_type == EXTEND_MACOS && !verify_mode && decode_macbinary_contents) {
401 /* build temporary file */
402 tfp = NULL; /* avoid compiler warnings `uninitialized' */
403 tfp = build_temporary_file();
405 crc = decode_lzhuf(afp, tfp,
406 hdr->original_size, hdr->packed_size,
407 name, method, &read_size);
409 decode_macbinary(fp, hdr->original_size, name);
411 /* TODO: set resource fork */
412 /* after processing, "Icon\r" is not needed. */
413 if (strcmp(basename(name), "Icon\r") == 0) {
416 #endif /* __APPLE__ */
417 unlink(temporary_name);
419 crc = decode_lzhuf(afp, fp,
420 hdr->original_size, hdr->packed_size,
421 name, method, &read_size);
423 #else /* HAVE_LIBAPPLEFILE */
424 crc = decode_lzhuf(afp, fp,
425 hdr->original_size, hdr->packed_size,
426 name, method, &read_size);
427 #endif /* HAVE_LIBAPPLEFILE */
430 remove_extracting_file_when_interrupt = FALSE;
431 signal(SIGINT, SIG_DFL);
433 signal(SIGHUP, SIG_DFL);
439 if (hdr->has_crc && crc != hdr->crc)
440 error("CRC error: \"%s\"", name);
442 else if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_DIRECTORY
443 || (hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK
444 || method == LZHDIRS_METHOD_NUM) {
445 /* ¢¬¤³¤ì¤Ç¡¢Symbolic Link ¤Ï¡¢Âç¾æÉפ«¡© */
446 if (!ignore_directory && !verify_mode) {
449 printf("EXTRACT %s (directory)\n", name);
452 /* NAME has trailing SLASH '/', (^_^) */
453 if ((hdr->unix_mode & UNIX_FILE_TYPEMASK) == UNIX_FILE_SYMLINK) {
457 if (skip_flg == FALSE) {
458 up_flag = inquire_extract(name);
459 if (up_flag == FALSE && force == FALSE) {
463 if (GETSTAT(name, &stbuf) == 0 && force != TRUE) {
464 if (stbuf.st_mtime >= hdr->unix_last_modified_stamp) {
466 printf("%s : Skipped...\n", name);
473 make_parent_path(name);
474 l_code = symlink(hdr->realname, name);
477 warning("Can't make Symbolic Link \"%s\" -> \"%s\"",
478 name, hdr->realname);
481 message("Symbolic Link %s -> %s",
482 name, hdr->realname);
485 warning("Can't make Symbolic Link %s -> %s",
486 name, hdr->realname);
490 else { /* make directory */
491 if (!output_to_stdout && !make_parent_path(name))
493 /* save directory information */
494 add_dirinfo(name, hdr);
499 if (force) /* force extract */
500 goto extract_regular;
502 error("Unknown file type: \"%s\". use `f' option to force extract.", name);
505 if (!output_to_stdout)
506 adjust_info(name, hdr);
511 /* ------------------------------------------------------------------------ */
512 /* EXTRACT COMMAND MAIN */
513 /* ------------------------------------------------------------------------ */
522 /* open archive file */
523 if ((afp = open_old_archive()) == NULL)
524 fatal_error("Cannot open archive file \"%s\"", archive_name);
526 if (archive_is_msdos_sfx1(archive_name))
527 seek_lha_header(afp);
529 /* extract each files */
530 while (get_header(afp, &hdr)) {
531 if (need_file(hdr.name)) {
533 read_size = extract_one(afp, &hdr);
534 if (read_size != hdr.packed_size) {
535 /* when error occurred in extract_one(), should adjust
536 point of file stream */
537 if (pos != -1 && afp != stdin)
538 fseeko(afp, pos + hdr.packed_size - read_size, SEEK_SET);
540 size_t i = hdr.packed_size - read_size;
541 while (i--) fgetc(afp);
546 fseeko(afp, hdr.packed_size, SEEK_CUR);
548 size_t i = hdr.packed_size;
549 while (i--) fgetc(afp);
554 /* close archive file */
557 /* adjust directory information */
564 is_directory_traversal(char *path)
568 for (; *path; path++) {
571 if (*path == '.') state = 1;
575 if (*path == '.') state = 2;
576 else if (*path == '/') state = 0;
580 if (*path == '/') return 1;
584 if (*path == '/') state = 0;
593 * restore directory information (time stamp).
594 * added by A.Iriyama 2003.12.12
597 typedef struct lhdDirectoryInfo_t {
598 struct lhdDirectoryInfo_t *next;
602 static LzHeaderList *dirinfo;
604 static void add_dirinfo(char *name, LzHeader *hdr)
608 if (memcmp(hdr->method, LZHDIRS_METHOD, 5) != 0)
611 p = xmalloc(sizeof(LzHeaderList));
613 memcpy(&p->hdr, hdr, sizeof(LzHeader));
614 strncpy(p->hdr.name, name, sizeof(p->hdr.name));
615 p->hdr.name[sizeof(p->hdr.name)-1] = 0;
618 LzHeaderList *tmp = dirinfo;
624 static void adjust_dirinfo()
627 adjust_info(dirinfo->hdr.name, &dirinfo->hdr);
630 LzHeaderList *tmp = dirinfo;
631 dirinfo = dirinfo->next;
637 #if HAVE_LIBAPPLEFILE
639 decode_macbinary(ofp, size, outPath)
644 af_file_t *afp = NULL;
646 unsigned char *datap;
649 if ((afp = af_open(temporary_name)) != NULL) {
651 datap = af_data(afp, &dlen);
652 fwrite(datap, sizeof(unsigned char), dlen, ofp);
655 } else { /* it may be not encoded in MacBinary */
657 if ((ifp = fopen(temporary_name, READ_BINARY)) == NULL) {
658 error("Cannot open a temporary file \"%s\"", temporary_name);
661 copyfile(ifp, ofp, size, 0, 0);
668 #endif /* HAVE_LIBAPPLEFILE */