OSDN Git Service

applied the patch for the DJGPP.
[lha/lha.git] / src / header.c
index 32ad2bf..e919a35 100644 (file)
@@ -1,15 +1,18 @@
 /* ------------------------------------------------------------------------ */
-/* LHa for UNIX                                                                                                                        */
-/*                             header.c -- header manipulate functions                                         */
-/*                                                                                                                                                     */
-/*             Modified                        Nobutaka Watazaki                                                       */
-/*                                                                                                                                                     */
-/*     Original                                                                                                Y.Tagawa                */
-/*     modified                                                                        1991.12.16      M.Oki                   */
-/*     Ver. 1.10  Symbolic Link added                          1993.10.01      N.Watazaki              */
-/*     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       */
+/* LHa for UNIX                                                             */
+/*              header.c -- header manipulate functions                     */
+/*                                                                          */
+/*      Modified                Nobutaka Watazaki                           */
+/*                                                                          */
+/*  Original                                                Y.Tagawa        */
+/*  modified                                    1991.12.16  M.Oki           */
+/*  Ver. 1.10  Symbolic Link added              1993.10.01  N.Watazaki      */
+/*  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,9 +26,8 @@
 #define strrchr  xstrrchr
 #endif
 
-/* ------------------------------------------------------------------------ */
 static char    *get_ptr;
-#define GET_BYTE()             (*get_ptr++ & 0xff)
+#define GET_BYTE()      (*get_ptr++ & 0xff)
 
 #if DUMP_HEADER
 static char    *start_ptr;
@@ -33,13 +35,13 @@ static char    *start_ptr;
 #define get_byte()      dump_get_byte()
 #define skip_bytes(len) dump_skip_bytes(len)
 #else
-#define setup_get(PTR) (get_ptr = (PTR))
-#define get_byte()             GET_BYTE()
+#define setup_get(PTR)  (get_ptr = (PTR))
+#define get_byte()      GET_BYTE()
 #define skip_bytes(len) (get_ptr += (len))
 #endif
-#define put_ptr                        get_ptr
-#define setup_put(PTR) (put_ptr = (PTR))
-#define put_byte(c)            (*put_ptr++ = (char)(c))
+#define put_ptr         get_ptr
+#define setup_put(PTR)  (put_ptr = (PTR))
+#define put_byte(c)     (*put_ptr++ = (char)(c))
 
 int optional_archive_kanji_code = NONE;
 int optional_system_kanji_code = NONE;
@@ -53,18 +55,16 @@ 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;
+    return sum & 0xff;
 }
 
 #if DUMP_HEADER
@@ -89,79 +89,76 @@ static void
 dump_skip_bytes(len)
     int len;
 {
+    if (len == 0) return;
     if (verbose_listing && verbose > 1) {
-        printf("%02d %2d:", get_ptr - start_ptr, len);
+        printf("%02d %2d: ", get_ptr - start_ptr, len);
         while (len--)
-            printf(" 0x%02x", GET_BYTE());
-        printf("\n");
+            printf("0x%02x ", GET_BYTE());
+        printf("... ignored\n");
     }
     else
         get_ptr += len;
 }
 #endif
 
-/* ------------------------------------------------------------------------ */
 static int
 get_word()
 {
-       int             b0, b1;
+    int b0, b1;
     int w;
 
 #if DUMP_HEADER
     if (verbose_listing && verbose > 1)
         printf("%02d %2d: ", get_ptr - start_ptr, 2);
 #endif
-       b0 = GET_BYTE();
-       b1 = GET_BYTE();
+    b0 = GET_BYTE();
+    b1 = GET_BYTE();
     w = (b1 << 8) + b0;
 #if DUMP_HEADER
     if (verbose_listing && verbose > 1)
         printf("%d(0x%04x)\n", w, w);
 #endif
-       return w;
+    return w;
 }
 
-/* ------------------------------------------------------------------------ */
 static void
 put_word(v)
-       unsigned int    v;
+    unsigned int    v;
 {
-       put_byte(v);
-       put_byte(v >> 8);
+    put_byte(v);
+    put_byte(v >> 8);
 }
 
