OSDN Git Service

Fix path check
[lha/lha.git] / src / lharc.c
index 48a15c8..64e60db 100644 (file)
@@ -64,7 +64,7 @@ init_variable()     /* Added N.Watazaki */
     verbose         = 0;
     noexec          = FALSE;    /* debugging option */
     force           = FALSE;
-    prof            = FALSE;
+    timestamp_archive = FALSE;
 
     compress_method = DEFAULT_LZHUFF_METHOD; /* defined in config.h */
 
@@ -87,7 +87,6 @@ init_variable()     /* Added N.Watazaki */
     delete_after_append = FALSE;
     generic_format      = FALSE;
 
-    remove_temporary_at_error               = FALSE;
     recover_archive_when_interrupt          = FALSE;
     remove_extracting_file_when_interrupt   = FALSE;
     get_filename_from_stdin                 = FALSE;
@@ -95,7 +94,7 @@ init_variable()     /* Added N.Watazaki */
     exclude_files                           = NULL;
     verify_mode                             = FALSE;
 
-    noconvertcase                           = FALSE;
+    convertcase                             = FALSE;
 
     extract_directory = NULL;
     temporary_fd = -1;
@@ -105,6 +104,12 @@ init_variable()     /* Added N.Watazaki */
 #else
     backup_old_archive = FALSE;
 #endif
+
+    extract_broken_archive = FALSE;
+    decode_macbinary_contents = FALSE;
+    sort_contents = TRUE;
+    recursive_archiving = TRUE;
+    dump_lzss = FALSE;
 }
 
 /* ------------------------------------------------------------------------ */
