OSDN Git Service

applied the patch for the DJGPP.
[lha/lha.git] / src / header.c
index 64d8b85..e919a35 100644 (file)
@@ -10,6 +10,9 @@
 /*  Ver. 1.13b Symbolic Link Bug Fix            1994.08.22  N.Watazaki      */
 /*  Ver. 1.14  Source All chagned               1995.01.14  N.Watazaki      */
 /*  Ver. 1.14i bug fixed                        2000.10.06  t.okamoto       */
+/*  Ver. 1.14i Contributed UTF-8 convertion for Mac OS X                    */
+/*                                              2002.06.29  Hiroto Sakai    */
+/*  Ver. 1.14i autoconfiscated & rewritten      2003.02.23  Koji Arai       */
 /* ------------------------------------------------------------------------ */
 #include "lha.h"
 
@@ -23,7 +26,6 @@
 #define strrchr  xstrrchr
 #endif
 
-/* ------------------------------------------------------------------------ */
 static char    *get_ptr;
 #define GET_BYTE()      (*get_ptr++ & 0xff)
 
@@ -53,16 +55,14 @@ int default_system_kanji_code = MULTIBYTE_FILENAME;
 int default_system_kanji_code = NONE;
 #endif
 
-/* ------------------------------------------------------------------------ */
 int
 calc_sum(p, len)
-    register char  *p;
-    register int    len;
+    void *p;
+    int len;
 {
-    register int    sum;
+    int sum = 0;
 
-    for (sum = 0; len; len--)
-        sum += *p++;
+    while (len--) sum += *(unsigned char*)p++;
 
     return sum & 0xff;
 }
@@ -101,11 +101,10 @@ dump_skip_bytes(len)
 }
 #endif
 
-/* ------------------------------------------------------------------------ */
 static int
 get_word()
 {
-    int             b0, b1;
+    int b0, b1;
     int w;
 
 #if DUMP_HEADER
@@ -122,7 +121,6 @@ get_word()
     return w;
 }
 
-/* ------------------------------------------------------------------------ */
 static void
 put_word(v)
     unsigned int    v;
@@ -131,11 +129,10 @@ put_word(v)
     put_byte(v >> 8);
 }
 
-/* ------------------------------------------------------------------------ */
 static long
 get_longword()
 {
-    long            b0, b1, b2, b3;
+    long b0, b1, b2, b3;
     long l;
 
 #if DUMP_HEADER
@@ -154,10 +151,9 @@ get_longword()
     return l;
 }
 
-/* ------------------------------------------------------------------------ */
 static void
 put_longword(v)
-    long            v;
+    long v;
 {
     put_byte(v);
     put_byte(v >> 8);
@@ -215,8 +211,8 @@ convert_filename(name, len, size,
                  from_delim, to_delim,
                  case_to)
     char *name;
-    int len;
-    int size;
+    int len;                    /* length of name */
+    int size;                   /* size of name buffer */
     int from_code, to_code, case_to;
     char *from_delim, *to_delim;
 
@@ -224,6 +220,20 @@ convert_filename(name, len, size,
     int i;
 #ifdef MULTIBYTE_FILENAME
     char tmp[FILENAME_LENGTH];
+    int to_code_save = NONE;
+
+    if (from_code == CODE_CAP) {
+        len = cap_to_sjis(tmp, name, sizeof(tmp));
+        strncpy(name, tmp, size);
+        name[size-1] = 0;
+        len = strlen(name);
+        from_code = CODE_SJIS;
+    }
+
+    if (to_code == CODE_CAP) {
+        to_code_save = CODE_CAP;
+        to_code = CODE_SJIS;
+    }
 
     if (from_code == CODE_SJIS && to_code == CODE_UTF8) {
         for (i = 0; i < len; i++)
@@ -347,151 +357,66 @@ convert_filename(name, len, size,
             continue;
         }
     }
-}
 
-/* ------------------------------------------------------------------------ */
-/*                                                                          */
-/* Generic stamp format:                                                    */
-/*                                                                          */
-/*  31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16                         */
-/* |<------- year ----->|<- month ->|<--- day ---->|                        */
-/*                                                                          */
-/*  15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0                         */
-/* |<--- hour --->|<---- minute --->|<- second*2 ->|                        */
-/*                                                                          */
-/* ------------------------------------------------------------------------ */
+#ifdef MULTIBYTE_FILENAME
+    if (to_code_save == CODE_CAP) {
+        len = sjis_to_cap(tmp, name, sizeof(tmp));
+        strncpy(name, tmp, size);
+        name[size-1] = 0;
+        len = strlen(name);
+    }
+#endif /* MULTIBYTE_FILENAME */
+}
 
 /*
- * NOTE : If you don't have `gettimeofday(2)', or your gettimeofday(2)
- * returns bogus timezone information, try FTIME, MKTIME, TIMELOCAL or TZSET.
+ * Generic (MS-DOS style) time stamp format (localtime):
+ *
+ *  31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ * |<---- year-1980 --->|<- month ->|<--- day ---->|
+ *
+ *  15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
+ * |<--- hour --->|<---- minute --->|<- second/2 ->|
+ *
  */
 