-/* ------------------------------------------------------------------------ */
 static long
 get_longword()
 {
-       long            b0, b1, b2, b3;
+    long b0, b1, b2, b3;
     long l;
 
 #if DUMP_HEADER
     if (verbose_listing && verbose > 1)
         printf("%02d %2d: ", get_ptr - start_ptr, 4);
 #endif
-       b0 = GET_BYTE();
-       b1 = GET_BYTE();
-       b2 = GET_BYTE();
-       b3 = GET_BYTE();
+    b0 = GET_BYTE();
+    b1 = GET_BYTE();
+    b2 = GET_BYTE();
+    b3 = GET_BYTE();
     l = (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
 #if DUMP_HEADER
     if (verbose_listing && verbose > 1)
         printf("%ld(0x%08lx)\n", l, l);
 #endif
-       return l;
+    return l;
 }
 
-/* ------------------------------------------------------------------------ */
 static void
 put_longword(v)
-       long            v;
+    long v;
 {
-       put_byte(v);
-       put_byte(v >> 8);
-       put_byte(v >> 16);
-       put_byte(v >> 24);
+    put_byte(v);
+    put_byte(v >> 8);
+    put_byte(v >> 16);
+    put_byte(v >> 24);
 }
 
 static int
@@ -173,15 +170,27 @@ get_bytes(buf, len, size)
 
 #if DUMP_HEADER
     if (verbose_listing && verbose > 1)
-        printf("%02d %2d: ", get_ptr - start_ptr, len);
-#endif
+        printf("%02d %2d: \"", get_ptr - start_ptr, len);
+
+    for (i = 0; i < len; i++) {
+        if (i < size) buf[i] = get_ptr[i];
+
+        if (verbose_listing && verbose > 1) {
+            if (isprint(buf[i]))
+                printf("%c", buf[i]);
+            else
+                printf("\\x%02x", (unsigned char)buf[i]);
+        }
+    }
+
+    if (verbose_listing && verbose > 1)
+        printf("\"\n");
+#else
     for (i = 0; i < len && i < size; i++)
         buf[i] = get_ptr[i];
-    get_ptr += len;
-#if DUMP_HEADER
-    if (verbose_listing && verbose > 1)
-        printf("\"%*.*s\"\n", i, i, buf);
 #endif
+
+    get_ptr += len;
     return i;
 }
 
@@ -202,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;
 
@@ -211,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++)
@@ -238,6 +261,21 @@ convert_filename(name, len, size,
     }
 #endif
 
