OSDN Git Service

Fix a bug: Could not extract 2G over files.
[lha/lha.git] / src / header.c
index 47eb908..dd251c0 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
@@ -91,80 +91,120 @@ dump_skip_bytes(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);
 }
 
+#ifdef HAVE_UINT64_T
+static uint64_t
+get_longlongword()
+{
+    uint64_t b0, b1, b2, b3, b4, b5, b6, b7;
+    uint64_t 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();
+    b4 = GET_BYTE();
+    b5 = GET_BYTE();
+    b6 = GET_BYTE();
+    b7 = GET_BYTE();
+
+    l = (b7 << 24) + (b6 << 16) + (b5 << 8) + b4;
+    l <<= 32;
+    l |= (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
+#if DUMP_HEADER
+    if (verbose_listing && verbose > 1)
+        printf("%lld(%#016llx)\n", l, l);
+#endif
+    return l;
+}
+
+static void
+put_longlongword(uint64_t v)
+{
+    put_byte(v);
+    put_byte(v >> 8);
+    put_byte(v >> 16);
+    put_byte(v >> 24);
+    put_byte(v >> 32);
+    put_byte(v >> 40);
+    put_byte(v >> 48);
+    put_byte(v >> 56);
+}
+#endif
+
 static int
 get_bytes(buf, len, size)
     char *buf;
@@ -215,8 +255,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 +264,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,215 +401,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 */
-#if 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);
+#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
-    /* Compile error will be caused */
-    CANNOT GET TIMEZONE INFORMATION ON YOUR SYSTEM.
-    TAKE THE ANOTHER WAY.
-#endif
-#endif /* HAVE_STRUCT_TM_TM_GMTOFF */
-}
-#endif
-#endif                         /* defined(HAVE_FTIME) || defined(HAVE_GETTIMEOFDAY) ||
-                     * defined(HAVE_TZSET) */
+    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
  *
@@ -574,11 +520,12 @@ unix_to_generic_stamp(t)
  *  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];
@@ -586,7 +533,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' */
 
@@ -596,6 +543,10 @@ get_extended_header(fp, hdr, header_size, hcrc)
     name_length = strlen(hdr->name);
 
     while (header_size) {
+#if DUMP_HEADER
+        if (verbose_listing && verbose > 1)
+            printf("---\n");
+#endif
         setup_get(data);
         if (sizeof(data) < header_size) {
             error("header size (%ld) too large.", header_size);
@@ -610,6 +561,9 @@ get_extended_header(fp, hdr, header_size, hcrc)
         ext_type = get_byte();
         switch (ext_type) {
         case 0:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < header crc >\n");
+#endif
             /* header crc (CRC-16) */
             hdr->header_crc = get_word();
             /* clear buffer for CRC calculation. */
@@ -617,44 +571,96 @@ get_extended_header(fp, hdr, header_size, hcrc)
             skip_bytes(header_size - n - 2);
             break;
         case 1:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < filename >\n");
+#endif
             /* filename */
             name_length =
                 get_bytes(hdr->name, header_size-n, sizeof(hdr->name)-1);
             hdr->name[name_length] = 0;
             break;
         case 2:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < directory >\n");
+#endif
             /* directory */
             dir_length = get_bytes(dirname, header_size-n, sizeof(dirname)-1);
             dirname[dir_length] = 0;
             break;
         case 0x40:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < MS-DOS attribute >\n");
+#endif
             /* MS-DOS attribute */
             hdr->attribute = get_word();
             break;
         case 0x41:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < Windows time stamp (FILETIME) >\n");
+#endif
             /* Windows time stamp (FILETIME structure) */
-            skip_bytes(header_size - n); /* ignored */
+            /* 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 0x42:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < 64bits file size header >\n");
+#endif
+#ifdef HAVE_UINT64_T
+            /* 64bits file size header (UNLHA32 extension) */
+            hdr->packed_size = get_longlongword();
+            hdr->original_size = get_longlongword();
+#else
+            skip_bytes(8);
+            skip_bytes(8);
+#endif
+
             break;
         case 0x50:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX permission >\n");
+#endif
             /* UNIX permission */
             hdr->unix_mode = get_word();
             break;
         case 0x51:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX gid and uid >\n");
+#endif
             /* UNIX gid and uid */
             hdr->unix_gid = get_word();
             hdr->unix_uid = get_word();
             break;
         case 0x52:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX group name >\n");
+#endif
             /* UNIX group name */
             i = get_bytes(hdr->group, header_size-n, sizeof(hdr->group)-1);
             hdr->group[i] = '\0';
             break;
         case 0x53:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX user name >\n");
+#endif
             /* UNIX user name */
             i = get_bytes(hdr->user, header_size-n, sizeof(hdr->user)-1);
             hdr->user[i] = '\0';
             break;
         case 0x54:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX last modifed time (time_t) >\n");
+#endif
             /* UNIX last modified time */
             hdr->unix_last_modified_stamp = (time_t) get_longword();
             break;
@@ -701,14 +707,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
  *
@@ -758,17 +779,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 */
     }
@@ -779,8 +801,8 @@ get_header_level0(fp, hdr, data)
     }
 
     get_bytes(hdr->method, 5, sizeof(hdr->method));
-    hdr->packed_size = get_longword();
-    hdr->original_size = get_longword();
+    hdr->packed_size = (unsigned long)get_longword();
+    hdr->original_size = (unsigned long)get_longword();
     hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
     hdr->attribute = get_byte(); /* MS-DOS attribute */
     hdr->header_level = get_byte();
@@ -802,7 +824,7 @@ get_header_level0(fp, hdr, data)
             hdr->has_crc = FALSE;
 
             return TRUE;
-        } 
+        }
 
         error("Unkonwn header (lha file?)");
         exit(1);
