OSDN Git Service

Fix a bug: Could not extract 2G over files.
[lha/lha.git] / src / header.c
index 54a48d2..dd251c0 100644 (file)
@@ -1,18 +1,23 @@
 /* ------------------------------------------------------------------------ */
-/* 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"
 
+#define DUMP_HEADER 1           /* for debugging */
+
 #if !STRCHR_8BIT_CLEAN
 /* should use 8 bit clean version */
 #undef strchr
 #define strrchr  xstrrchr
 #endif
 
-/* ------------------------------------------------------------------------ */
 static char    *get_ptr;
-#define setup_get(PTR) (get_ptr = (PTR))
-#define get_byte()             (*get_ptr++ & 0xff)
-#define put_ptr                        get_ptr
-#define setup_put(PTR) (put_ptr = (PTR))
-#define put_byte(c)            (*put_ptr++ = (char)(c))
+#define GET_BYTE()      (*get_ptr++ & 0xff)
+
+#if DUMP_HEADER
+static char    *start_ptr;
+#define setup_get(PTR)  (start_ptr = get_ptr = (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 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))
 
 int optional_archive_kanji_code = NONE;
 int optional_system_kanji_code = NONE;
@@ -41,72 +55,185 @@ 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;
 }
 
-/* ------------------------------------------------------------------------ */
-static unsigned short
+#if DUMP_HEADER
+static int
+dump_get_byte()
+{
+    int c;
+
+    if (verbose_listing && verbose > 1)
+        printf("%02d %2d: ", get_ptr - start_ptr, 1);
+    c = GET_BYTE();
+    if (verbose_listing && verbose > 1) {
+        if (isprint(c))
+            printf("%d(0x%02x) '%c'\n", c, c, c);
+        else
+            printf("%d(0x%02x)\n", c, c);
+    }
+    return c;
+}
+
+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);
+        while (len--)
+            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;
 
-       b0 = get_byte();
-       b1 = get_byte();
-       return (b1 << 8) + b0;
+#if DUMP_HEADER
+    if (verbose_listing && verbose > 1)
+        printf("%02d %2d: ", get_ptr - start_ptr, 2);
+#endif
+    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;
 }
 
-/* ------------------------------------------------------------------------ */
 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;
 
-       b0 = get_byte();
-       b1 = get_byte();
-       b2 = get_byte();
-       b3 = get_byte();
-       return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
+#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();
+    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;
 }
 
-/* ------------------------------------------------------------------------ */
 static void
 put_longword(v)
-       long            v;
+    long v;
+{
+    put_byte(v);
+    put_byte(v >> 8);
+    put_byte(v >> 16);
+    put_byte(v >> 24);
+}
+
+#ifdef HAVE_UINT64_T
+static uint64_t
+get_longlongword()
 {
-       put_byte(v);
-       put_byte(v >> 8);
-       put_byte(v >> 16);
-       put_byte(v >> 24);
+    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;
     int len, size;
 {
     int i;
+
+#if DUMP_HEADER
+    if (verbose_listing && verbose > 1)
+        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];
+#endif
+
     get_ptr += len;
     return i;
 }
@@ -128,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;
 