-#ifdef HAVE_FTIME
-#include <sys/timeb.h>
-#endif
-
-#if !defined(HAVE_MKTIME) && !defined(HAVE_TIMELOCAL)
-static long
-gettz()
+static time_t
+generic_to_unix_stamp(t)
+    long t;
 {
-#ifdef HAVE_TZSET
-#if defined(_MINIX)
-    extern long     timezone;       /* not defined in time.h */
-#endif
+    struct tm tm;
 
-    tzset();
-    return timezone;
-#elif HAVE_FTIME        
-    struct timeb    buf;
-
-    ftime(&buf);
-    return buf.timezone * 60L;
-#elif HAVE_STRUCT_TM_TM_GMTOFF
-    time_t tt;
-
-    time(&tt);
-    return -localtime(&tt)->tm_gmtoff;
-#elif GETTIMEOFDAY_HAS_2ND_ARG
-    struct timeval  tp;
-    struct timezone tzp;
-    gettimeofday(&tp, &tzp);/* specific to 4.3BSD */
-    /*
-     * return (tzp.tz_minuteswest * 60L + (tzp.tz_dsttime != 0 ? 60L *
-     * 60L : 0));
-     */
-    return (tzp.tz_minuteswest * 60L);
-#else
-    /* Compile error will be caused */
-    CANNOT GET TIMEZONE INFORMATION ON YOUR SYSTEM.
-    TAKE THE ANOTHER WAY.
-#endif
-}
-#endif
+#define subbits(n, off, len) (((n) >> (off)) & ((1 << (len))-1))
 
-/* ------------------------------------------------------------------------ */
-static          time_t
-generic_to_unix_stamp(t)
-    long            t;
-{
-#if defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL)
-    struct tm       dostm;
-
-    /*
-     * special case:  if MSDOS format date and time were zero, then we
-     * set time to be zero here too.
-     */
-    if (t == 0)
-        return (time_t) 0;
-
-    dostm.tm_sec = (t & 0x1f) * 2;
-    dostm.tm_min = t >> 5 & 0x3f;
-    dostm.tm_hour = t >> 11 & 0x1f;
-    dostm.tm_mday = t >> 16 & 0x1f;
-    dostm.tm_mon = (t >> (16+5) & 0x0f) - 1;    /* 0..11 */
-    dostm.tm_year = (t >> (16+9) & 0x7f) + 80;
-    dostm.tm_isdst = -1;
+    tm.tm_sec  = subbits(t,  0, 5) * 2;
+    tm.tm_min  = subbits(t,  5, 6);
+    tm.tm_hour = subbits(t, 11, 5);
+    tm.tm_mday = subbits(t, 16, 5);
+    tm.tm_mon  = subbits(t, 21, 4) - 1;
+    tm.tm_year = subbits(t, 25, 7) + 80;
+    tm.tm_isdst = -1;
 
 #if HAVE_MKTIME
-    return (time_t) mktime(&dostm);
-#else /* HAVE_TIMELOCAL is defined */
-    return (time_t) timelocal(&dostm);
+    return mktime(&tm);
+#else
+    return timelocal(&tm);
 #endif
-
-#else               /* defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) */
-    int             year, month, day, hour, min, sec;
-    long            longtime;
-    static unsigned int dsboy[12] = {0, 31, 59, 90, 120, 151,
-    181, 212, 243, 273, 304, 334};
-    unsigned int    days;
-
-    /*
-     * special case:  if MSDOS format date and time were zero, then we
-     * set time to be zero here too.
-     */
-    if (t == 0)
-        return (time_t) 0;
-
-    year = ((int) (t >> 16 + 9) & 0x7f) + 1980;
-    month = (int) (t >> 16 + 5) & 0x0f; /* 1..12 means Jan..Dec */
-    day = (int) (t >> 16) & 0x1f;   /* 1..31 means 1st,...31st */
-
-    hour = ((int) t >> 11) & 0x1f;
-    min = ((int) t >> 5) & 0x3f;
-    sec = ((int) t & 0x1f) * 2;
-
-    /* Calculate days since 1970.01.01 */
-    days = (365 * (year - 1970) +   /* days due to whole years */
-        (year - 1970 + 1) / 4 + /* days due to leap years */
-        dsboy[month - 1] +  /* days since beginning of this year */
-        day - 1);   /* days since beginning of month */
-
-    if ((year % 4 == 0) &&
-        (year % 100 != 0 || year % 400 == 0) &&     /* 1999.5.24 t.oka */
-        (month >= 3))   /* if this is a leap year and month */
-        days++;     /* is March or later, add a day */
-
-    /* Knowing the days, we can find seconds */
-    longtime = (((days * 24) + hour) * 60 + min) * 60 + sec;
-    longtime += gettz();    /* adjust for timezone */
-
-    /* LONGTIME is now the time in seconds, since 1970/01/01 00:00:00.  */
-    return (time_t) longtime;
-#endif              /* defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) */
 }
 