@@ -873,7 +895,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;
@@ -882,7 +905,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 */
     }
@@ -893,8 +917,8 @@ get_header_level1(fp, hdr, data)
     }
 
     get_bytes(hdr->method, 5, sizeof(hdr->method));
-    hdr->packed_size = get_longword(); /* skip size */
-    hdr->original_size = get_longword();
+    hdr->packed_size = (unsigned long)get_longword(); /* skip size */
+    hdr->original_size = (unsigned long)get_longword();
     hdr->unix_last_modified_stamp = generic_to_unix_stamp(get_longword());
     hdr->attribute = get_byte(); /* 0x20 fixed */
     hdr->header_level = get_byte();
@@ -912,7 +936,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 */
 
@@ -965,21 +989,23 @@ 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 */
     }
 
     get_bytes(hdr->method, 5, sizeof(hdr->method));
-    hdr->packed_size = get_longword();
-    hdr->original_size = get_longword();
+    hdr->packed_size = (unsigned long)get_longword();
+    hdr->original_size = (unsigned long)get_longword();
     hdr->unix_last_modified_stamp = get_longword();
     hdr->attribute = get_byte(); /* reserved */
     hdr->header_level = get_byte();
@@ -1001,7 +1027,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));
 
@@ -1044,20 +1070,22 @@ 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 */
     }
 
     get_bytes(hdr->method, 5, sizeof(hdr->method));
-    hdr->packed_size = get_longword();
-    hdr->original_size = get_longword();
+    hdr->packed_size = (unsigned long)get_longword();
+    hdr->original_size = (unsigned long)get_longword();
     hdr->unix_last_modified_stamp = get_longword();
     hdr->attribute = get_byte(); /* reserved */
     hdr->header_level = get_byte();
@@ -1080,7 +1108,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));
 
@@ -1114,7 +1142,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 */
     }
@@ -1144,7 +1172,7 @@ get_header(fp, hdr)
     /* filename conversion */
     switch (hdr->extend_type) {
     case EXTEND_MSDOS:
-        filename_case = noconvertcase ? NONE : TO_LOWER;
+        filename_case = convertcase ? TO_LOWER : NONE;
         break;
     case EXTEND_HUMAN:
     case EXTEND_OS68K:
@@ -1162,7 +1190,7 @@ get_header(fp, hdr)
         break;
 
     default:
-        filename_case = noconvertcase ? NONE : TO_LOWER;
+        filename_case = convertcase ? TO_LOWER : NONE;
         break;
     }
 