@@ -137,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++)
@@ -164,6 +305,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 &&
@@ -207,8 +363,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) {
@@ -245,245 +401,155 @@ 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
  *
  *             size  field name
  *  --------------------------------
  *  base header:         :
- *                2  next-header size  [*1]
+ *           2 or 4  next-header size  [*1]
  *  --------------------------------------
  *  ext header:   1  ext-type            ^
  *                ?  contents            | [*1] next-header size
- *                2  next-header size    v
+ *           2 or 4  next-header size    v
  *  --------------------------------------
  *
+ *  on level 1, 2 header:
+ *    size field is 2 bytes
+ *  on level 3 header:
+ *    size field is 4 bytes
  */
-static int
-get_extended_header(fp, hdr, header_size)
+
+static ssize_t
+get_extended_header(fp, hdr, header_size, hcrc)
     FILE *fp;
     LzHeader *hdr;
-    int header_size;
+    size_t header_size;
+    unsigned int *hcrc;
 {
     char data[LZHEADER_STORAGE];
     int name_length;
     char dirname[FILENAME_LENGTH];
     int dir_length = 0;
     int i;
-    int 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' */
 
     if (hdr->header_level == 0)
         return 0;
 
     name_length = strlen(hdr->name);
 
-    for (; header_size != 0; whole_size += header_size = get_word()) {
+    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 too large.");
+            error("header size (%ld) too large.", header_size);
             exit(1);
         }
 
@@ -491,67 +557,149 @@ get_extended_header(fp, hdr, header_size)
             error("Invalid header (LHa file ?)");
             return -1;
         }
-        switch (get_byte()) {
+
+        ext_type = get_byte();
+        switch (ext_type) {
         case 0:
-            /* header crc */
-            setup_get(get_ptr + header_size - 3); /* FIXME: ignored? */
+#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. */
+            data[1] = data[2] = 0;
+            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-3, sizeof(hdr->name)-1);
+                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-3, sizeof(dirname)-1);
+            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 */
-            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:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < Windows time stamp (FILETIME) >\n");
+#endif
+            /* 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 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 */
-            if (hdr->extend_type == EXTEND_UNIX)
-                hdr->unix_mode = get_word();
+            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 */
-            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:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX group name >\n");
+#endif
             /* UNIX group name */
-            if (hdr->extend_type == EXTEND_UNIX) {
-                i = get_bytes(hdr->group, header_size-3, 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:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX user name >\n");
+#endif
             /* UNIX user name */
-            if (hdr->extend_type == EXTEND_UNIX) {
-                i = get_bytes(hdr->user, header_size-3, 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:
+#if DUMP_HEADER
+            if (verbose_listing && verbose > 1) printf("     < UNIX last modifed time (time_t) >\n");
+#endif
             /* 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 */
-            setup_get(get_ptr + header_size - 3);
+            /* 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;
         }
+
+        if (hcrc)
+            *hcrc = calccrc(*hcrc, data, header_size);
+
+        if (hdr->size_field_length == 2)
+            whole_size += header_size = get_word();
+        else
+            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.",
@@ -559,14 +707,29 @@ get_extended_header(fp, hdr, header_size)
             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
  *
@@ -585,11 +748,13 @@ get_extended_header(fp, hdr, header_size)
  *    20      1  level (0x00 fixed)                |
  *    21      1  name length                       |
  *    22      X  pathname                          |
- * X +22      2  file crc                          |
+ * 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')
@@ -614,16 +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 */
     }
@@ -634,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();
@@ -657,7 +824,7 @@ get_header_level0(fp, hdr, data)
             hdr->has_crc = FALSE;
 
             return TRUE;
-        } 
+        }
 
         error("Unkonwn header (lha file?)");
         exit(1);
@@ -685,8 +852,9 @@ get_header_level0(fp, hdr, data)
         }
     }
     if (extend_size > 0)
-        setup_get(get_ptr + extend_size);
+        skip_bytes(extend_size);
 
+    hdr->header_size += 2;
     return TRUE;
 }
 
@@ -708,7 +876,7 @@ get_header_level0(fp, hdr, data)
  *    20       1  level (0x01 fixed)               |
  *    21       1  name length                      |
  *    22       X  filename                         |
- * X+ 22       2  file crc                         |
+ * X+ 22       2  file crc (CRC-16)                |
  * X+ 24       1  OS ID                            |
  * X +25       Y  ???                              |
  * X+Y+25      2  next-header size                 v
@@ -727,15 +895,18 @@ 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;
 
+    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 */
     }
@@ -746,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();
@@ -765,12 +936,12 @@ 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)
-        setup_get(get_ptr + dummy); /* skip old style extend header */
+        skip_bytes(dummy); /* skip old style extend header */
 
     extend_size = get_word();
-    extend_size = get_extended_header(fp, hdr, extend_size);
+    extend_size = get_extended_header(fp, hdr, extend_size, 0);
     if (extend_size == -1)
         return FALSE;
 