-/* ------------------------------------------------------------------------ */
 static long
 unix_to_generic_stamp(t)
-    time_t          t;
+    time_t t;
 {
-    struct tm      *tm = localtime(&t);
-
-    return ((((long) (tm->tm_year - 80)) << 25) +
-        (((long) (tm->tm_mon + 1)) << 21) +
-        (((long) tm->tm_mday) << 16) +
-        (long) ((tm->tm_hour << 11) +
-            (tm->tm_min << 5) +
-            (tm->tm_sec / 2)));
+    struct tm *tm = localtime(&t);
+
+    tm->tm_year -= 80;
+    tm->tm_mon += 1;
+
+    return ((long)(tm->tm_year << 25) +
+            (tm->tm_mon  << 21) +
+            (tm->tm_mday << 16) +
+            (tm->tm_hour << 11) +
+            (tm->tm_min  << 5) +
+            (tm->tm_sec / 2));
 }
 
 static unsigned long
@@ -499,10 +424,11 @@ wintime_to_unix_stamp()
 {
 #if HAVE_UINT64_T
     uint64_t t;
-    uint64_t epoch = 0x019db1ded53e8000; /* 1970-01-01 00:00:00 (UTC) */
+    uint64_t epoch = ((uint64_t)0x019db1de << 32) + 0xd53e8000;
+                     /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */
 
-    t = get_longword();
-    t += (uint64_t)get_longword() << 32;
+    t = (unsigned long)get_longword();
+    t |= (uint64_t)(unsigned long)get_longword() << 32;
     t = (t - epoch) / 10000000;
     return t;
 #else
@@ -532,10 +458,6 @@ wintime_to_unix_stamp()
 #endif
 }
 
-/* ------------------------------------------------------------------------ */
-/* build header functions                                                   */
-/* ------------------------------------------------------------------------ */
-
 /*
  * extended header
  *
@@ -693,8 +615,8 @@ get_extended_header(fp, hdr, header_size, hcrc)
             name_length = sizeof(hdr->name) - dir_length - 1;
             hdr->name[name_length] = 0;
         }
-        strcat(dirname, hdr->name);
-        strcpy(hdr->name, dirname);
+        strcat(dirname, hdr->name); /* ok */
+        strcpy(hdr->name, dirname); /* ok */
         name_length += dir_length;
     }
 
@@ -797,7 +719,7 @@ get_header_level0(fp, hdr, data)
     hdr->name[i] = '\0';
 
     /* defaults for other type */
-    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -810,7 +732,7 @@ get_header_level0(fp, hdr, data)
             hdr->has_crc = FALSE;
 
             return TRUE;
-        } 
+        }
 
         error("Unkonwn header (lha file?)");
         exit(1);