+    /* special case: if `name' has small lettter, not convert case. */
+    if (from_code == CODE_SJIS && case_to == TO_LOWER) {
+        for (i = 0; i < len; i++) {
+#ifdef MULTIBYTE_FILENAME
+            if (SJIS_FIRST_P(name[i]) && SJIS_SECOND_P(name[i+1]))
+                i++;
+            else
+#endif
+            if (islower(name[i])) {
+                case_to = NONE;
+                break;
+            }
+        }
+    }
+
     for (i = 0; i < len; i ++) {
 #ifdef MULTIBYTE_FILENAME
         if (from_code == CODE_EUC &&
@@ -281,8 +319,8 @@ convert_filename(name, len, size,
             continue;
         }
         if (from_code == CODE_SJIS &&
-            SJC_FIRST_P(name[i]) &&
-            SJC_SECOND_P(name[i+1])) {
+            SJIS_FIRST_P(name[i]) &&
+            SJIS_SECOND_P(name[i+1])) {
             int c1, c2;
 
             if (to_code != CODE_EUC) {
@@ -319,209 +357,107 @@ 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 ->|
+ *
  */
 
-/* choose one */
-#if defined(HAVE_MKTIME)
-#ifdef HAVE_TIMELOCAL
-#undef HAVE_TIMELOCAL
-#endif
-#endif                         /* defined(HAVE_MKTIME) */
+static time_t
+generic_to_unix_stamp(t)
+    long t;
+{
+    struct tm tm;
 
-#if defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL)
-#ifdef HAVE_TZSET
-#undef HAVE_TZSET
-#endif
-#endif                         /* defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) */
+#define subbits(n, off, len) (((n) >> (off)) & ((1 << (len))-1))
 
-#if defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) || defined(HAVE_TZSET)
-#ifdef HAVE_FTIME
-#undef HAVE_FTIME
-#endif
-#endif
+    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 defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) || defined(HAVE_TZSET) || defined(HAVE_FTIME)
-#ifdef HAVE_GETTIMEOFDAY
-#undef HAVE_GETTIMEOFDAY
-#endif
+#if HAVE_MKTIME
+    return mktime(&tm);
 #else
-#ifndef HAVE_GETTIMEOFDAY
-#define HAVE_GETTIMEOFDAY              /* use gettimeofday() */
-#endif
-#endif
-
-#ifdef HAVE_FTIME
-#include <sys/timeb.h>
-#endif
-
-/*
- * You may define as : #define TIMEZONE_HOOK           \ extern long
- * timezone ;  \ extern void tzset();
- */
-#ifdef TIMEZONE_HOOK
-TIMEZONE_HOOK
-/* Which do you like better, `TIMEZONE_HOOK' or `TIMEZONE_HOOK;' ? */
-#endif
-
-#if defined(HAVE_TZSET) && defined(_MINIX)
-extern long     timezone;              /* not defined in time.h */
+    return timelocal(&tm);
 #endif
+}
 
-/* ------------------------------------------------------------------------ */
-#if defined(HAVE_FTIME) || defined(HAVE_GETTIMEOFDAY) || defined(HAVE_TZSET)
 static long
-gettz()
-#ifdef HAVE_TZSET
+unix_to_generic_stamp(t)
+    time_t t;
 {
-       tzset();
-       return timezone;
-}
-#endif
+    struct tm *tm = localtime(&t);
 
-/* ------------------------------------------------------------------------ */
-#if !defined(HAVE_TZSET) && defined(HAVE_FTIME)
-{
-       struct timeb    buf;
+    tm->tm_year -= 80;
+    tm->tm_mon += 1;
 
-       ftime(&buf);
-       return buf.timezone * 60L;
+    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));
 }
-#endif
 
-/* ------------------------------------------------------------------------ */
-#if !defined(HAVE_TZSET) && !defined(HAVE_FTIME)       /* maybe defined(HAVE_GETTIMEOFDAY) */
+static unsigned long
+wintime_to_unix_stamp()
 {
-#ifdef HAVE_STRUCT_TM_TM_GMTOFF
-       time_t tt;
-
-       time(&tt);
-       return -localtime(&tt)->tm_gmtoff;
-#else /* HAVE_STRUCT_TM_TM_GMTOFF */
-       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);
-#endif /* HAVE_STRUCT_TM_TM_GMTOFF */
-}
-#endif
-#endif                         /* defined(HAVE_FTIME) || defined(HAVE_GETTIMEOFDAY) ||
-                     * defined(HAVE_TZSET) */
+#if HAVE_UINT64_T
+    uint64_t t;
+    uint64_t epoch = ((uint64_t)0x019db1de << 32) + 0xd53e8000;
+                     /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */
+
+    t = (unsigned long)get_longword();
+    t |= (uint64_t)(unsigned long)get_longword() << 32;
+    t = (t - epoch) / 10000000;
+    return t;
+#else
+    int i, borrow;
+    unsigned long t, q, x;
+    unsigned long wintime[8];
+    unsigned long epoch[8] = {0x01,0x9d,0xb1,0xde, 0xd5,0x3e,0x80,0x00};
+                                /* 1970-01-01 00:00:00 (UTC) */
+    /* wintime -= epoch */
+    borrow = 0;
+    for (i = 7; i >= 0; i--) {
+        wintime[i] = (unsigned)get_byte() - epoch[i] - borrow;
+        borrow = (wintime[i] > 0xff) ? 1 : 0;
+        wintime[i] &= 0xff;
+    }
 
-/* ------------------------------------------------------------------------ */
-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;
-#if 0
-       dostm.tm_isdst = 0;     /* correct? */
-#endif
-       dostm.tm_isdst = -1;    /* correct? */
-#ifdef HAVE_MKTIME
-       return (time_t) mktime(&dostm);
-#else                          /* maybe defined(HAVE_TIMELOCAL) */
-       return (time_t) timelocal(&dostm);
+    /* q = wintime / 10000000 */
+    t = q = 0;
+    x = 10000000;               /* x: 24bit */
+    for (i = 0; i < 8; i++) {
+        t = (t << 8) + wintime[i]; /* 24bit + 8bit. t must be 32bit variable */
+        q <<= 8;                   /* q must be 32bit (time_t) */
+        q += t / x;
+        t %= x;     /* 24bit */
+    }
+    return q;
 #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;
-{
-       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)));
-}
-
-/* ------------------------------------------------------------------------ */
-/* build header functions                                                                                                      */
-/* ------------------------------------------------------------------------ */
-
 /*
  * extended header
  *
@@ -535,16 +471,17 @@ unix_to_generic_stamp(t)
  *           2 or 4  next-header size    v
  *  --------------------------------------
  *
- *  on level 0, 1, 2 header:
+ *  on level 1, 2 header:
  *    size field is 2 bytes
  *  on level 3 header:
  *    size field is 4 bytes
  */
-static long
+
+static ssize_t
 get_extended_header(fp, hdr, header_size, hcrc)
     FILE *fp;
     LzHeader *hdr;