@@ -778,7 +949,7 @@ get_header_level1(fp, hdr, data)
     /* the `packed_size' field contains the extended header size. */
     /* the `header_size' field does not. */
     hdr->packed_size -= extend_size;
-    hdr->header_size += extend_size;
+    hdr->header_size += extend_size + 2;
 
     return TRUE;
 }
@@ -795,9 +966,9 @@ get_header_level1(fp, hdr, data)
  *     7       4  packed size       [*2]           |
  *    11       4  original size                    |
  *    15       4  time                             |
- *    19       1  RESERVED                         | [*1] total header size
+ *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
  *    20       1  level (0x02 fixed)               |      (X+26+(1))
- *    21       2  file crc                         |
+ *    21       2  file crc (CRC-16)                |
  *    23       1  OS ID                            |
  *    24       2  next-header size                 |
  * -----------------------------------             |
@@ -818,19 +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();
@@ -845,15 +1020,100 @@ get_header_level2(fp, hdr, data)
     hdr->extend_type = get_byte();
     extend_size = get_word();
 
-    extend_size = get_extended_header(fp, hdr, extend_size);
+    INITIALIZE_CRC(hcrc);
+    hcrc = calccrc(hcrc, data, get_ptr - data);
+
+    extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
     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 */
-        fgetc(fp);
+        hcrc = UPDATE_CRC(hcrc, fgetc(fp));
 
-    /* FIXME: no collate the header CRC */
+    if (hdr->header_crc != hcrc)
+        error("header CRC error");
+
+    return TRUE;
+}
+
+/*
+ * level 3 header
+ *
+ *
+ * offset   size  field name
+ * --------------------------------------------------
+ *     0       2  size field length (4 fixed)      ^
+ *     2       5  method ID                        |
+ *     7       4  packed size       [*2]           |
+ *    11       4  original size                    |
+ *    15       4  time                             |
+ *    19       1  RESERVED (0x20 fixed)            | [*1] total header size
+ *    20       1  level (0x03 fixed)               |      (X+32)
+ *    21       2  file crc (CRC-16)                |
+ *    23       1  OS ID                            |
+ *    24       4  total header size [*1]           |
+ *    28       4  next-header size                 |
+ * -----------------------------------             |
+ *    32       X  ext-header                       |
+ *                 :                               v
+ * -------------------------------------------------
+ * X +32          data                             ^
+ *                 :                               | [*2] packed size
+ *                 :                               v
+ * -------------------------------------------------
+ *
+ */
+static int
+get_header_level3(fp, hdr, data)
+    FILE *fp;
+    LzHeader *hdr;
+    char *data;
+{
+    size_t header_size;
+    ssize_t extend_size;
+    int padding;
+    unsigned int hcrc;
+
+    hdr->size_field_length = get_word();
+
+    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 = (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();
+
+    /* defaults for other type */
+    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
+    hdr->unix_gid = 0;
+    hdr->unix_uid = 0;
+
+    hdr->has_crc = TRUE;
+    hdr->crc = get_word();
+    hdr->extend_type = get_byte();
+    hdr->header_size = header_size = get_longword();
+    extend_size = get_longword();
+
+    INITIALIZE_CRC(hcrc);
+    hcrc = calccrc(hcrc, data, get_ptr - data);
+
+    extend_size = get_extended_header(fp, hdr, extend_size, &hcrc);
+    if (extend_size == -1)
+        return FALSE;
+
+    padding = header_size - I_LEVEL3_HEADER_SIZE - extend_size;
+    while (padding--)           /* padding should be 0 */
+        hcrc = UPDATE_CRC(hcrc, fgetc(fp));
+
+    if (hdr->header_crc != hcrc)
+        error("header CRC error");
 
     return TRUE;
 }
@@ -882,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 */
     }
@@ -900,6 +1160,10 @@ get_header(fp, hdr)
         if (get_header_level2(fp, hdr, data) == FALSE)
             return FALSE;
         break;
+    case 3:
+        if (get_header_level3(fp, hdr, data) == FALSE)
+            return FALSE;
+        break;
     default:
         error("Unknown level header (level %d)", data[I_HEADER_LEVEL]);
         return FALSE;