@@ -1183,17 +1211,163 @@ get_header(fp, hdr)
                      system_kanji_code,
                      archive_delim, system_delim, filename_case);
 
+    if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
+        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;
+}
+
+static int
+copy_path_element(char *dst, const char *src, int size)
+{
+    int i;
+
+    if (size < 1) return 0;
+
+    for (i = 0; i < size; i++) {
+        dst[i] = src[i];
+       if (dst[i] == '\0')
+           return i;
+        if (dst[i] == '/') {
+            dst[++i] = 0;
+            return i;
+        }
+    }
+
+    dst[--i] = 0;
+
+    return i;
+}
+
+/* remove leading "xxx/../" and "./" */
+static int
+remove_dots(char *newpath, char *path, size_t size)
+{
+    int len;
+    char *p = newpath;
+
+    path = remove_leading_dots(path);
+
+    while (*path) {
+        if (path[0] == '.' && path[1] == '/')
+            path += 2;
+        else {
+            int len;
+            len = copy_path_element(newpath, path, size);
+
+            path += len;
+            newpath += len;
+            size -= len;
+            if (size <= 1)
+                break;
+        }
+    }
+
+    /* When newpath is empty, set "." */
+    if (newpath == p) {
+        strcpy(newpath, ".");
+        newpath++;
+    }
+
+    return newpath - p;         /* string length */
+}
+
 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));
 
@@ -1201,24 +1375,56 @@ 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 = remove_dots(hdr->name, 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. */
+#ifdef __DJGPP__
+    hdr->unix_mode = 0;
+    if (S_ISREG(v_stat->st_mode))
+           hdr->unix_mode = hdr->unix_mode | UNIX_FILE_REGULAR;
+    if (S_ISDIR(v_stat->st_mode))
+           hdr->unix_mode = hdr->unix_mode | UNIX_FILE_DIRECTORY;
+    if (S_ISLNK(v_stat->st_mode))
+           hdr->unix_mode = hdr->unix_mode | UNIX_FILE_SYMLINK;
+    if (v_stat->st_mode & S_IRUSR) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_OWNER_READ_PERM;
+    if (v_stat->st_mode & S_IRGRP) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_GROUP_READ_PERM;
+    if (v_stat->st_mode & S_IROTH) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_OTHER_READ_PERM;
+    if (v_stat->st_mode & S_IWUSR) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_OWNER_WRITE_PERM;
+    if (v_stat->st_mode & S_IWGRP) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_GROUP_WRITE_PERM;
+    if (v_stat->st_mode & S_IWOTH) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_OTHER_WRITE_PERM;
+    if (v_stat->st_mode & S_IXUSR) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_OWNER_EXEC_PERM;
+    if (v_stat->st_mode & S_IXGRP) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_GROUP_EXEC_PERM;
+    if (v_stat->st_mode & S_IXOTH) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_OTHER_EXEC_PERM;
+    if (v_stat->st_mode & S_ISUID) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_SETUID;
+    if (v_stat->st_mode & S_ISGID) 
+           hdr->unix_mode = hdr->unix_mode | UNIX_SETGID;
+#endif /* __DJGPP__ */
 #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
@@ -1244,26 +1450,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++], "/");
-       }
+    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);
+        }
+    }
 
 #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_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
 }
 
@@ -1303,17 +1509,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);
@@ -1330,9 +1533,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);
@@ -1343,7 +1546,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 {
@@ -1356,7 +1559,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);
     }
@@ -1364,16 +1567,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) {
@@ -1402,7 +1605,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 */
     }
@@ -1456,14 +1659,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;
@@ -1535,11 +1738,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);
@@ -1553,7 +1756,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;
@@ -1568,15 +1771,30 @@ write_header(fp, hdr)
     if (optional_system_kanji_code)
         system_kanji_code = optional_system_kanji_code;
 
-    if (generic_format)
+    if (generic_format && convertcase)
         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 ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
+        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,
@@ -1603,7 +1821,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>
@@ -1784,8 +2002,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)
@@ -1812,11 +2030,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 */