-    long header_size;
+    size_t header_size;
     unsigned int *hcrc;
 {
     char data[LZHEADER_STORAGE];
@@ -552,7 +489,7 @@ get_extended_header(fp, hdr, header_size, hcrc)
     char dirname[FILENAME_LENGTH];
     int dir_length = 0;
     int i;
-    long whole_size = header_size;
+    ssize_t whole_size = header_size;
     int ext_type;
     int n = 1 + hdr->size_field_length; /* `ext-type' + `next-header size' */
 
@@ -578,9 +515,9 @@ get_extended_header(fp, hdr, header_size, hcrc)
         case 0:
             /* header crc (CRC-16) */
             hdr->header_crc = get_word();
-            *--get_ptr = 0;     /* clear buffer for CRC calculation. */
-            *--get_ptr = 0;
-            skip_bytes(header_size - n);
+            /* clear buffer for CRC calculation. */
+            data[1] = data[2] = 0;
+            skip_bytes(header_size - n - 2);
             break;
         case 1:
             /* filename */
@@ -595,51 +532,72 @@ get_extended_header(fp, hdr, header_size, hcrc)
             break;
         case 0x40:
             /* MS-DOS attribute */
-            if (hdr->extend_type == EXTEND_MSDOS ||
-                hdr->extend_type == EXTEND_HUMAN ||
-                hdr->extend_type == EXTEND_GENERIC)
-                hdr->attribute = get_word();
+            hdr->attribute = get_word();
+            break;
+        case 0x41:
+            /* Windows time stamp (FILETIME structure) */
+            /* it is time in 100 nano seconds since 1601-01-01 00:00:00 */
+
+            skip_bytes(8); /* create time is ignored */
+
+            /* set last modified time */
+            if (hdr->header_level >= 2)
+                skip_bytes(8);  /* time_t has been already set */
+            else
+                hdr->unix_last_modified_stamp = wintime_to_unix_stamp();
+
+            skip_bytes(8); /* last access time is ignored */
+
             break;
         case 0x50:
             /* UNIX permission */
-            if (hdr->extend_type == EXTEND_UNIX)
-                hdr->unix_mode = get_word();
+            hdr->unix_mode = get_word();
             break;
         case 0x51:
             /* UNIX gid and uid */
-            if (hdr->extend_type == EXTEND_UNIX) {
-                hdr->unix_gid = get_word();
-                hdr->unix_uid = get_word();
-            }
+            hdr->unix_gid = get_word();
+            hdr->unix_uid = get_word();
             break;
         case 0x52:
             /* UNIX group name */
-            if (hdr->extend_type == EXTEND_UNIX) {
-                i = get_bytes(hdr->group, header_size-n, sizeof(hdr->group)-1);
-                hdr->group[i] = '\0';
-            }
+            i = get_bytes(hdr->group, header_size-n, sizeof(hdr->group)-1);
+            hdr->group[i] = '\0';
             break;
         case 0x53:
             /* UNIX user name */
-            if (hdr->extend_type == EXTEND_UNIX) {
-                i = get_bytes(hdr->user, header_size-n, sizeof(hdr->user)-1);
-                hdr->user[i] = '\0';
-            }
+            i = get_bytes(hdr->user, header_size-n, sizeof(hdr->user)-1);
+            hdr->user[i] = '\0';
             break;
         case 0x54:
             /* UNIX last modified time */
-            if (hdr->extend_type == EXTEND_UNIX)
-                hdr->unix_last_modified_stamp = (time_t) get_longword();
+            hdr->unix_last_modified_stamp = (time_t) get_longword();
             break;
         default:
             /* other headers */
+            /* 0x39: multi-disk header
+               0x3f: uncompressed comment
+               0x42: 64bit large file size
+               0x48-0x4f(?): reserved for authenticity verification
+               0x7d: encapsulation
+               0x7e: extended attribute - platform information
+               0x7f: extended attribute - permission, owner-id and timestamp
+                     (level 3 on OS/2)
+               0xc4: compressed comment (dict size: 4096)
+               0xc5: compressed comment (dict size: 8192)
+               0xc6: compressed comment (dict size: 16384)
+               0xc7: compressed comment (dict size: 32768)
+               0xc8: compressed comment (dict size: 65536)
+               0xd0-0xdf(?): operating systemm specific information
+               0xfc: encapsulation (another opinion)
+               0xfe: extended attribute - platform information(another opinion)
+               0xff: extended attribute - permission, owner-id and timestamp
+                     (level 3 on UNLHA32) */
             if (verbose)
                 warning("unknown extended header 0x%02x", ext_type);
             skip_bytes(header_size - n);
             break;
         }
 