@@ -914,7 +836,7 @@ get_header_level1(fp, hdr, data)
     hdr->name[i] = '\0';
 
     /* defaults for other type */
-    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -997,7 +919,7 @@ get_header_level2(fp, hdr, data)
     hdr->header_level = get_byte();
 
     /* defaults for other type */
-    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -1077,7 +999,7 @@ get_header_level3(fp, hdr, data)
     hdr->header_level = get_byte();
 
     /* defaults for other type */
-    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -1197,7 +1119,7 @@ get_header(fp, hdr)
                      system_kanji_code,
                      archive_delim, system_delim, filename_case);
 
-    if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
+    if (S_ISLNK(hdr->unix_mode)) {
         char *p;
         /* split symbolic link */
         p = strchr(hdr->name, '|');
@@ -1205,7 +1127,7 @@ get_header(fp, hdr)
             /* hdr->name is symbolic link name */
             /* hdr->realname is real name */
             *p = 0;
-            strncpy(hdr->realname, p+1, sizeof(hdr->realname));
+            strcpy(hdr->realname, p+1); /* ok */
         }
         else
             error("unknown symlink name \"%s\"", hdr->name);
@@ -1230,13 +1152,11 @@ seek_lha_header(fp)
             continue;
         /* found "-l??-" keyword (as METHOD type string) */
 
-        /* size and checksum validate check */
-
         /* level 0 or 1 header */
         if ((p[I_HEADER_LEVEL] == 0 || p[I_HEADER_LEVEL] == 1)
             && p[I_HEADER_SIZE] > 20
             && p[I_HEADER_CHECKSUM] == calc_sum(p+2, p[I_HEADER_SIZE])) {
-            if (fseek(fp, (p - buffer) - n, SEEK_CUR) == -1)
+            if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
                 fatal_error("cannot seek header");
             return 0;
         }
@@ -1245,18 +1165,55 @@ seek_lha_header(fp)
         if (p[I_HEADER_LEVEL] == 2
             && p[I_HEADER_SIZE] >= 24
             && p[I_ATTRIBUTE] == 0x20) {
-            if (fseek(fp, (p - buffer) - n, SEEK_CUR) == -1)
+            if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
                 fatal_error("cannot seek header");
             return 0;
         }
     }
 
-    if (fseek(fp, -n, SEEK_CUR) == -1)
+    if (fseeko(fp, -n, SEEK_CUR) == -1)
         fatal_error("cannot seek header");
     return -1;
 }
 
-/* ------------------------------------------------------------------------ */
+
+/* remove leading `xxxx/..' */
+static char *
+remove_leading_dots(char *path)
+{
+    char *first = path;
+    char *ptr = 0;
+
+    if (strcmp(first, "..") == 0) {
+        warning("Removing leading `..' from member name.");
+        return first+1;         /* change to "." */
+    }
+
+    if (strstr(first, "..") == 0)
+        return first;
+
+    while (path && *path) {
+
+        if (strcmp(path, "..") == 0)
+            ptr = path = path+2;
+        else if (strncmp(path, "../", 3) == 0)
+            ptr = path = path+3;
+        else
+            path = strchr(path, '/');
+
+        if (path && *path == '/') {
+            path++;
+        }
+    }
+
+    if (ptr) {
+        warning("Removing leading `%.*s' from member name.", ptr-first, first);
+        return ptr;
+    }
+
+    return first;
+}
+
 void
 init_header(name, v_stat, hdr)
     char           *name;
@@ -1275,8 +1232,9 @@ init_header(name, v_stat, hdr)
     hdr->original_size = v_stat->st_size;
     hdr->attribute = GENERIC_ATTRIBUTE;
     hdr->header_level = header_level;
-    strcpy(hdr->name, name);
-    len = strlen(name);
+    len = str_safe_copy(hdr->name,
+                        remove_leading_dots(name),
+                        sizeof(hdr->name));
     hdr->crc = 0x0000;
     hdr->extend_type = EXTEND_UNIX;
     hdr->unix_last_modified_stamp = v_stat->st_mtime;
@@ -1318,11 +1276,16 @@ init_header(name, v_stat, hdr)
         memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
         hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
         hdr->original_size = 0;
