OSDN Git Service

Fix a bug: Could not extract 2G over files.
[lha/lha.git] / src / header.c
index e919a35..dd251c0 100644 (file)
@@ -161,6 +161,50 @@ put_longword(v)
     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;
@@ -499,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);
@@ -513,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. */
@@ -520,21 +571,33 @@ 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) */
             /* it is time in 100 nano seconds since 1601-01-01 00:00:00 */
 
@@ -549,26 +612,55 @@ get_extended_header(fp, hdr, header_size, hcrc)
             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;
@@ -709,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();
@@ -719,7 +811,7 @@ get_header_level0(fp, hdr, data)
     hdr->name[i] = '\0';
 
     /* defaults for other type */
-    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
+    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -825,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();
@@ -836,7 +928,7 @@ get_header_level1(fp, hdr, data)
     hdr->name[i] = '\0';
 
     /* defaults for other type */
-    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
+    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -912,14 +1004,14 @@ get_header_level2(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 = get_longword();
     hdr->attribute = get_byte(); /* reserved */
     hdr->header_level = get_byte();
 
     /* defaults for other type */
-    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
+    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -992,14 +1084,14 @@ get_header_level3(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 = get_longword();
     hdr->attribute = get_byte(); /* reserved */
     hdr->header_level = get_byte();
 
     /* defaults for other type */
-    hdr->unix_mode = S_IFREG | UNIX_RW_RW_RW;
+    hdr->unix_mode = UNIX_FILE_REGULAR | UNIX_RW_RW_RW;
     hdr->unix_gid = 0;
     hdr->unix_uid = 0;
 
@@ -1080,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:
@@ -1098,7 +1190,7 @@ get_header(fp, hdr)
         break;
 
     default:
-        filename_case = noconvertcase ? NONE : TO_LOWER;
+        filename_case = convertcase ? TO_LOWER : NONE;
         break;
     }
 
@@ -1119,7 +1211,7 @@ get_header(fp, hdr)
                      system_kanji_code,
                      archive_delim, system_delim, filename_case);
 
-    if (S_ISLNK(hdr->unix_mode)) {
+    if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
         char *p;
         /* split symbolic link */
         p = strchr(hdr->name, '|');
@@ -1214,6 +1306,61 @@ remove_leading_dots(char *path)
     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;
@@ -1232,15 +1379,46 @@ init_header(name, v_stat, hdr)
     hdr->original_size = v_stat->st_size;
     hdr->attribute = GENERIC_ATTRIBUTE;
     hdr->header_level = header_level;
-    len = str_safe_copy(hdr->name,
-                        remove_leading_dots(name),
-                        sizeof(hdr->name));
+
+    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. */
+#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;
 #endif
@@ -1285,7 +1463,7 @@ init_header(name, v_stat, hdr)
         }
     }
 
-#if defined(S_IFLNK) && !defined(__DJGPP__)
+#ifdef S_IFLNK
     if (is_symlink(v_stat)) {
         memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE);
         hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE;
@@ -1593,14 +1771,14 @@ 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 == 0) {
         archive_delim = "\\";
     }
 
-    if (S_ISLNK(hdr->unix_mode)) {
+    if ((hdr->unix_mode & UNIX_FILE_SYMLINK) == UNIX_FILE_SYMLINK) {
         char *p;
         p = strchr(hdr->name, '|');
         if (p) {