-    next:
         if (hcrc)
             *hcrc = calccrc(*hcrc, data, header_size);
 
@@ -649,6 +607,7 @@ get_extended_header(fp, hdr, header_size, hcrc)
             whole_size += header_size = get_longword();
     }
 
+    /* concatenate dirname and filename */
     if (dir_length) {
         if (name_length + dir_length >= sizeof(hdr->name)) {
             warning("the length of pathname \"%s%s\" is too long.",
@@ -656,14 +615,29 @@ 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;
     }
 
     return whole_size;
 }
 
+#define I_HEADER_SIZE           0               /* level 0,1,2   */
+#define I_HEADER_CHECKSUM       1               /* level 0,1     */
+#define I_METHOD                2               /* level 0,1,2,3 */
+#define I_PACKED_SIZE           7               /* level 0,1,2,3 */
+#define I_ATTRIBUTE             19              /* level 0,1,2,3 */
+#define I_HEADER_LEVEL          20              /* level 0,1,2,3 */
+
+#define COMMON_HEADER_SIZE      21      /* size of common part */
+
+#define I_GENERIC_HEADER_SIZE 24 /* + name_length */
+#define I_LEVEL0_HEADER_SIZE  36 /* + name_length (unix extended) */
+#define I_LEVEL1_HEADER_SIZE  27 /* + name_length */
+#define I_LEVEL2_HEADER_SIZE  26 /* + padding */
+#define I_LEVEL3_HEADER_SIZE  32
+
 /*
  * level 0 header
  *
@@ -685,8 +659,10 @@ get_extended_header(fp, hdr, header_size, hcrc)
  * X +22      2  file crc (CRC-16)                 |
  * X +24      Y  ext-header(old style)             v
  * -------------------------------------------------
- * X+Y+24   [*2] data
- *            :
+ * X+Y+24        data                              ^
+ *                 :                               | [*2] packed size
+ *                 :                               v
+ * -------------------------------------------------
  *
  * ext-header(old style)
  *     0      1  ext-type ('U')
@@ -711,17 +687,18 @@ get_header_level0(fp, hdr, data)
     LzHeader *hdr;
     char *data;
 {
-    int header_size;
+    size_t header_size;
+    ssize_t extend_size;
     int checksum;
     int name_length;
     int i;
-    int extend_size;
 
     hdr->size_field_length = 2; /* in bytes */
     hdr->header_size = header_size = get_byte();
     checksum = get_byte();
 
-    if (fread(data+I_NAME_LENGTH, header_size+2-I_NAME_LENGTH, 1, fp) == 0) {
+    if (fread(data + COMMON_HEADER_SIZE,
+              header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) {
         error("Invalid header (LHarc file ?)");
         return FALSE;   /* finish */
     }
@@ -742,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;
 
@@ -755,7 +732,7 @@ get_header_level0(fp, hdr, data)
             hdr->has_crc = FALSE;
 
             return TRUE;
-        } 
+        }
 
         error("Unkonwn header (lha file?)");
         exit(1);
@@ -826,7 +803,8 @@ get_header_level1(fp, hdr, data)
     LzHeader *hdr;
     char *data;
 {
-    int header_size, extend_size;
+    size_t header_size;
+    ssize_t extend_size;
     int checksum;
     int name_length;
     int i, dummy;
@@ -835,7 +813,8 @@ get_header_level1(fp, hdr, data)
     hdr->header_size = header_size = get_byte();
     checksum = get_byte();
 
-    if (fread(data+I_NAME_LENGTH, header_size+2-I_NAME_LENGTH, 1, fp) == 0) {
+    if (fread(data + COMMON_HEADER_SIZE,
+              header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) {
         error("Invalid header (LHarc file ?)");
         return FALSE;   /* finish */
     }
@@ -857,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;
 
@@ -865,7 +844,7 @@ get_header_level1(fp, hdr, data)
     hdr->crc = get_word();
     hdr->extend_type = get_byte();
 
-    dummy = header_size+2 - name_length - 27;
+    dummy = header_size+2 - name_length - I_LEVEL1_HEADER_SIZE;
     if (dummy > 0)
         skip_bytes(dummy); /* skip old style extend header */
 
@@ -918,14 +897,16 @@ get_header_level2(fp, hdr, data)
     LzHeader *hdr;
     char *data;
 {
-    int header_size, extend_size;
+    size_t header_size;
+    ssize_t extend_size;
     int padding;
     unsigned int hcrc;
 
     hdr->size_field_length = 2; /* in bytes */
     hdr->header_size = header_size = get_word();
 
-    if (fread(data + I_NAME_LENGTH, 26 - I_NAME_LENGTH, 1, fp) == 0) {
+    if (fread(data + COMMON_HEADER_SIZE,
+              I_LEVEL2_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
         error("Invalid header (LHarc file ?)");
         return FALSE;   /* finish */
     }
@@ -938,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;
 
@@ -954,7 +935,7 @@ get_header_level2(fp, hdr, data)
     if (extend_size == -1)
         return FALSE;
 
-    padding = header_size - 26 - extend_size;
+    padding = header_size - I_LEVEL2_HEADER_SIZE - extend_size;
     while (padding--)           /* padding should be 0 or 1 */
         hcrc = UPDATE_CRC(hcrc, fgetc(fp));
 
@@ -997,13 +978,15 @@ get_header_level3(fp, hdr, data)
     LzHeader *hdr;
     char *data;
 {
-    long header_size, extend_size;
+    size_t header_size;
+    ssize_t extend_size;
     int padding;
     unsigned int hcrc;
 
     hdr->size_field_length = get_word();
 
-    if (fread(data + I_NAME_LENGTH, 32 - I_NAME_LENGTH, 1, fp) == 0) {
+    if (fread(data + COMMON_HEADER_SIZE,
+              I_LEVEL3_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) {
         error("Invalid header (LHarc file ?)");
         return FALSE;   /* finish */
     }
@@ -1016,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;
 
@@ -1033,7 +1016,7 @@ get_header_level3(fp, hdr, data)
     if (extend_size == -1)
         return FALSE;
 
-    padding = header_size - 32 - extend_size;
+    padding = header_size - I_LEVEL3_HEADER_SIZE - extend_size;
     while (padding--)           /* padding should be 0 */
         hcrc = UPDATE_CRC(hcrc, fgetc(fp));
 
@@ -1067,7 +1050,7 @@ get_header(fp, hdr)
     }
     data[0] = end_mark;
 
-    if (fread(data + 1, I_NAME_LENGTH - 1, 1, fp) == 0) {
+    if (fread(data + 1, COMMON_HEADER_SIZE - 1, 1, fp) == 0) {
         error("Invalid header (LHarc file ?)");
         return FALSE;           /* finish */
     }
@@ -1116,9 +1099,6 @@ get_header(fp, hdr)
 
     default:
         filename_case = noconvertcase ? NONE : TO_LOWER;
-        /* FIXME: if small letter is included in filename,
-           the generic_to_unix_filename() do not case conversion,
-           but this code does not consider it. */
         break;
     }
 
@@ -1139,17 +1119,108 @@ get_header(fp, hdr)
                      system_kanji_code,
                      archive_delim, system_delim, filename_case);
 
+    if (S_ISLNK(hdr->unix_mode)) {
+        char *p;
+        /* split symbolic link */
+        p = strchr(hdr->name, '|');
+        if (p) {
+            /* hdr->name is symbolic link name */
+            /* hdr->realname is real name */
+            *p = 0;
+            strcpy(hdr->realname, p+1); /* ok */
+        }
+        else
+            error("unknown symlink name \"%s\"", hdr->name);
+    }
+
     return TRUE;
 }
 
-/* ------------------------------------------------------------------------ */
+/* skip SFX header */
+int
+seek_lha_header(fp)
+    FILE *fp;
+{
+    unsigned char   buffer[64 * 1024]; /* max seek size */
+    unsigned char  *p;
+    int             n;
+
+    n = fread(buffer, 1, sizeof(buffer), fp);
+
+    for (p = buffer; p < buffer + n; p++) {
+        if (! (p[I_METHOD]=='-' && p[I_METHOD+1]=='l' && p[I_METHOD+4]=='-'))
+            continue;
+        /* found "-l??-" keyword (as METHOD type string) */
+
+        /* 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 (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
+                fatal_error("cannot seek header");
+            return 0;
+        }
+
+        /* level 2 header */
+        if (p[I_HEADER_LEVEL] == 2
+            && p[I_HEADER_SIZE] >= 24
+            && p[I_ATTRIBUTE] == 0x20) {
+            if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1)
+                fatal_error("cannot seek header");
+            return 0;
+        }
+    }
+
+    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;
-       struct stat    *v_stat;
-       LzHeader       *hdr;
+    char           *name;
+    struct stat    *v_stat;
+    LzHeader       *hdr;
 {
-       int             len;
+    int             len;
 
     memset(hdr, 0, sizeof(LzHeader));
 
@@ -1157,24 +1228,25 @@ init_header(name, v_stat, hdr)
        but need set for empty files */
     memcpy(hdr->method, LZHUFF0_METHOD, METHOD_TYPE_STORAGE);
 
-       hdr->packed_size = 0;
-       hdr->original_size = v_stat->st_size;
-       hdr->attribute = GENERIC_ATTRIBUTE;
-       hdr->header_level = header_level;
-       strcpy(hdr->name, name);
-       len = strlen(name);
-       hdr->crc = 0x0000;
-       hdr->extend_type = EXTEND_UNIX;
-       hdr->unix_last_modified_stamp = v_stat->st_mtime;
-       /* since 00:00:00 JAN.1.1970 */
+    hdr->packed_size = 0;
+    hdr->original_size = v_stat->st_size;
+    hdr->attribute = GENERIC_ATTRIBUTE;
+    hdr->header_level = header_level;
+    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;
+    /* since 00:00:00 JAN.1.1970 */
 #ifdef NOT_COMPATIBLE_MODE
-       /* Please need your modification in this space. */
+    /* Please need your modification in this space. */
 #else
-       hdr->unix_mode = v_stat->st_mode;
+    hdr->unix_mode = v_stat->st_mode;
 #endif
 
-       hdr->unix_uid = v_stat->st_uid;
-       hdr->unix_gid = v_stat->st_gid;
+    hdr->unix_uid = v_stat->st_uid;
+    hdr->unix_gid = v_stat->st_gid;
 
 #if INCLUDE_OWNER_NAME_IN_HEADER
 #if HAVE_GETPWUID
@@ -1200,26 +1272,26 @@ init_header(name, v_stat, hdr)
     }
 #endif
 #endif /* INCLUDE_OWNER_NAME_IN_HEADER */
-       if (is_directory(v_stat)) {
-               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++], "/");
-       }
-
-#ifdef S_IFLNK
-       if (is_symlink(v_stat)) {
-               char    lkname[FILENAME_LENGTH];
-               int             len;
-               memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
-               hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
-               hdr->original_size = 0;
-               len = readlink(name, lkname, sizeof(lkname));
-               if (xsnprintf(hdr->name, sizeof(hdr->name),
-                      "%s|%.*s", hdr->name, len, lkname) == -1)
-            error("file name is too long (%s -> %.*s)", hdr->name, len, lkname);
-       }
+    if (is_directory(v_stat)) {
+        memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
+        hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
+        hdr->original_size = 0;
+        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);
+        }
+    }
+
+#if defined(S_IFLNK) && !defined(__DJGPP__)
+    if (is_symlink(v_stat)) {
+        memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
+        hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
+        hdr->original_size = 0;
+        readlink(name, hdr->realname, sizeof(hdr->realname));
+    }
 #endif
 }
 
@@ -1259,17 +1331,14 @@ write_unix_info(hdr)
     }
 }
 