-        if (len > 0 && hdr->name[len - 1] != '/')
-            strcpy(&hdr->name[len++], "/");
+        if (len > 0 && hdr->name[len - 1] != '/') {
+            if (len < sizeof(hdr->name)-1)
+                strcpy(&hdr->name[len++], "/"); /* ok */
+            else
+                warning("the length of dirname \"%s\" is too long.",
+                        hdr->name);
+        }
     }
 
-#ifdef S_IFLNK
+#if defined(S_IFLNK) && !defined(__DJGPP__)
     if (is_symlink(v_stat)) {
         memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
         hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
@@ -1368,9 +1331,6 @@ write_unix_info(hdr)
     }
 }
 
-/* ------------------------------------------------------------------------ */
-/* Write unix extended header or generic header. */
-
 static size_t
 write_header_level0(data, hdr, pathname)
     LzHeader *hdr;
@@ -1600,11 +1560,11 @@ write_header_level2(data, hdr, pathname)
         header_size++;
     }
 
-    /* put hader size */
+    /* put header size */
     setup_put(data + I_HEADER_SIZE);
     put_word(header_size);
 
-    /* put hader CRC in extended header */
+    /* put header CRC in extended header */
     INITIALIZE_CRC(hcrc);
     hcrc = calccrc(hcrc, data, (unsigned int) header_size);
     setup_put(headercrc_ptr);
@@ -1636,11 +1596,11 @@ write_header(fp, hdr)
     if (generic_format)
         filename_case = TO_UPPER;
 
-    if (hdr->header_level == HEADER_LEVEL0) {
+    if (hdr->header_level == 0) {
         archive_delim = "\\";
     }
 
-    if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
+    if (S_ISLNK(hdr->unix_mode)) {
         char *p;
         p = strchr(hdr->name, '|');
         if (p) {
@@ -1892,4 +1852,106 @@ sjis2euc(int *p1, int *p2)
     *p1 |= 0x80;
     *p2 |= 0x80;
 }
+
+static int
+hex2int(int c)
+{
+    switch (c) {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+        return c - '0';
+
+    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+        return c - 'a' + 10;
+
+    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+        return c - 'A' + 10;
+    default:
+        return -1;
+    }
+}
+
+static int
+int2hex(int c)
+{
+    switch (c) {
+    case 0: case 1: case 2: case 3: case 4:
+    case 5: case 6: case 7: case 8: case 9:
+        return c + '0';
+
+    case 10: case 11: case 12: case 13: case 14: case 15:
+        return c + 'a' - 10;
+
+    default:
+        return -1;
+    }
+}
+
+int
+cap_to_sjis(char *dst, const char *src, size_t dstsize)
+{
+    int i, j;
+    size_t len = strlen(src);
+    int a, b;
+
+    for (i = j = 0; i < len && i < dstsize; i++) {
+        if (src[i] != ':') {
+            dst[j++] = src[i];
+            continue;
+        }
+
+        i++;
+        a = hex2int((unsigned char)src[i]);
+        b = hex2int((unsigned char)src[i+1]);
+
+        if (a == -1 || b == -1) {
+            /* leave as it */
+            dst[j++] = ':';
+            strncpy(dst+j, src+i, dstsize-j);
+            dst[dstsize-1] = 0;
+            return strlen(dst);
+        }
+
+        i++;
+
+        dst[j++] = a * 16 + b;
+    }
+    dst[j] = 0;
+    return j;
+}
+
+int
+sjis_to_cap(char *dst, const char *src, size_t dstsize)
+{
+    int i, j;
+    size_t len = strlen(src);
+    int a, b;
+
+    for (i = j = 0; i < len && i < dstsize; i++) {
+        if (src[i] == ':') {
+            strncpy(dst+j, ":3a", dstsize-j);
+            dst[dstsize-1] = 0;
+            j = strlen(dst);
+            continue;
+        }
+        if (isprint(src[i])) {
+            dst[j++] = src[i];
+            continue;
+        }
+
+        if (j + 3 >= dstsize) {
+            dst[j] = 0;
+            return j;
+        }
+
+        a = int2hex((unsigned char)src[i] / 16);
+        b = int2hex((unsigned char)src[i] % 16);
+
+        dst[j++] = ':';
+        dst[j++] = a;
+        dst[j++] = b;
+    }
+    dst[j] = 0;
+    return j;
+}
 #endif /* MULTIBYTE_FILENAME */