@@ -118,19 +123,49 @@ init_variable()     /* Added N.Watazaki */
 static void
 print_tiny_usage()
 {
-    fprintf(stderr, "\
+    fprintf(stdout, "\
+usage: lha [-]<commands>[<options>] [-<options> ...] archive_file [file...]\n\
+  commands:  [axelvudmcpt]\n\
+  options:   [q[012]vnfto[567]dizg012%s%s[w=<dir>|x=<pattern>]]\n\
+  long options: --system-kanji-code={euc,sjis,utf8,cap}\n\
+                --archive-kanji-code={euc,sjis,utf8,cap}\n\
+                --extract-broken-archive\n\
+                --convert-filename-case\n\
+                --ignore-mac-files\n\
+                --timestamp-archive\n\
+                --traditional\n\
+                --help\n\
+                --version\n"
+#ifdef EUC
+            ,"e"
+#else
+            ,""
+#endif
+#if HAVE_LIBAPPLEFILE
+            ,"b"                 /* decode_macbinary_contents */
+#else
+            ,""
+#endif
+            );
+}
+
+static void
+print_usage()
+{
+    fprintf(stdout, "\
 LHarc    for UNIX  V 1.02  Copyright(C) 1989  Y.Tagawa\n\
 LHx      for MSDOS V C2.01 Copyright(C) 1990  H.Yoshizaki\n\
 LHx(arc) for OSK   V 2.01  Modified     1990  Momozou\n\
 LHa      for UNIX  V 1.00  Copyright(C) 1992  Masaru Oki\n\
 LHa      for UNIX  V 1.14  Modified     1995  Nobutaka Watazaki\n\
 LHa      for UNIX  V 1.14i Modified     2000  Tsugio Okamoto\n\
-                   Autoconfiscated 2001-2003  Koji Arai\n\
+LHA-PMA  for UNIX  V 2     PMA added    2000  Maarten ter Huurne\n\
+                   Autoconfiscated 2001-2008  Koji Arai\n\
 ");
-    fprintf(stderr, "\
-usage: lha [-]<commands>[<options>] [-<options> ...] archive_file [file...]\n\
-  commands:  [axelvudmcpt]\n\
-  options:   [q[012]vnfto[567]dizg012e[w=<dir>|x=<pattern>]]\n\
+
+    print_tiny_usage();
+
+    fprintf(stdout, "\
 commands:                           options:\n\
  a   Add(or replace) to archive      q{num} quiet (num:quiet mode)\n\
  x,e EXtract from archive            v  verbose\n\
@@ -138,76 +173,362 @@ commands:                           options:\n\
  u   Update newer files to archive   f  force (over write at extract)\n\
  d   Delete from archive             t  FILES are TEXT file\n");
 #ifdef SUPPORT_LH7
-    fprintf(stderr, "\
+    fprintf(stdout, "\
  m   Move to archive (means 'ad')    o[567] compression method (a/u/c)\n\
 ");
 #endif
 #ifndef SUPPORT_LH7
-    fprintf(stderr, "\
+    fprintf(stdout, "\
  m   Move to archive (means 'ad')    o  use LHarc compatible method (a/u/c)\n\
 ");
 #endif
-    fprintf(stderr, "\
+    fprintf(stdout, "\
  c   re-Construct new archive        d  delete FILES after (a/u/c)\n\
  p   Print to STDOUT from archive    i  ignore directory path (x/e)\n\
  t   Test file CRC in archive        z  files not compress (a/u/c)\n\
                                      g  Generic format (for compatibility)\n\
-                                        or not convert case when extracting\n\
                                      0/1/2 header level (a/u/c)\n\
 ");
 #ifdef EUC
-    fprintf(stderr, "\
+    fprintf(stdout, "\
                                      e  TEXT code convert from/to EUC\n\
 ");
 #endif
-    fprintf(stderr, "\
+#if HAVE_LIBAPPLEFILE
+    fprintf(stdout, "\
+                                     b  decode MacBinary (x/e)\n\
+");
+#endif
+    fprintf(stdout, "\
                                      w=<dir> specify extract directory (x/e)\n\
                                      x=<pattern>  eXclude files (a/u/c)\n\
 ");
-#if IGNORE_DOT_FILES            /* experimental feature */
-    fprintf(stderr, "\
-                                     X ignore dot files (a/u/c)\n\
-");
-#endif
 }
 
-static void
-parse_option(int argc, char **argv)
+#include "getopt_long.h"
+
+/*
+  Parse LHA options
+*/
+static int
+parse_suboption(int argc, char **argv)
 {
-    char *opt;
+    enum {
+        HELP_OPTION = 256,
+        VERSION_OPTION,
+        SYSTEM_KANJI_CODE_OPTION,
+        ARCHIVE_KANJI_CODE_OPTION,
+        TRADITIONAL_BEHAVIOR,
+        IGNORE_MAC_FILES,
+        DEBUG_OPTION,
+    };
+
+    struct option long_options[] = {
+        /* These options set a flag. */
+        {"help",    no_argument,       0, HELP_OPTION},
+        {"version", no_argument,       0, VERSION_OPTION},
+
+        {"system-kanji-code", required_argument, 0, SYSTEM_KANJI_CODE_OPTION},
+        {"archive-kanji-code", required_argument, 0, ARCHIVE_KANJI_CODE_OPTION},
+        {"extract-broken-archive", no_argument, &extract_broken_archive, 1},
+        {"convert-filename-case", no_argument, &convertcase, TRUE},
+        {"traditional", no_argument, 0, TRADITIONAL_BEHAVIOR},
+        {"ignore-mac-files", no_argument, 0, IGNORE_MAC_FILES},
+        {"timestamp-archive", no_argument, &timestamp_archive, 1},
+        {"debug", required_argument, 0, DEBUG_OPTION},
+        {0, 0, 0, 0}
+    };
     int i;
 
-    argv++; argc--;             /* exclude command name */
+    char short_options[256] = "q[012]vnfto[567]dizg012ew:x:";
+    /* "[...]" means optional 1 byte argument (original extention) */
 
-    if (argc < 1) {
-        print_tiny_usage();
-        exit(0);
+#if HAVE_LIBAPPLEFILE
+    strncat(short_options, "b", sizeof(short_options)-strlen(short_options)-1);
+#endif
+
+    /* parse option */
+    while (1) {
+        int option_index = 0;
+        int c = getopt_long(argc, argv,
+                            short_options, long_options,
+                            &option_index);
+
+        if (c == -1) break;     /* end of options */
+
+        switch (c) {
+        case 0:
+            /* Already set a flag variable by the definition of the
+               long_options. */
+            break;
+        case '?':
+            /* Invalid option */
+            print_tiny_usage();
+            exit(2);
+        case HELP_OPTION:
+            print_usage();
+            exit(0);
+        case VERSION_OPTION:
+            print_version();
+            exit(0);
+        case 'q':
+            if (!optarg) {
+                /* In quiet mode, no confirm to overwrite */
+                force = TRUE;
+                quiet = TRUE;
+                break;
+            }
+
+            switch (*optarg) {
+            case '0':           /* no quiet */
+            case '1':           /* no use the incremental indicator */
+                quiet_mode = *optarg - '0';
+                break;
+            case '2':           /* no output */
+                /* fall through */
+            default:
+                force = TRUE;
+                quiet = TRUE;
+                break;
+            }
+            break;
+        case 'f':
+            force = TRUE;
+            break;
+        case 'v':
+            verbose++;
+            break;
+        case 't':
+            text_mode = TRUE;
+            break;
+#ifdef EUC
+        case 'e':
+            text_mode = TRUE;
+            euc_mode = TRUE;
+            break;
+#endif
+#if HAVE_LIBAPPLEFILE
+        case 'b':
+            decode_macbinary_contents = TRUE;
+            break;
+#endif
+        case 'n':
+            noexec = TRUE;
+            break;
+        case 'g':
+            generic_format = TRUE;
+            header_level = 0;
+            break;
+        case 'd':
+            delete_after_append = TRUE;
+            break;
+        case 'o':
+            if (!optarg) {
+                compress_method = LZHUFF1_METHOD_NUM;
+                header_level = 0;
+                break;
+            }
+            switch (*optarg) {
+            case '5':
+                compress_method = LZHUFF5_METHOD_NUM;
+                break;
+#ifdef SUPPORT_LH7
+            case '6':
+                compress_method = LZHUFF6_METHOD_NUM;
+                break;
+            case '7':
+                compress_method = LZHUFF7_METHOD_NUM;
+                break;
+#endif
+            default:
+                error("invalid compression method 'o%c'", *optarg);
+                return -1;
+            }
+            break;
+        case 'z':
+            compress_method = LZHUFF0_METHOD_NUM;   /* Changed N.Watazaki */
+            break;
+        case 'i':
+            ignore_directory = TRUE;
+            break;
+        case 'x':
+            if (!optarg) {
+                error("exclude files does not specified for `-x'");
+                exit(2);
+            }
+
+            for (i = 0; exclude_files && exclude_files[i]; i++)
+                ;
+            exclude_files = (char**)xrealloc(exclude_files,
+                                             sizeof(char*) * (i+2));
+
+            if (*optarg == '=')
+                optarg++;
+            exclude_files[i] = optarg;
+            exclude_files[i+1] = 0;
+
+            break;
+        case 'w':
+            if (!optarg) {
+                error("working directory does not specified for `-w'");
+                exit(2);
+            }
+            if (*optarg == '=')
+                optarg++;
+
+            extract_directory = optarg;
+            break;
+        case '0':
+            header_level = 0;
+            break;
+        case '1':
+            header_level = 1;
+            break;
+        case '2':
+            header_level = 2;
+            break;
+        case SYSTEM_KANJI_CODE_OPTION:
+            if (!optarg) {
+                error("kanji code not specified for --%s",
+                      long_options[option_index].name);
+                return -1;
+            }
+            if (strcmp(optarg, "euc") == 0) {
+                optional_system_kanji_code = CODE_EUC;
+            }
+            else if (strcmp(optarg, "sjis") == 0) {
+                optional_system_kanji_code = CODE_SJIS;
+            }
+            else if (strcmp(optarg, "utf8") == 0) {
+                optional_system_kanji_code = CODE_UTF8;
+            }
+            else if (strcmp(optarg, "cap") == 0) {
+                optional_system_kanji_code = CODE_CAP;
+            }
+            else {
+                error("unknown kanji code \"%s\"", optarg);
+                return -1;
+            }
+            break;
+
+        case ARCHIVE_KANJI_CODE_OPTION:
+            if (!optarg) {
+                error("kanji code not specified for --%s",
+                      long_options[option_index].name);
+                return -1;
+            }
+            if (strcmp(optarg, "euc") == 0) {
+                optional_archive_kanji_code = CODE_EUC;
+            }
+            else if (strcmp(optarg, "sjis") == 0) {
+                optional_archive_kanji_code = CODE_SJIS;
+            }
+            else if (strcmp(optarg, "utf8") == 0) {
+                optional_archive_kanji_code = CODE_UTF8;
+            }
+            else if (strcmp(optarg, "cap") == 0) {
+                optional_archive_kanji_code = CODE_CAP;
+            }
+            else {
+                error("unknown kanji code \"%s\"", optarg);
+                return -1;
+            }
+            break;
+
+        case TRADITIONAL_BEHAVIOR:
+            convertcase = TRUE;
+            break;
+
+        case IGNORE_MAC_FILES:
+            /* ignore Mac specific files (._*, .DS_Store and Icon\r)
+               when archiving */
+            for (i = 0; exclude_files && exclude_files[i]; i++)
+                ;
+            exclude_files = (char**)xrealloc(exclude_files,
+                                             sizeof(char*) * (i+4));
+
+            exclude_files[i] = xstrdup("._*");
+            exclude_files[i+1] = xstrdup(".DS_Store");
+            exclude_files[i+2] = xstrdup("Icon\r");
+            exclude_files[i+3] = 0;
+            break;
+
+        case DEBUG_OPTION:
+            if (!optarg) {
+                error("debugging item is not specified for --%s",
+                      long_options[option_index].name);
+                return -1;
+            }
+            if (strcmp(optarg, "nosort") == 0) {
+                sort_contents = FALSE;
+            }
+            else if (strcmp(optarg, "norecursion") == 0) {
+                recursive_archiving = FALSE;
+            }
+            else if (strcmp(optarg, "dumplzss") == 0) {
+                dump_lzss = TRUE;
+                quiet = TRUE;
+            }
+            else {
+                error("unknown debugging item \"%s\" for --%s",
+                      optarg, long_options[option_index].name);
+                return -1;
+            }
+            break;
+
+        default:
+            error("unknown option `-%c'.", c);
+            return -1;
+        }
     }
 
-    if (strcmp(*argv, "--help") == 0) {
-        print_tiny_usage();
+    argc -= optind;
+    argv += optind;
+
+    if (!archive_name) {
+        archive_name = *argv++;
+        argc--;
+    }
+
+    cmd_filec = argc;
+    cmd_filev = argv;
+
+    return 0;
+}
+
+/*
+  Parse LHA command and options.
+*/
+static int
+parse_option(int argc, char **argv)
+{
+    char *cmd_char;
+
+    if (argv[1] == NULL || strcmp(argv[1], "--help") == 0) {
+        print_usage();
         exit(0);
     }
-    if (strcmp(*argv, "--version") == 0) {
+
+    if (strcmp(argv[1], "--version") == 0) {
         print_version();
         exit(0);
     }
 
-    if (argc == 1) {
-        archive_name = *argv++; argc--;
+    if (argc == 2 && *argv[1] != '-') {
+        archive_name = argv[1];
         cmd = CMD_LIST;
-        cmd_filec = argc;
-        cmd_filev = argv;
-        return;
+        cmd_filec = 0;
+        cmd_filev = 0;
+        return 0;
     }
 
-    opt = *argv++; argc--;
+    cmd_char = argv[1];
 
-    if (opt[0] == '-')
-        opt++;
+    if (cmd_char[0] == '-')
+        cmd_char++;
 
-    /* commands */
-    switch (*opt) {
+    /* parse commands */
+    switch (*cmd_char) {
     case 'x':
     case 'e':
         cmd = CMD_EXTRACT;
@@ -256,223 +577,26 @@ parse_option(int argc, char **argv)
         break;
 
     default:
-        print_tiny_usage();
-        exit(2);
+        error("unknown command `-%c'", *cmd_char);
+        return -1;
     }
 
-    /* options */
-    for (;;) {
-        char *p = opt+1;
-
-        while ( *p != 0 ) {
-            switch ((*p++)) {
-            case 'q':
-                switch (*p) {
-                case '0':           /* no quiet */
-                case '1':           /* no use the incremental indicator */
-                    quiet_mode = *p - '0';
-                    ++p;
-                    break;
-                case '2':           /* no output */
-                    ++p;
-                    /* fall through */
-                default:
-                    /* In quiet mode, no confirm to overwrite */
-                    force = TRUE;
-                    quiet = TRUE;
-                    break;
-                }
-                break;
-            case 'f':
-                force = TRUE;
-                break;
-            case 'p':
-                prof = TRUE;
-                break;
-            case 'v':
-                verbose++;
-                break;
-            case 't':
-                text_mode = TRUE;
-                break;
-#ifdef EUC
-            case 'e':
-                text_mode = TRUE;
-                euc_mode = TRUE;
-                break;
-#endif
-            case 'n':
-                noexec = TRUE;
-                break;
-            case 'g':
-                generic_format = TRUE;
-                noconvertcase = TRUE;
-                header_level = 0;
-                break;
-            case 'd':
-                delete_after_append = TRUE;
-                break;
-            case 'o':
-                switch (*p) {
-                case 0:
-                    compress_method = LZHUFF1_METHOD_NUM;
-                    header_level = 0;
-                    break;
-                case '5':
-                    compress_method = LZHUFF5_METHOD_NUM;
-                    p++;
-                    break;
-#ifdef SUPPORT_LH7
-                case '6':
-                    compress_method = LZHUFF6_METHOD_NUM;
-                    p++;
-                    break;
-                case '7':
-                    compress_method = LZHUFF7_METHOD_NUM;
-                    p++;
-                    break;
-#endif
-                default:
-                    error("invalid compression method 'o%c'", *p);
-                    exit(2);
-                }
-                break;
-            case 'z':
-                compress_method = LZHUFF0_METHOD_NUM;   /* Changed N.Watazaki */
-                break;
-            case 'i':
-                ignore_directory = TRUE;
-                break;
-            case 'x':
-                if (*p == '=')
-                    p++;
-
-                for (i = 0; exclude_files && exclude_files[i]; i++)
-                    ;
-                exclude_files = (char**)xrealloc(exclude_files,
-                                                 sizeof(char*) * (i+2));
-
-                if (*p == 0) {
-                    if (*argv == 0) {
-                        print_tiny_usage();
-                        exit(2);
-                    }
-                    exclude_files[i] = *argv++; argc--;
-                    exclude_files[i+1] = 0;
-                    goto next;
-                }
-                else {
-                    exclude_files[i] = p;
-                    exclude_files[i+1] = 0;
-                    p += strlen(p);
-                }
-                break;
-#if IGNORE_DOT_FILES            /* experimental feature */
-            case 'X':
-                for (i = 0; exclude_files && exclude_files[i]; i++)
-                    ;
-                exclude_files = (char**)xrealloc(exclude_files,
-                                                 sizeof(char*) * (i+2));
-
-                exclude_files[i] = xstrdup(".*");
-                exclude_files[i+1] = 0;
-                break;
-#endif
-            case 'w':
-                if (*p == '=')
-                    p++;
-                if (*p == 0) {
-                    if (*argv == 0) {
-                        print_tiny_usage();
-                        exit(2);
-                    }
-                    extract_directory = *argv++; argc--;
-                    goto next;
-                }
-                else {
-                    extract_directory = p;
-                    p += strlen(p);
-                }
-                break;
-            case '0':
-                header_level = 0;
-                break;
-            case '1':
-                header_level = 1;
-                break;
-            case '2':
-                header_level = 2;
-                break;
-            default:
-                error("Unknown option '%c'.", p[-1]);
-                exit(2);
-            }
-        }
-
-    next:
-        opt = *argv;
-        if (!opt || opt[0] != '-')
-            break;
-
-        /* special archive name */
-        if (strcmp(opt, "-") == 0)
-            break;
-
-        /* GNU style long options */
-        if (opt[0] == '-' && opt[1] == '-') {
-            opt += 2;
-
-            if (strncmp(opt, "system-kanji-code=",
-                        sizeof("system-kanji-code=")-1) == 0) {
-                opt += sizeof("system-kanji-code=")-1;
-                if (strcmp(opt, "euc") == 0) {
-                    optional_system_kanji_code = CODE_EUC;
-                }
-                else if (strcmp(opt, "sjis") == 0) {
-                    optional_system_kanji_code = CODE_SJIS;
-                }
-                else if (strcmp(opt, "utf8") == 0) {
-                    optional_system_kanji_code = CODE_UTF8;
-                }
-                else if (strcmp(opt, "cap") == 0) {
-                    optional_system_kanji_code = CODE_CAP;
-                }
-                else {
-                    print_tiny_usage();
-                    exit(2);
-                }
-            }
-            else if (strncmp(opt, "archive-kanji-code=",
-                             sizeof("archive-kanji-code=")-1) == 0) {
-                opt += sizeof("archive-kanji-code=")-1;
-                if (strcmp(opt, "euc") == 0) {
-                    optional_archive_kanji_code = CODE_EUC;
-                }
-                else if (strcmp(opt, "sjis") == 0) {
-                    optional_archive_kanji_code = CODE_SJIS;
-                }
-                else if (strcmp(opt, "utf8") == 0) {
-                    optional_archive_kanji_code = CODE_UTF8;
-                }
-                else if (strcmp(opt, "cap") == 0) {
-                    optional_archive_kanji_code = CODE_CAP;
-                }
-                else {
-                    print_tiny_usage();
-                    exit(2);
-                }
-            }
-            argv++; argc--;
-            goto next;
-        }
-
-        argv++; argc--;
+    if (cmd_char[1] == '\0') {
+        /* argv[1] is command name */
+        argv[1] = argv[0];
+        argv++;
+        argc--;
+    }
+    else {
+        /* Eliminate command character
+           e.g.) lha  cv foo.lzh -> lha -v foo.lzh
+                 lha -cv foo.lzh -> lha -v foo.lzh
+        */
+        cmd_char[0] = '-';
+        argv[1] = cmd_char;
     }
 
-    archive_name = *argv++; argc--;
-
-    cmd_filec = argc;
-    cmd_filev = argv;
+    return parse_suboption(argc, argv);
 }
 
 /* ------------------------------------------------------------------------ */
@@ -487,9 +611,15 @@ main(argc, argv)
 
     init_variable();        /* Added N.Watazaki */
 
-    parse_option(argc, argv);
+    if (parse_option(argc, argv) == -1) {
+        fputs("\n", stderr);
+        print_tiny_usage();
+        exit(2);
+    }
 
     if (!archive_name) {
+        error("archive file does not specified");
+        fputs("\n", stderr);
         print_tiny_usage();
         exit(2);
     }
@@ -560,11 +690,6 @@ main(argc, argv)
         break;
     }
 
-#ifdef USE_PROF
-    if (!prof)
-        exit(0);
-#endif
-
     if (error_occurred)
         return 1;
     return 0;
@@ -579,6 +704,7 @@ print_version()
        defined in config.h by configure script */
     fprintf(stderr, "%s version %s (%s)\n",
             PACKAGE_NAME, PACKAGE_VERSION, PLATFORM);
+    fprintf(stderr, "  configure options: %s\n", LHA_CONFIGURE_OPTIONS);
 }
 
 void
@@ -677,31 +803,37 @@ fatal_error(fmt, va_alist)
     else
         fputs("\n", stderr);
 
-    if (remove_temporary_at_error) {
-        if (temporary_fd != -1)
-            close(temporary_fd);
+    exit(1);
+}
+
+void
+cleanup()
+{
+    if (temporary_fd != -1) {
+        close(temporary_fd);
+        temporary_fd = -1;
         unlink(temporary_name);
     }
 
-    exit(1);
+    if (recover_archive_when_interrupt) {
+        rename(backup_archive_name, archive_name);
+        recover_archive_when_interrupt = FALSE;
+    }
+    if (remove_extracting_file_when_interrupt) {
+        message("Removing: %s", writing_filename);
+        unlink(writing_filename);
+        remove_extracting_file_when_interrupt = FALSE;
+    }
 }
 
-/* ------------------------------------------------------------------------ */
 RETSIGTYPE
 interrupt(signo)
     int signo;
 {
     message("Interrupted");
 
-    if (temporary_fd != -1)
-        close(temporary_fd);
-    unlink(temporary_name);
-    if (recover_archive_when_interrupt)
-        rename(backup_archive_name, archive_name);
-    if (remove_extracting_file_when_interrupt) {
-        message("Removing: %s", writing_filename);
-        unlink(writing_filename);
-    }
+    cleanup();
+
     signal(SIGINT, SIG_DFL);
 #ifdef SIGHUP
     signal(SIGHUP, SIG_DFL);
@@ -745,7 +877,7 @@ sort_by_ascii(a, b)
 static void
 sort_files()
 {
-    if (cmd_filec > 1)
+    if (cmd_filec > 1 && sort_contents)
         qsort(cmd_filev, cmd_filec, sizeof(char *), sort_by_ascii);
 }
 
@@ -778,7 +910,7 @@ xstrdup(str)
 {
     int len = strlen(str);
     char *p = (char *)xmalloc(len + 1);
-    strcpy(p, str);
+    strcpy(p, str);             /* ok */
     return p;
 }
 
@@ -871,7 +1003,7 @@ include_path_p(path, name)
     char           *n = name;
     while (*path)
         if (*path++ != *n++)
-            return (path[-1] == '/' && *n == '\0');
+            return (path[-1] == '/' && n[-1] == '\0');
     return (*n == '/' || (n != name && path[-1] == '/' && n[-1] == '/'));
 }
 
@@ -898,11 +1030,11 @@ cleaning_files(v_filec, v_filev)
     /* flags & 0x02 :   1: directory, 0 : regular file */
     /* flags & 0x04 :   1: need delete */
 
-    
     for (i = 0; i < filec; i++)
         if (GETSTAT(filev[i], &stbuf) < 0) {
             flags[i] = 0x04;
-            warning("Cannot access \"%s\", ignored.", filev[i]);
+            warning("Cannot access \"%s\" : %s; ignored.", filev[i],
+                    strerror(errno));
         }
         else {
             if (is_regularfile(&stbuf))
@@ -912,7 +1044,7 @@ cleaning_files(v_filec, v_filev)
 #ifdef S_IFLNK
             else if (is_symlink(&stbuf)) /* t.okamoto */
                 flags[i] = 0x00;
-#endif          
+#endif
             else {
                 flags[i] = 0x04;
                 warning("Cannot archive \"%s\", ignored.", filev[i]);
@@ -973,11 +1105,15 @@ find_files(name, v_filec, v_filev)
     DIR            *dirp;
     struct dirent  *dp;
     struct stat     tmp_stbuf, arc_stbuf, fil_stbuf;
+    int exist_tmp = 1, exist_arc = 1;
 
-    strcpy(newname, name);
-    len = strlen(name);
-    if (len > 0 && newname[len - 1] != '/')
-        newname[len++] = '/';
+    len = str_safe_copy(newname, name, sizeof(newname));
+    if (len > 0 && newname[len - 1] != '/') {
+        if (len < sizeof(newname)-1)
+            strcpy(&newname[len++], "/"); /* ok */
+        else
+            warning("the length of pathname \"%s\" is too long.", name);
+    }
 
     dirp = opendir(name);
     if (!dirp)
@@ -985,48 +1121,57 @@ find_files(name, v_filec, v_filev)
 
     init_sp(&sp);
 
-    GETSTAT(temporary_name, &tmp_stbuf);
-    GETSTAT(archive_name, &arc_stbuf);
+    if (GETSTAT(temporary_name, &tmp_stbuf) == -1)
+        exist_tmp = 0;
+    if (GETSTAT(archive_name, &arc_stbuf) == -1)
+        exist_arc = 0;
+
+    while ((dp = readdir(dirp)) != NULL) {
+        n = NAMLEN(dp);
+
+        /* exclude '.' and '..' */
+        if (strncmp(dp->d_name, ".", n) == 0
+            || strncmp(dp->d_name, "..", n) == 0)
+            continue;
 
-    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+        /* exclude exclude_files supplied by user */
         for (i = 0; exclude_files && exclude_files[i]; i++) {
             if (fnmatch(exclude_files[i], dp->d_name,
                         FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD) == 0)
                 goto next;
         }
 
-        n = NAMLEN(dp);
+        if (len + n >= sizeof(newname)) {
+            warning("filename is too long");
+            continue;
+        }
+
         strncpy(newname + len, dp->d_name, n);
         newname[len + n] = '\0';
         if (GETSTAT(newname, &fil_stbuf) < 0)
             continue;
-#if !defined(HAVE_STRUCT_STAT_ST_INO) || __MINGW32__
-        if ( dp->d_name[0] != '.' ||
-            (n != 1 &&
-             (dp->d_name[1] != '.' ||
-              n != 2))  ) {
-            add_sp(&sp, newname, len+n+1);
-        }
-#else       
-        if ((dp->d_ino != 0) &&
-        /* exclude '.' and '..' */
-            ((dp->d_name[0] != '.') ||
-             ((n != 1) &&
-              ((dp->d_name[1] != '.') ||
-               (n != 2)))) &&
-            ((tmp_stbuf.st_dev != fil_stbuf.st_dev ||
-              tmp_stbuf.st_ino != fil_stbuf.st_ino) &&
-             (arc_stbuf.st_dev != fil_stbuf.st_dev ||
-              arc_stbuf.st_ino != fil_stbuf.st_ino))) {
-            add_sp(&sp, newname, len + n + 1);
-        }
+
+#if defined(HAVE_STRUCT_STAT_ST_INO) && !__MINGW32__
+        /* MinGW has meaningless st_ino */
+
+        /* exclude temporary file, archive file and these links */
+        if (exist_tmp &&
+            tmp_stbuf.st_dev == fil_stbuf.st_dev &&
+            tmp_stbuf.st_ino == fil_stbuf.st_ino)
+            continue;
+
+        if (exist_arc &&
+            arc_stbuf.st_dev == fil_stbuf.st_dev &&
+            arc_stbuf.st_ino == fil_stbuf.st_ino)
+            continue;
 #endif
+        add_sp(&sp, newname, len+n+1);
     next:
         ;
     }
     closedir(dirp);
     finish_sp(&sp, v_filec, v_filev);
-    if (*v_filec > 1)
+    if (*v_filec > 1 && sort_contents)
         qsort(*v_filev, *v_filec, sizeof(char *), sort_by_ascii);
     cleaning_files(v_filec, v_filev);
 
@@ -1052,20 +1197,31 @@ build_temporary_name()
 #ifdef TMP_FILENAME_TEMPLATE
     /* "/tmp/lhXXXXXX" etc. */
     if (extract_directory == NULL) {
-        strcpy(temporary_name, TMP_FILENAME_TEMPLATE);
+        str_safe_copy(temporary_name, TMP_FILENAME_TEMPLATE,
+                      sizeof(temporary_name));
     }
     else {
         xsnprintf(temporary_name, sizeof(temporary_name),
                   "%s/lhXXXXXX", extract_directory);
     }
 #else
-    char           *p, *s;
-
-    strcpy(temporary_name, archive_name);
-    for (p = temporary_name, s = (char *) 0; *p; p++)
-        if (*p == '/')
-            s = p;
-    strcpy((s ? s + 1 : temporary_name), "lhXXXXXX");
+    char *s;
+
+    str_safe_copy(temporary_name, archive_name, sizeof(temporary_name));
+    s = strrchr(temporary_name, '/');
+    if (s) {
+        int len;
+        len = s - temporary_name;
+        if (len + strlen("lhXXXXXX") < sizeof(temporary_name))
+            /* use directory at archive file */
+            strcpy(s, "lhXXXXXX"); /* ok */
+        else
+            /* use current directory */
+            str_safe_copy(temporary_name, "lhXXXXXX", sizeof(temporary_name));
+    }
+    else
+        /* use current directory */
+        str_safe_copy(temporary_name, "lhXXXXXX", sizeof(temporary_name));
 #endif
 #ifdef HAVE_MKSTEMP
     {
@@ -1092,9 +1248,10 @@ build_temporary_name()
 
 /* ------------------------------------------------------------------------ */
 static void
-modify_filename_extention(buffer, ext)
+modify_filename_extention(buffer, ext, size)
     char           *buffer;
     char           *ext;
+    size_t size;
 {
     register char  *p, *dot;
 
@@ -1108,28 +1265,30 @@ modify_filename_extention(buffer, ext)
     if (dot)
         p = dot;
 
-    strcpy(p, ext);
+    str_safe_copy(p, ext, size - (p - buffer));
 }
 
 /* ------------------------------------------------------------------------ */
 /* build backup file name */
 void
-build_backup_name(buffer, original)
+build_backup_name(buffer, original, size)
     char           *buffer;
     char           *original;
+    size_t size;
 {
-    strcpy(buffer, original);
-    modify_filename_extention(buffer, BACKUPNAME_EXTENTION);    /* ".bak" */
+    str_safe_copy(buffer, original, size);
+    modify_filename_extention(buffer, BACKUPNAME_EXTENTION, size);    /* ".bak" */
 }
 
 /* ------------------------------------------------------------------------ */
 void
-build_standard_archive_name(buffer, orginal)
+build_standard_archive_name(buffer, original, size)
     char           *buffer;
-    char           *orginal;
+    char           *original;
+    size_t size;
 {
-    strcpy(buffer, orginal);
-    modify_filename_extention(buffer, ARCHIVENAME_EXTENTION);   /* ".lzh" */
+    str_safe_copy(buffer, original, size);
+    modify_filename_extention(buffer, ARCHIVENAME_EXTENTION, size);   /* ".lzh" */
 }
 
 /* ------------------------------------------------------------------------ */
@@ -1193,13 +1352,13 @@ open_old_archive_1(name, v_fp)
 FILE           *
 open_old_archive()
 {
-    FILE           *fp;
-    char           *p;
+    FILE           *fp = NULL;
+    char           *p = NULL, *ext, *ext2;
     static char expanded_archive_name[FILENAME_LENGTH];
 
     if (!strcmp(archive_name, "-")) {
         if (cmd == CMD_EXTRACT || cmd == CMD_LIST) {
-#if __MINGW32__
+#if defined(__MINGW32__) || defined(__DJGPP__)
             setmode(fileno(stdin), O_BINARY);
 #endif
             return stdin;
@@ -1207,46 +1366,60 @@ open_old_archive()
         else
             return NULL;
     }
-    p = strrchr(archive_name, '.');
-    if (p) {
-        if (strcasecmp(".LZH", p) == 0
-            || strcasecmp(".LZS", p) == 0
-            || strcasecmp(".COM", p) == 0  /* DOS SFX */
-            || strcasecmp(".EXE", p) == 0
-            || strcasecmp(".X", p) == 0    /* HUMAN SFX */
-            || strcasecmp(".BAK", p) == 0) {   /* for BackUp */
-            open_old_archive_1(archive_name, &fp);
-            return fp;
+
+    ext2 = strrchr(archive_name, '.');
+    if (ext2) {
+        ext2++;
+
+        /* .com: DOS SFX
+           .exe: DOS SFX
+           .x:   HUMAN SFX
+           .bak: Backup file
+           .lha: Amiga(?) */
+        p = xstrdup("lzh," ADDITIONAL_SUFFIXES);
+        for (ext = strtok(p, ",");
+             ext;
+             ext = strtok(NULL, ",")) {
+
+            if (*ext == 0) continue;
+
+            if (strcasecmp(ext, ext2)) {
+                /* Try to open file just specified filename
+                   with usual suffixes.
+                   Return NULL if the file is not exist. */
+
+                open_old_archive_1(archive_name, &fp);
+                goto ret;       /* found or not */
+            }
         }
+        free(p);
+        p = NULL;
     }
 
+    /* Try to open file just specified filename */
     if (open_old_archive_1(archive_name, &fp))
-        return fp;
-    xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
-              "%s.lzh", archive_name);
-    if (open_old_archive_1(expanded_archive_name, &fp)) {
-        archive_name = expanded_archive_name;
-        return fp;
-    }
-    /*
-     * if ( (errno&0xffff)!=E_PNNF ) { archive_name =
-     * expanded_archive_name; return NULL; }
-     */
-    xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
-              "%s.lzs", archive_name);
-    if (open_old_archive_1(expanded_archive_name, &fp)) {
-        archive_name = expanded_archive_name;
-        return fp;
+        goto ret;               /* found */
+
+    /* Try to open file with implicit suffixes */
+    p = xstrdup("lzh," ADDITIONAL_SUFFIXES);
+    for (ext = strtok(p, ",");
+         ext;
+         ext = strtok(NULL, ",")) {
+
+        if (*ext == 0) continue;
+
+        xsnprintf(expanded_archive_name, sizeof(expanded_archive_name),
+                  "%s.%s", archive_name, ext);
+
+        if (open_old_archive_1(expanded_archive_name, &fp)) {
+            archive_name = expanded_archive_name;
+            goto ret;           /* found */
+        }
     }
-    /*
-     * if ( (errno&0xffff)!=E_PNNF ) { archive_name =
-     * expanded_archive_name; return NULL; }
-     */
-    /*
-     * sprintf( expanded_archive_name , "%s.lzh",archive_name);
-     * archive_name = expanded_archive_name;
-     */
-    return NULL;
+
+ret:
+    if (p) free(p);
+    return fp;
 }
 
 /* ------------------------------------------------------------------------ */
@@ -1279,17 +1452,12 @@ write_archive_tail(nafp)
 }
 
 /* ------------------------------------------------------------------------ */
+#undef exit
+
 void
-copy_old_one(oafp, nafp, hdr)
-    FILE           *oafp, *nafp;
-    LzHeader       *hdr;
+lha_exit(status)
+    int status;
 {
-    if (noexec) {
-        fseek(oafp, hdr->header_size + hdr->packed_size, SEEK_CUR);
-    }
-    else {
-        reading_filename = archive_name;
-        writing_filename = temporary_name;
-        copyfile(oafp, nafp, hdr->header_size + hdr->packed_size, 0, 0);
-    }
+    cleanup();
+    exit(status);
 }