-/* ------------------------------------------------------------------------ */
-/* Write unix extended header or generic header. */
-
-static int
+static size_t
 write_header_level0(data, hdr, pathname)
     LzHeader *hdr;
     char *data, *pathname;
 {
     int limit;
     int name_length;
-    int header_size;
+    size_t header_size;
 
     setup_put(data);
     memset(data, 0, LZHEADER_STORAGE);
@@ -1286,9 +1355,9 @@ write_header_level0(data, hdr, pathname)
     /* write pathname (level 0 header contains the directory part) */
     name_length = strlen(pathname);
     if (generic_format)
-        limit = 255 - I_GENERIC_HEADER_BOTTOM + 2;
+        limit = 255 - I_GENERIC_HEADER_SIZE + 2;
     else
-        limit = 255 - I_UNIX_EXTEND_BOTTOM + 2;
+        limit = 255 - I_LEVEL0_HEADER_SIZE + 2;
 
     if (name_length > limit) {
         warning("the length of pathname \"%s\" is too long.", pathname);
@@ -1299,7 +1368,7 @@ write_header_level0(data, hdr, pathname)
     put_word(hdr->crc);
 
     if (generic_format) {
-        header_size = I_GENERIC_HEADER_BOTTOM + name_length - 2;
+        header_size = I_GENERIC_HEADER_SIZE + name_length - 2;
         data[I_HEADER_SIZE] = header_size;
         data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
     } else {
@@ -1312,7 +1381,7 @@ write_header_level0(data, hdr, pathname)
         put_word(hdr->unix_gid);
 
         /* size of extended header is 12 */
-        header_size = I_UNIX_EXTEND_BOTTOM + name_length - 2;
+        header_size = I_LEVEL0_HEADER_SIZE + name_length - 2;
         data[I_HEADER_SIZE] = header_size;
         data[I_HEADER_CHECKSUM] = calc_sum(data + I_METHOD, header_size);
     }
@@ -1320,16 +1389,16 @@ write_header_level0(data, hdr, pathname)
     return header_size + 2;
 }
 
-static int
+static size_t
 write_header_level1(data, hdr, pathname)
     LzHeader *hdr;
     char *data, *pathname;
 {
     int name_length, dir_length, limit;
     char *basename, *dirname;
-    int header_size;
+    size_t header_size;
     char *extend_header_top;
-    int extend_header_size;
+    size_t extend_header_size;
 
     basename = strrchr(pathname, LHA_PATHSEP);
     if (basename) {
@@ -1358,7 +1427,7 @@ write_header_level1(data, hdr, pathname)
     put_byte(hdr->header_level); /* level 1 */
 
     /* level 1 header: write filename (basename only) */
-    limit = 255 - 27 + 2;
+    limit = 255 - I_LEVEL1_HEADER_SIZE + 2;
     if (name_length > limit) {
         put_byte(0);            /* name length */
     }
@@ -1412,14 +1481,14 @@ write_header_level1(data, hdr, pathname)
     return header_size + extend_header_size + 2;
 }
 
-static int
+static size_t
 write_header_level2(data, hdr, pathname)
     LzHeader *hdr;
     char *data, *pathname;
 {
     int name_length, dir_length;
     char *basename, *dirname;
-    int header_size;
+    size_t header_size;
     char *extend_header_top;
     char *headercrc_ptr;
     unsigned int hcrc;
@@ -1491,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);
@@ -1509,7 +1578,7 @@ write_header(fp, hdr)
     FILE           *fp;
     LzHeader       *hdr;
 {
-    int header_size;
+    size_t header_size;
     char data[LZHEADER_STORAGE];
 
     int archive_kanji_code = CODE_SJIS;
@@ -1527,12 +1596,27 @@ write_header(fp, hdr)
     if (generic_format)
         filename_case = TO_UPPER;
 
-    if (hdr->header_level == HEADER_LEVEL0) {
+    if (hdr->header_level == 0) {
         archive_delim = "\\";
     }
 
-    strncpy(pathname, hdr->name, sizeof(pathname));
-    pathname[sizeof(pathname)-1] = 0;
+    if (S_ISLNK(hdr->unix_mode)) {
+        char *p;
+        p = strchr(hdr->name, '|');
+        if (p) {
+            error("symlink name \"%s\" contains '|' char. change it into '_'",
+                  hdr->name);
+            *p = '_';
+        }
+        if (xsnprintf(pathname, sizeof(pathname),
+                      "%s|%s", hdr->name, hdr->realname) == -1)
+            error("file name is too long (%s -> %s)", hdr->name, hdr->realname);
+    }
+    else {
+        strncpy(pathname, hdr->name, sizeof(pathname));
+        pathname[sizeof(pathname)-1] = 0;
+    }
+
     convert_filename(pathname, strlen(pathname), sizeof(pathname),
                      system_kanji_code,
                      archive_kanji_code,
@@ -1559,7 +1643,7 @@ write_header(fp, hdr)
 
 #if MULTIBYTE_FILENAME
 
-#if defined(__APPLE__)
+#if defined(__APPLE__)  /* Added by Hiroto Sakai */
 
 #include <CoreFoundation/CFString.h>
 #include <CoreFoundation/CFStringEncodingExt.h>
@@ -1740,8 +1824,8 @@ utf8_to_sjis(char *dst, const char *src, size_t dstsize)
 
 /*
  * SJIS <-> EUC ÊÑ´¹´Ø¿ô
- * ¡ÖÆüËܸì¾ðÊó½èÍý¡×  ¥½¥Õ¥È¥Ð¥ó¥¯(³ô)
- *     ¤è¤êÈ´¿è(by Koji Arai)
+ * ¡ÖÆüËܸì¾ðÊó½èÍý¡×   ¥½¥Õ¥È¥Ð¥ó¥¯(³ô)
+ *  ¤è¤êÈ´¿è(by Koji Arai)
  */
 void
 euc2sjis(int *p1, int *p2)
@@ -1768,11 +1852,106 @@ sjis2euc(int *p1, int *p2)
     *p1 |= 0x80;
     *p2 |= 0x80;
 }
-#endif /* MULTIBYTE_FILENAME */
 
-/* Local Variables: */
-/* mode:c */
-/* tab-width:4 */
-/* compile-command:"gcc -c header.c" */
-/* End: */
-/* vi: set tabstop=4: */
+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 */