@@ -908,12 +1172,13 @@ 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:
     case EXTEND_XOSK:
     case EXTEND_UNIX:
+    case EXTEND_JAVA:
         filename_case = NONE;
         break;
 
@@ -925,10 +1190,7 @@ get_header(fp, hdr)
         break;
 
     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. */
+        filename_case = convertcase ? TO_LOWER : NONE;
         break;
     }
 
@@ -949,45 +1211,220 @@ 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));
 
-       if (compress_method == LZHUFF5_METHOD_NUM)  /* Changed N.Watazaki */
-               memcpy(hdr->method, LZHUFF5_METHOD, METHOD_TYPE_STORAGE);
-       else if (compress_method)
-               memcpy(hdr->method, LZHUFF1_METHOD, METHOD_TYPE_STORAGE);
-       else
-               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 */
+    /* the `method' member is rewrote by the encoding function.
+       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;
+
+    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
@@ -1013,40 +1450,73 @@ 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
 }
 
-/* ------------------------------------------------------------------------ */
-/* Write unix extended header or generic header. */
+static void
+write_unix_info(hdr)
+    LzHeader *hdr;
+{
+    /* UNIX specific informations */
+
+    put_word(5);            /* size */
+    put_byte(0x50);         /* permission */
+    put_word(hdr->unix_mode);
+
+    put_word(7);            /* size */
+    put_byte(0x51);         /* gid and uid */
+    put_word(hdr->unix_gid);
+    put_word(hdr->unix_uid);
+
+    if (hdr->group[0]) {
+        int len = strlen(hdr->group);
+        put_word(len + 3);  /* size */
+        put_byte(0x52);     /* group name */
+        put_bytes(hdr->group, len);
+    }
 
-static int
-write_header_level0(data, hdr, lzname)
+    if (hdr->user[0]) {
+        int len = strlen(hdr->user);
+        put_word(len + 3);  /* size */
+        put_byte(0x53);     /* user name */
+        put_bytes(hdr->user, len);
+    }
+
+    if (hdr->header_level == 1) {
+        put_word(7);        /* size */
+        put_byte(0x54);     /* time stamp */
+        put_longword(hdr->unix_last_modified_stamp);
+    }
+}
+
+static size_t
+write_header_level0(data, hdr, pathname)
     LzHeader *hdr;
-    char *data, *lzname;
+    char *data, *pathname;
 {
     int limit;
     int name_length;
-    int header_size;
+    size_t header_size;
 
     setup_put(data);
     memset(data, 0, LZHEADER_STORAGE);
@@ -1060,23 +1530,23 @@ write_header_level0(data, hdr, lzname)
     put_byte(hdr->attribute);
     put_byte(hdr->header_level); /* level 0 */
 
-    /* level 0 header: write pathname (contain the directory part) */
-    name_length = strlen(lzname);
+    /* 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.", lzname);
+        warning("the length of pathname \"%s\" is too long.", pathname);
         name_length = limit;
     }
     put_byte(name_length);
-    put_bytes(lzname, name_length);
+    put_bytes(pathname, name_length);
     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 {
@@ -1089,7 +1559,7 @@ write_header_level0(data, hdr, lzname)
         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);
     }
@@ -1097,26 +1567,26 @@ write_header_level0(data, hdr, lzname)
     return header_size + 2;
 }
 
-static int
-write_header_level1(data, hdr, lzname)
+static size_t
+write_header_level1(data, hdr, pathname)
     LzHeader *hdr;
-    char *data, *lzname;
+    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(lzname, LHA_PATHSEP);
+    basename = strrchr(pathname, LHA_PATHSEP);
     if (basename) {
         basename++;
         name_length = strlen(basename);
-        dirname = lzname;
+        dirname = pathname;
         dir_length = basename - dirname;
     }
     else {
-        basename = lzname;
+        basename = pathname;
         name_length = strlen(basename);
         dirname = "";
         dir_length = 0;
@@ -1135,7 +1605,7 @@ write_header_level1(data, hdr, lzname)
     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 */
     }
@@ -1170,38 +1640,8 @@ write_header_level1(data, hdr, lzname)
         put_bytes(dirname, dir_length);
     }
 
