X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=src%2Fheader.c;h=035d5d26a027fe308d3dacd4693ec192c519a4cb;hb=82efba6a7e8997694bc94370a1d7aae808308e9a;hp=64d8b854ae836196c793e3b7ca4e711c929c72b8;hpb=710be467898c3bbebf6e1807699c3a0b5b195941;p=lha%2Flha.git diff --git a/src/header.c b/src/header.c index 64d8b85..035d5d2 100644 --- a/src/header.c +++ b/src/header.c @@ -10,6 +10,9 @@ /* Ver. 1.13b Symbolic Link Bug Fix 1994.08.22 N.Watazaki */ /* Ver. 1.14 Source All chagned 1995.01.14 N.Watazaki */ /* Ver. 1.14i bug fixed 2000.10.06 t.okamoto */ +/* Ver. 1.14i Contributed UTF-8 convertion for Mac OS X */ +/* 2002.06.29 Hiroto Sakai */ +/* Ver. 1.14i autoconfiscated & rewritten 2003.02.23 Koji Arai */ /* ------------------------------------------------------------------------ */ #include "lha.h" @@ -23,19 +26,17 @@ #define strrchr xstrrchr #endif -/* ------------------------------------------------------------------------ */ static char *get_ptr; -#define GET_BYTE() (*get_ptr++ & 0xff) +static char *start_ptr; +static off_t storage_size; +#define setup_get(PTR, SIZE) (start_ptr = get_ptr = (PTR), storage_size = (SIZE)) #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)) +#define get_byte() _get_byte() +#define skip_bytes(len) _skip_bytes(len) #endif #define put_ptr get_ptr #define setup_put(PTR) (put_ptr = (PTR)) @@ -53,20 +54,40 @@ 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; + unsigned char *pc = (unsigned char*)p; - for (sum = 0; len; len--) - sum += *p++; + while (len--) sum += *pc++; return sum & 0xff; } +static void +_skip_bytes(len) +{ + if (len < 0) { + error("Invalid header: %d", len); + exit(1); + } + get_ptr += len; +} + +static int +_get_byte() +{ + if (get_ptr < start_ptr || get_ptr - start_ptr >= storage_size) { + error("Invalid header"); + exit(1); + } + + return (*get_ptr++ & 0xff); +} + #if DUMP_HEADER static int dump_get_byte() @@ -75,7 +96,7 @@ dump_get_byte() if (verbose_listing && verbose > 1) printf("%02d %2d: ", get_ptr - start_ptr, 1); - c = GET_BYTE(); + c = _get_byte(); if (verbose_listing && verbose > 1) { if (isprint(c)) printf("%d(0x%02x) '%c'\n", c, c, c); @@ -92,28 +113,31 @@ dump_skip_bytes(len) if (len == 0) return; if (verbose_listing && verbose > 1) { printf("%02d %2d: ", get_ptr - start_ptr, len); + if (len < 0) { + error("Invalid header: %d", len); + exit(1); + } while (len--) - printf("0x%02x ", GET_BYTE()); + printf("0x%02x ", _get_byte()); printf("... ignored\n"); } else - get_ptr += len; + _skip_bytes(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) @@ -122,7 +146,6 @@ get_word() return w; } -/* ------------------------------------------------------------------------ */ static void put_word(v) unsigned int v; @@ -131,21 +154,20 @@ put_word(v) put_byte(v >> 8); } -/* ------------------------------------------------------------------------ */ static long get_longword() { - long b0, b1, b2, b3; + long b0, b1, b2, b3; long l; #if DUMP_HEADER 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) @@ -154,16 +176,58 @@ get_longword() return l; } -/* ------------------------------------------------------------------------ */ static void -put_longword(v) - long v; +put_longword(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() +{ + 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) @@ -175,9 +239,13 @@ get_bytes(buf, len, size) #if DUMP_HEADER if (verbose_listing && verbose > 1) printf("%02d %2d: \"", get_ptr - start_ptr, len); + if (len < 0) { + error("Invalid header: %d", len); + exit(1); + } - for (i = 0; i < len; i++) { - if (i < size) buf[i] = get_ptr[i]; + for (i = 0; i < len && i < size; i++) { + buf[i] = get_ptr[i]; if (verbose_listing && verbose > 1) { if (isprint(buf[i])) @@ -190,6 +258,10 @@ get_bytes(buf, len, size) if (verbose_listing && verbose > 1) printf("\"\n"); #else + if (len < 0) { + error("Invalid header: %d", len); + exit(1); + } for (i = 0; i < len && i < size; i++) buf[i] = get_ptr[i]; #endif @@ -215,8 +287,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,11 +296,48 @@ 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; + } +#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; + } + } + } +#ifdef MULTIBYTE_FILENAME if (from_code == CODE_SJIS && to_code == CODE_UTF8) { - for (i = 0; i < len; i++) - /* FIXME: provisionally fix for the Mac OS CoreFoundation */ - if ((unsigned char)name[i] == LHA_PATHSEP) name[i] = '/'; + for (i = 0; i < len; i++) { + if (SJIS_FIRST_P(name[i]) && SJIS_SECOND_P(name[i+1])) + i++; + else { + /* FIXME: provisionally fix for the Mac OS CoreFoundation */ + if (strchr(from_delim, name[i])) + name[i] = '/'; + } + } sjis_to_utf8(tmp, name, sizeof(tmp)); strncpy(name, tmp, size); name[size-1] = 0; @@ -251,21 +360,6 @@ 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 && @@ -347,151 +441,66 @@ convert_filename(name, len, size, continue; } } -} -/* ------------------------------------------------------------------------ */ -/* */ -/* Generic stamp format: */ -/* */ -/* 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 */ -/* |<------- year ----->|<- month ->|<--- day ---->| */ -/* */ -/* 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 */ -/* |<--- hour --->|<---- minute --->|<- second*2 ->| */ -/* */ -/* ------------------------------------------------------------------------ */ +#ifdef MULTIBYTE_FILENAME + if (to_code_save == CODE_CAP) { + len = sjis_to_cap(tmp, name, sizeof(tmp)); + strncpy(name, tmp, size); + name[size-1] = 0; + len = strlen(name); + } +#endif /* MULTIBYTE_FILENAME */ +} /* - * NOTE : If you don't have `gettimeofday(2)', or your gettimeofday(2) - * returns bogus timezone information, try FTIME, MKTIME, TIMELOCAL or TZSET. + * Generic (MS-DOS style) time stamp format (localtime): + * + * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 + * |<---- year-1980 --->|<- month ->|<--- day ---->| + * + * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * |<--- hour --->|<---- minute --->|<- second/2 ->| + * */ -#ifdef HAVE_FTIME -#include -#endif - -#if !defined(HAVE_MKTIME) && !defined(HAVE_TIMELOCAL) -static long -gettz() +static time_t +generic_to_unix_stamp(t) + long t; { -#ifdef HAVE_TZSET -#if defined(_MINIX) - extern long timezone; /* not defined in time.h */ -#endif + struct tm tm; - tzset(); - return timezone; -#elif HAVE_FTIME - struct timeb buf; - - ftime(&buf); - return buf.timezone * 60L; -#elif HAVE_STRUCT_TM_TM_GMTOFF - time_t tt; - - time(&tt); - return -localtime(&tt)->tm_gmtoff; -#elif GETTIMEOFDAY_HAS_2ND_ARG - struct timeval tp; - struct timezone tzp; - gettimeofday(&tp, &tzp);/* specific to 4.3BSD */ - /* - * return (tzp.tz_minuteswest * 60L + (tzp.tz_dsttime != 0 ? 60L * - * 60L : 0)); - */ - return (tzp.tz_minuteswest * 60L); -#else - /* Compile error will be caused */ - CANNOT GET TIMEZONE INFORMATION ON YOUR SYSTEM. - TAKE THE ANOTHER WAY. -#endif -} -#endif +#define subbits(n, off, len) (((n) >> (off)) & ((1 << (len))-1)) -/* ------------------------------------------------------------------------ */ -static time_t -generic_to_unix_stamp(t) - long t; -{ -#if defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) - struct tm dostm; - - /* - * special case: if MSDOS format date and time were zero, then we - * set time to be zero here too. - */ - if (t == 0) - return (time_t) 0; - - dostm.tm_sec = (t & 0x1f) * 2; - dostm.tm_min = t >> 5 & 0x3f; - dostm.tm_hour = t >> 11 & 0x1f; - dostm.tm_mday = t >> 16 & 0x1f; - dostm.tm_mon = (t >> (16+5) & 0x0f) - 1; /* 0..11 */ - dostm.tm_year = (t >> (16+9) & 0x7f) + 80; - dostm.tm_isdst = -1; + tm.tm_sec = subbits(t, 0, 5) * 2; + tm.tm_min = subbits(t, 5, 6); + tm.tm_hour = subbits(t, 11, 5); + tm.tm_mday = subbits(t, 16, 5); + tm.tm_mon = subbits(t, 21, 4) - 1; + tm.tm_year = subbits(t, 25, 7) + 80; + tm.tm_isdst = -1; #if HAVE_MKTIME - return (time_t) mktime(&dostm); -#else /* HAVE_TIMELOCAL is defined */ - return (time_t) timelocal(&dostm); + return mktime(&tm); +#else + return timelocal(&tm); #endif - -#else /* defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) */ - int year, month, day, hour, min, sec; - long longtime; - static unsigned int dsboy[12] = {0, 31, 59, 90, 120, 151, - 181, 212, 243, 273, 304, 334}; - unsigned int days; - - /* - * special case: if MSDOS format date and time were zero, then we - * set time to be zero here too. - */ - if (t == 0) - return (time_t) 0; - - year = ((int) (t >> 16 + 9) & 0x7f) + 1980; - month = (int) (t >> 16 + 5) & 0x0f; /* 1..12 means Jan..Dec */ - day = (int) (t >> 16) & 0x1f; /* 1..31 means 1st,...31st */ - - hour = ((int) t >> 11) & 0x1f; - min = ((int) t >> 5) & 0x3f; - sec = ((int) t & 0x1f) * 2; - - /* Calculate days since 1970.01.01 */ - days = (365 * (year - 1970) + /* days due to whole years */ - (year - 1970 + 1) / 4 + /* days due to leap years */ - dsboy[month - 1] + /* days since beginning of this year */ - day - 1); /* days since beginning of month */ - - if ((year % 4 == 0) && - (year % 100 != 0 || year % 400 == 0) && /* 1999.5.24 t.oka */ - (month >= 3)) /* if this is a leap year and month */ - days++; /* is March or later, add a day */ - - /* Knowing the days, we can find seconds */ - longtime = (((days * 24) + hour) * 60 + min) * 60 + sec; - longtime += gettz(); /* adjust for timezone */ - - /* LONGTIME is now the time in seconds, since 1970/01/01 00:00:00. */ - return (time_t) longtime; -#endif /* defined(HAVE_MKTIME) || defined(HAVE_TIMELOCAL) */ } -/* ------------------------------------------------------------------------ */ static long unix_to_generic_stamp(t) - time_t t; + time_t t; { - struct tm *tm = localtime(&t); - - return ((((long) (tm->tm_year - 80)) << 25) + - (((long) (tm->tm_mon + 1)) << 21) + - (((long) tm->tm_mday) << 16) + - (long) ((tm->tm_hour << 11) + - (tm->tm_min << 5) + - (tm->tm_sec / 2))); + struct tm *tm = localtime(&t); + + tm->tm_year -= 80; + tm->tm_mon += 1; + + return ((long)(tm->tm_year << 25) + + (tm->tm_mon << 21) + + (tm->tm_mday << 16) + + (tm->tm_hour << 11) + + (tm->tm_min << 5) + + (tm->tm_sec / 2)); } static unsigned long @@ -499,10 +508,11 @@ wintime_to_unix_stamp() { #if HAVE_UINT64_T uint64_t t; - uint64_t epoch = 0x019db1ded53e8000; /* 1970-01-01 00:00:00 (UTC) */ + uint64_t epoch = ((uint64_t)0x019db1de << 32) + 0xd53e8000; + /* 0x019db1ded53e8000ULL: 1970-01-01 00:00:00 (UTC) */ - t = get_longword(); - t += (uint64_t)get_longword() << 32; + t = (unsigned long)get_longword(); + t |= (uint64_t)(unsigned long)get_longword() << 32; t = (t - epoch) / 10000000; return t; #else @@ -532,10 +542,6 @@ wintime_to_unix_stamp() #endif } -/* ------------------------------------------------------------------------ */ -/* build header functions */ -/* ------------------------------------------------------------------------ */ - /* * extended header * @@ -577,7 +583,11 @@ get_extended_header(fp, hdr, header_size, hcrc) name_length = strlen(hdr->name); while (header_size) { - setup_get(data); +#if DUMP_HEADER + if (verbose_listing && verbose > 1) + printf("---\n"); +#endif + setup_get(data, sizeof(data)); if (sizeof(data) < header_size) { error("header size (%ld) too large.", header_size); exit(1); @@ -591,6 +601,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. */ @@ -598,21 +611,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 */ @@ -627,26 +652,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; @@ -693,8 +747,8 @@ get_extended_header(fp, hdr, header_size, hcrc) name_length = sizeof(hdr->name) - dir_length - 1; hdr->name[name_length] = 0; } - strcat(dirname, hdr->name); - strcpy(hdr->name, dirname); + strcat(dirname, hdr->name); /* ok */ + strcpy(hdr->name, dirname); /* ok */ name_length += dir_length; } @@ -766,6 +820,7 @@ get_header_level0(fp, hdr, data) char *data; { size_t header_size; + ssize_t remain_size; ssize_t extend_size; int checksum; int name_length; @@ -775,8 +830,14 @@ get_header_level0(fp, hdr, data) hdr->header_size = header_size = get_byte(); checksum = get_byte(); - if (fread(data + COMMON_HEADER_SIZE, - header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) { + /* The data variable has been already read as COMMON_HEADER_SIZE bytes. + So we must read the remaining header size by the header_size. */ + remain_size = header_size + 2 - COMMON_HEADER_SIZE; + if (remain_size <= 0) { + error("Invalid header size (LHarc file ?)"); + return FALSE; + } + if (fread(data + COMMON_HEADER_SIZE, remain_size, 1, fp) == 0) { error("Invalid header (LHarc file ?)"); return FALSE; /* finish */ } @@ -787,8 +848,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(); @@ -810,7 +871,7 @@ get_header_level0(fp, hdr, data) hdr->has_crc = FALSE; return TRUE; - } + } error("Unkonwn header (lha file?)"); exit(1); @@ -882,6 +943,7 @@ get_header_level1(fp, hdr, data) char *data; { size_t header_size; + ssize_t remain_size; ssize_t extend_size; int checksum; int name_length; @@ -891,8 +953,14 @@ get_header_level1(fp, hdr, data) hdr->header_size = header_size = get_byte(); checksum = get_byte(); - if (fread(data + COMMON_HEADER_SIZE, - header_size + 2 - COMMON_HEADER_SIZE, 1, fp) == 0) { + /* The data variable has been already read as COMMON_HEADER_SIZE bytes. + So we must read the remaining header size by the header_size. */ + remain_size = header_size + 2 - COMMON_HEADER_SIZE; + if (remain_size <= 0) { + error("Invalid header size (LHarc file ?)"); + return FALSE; + } + if (fread(data + COMMON_HEADER_SIZE, remain_size, 1, fp) == 0) { error("Invalid header (LHarc file ?)"); return FALSE; /* finish */ } @@ -903,8 +971,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(); @@ -976,6 +1044,7 @@ get_header_level2(fp, hdr, data) char *data; { size_t header_size; + ssize_t remain_size; ssize_t extend_size; int padding; unsigned int hcrc; @@ -983,6 +1052,13 @@ get_header_level2(fp, hdr, data) hdr->size_field_length = 2; /* in bytes */ hdr->header_size = header_size = get_word(); + /* The data variable has been already read as COMMON_HEADER_SIZE bytes. + So we must read the remaining header size without ext-header. */ + remain_size = header_size - I_LEVEL2_HEADER_SIZE; + if (remain_size < 0) { + error("Invalid header size (LHarc file ?)"); + return FALSE; + } if (fread(data + COMMON_HEADER_SIZE, I_LEVEL2_HEADER_SIZE - COMMON_HEADER_SIZE, 1, fp) == 0) { error("Invalid header (LHarc file ?)"); @@ -990,8 +1066,8 @@ 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(); @@ -1014,7 +1090,12 @@ get_header_level2(fp, hdr, data) return FALSE; padding = header_size - I_LEVEL2_HEADER_SIZE - extend_size; - while (padding--) /* padding should be 0 or 1 */ + /* padding should be 0 or 1 */ + if (padding != 0 && padding != 1) { + error("Invalid header size (padding: %d)", padding); + return FALSE; + } + while (padding--) hcrc = UPDATE_CRC(hcrc, fgetc(fp)); if (hdr->header_crc != hcrc) @@ -1057,6 +1138,7 @@ get_header_level3(fp, hdr, data) char *data; { size_t header_size; + ssize_t remain_size; ssize_t extend_size; int padding; unsigned int hcrc; @@ -1070,8 +1152,8 @@ 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(); @@ -1085,6 +1167,11 @@ get_header_level3(fp, hdr, data) hdr->crc = get_word(); hdr->extend_type = get_byte(); hdr->header_size = header_size = get_longword(); + remain_size = header_size - I_LEVEL3_HEADER_SIZE; + if (remain_size < 0) { + error("Invalid header size (LHarc file ?)"); + return FALSE; + } extend_size = get_longword(); INITIALIZE_CRC(hcrc); @@ -1094,9 +1181,12 @@ get_header_level3(fp, hdr, data) 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)); + padding = remain_size - extend_size; + /* padding should be 0 */ + if (padding != 0) { + error("Invalid header size (padding: %d)", padding); + return FALSE; + } if (hdr->header_crc != hcrc) error("header CRC error"); @@ -1121,7 +1211,7 @@ get_header(fp, hdr) memset(hdr, 0, sizeof(LzHeader)); - setup_get(data); + setup_get(data, sizeof(data)); if ((end_mark = getc(fp)) == EOF || end_mark == 0) { return FALSE; /* finish */ @@ -1158,7 +1248,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: @@ -1176,7 +1266,7 @@ get_header(fp, hdr) break; default: - filename_case = noconvertcase ? NONE : TO_LOWER; + filename_case = convertcase ? TO_LOWER : NONE; break; } @@ -1205,7 +1295,7 @@ get_header(fp, hdr) /* hdr->name is symbolic link name */ /* hdr->realname is real name */ *p = 0; - strncpy(hdr->realname, p+1, sizeof(hdr->realname)); + strcpy(hdr->realname, p+1); /* ok */ } else error("unknown symlink name \"%s\"", hdr->name); @@ -1226,17 +1316,17 @@ seek_lha_header(fp) 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]=='-')) + if (! (p[I_METHOD]=='-' && + (p[I_METHOD+1]=='l' || p[I_METHOD+1]=='p') && + p[I_METHOD+4]=='-')) continue; - /* found "-l??-" keyword (as METHOD type string) */ - - /* size and checksum validate check */ + /* found "-[lp]??-" 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 (fseek(fp, (p - buffer) - n, SEEK_CUR) == -1) + if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1) fatal_error("cannot seek header"); return 0; } @@ -1245,18 +1335,118 @@ seek_lha_header(fp) if (p[I_HEADER_LEVEL] == 2 && p[I_HEADER_SIZE] >= 24 && p[I_ATTRIBUTE] == 0x20) { - if (fseek(fp, (p - buffer) - n, SEEK_CUR) == -1) + if (fseeko(fp, (p - buffer) - n, SEEK_CUR) == -1) fatal_error("cannot seek header"); return 0; } } - if (fseek(fp, -n, SEEK_CUR) == -1) + if (fseeko(fp, -n, SEEK_CUR) == -1) fatal_error("cannot seek header"); return -1; } -/* ------------------------------------------------------------------------ */ + +/* remove leading `xxxx/..' */ +static char * +remove_leading_dots(char *path) +{ + char *first = path; + char *ptr = 0; + + if (strcmp(first, "..") == 0) { + warning("Removing leading `..' from member name."); + return first+1; /* change to "." */ + } + + if (strstr(first, "..") == 0) + return first; + + while (path && *path) { + + if (strcmp(path, "..") == 0) + ptr = path = path+2; + else if (strncmp(path, "../", 3) == 0) + ptr = path = path+3; + else + path = strchr(path, '/'); + + if (path && *path == '/') { + path++; + } + } + + if (ptr) { + warning("Removing leading `%.*s' from member name.", ptr-first, first); + return ptr; + } + + return first; +} + +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; +} + +/* + canonicalize path + + remove leading "xxx/../" + remove "./", "././", "././ ... ./" + remove duplicated "/" +*/ +static int +canon_path(char *newpath, char *path, size_t size) +{ + 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; + } + + /* remove duplicated '/' */ + while (*path == '/') path++; + } + + /* 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; @@ -1275,14 +1465,46 @@ init_header(name, v_stat, hdr) hdr->original_size = v_stat->st_size; hdr->attribute = GENERIC_ATTRIBUTE; hdr->header_level = header_level; - strcpy(hdr->name, name); - len = strlen(name); + + len = canon_path(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 @@ -1318,8 +1540,13 @@ init_header(name, v_stat, hdr) memcpy(hdr->method, LZHDIRS_METHOD, METHOD_TYPE_STORAGE); hdr->attribute = GENERIC_DIRECTORY_ATTRIBUTE; hdr->original_size = 0; - if (len > 0 && hdr->name[len - 1] != '/') - strcpy(&hdr->name[len++], "/"); + if (len > 0 && hdr->name[len - 1] != '/') { + if (len < sizeof(hdr->name)-1) + strcpy(&hdr->name[len++], "/"); /* ok */ + else + warning("the length of dirname \"%s\" is too long.", + hdr->name); + } } #ifdef S_IFLNK @@ -1368,9 +1595,6 @@ write_unix_info(hdr) } } -/* ------------------------------------------------------------------------ */ -/* Write unix extended header or generic header. */ - static size_t write_header_level0(data, hdr, pathname) LzHeader *hdr; @@ -1600,11 +1824,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); @@ -1633,10 +1857,10 @@ 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 = "\\"; } @@ -1863,9 +2087,9 @@ utf8_to_sjis(char *dst, const char *src, size_t dstsize) } /* - * SJIS <-> EUC ÊÑ´¹´Ø¿ô - * ¡ÖÆüËܸì¾ðÊó½èÍý¡× ¥½¥Õ¥È¥Ð¥ó¥¯(³ô) - * ¤è¤êÈ´¿è(by Koji Arai) + * SJIS <-> EUC 変換関数 + * 「日本語情報処理」 ソフトバンク(æ ª) + * より抜粋(by Koji Arai) */ void euc2sjis(int *p1, int *p2) @@ -1892,4 +2116,106 @@ sjis2euc(int *p1, int *p2) *p1 |= 0x80; *p2 |= 0x80; } + +static int +hex2int(int c) +{ + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return c - '0'; + + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + return c - 'a' + 10; + + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + return c - 'A' + 10; + default: + return -1; + } +} + +static int +int2hex(int c) +{ + switch (c) { + case 0: case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: case 9: + return c + '0'; + + case 10: case 11: case 12: case 13: case 14: case 15: + return c + 'a' - 10; + + default: + return -1; + } +} + +int +cap_to_sjis(char *dst, const char *src, size_t dstsize) +{ + int i, j; + size_t len = strlen(src); + int a, b; + + for (i = j = 0; i < len && i < dstsize; i++) { + if (src[i] != ':') { + dst[j++] = src[i]; + continue; + } + + i++; + a = hex2int((unsigned char)src[i]); + b = hex2int((unsigned char)src[i+1]); + + if (a == -1 || b == -1) { + /* leave as it */ + dst[j++] = ':'; + strncpy(dst+j, src+i, dstsize-j); + dst[dstsize-1] = 0; + return strlen(dst); + } + + i++; + + dst[j++] = a * 16 + b; + } + dst[j] = 0; + return j; +} + +int +sjis_to_cap(char *dst, const char *src, size_t dstsize) +{ + int i, j; + size_t len = strlen(src); + int a, b; + + for (i = j = 0; i < len && i < dstsize; i++) { + if (src[i] == ':') { + strncpy(dst+j, ":3a", dstsize-j); + dst[dstsize-1] = 0; + j = strlen(dst); + continue; + } + if (isprint(src[i])) { + dst[j++] = src[i]; + continue; + } + + if (j + 3 >= dstsize) { + dst[j] = 0; + return j; + } + + a = int2hex((unsigned char)src[i] / 16); + b = int2hex((unsigned char)src[i] % 16); + + dst[j++] = ':'; + dst[j++] = a; + dst[j++] = b; + } + dst[j] = 0; + return j; +} #endif /* MULTIBYTE_FILENAME */