-    if (!generic_format) {
-        /* UNIX specific informations */
-
-        put_word(5);            /* size */
-        put_byte(0x50);         /* permission */
-        put_word(hdr->unix_mode);
-
-        put_word(7);            /* size */
-        put_byte(0x51);         /* gid and uid */
-        put_word(hdr->unix_gid);
-        put_word(hdr->unix_uid);
-
-        if (hdr->group[0]) {
-            int len = strlen(hdr->group);
-            put_word(len + 3);  /* size */
-            put_byte(0x52);     /* group name */
-            put_bytes(hdr->group, len);
-        }
-
-        if (hdr->user[0]) {
-            int len = strlen(hdr->user);
-            put_word(len + 3);  /* size */
-            put_byte(0x53);     /* user name */
-            put_bytes(hdr->user, len);
-        }
-
-        if (hdr->header_level == 1) {
-            put_word(7);        /* size */
-            put_byte(0x54);     /* time stamp */
-            put_longword(hdr->unix_last_modified_stamp);
-        }
-    }       /* if generic .. */
+    if (!generic_format)
+        write_unix_info(hdr);
 
     put_word(0x0000);           /* next header size */
 
@@ -1219,27 +1659,27 @@ write_header_level1(data, hdr, lzname)
     return header_size + extend_header_size + 2;
 }
 
-static int
-write_header_level2(data, hdr, lzname)
+static size_t
+write_header_level2(data, hdr, pathname)
     LzHeader *hdr;
-    char *data, *lzname;
+    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 short hcrc;
+    unsigned int hcrc;
 
-    basename = strrchr(lzname, LHA_PATHSEP);
+    basename = strrchr(pathname, LHA_PATHSEP);
     if (basename) {
         basename++;
         name_length = strlen(basename);
-        dirname = lzname;
+        dirname = pathname;
         dir_length = basename - dirname;
     }
     else {
-        basename = lzname;
+        basename = pathname;
         name_length = strlen(basename);
         dirname = "";
         dir_length = 0;
@@ -1285,38 +1725,8 @@ write_header_level2(data, hdr, lzname)
         put_bytes(dirname, dir_length);
     }
 
-    if (!generic_format) {
-        /* UNIX specific informations */
-
-        put_word(5);            /* size */
-        put_byte(0x50);         /* permission */
-        put_word(hdr->unix_mode);
-
-        put_word(7);            /* size */
-        put_byte(0x51);         /* gid and uid */
-        put_word(hdr->unix_gid);
-        put_word(hdr->unix_uid);
-
-        if (hdr->group[0]) {
-            int len = strlen(hdr->group);
-            put_word(len + 3);  /* size */
-            put_byte(0x52);     /* group name */
-            put_bytes(hdr->group, len);
-        }
-
-        if (hdr->user[0]) {
-            int len = strlen(hdr->user);
-            put_word(len + 3);  /* size */
-            put_byte(0x53);     /* user name */
-            put_bytes(hdr->user, len);
-        }
-
-        if (hdr->header_level == 1) {
-            put_word(7);        /* size */
-            put_byte(0x54);     /* time stamp */
-            put_longword(hdr->unix_last_modified_stamp);
-        }
-    }       /* if generic .. */
+    if (!generic_format)
+        write_unix_info(hdr);
 
     put_word(0x0000);           /* next header size */
 
@@ -1328,12 +1738,13 @@ write_header_level2(data, hdr, lzname)
         header_size++;
     }
 
-    /* put hader size */
+    /* put header size */
     setup_put(data + I_HEADER_SIZE);
     put_word(header_size);
 
-    /* put hader CRC in extended header */
-    hcrc = calc_header_crc(data, (unsigned int) header_size);
+    /* put header CRC in extended header */
+    INITIALIZE_CRC(hcrc);
+    hcrc = calccrc(hcrc, data, (unsigned int) header_size);
     setup_put(headercrc_ptr);
     put_word(hcrc);
 
@@ -1345,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;
@@ -1360,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,
@@ -1395,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>
@@ -1576,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)
@@ -1604,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 */