diff options
Diffstat (limited to 'util.c')
-rw-r--r-- | util.c | 1450 |
1 files changed, 1450 insertions, 0 deletions
@@ -0,0 +1,1450 @@ +/* + util.c + + Copyright (c) 1990-2008 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2007-Mar-4 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + * util.c by Mark Adler. + */ +#define __UTIL_C + +#include "zip.h" +#include "ebcdic.h" +#include <ctype.h> + +#ifdef MSDOS16 +# include <dos.h> +#endif + +#ifdef NO_MKTIME +# ifndef IZ_MKTIME_ONLY +# define IZ_MKTIME_ONLY /* only mktime() related code is pulled in */ +# endif +# include "timezone.c" +#endif + +uch upper[256], lower[256]; +/* Country-dependent case map table */ + + +#ifndef UTIL /* UTIL picks out namecmp code (all utils) */ + +/* RISC OS uses # as its single-character wildcard */ +#ifdef RISCOS +# define WILDCHR_SINGLE '#' +# define WILDCHR_MULTI '*' +# define DIRSEP_CHR '.' +#endif + +#ifdef VMS +# define WILDCHR_SINGLE '%' +# define WILDCHR_MULTI '*' +# define DIRSEP_CHR '.' +#endif + +#ifndef WILDCHR_SINGLE +# define WILDCHR_SINGLE '?' +#endif +#ifndef WILDCHR_MULTI +# define WILDCHR_MULTI '*' +#endif +#ifndef DIRSEP_CHR +# define DIRSEP_CHR '/' +#endif + +/* Local functions */ +local int recmatch OF((ZCONST char *, ZCONST char *, int)); +#if defined(UNICODE_SUPPORT) && defined(WIN32) + local long recmatchw OF((ZCONST wchar_t *, ZCONST wchar_t *, int)); +#endif +local int count_args OF((char *s)); + +#ifdef MSDOS16 + local unsigned ident OF((unsigned chr)); +#endif + +#ifndef HAVE_FSEEKABLE + +/* 2004-11-12 SMS. + Changed to use z*o() functions, and ftell() test from >= 0 to != -1. + This solves problems with negative 32-bit offsets, even on small-file + products. +*/ +int fseekable( fp) +FILE *fp; +{ + zoff_t x; + + return (fp == NULL || + ((zfseeko( fp, ((zoff_t) -1), SEEK_CUR) == 0) && /* Seek ok. */ + ((x = zftello( fp)) != ((zoff_t) -1)) && /* Tell ok. */ + (zfseeko( fp, ((zoff_t) 1), SEEK_CUR) == 0) && /* Seek ok. */ + (zftello( fp) == x+ 1))); /* Tells agree. */ +} +#endif /* HAVE_FSEEKABLE */ + + +char *isshexp(p) +char *p; /* candidate sh expression */ +/* If p is a sh expression, a pointer to the first special character is + returned. Otherwise, NULL is returned. */ +{ + for (; *p; INCSTR(p)) + if (*p == '\\' && *(p+1)) + p++; +#ifdef VMS + else if (*p == WILDCHR_SINGLE || *p == WILDCHR_MULTI) +#else /* !VMS */ + else if (*p == WILDCHR_SINGLE || *p == WILDCHR_MULTI || *p == '[') +#endif /* ?VMS */ + return p; + return NULL; +} + +#ifdef UNICODE_SUPPORT +# ifdef WIN32 + +wchar_t *isshexpw(pw) + wchar_t *pw; /* candidate sh expression */ +/* If pw is a sh expression, a pointer to the first special character is + returned. Otherwise, NULL is returned. */ +{ + for (; *pw; pw++) + if (*pw == (wchar_t)'\\' && *(pw+1)) + pw++; + else if (*pw == (wchar_t)WILDCHR_SINGLE || *pw == (wchar_t)WILDCHR_MULTI || + *pw == (wchar_t)'[') + return pw; + return NULL; +} + +# endif +#endif + + +#ifdef UNICODE_SUPPORT +# ifdef WIN32 + +local long recmatchw(pw, sw, cs) +ZCONST wchar_t *pw; /* sh pattern to match */ +ZCONST wchar_t *sw; /* string to match it to */ +int cs; /* flag: force case-sensitive matching */ +/* Recursively compare the sh pattern p with the string s and return 1 if + they match, and 0 or 2 if they don't or if there is a syntax error in the + pattern. This routine recurses on itself no deeper than the number of + characters in the pattern. */ +{ + long c; /* pattern char or start of range in [-] loop */ + /* Get first character, the pattern for new recmatch calls follows */ + + c = (long)*(pw++); + + /* If that was the end of the pattern, match if string empty too */ + if (c == 0) + return *sw == 0; + + /* '?' matches any character (but not an empty string) */ + if ((wchar_t)c == (wchar_t)WILDCHR_SINGLE) { + if (wild_stop_at_dir) + return (*sw && *sw != (wchar_t)DIRSEP_CHR) ? recmatchw(pw, sw + 1, cs) : 0; + else + return *sw ? recmatchw(pw, sw + 1, cs) : 0; + } + + /* WILDCHR_MULTI ('*') matches any number of characters, including zero */ + if (!no_wild && (wchar_t)c == (wchar_t)WILDCHR_MULTI) + { + if (wild_stop_at_dir) { + /* Check for an immediately following WILDCHR_MULTI */ + if (*pw != (wchar_t)WILDCHR_MULTI) { + /* Single WILDCHR_MULTI ('*'): this doesn't match slashes */ + for (; *sw && *sw != (wchar_t)DIRSEP_CHR; sw++) + if ((c = recmatchw(pw, sw, cs)) != 0) + return c; + /* end of pattern: matched if at end of string, else continue */ + if (*pw == 0) + return (*sw == 0); + /* continue to match if at DIRSEP_CHR in pattern, else give up */ + return (*pw == (wchar_t)DIRSEP_CHR || (*pw == (wchar_t)'\\' && + pw[1] == (wchar_t)DIRSEP_CHR)) + ? recmatchw(pw, sw, cs) : 2; + } + /* Two consecutive WILDCHR_MULTI ("**"): this matches DIRSEP_CHR ('/') */ + pw++; /* move p past the second WILDCHR_MULTI */ + /* continue with the normal non-WILD_STOP_AT_DIR code */ + } /* wild_stop_at_dir */ + + /* Not wild_stop_at_dir */ + if (*pw == 0) + return 1; + if (!isshexpw((wchar_t *)pw)) + { + /* optimization for rest of pattern being a literal string */ + + /* optimization to handle patterns like *.txt */ + /* if the first char in the pattern is '*' and there */ + /* are no other shell expression chars, i.e. a literal string */ + /* then just compare the literal string at the end */ + + ZCONST wchar_t *swrest; + + swrest = sw + (wcslen(sw) - wcslen(pw)); + if (swrest - sw < 0) + /* remaining literal string from pattern is longer than rest of + test string, there can't be a match + */ + return 0; + else + /* compare the remaining literal pattern string with the last bytes + of the test string to check for a match */ + return ((cs ? wcscmp(pw, swrest) : _wcsicmp(pw, swrest)) == 0); + } + else + { + /* pattern contains more wildcards, continue with recursion... */ + for (; *sw; sw++) + if ((c = recmatchw(pw, sw, cs)) != 0) + return c; + return 2; /* 2 means give up--shmatch will return false */ + } + } + + /* Parse and process the list of characters and ranges in brackets */ + if (!no_wild && allow_regex && (wchar_t)c == '[') + { + int e; /* flag true if next char to be taken literally */ + ZCONST wchar_t *qw; /* pointer to end of [-] group */ + int r; /* flag true to match anything but the range */ + + if (*sw == 0) /* need a character to match */ + return 0; + pw += (r = (*pw == (wchar_t)'!' || *pw == (wchar_t)'^')); /* see if reverse */ + for (qw = pw, e = 0; *qw; qw++) /* find closing bracket */ + if (e) + e = 0; + else + if (*qw == (wchar_t)'\\') + e = 1; + else if (*qw == (wchar_t)']') + break; + if (*qw != (wchar_t)']') /* nothing matches if bad syntax */ + return 0; + for (c = 0, e = *pw == (wchar_t)'-'; pw < qw; pw++) /* go through the list */ + { + if (e == 0 && *pw == (wchar_t)'\\') /* set escape flag if \ */ + e = 1; + else if (e == 0 && *pw == (wchar_t)'-') /* set start of range if - */ + c = *(pw-1); + else + { + wchar_t cc = (cs ? *sw : towupper(*sw)); + wchar_t uc = (wchar_t) c; + + if (*(pw+1) != (wchar_t)'-') + for (uc = uc ? uc : *pw; cc <= *pw; uc++) + /* compare range */ + if ((cs ? uc : towupper(uc)) == cc) + return r ? 0 : recmatchw(qw + 1, sw + 1, cs); + c = e = 0; /* clear range, escape flags */ + } + } + return r ? recmatchw(qw + 1, sw + 1, cs) : 0; + /* bracket match failed */ + } + + /* If escape ('\'), just compare next character */ + if (!no_wild && (wchar_t)c == (wchar_t)'\\') + if ((c = *pw++) == '\0') /* if \ at end, then syntax error */ + return 0; + + /* Just a character--compare it */ + return (cs ? (wchar_t)c == *sw : towupper((wchar_t)c) == towupper(*sw)) ? + recmatchw(pw, sw + 1, cs) : 0; +} + +# endif +#endif + + +local int recmatch(p, s, cs) +ZCONST char *p; /* sh pattern to match */ +ZCONST char *s; /* string to match it to */ +int cs; /* flag: force case-sensitive matching */ +/* Recursively compare the sh pattern p with the string s and return 1 if + they match, and 0 or 2 if they don't or if there is a syntax error in the + pattern. This routine recurses on itself no deeper than the number of + characters in the pattern. */ +{ + int c; /* pattern char or start of range in [-] loop */ + /* Get first character, the pattern for new recmatch calls follows */ + + /* This fix provided by akt@m5.dion.ne.jp for Japanese. + See 21 July 2006 mail. + It only applies when p is pointing to a doublebyte character and + things like / and wildcards are not doublebyte. This probably + should not be needed. */ + +#ifdef _MBCS + if (CLEN(p) == 2) { + if (CLEN(s) == 2) { + return (*p == *s && *(p+1) == *(s+1)) ? + recmatch(p + 2, s + 2, cs) : 0; + } else { + return 0; + } + } +#endif /* ?_MBCS */ + + c = *POSTINCSTR(p); + + /* If that was the end of the pattern, match if string empty too */ + if (c == 0) + return *s == 0; + + /* '?' (or '%' or '#') matches any character (but not an empty string) */ + if (c == WILDCHR_SINGLE) { + if (wild_stop_at_dir) + return (*s && *s != DIRSEP_CHR) ? recmatch(p, s + CLEN(s), cs) : 0; + else + return *s ? recmatch(p, s + CLEN(s), cs) : 0; + } + + /* WILDCHR_MULTI ('*') matches any number of characters, including zero */ +#ifdef AMIGA + if (!no_wild && c == '#' && *p == '?') /* "#?" is Amiga-ese for "*" */ + c = WILDCHR_MULTI, p++; +#endif /* AMIGA */ + if (!no_wild && c == WILDCHR_MULTI) + { + if (wild_stop_at_dir) { + /* Check for an immediately following WILDCHR_MULTI */ +# ifdef AMIGA + if ((c = p[0]) == '#' && p[1] == '?') /* "#?" is Amiga-ese for "*" */ + c = WILDCHR_MULTI, p++; + if (c != WILDCHR_MULTI) { +# else /* !AMIGA */ + if (*p != WILDCHR_MULTI) { +# endif /* ?AMIGA */ + /* Single WILDCHR_MULTI ('*'): this doesn't match slashes */ + for (; *s && *s != DIRSEP_CHR; INCSTR(s)) + if ((c = recmatch(p, s, cs)) != 0) + return c; + /* end of pattern: matched if at end of string, else continue */ + if (*p == 0) + return (*s == 0); + /* continue to match if at DIRSEP_CHR in pattern, else give up */ + return (*p == DIRSEP_CHR || (*p == '\\' && p[1] == DIRSEP_CHR)) + ? recmatch(p, s, cs) : 2; + } + /* Two consecutive WILDCHR_MULTI ("**"): this matches DIRSEP_CHR ('/') */ + p++; /* move p past the second WILDCHR_MULTI */ + /* continue with the normal non-WILD_STOP_AT_DIR code */ + } /* wild_stop_at_dir */ + + /* Not wild_stop_at_dir */ + if (*p == 0) + return 1; + if (!isshexp((char *)p)) + { + /* optimization for rest of pattern being a literal string */ + + /* optimization to handle patterns like *.txt */ + /* if the first char in the pattern is '*' and there */ + /* are no other shell expression chars, i.e. a literal string */ + /* then just compare the literal string at the end */ + + ZCONST char *srest; + + srest = s + (strlen(s) - strlen(p)); + if (srest - s < 0) + /* remaining literal string from pattern is longer than rest of + test string, there can't be a match + */ + return 0; + else + /* compare the remaining literal pattern string with the last bytes + of the test string to check for a match */ +#ifdef _MBCS + { + ZCONST char *q = s; + + /* MBCS-aware code must not scan backwards into a string from + * the end. + * So, we have to move forward by character from our well-known + * character position s in the test string until we have advanced + * to the srest position. + */ + while (q < srest) + INCSTR(q); + /* In case the byte *srest is a trailing byte of a multibyte + * character, we have actually advanced past the position (srest). + * For this case, the match has failed! + */ + if (q != srest) + return 0; + return ((cs ? strcmp(p, q) : namecmp(p, q)) == 0); + } +#else /* !_MBCS */ + return ((cs ? strcmp(p, srest) : namecmp(p, srest)) == 0); +#endif /* ?_MBCS */ + } + else + { + /* pattern contains more wildcards, continue with recursion... */ + for (; *s; INCSTR(s)) + if ((c = recmatch(p, s, cs)) != 0) + return c; + return 2; /* 2 means give up--shmatch will return false */ + } + } + +#ifndef VMS /* No bracket matching in VMS */ + /* Parse and process the list of characters and ranges in brackets */ + if (!no_wild && allow_regex && c == '[') + { + int e; /* flag true if next char to be taken literally */ + ZCONST char *q; /* pointer to end of [-] group */ + int r; /* flag true to match anything but the range */ + + if (*s == 0) /* need a character to match */ + return 0; + p += (r = (*p == '!' || *p == '^')); /* see if reverse */ + for (q = p, e = 0; *q; q++) /* find closing bracket */ + if (e) + e = 0; + else + if (*q == '\\') + e = 1; + else if (*q == ']') + break; + if (*q != ']') /* nothing matches if bad syntax */ + return 0; + for (c = 0, e = *p == '-'; p < q; p++) /* go through the list */ + { + if (e == 0 && *p == '\\') /* set escape flag if \ */ + e = 1; + else if (e == 0 && *p == '-') /* set start of range if - */ + c = *(p-1); + else + { + uch cc = (cs ? (uch)*s : case_map((uch)*s)); + uch uc = (uch) c; + if (*(p+1) != '-') + for (uc = uc ? uc : (uch)*p; uc <= (uch)*p; uc++) + /* compare range */ + if ((cs ? uc : case_map(uc)) == cc) + return r ? 0 : recmatch(q + CLEN(q), s + CLEN(s), cs); + c = e = 0; /* clear range, escape flags */ + } + } + return r ? recmatch(q + CLEN(q), s + CLEN(s), cs) : 0; + /* bracket match failed */ + } +#endif /* !VMS */ + + /* If escape ('\'), just compare next character */ + if (!no_wild && c == '\\') + if ((c = *p++) == '\0') /* if \ at end, then syntax error */ + return 0; + +#ifdef VMS + /* 2005-11-06 SMS. + Handle "..." wildcard in p with "." or "]" in s. + */ + if ((c == '.') && (*p == '.') && (*(p+ CLEN( p)) == '.') && + ((*s == '.') || (*s == ']'))) + { + /* Match "...]" with "]". Continue after "]" in both. */ + if ((*(p+ 2* CLEN( p)) == ']') && (*s == ']')) + return recmatch( (p+ 3* CLEN( p)), (s+ CLEN( s)), cs); + + /* Else, look for a reduced match in s, until "]" in or end of s. */ + for (; *s && (*s != ']'); INCSTR(s)) + if (*s == '.') + /* If reduced match, then continue after "..." in p, "." in s. */ + if ((c = recmatch( (p+ CLEN( p)), s, cs)) != 0) + return (int)c; + + /* Match "...]" with "]". Continue after "]" in both. */ + if ((*(p+ 2* CLEN( p)) == ']') && (*s == ']')) + return recmatch( (p+ 3* CLEN( p)), (s+ CLEN( s)), cs); + + /* No reduced match. Quit. */ + return 2; + } + +#endif /* def VMS */ + + /* Just a character--compare it */ + return (cs ? c == *s : case_map((uch)c) == case_map((uch)*s)) ? + recmatch(p, s + CLEN(s), cs) : 0; +} + + +int shmatch(p, s, cs) +ZCONST char *p; /* sh pattern to match */ +ZCONST char *s; /* string to match it to */ +int cs; /* force case-sensitive match if TRUE */ +/* Compare the sh pattern p with the string s and return true if they match, + false if they don't or if there is a syntax error in the pattern. */ +{ + return recmatch(p, s, cs) == 1; +} + + +#if defined(DOS) || defined(WIN32) + +#ifdef UNICODE_SUPPORT + +int dosmatchw(pw, sw, cs) +ZCONST wchar_t *pw; /* dos pattern to match */ +ZCONST wchar_t *sw; /* string to match it to */ +int cs; /* force case-sensitive match if TRUE */ +/* Treat filenames without periods as having an implicit trailing period */ +{ + wchar_t *sw1; /* revised string to match */ + int r; /* result */ + + if (wcschr(pw, (wchar_t)'.') && !wcschr(sw, (wchar_t)'.') && + ((sw1 = (wchar_t *)malloc((wcslen(sw) + 2) * sizeof(wchar_t))) != NULL)) + { + wcscpy(sw1, sw); + wcscat(sw1, L"."); + } + else + { + /* will usually be OK */ + sw1 = (wchar_t *)sw; + } + + r = recmatchw(pw, sw1, cs) == 1; + if (sw != sw1) + free((zvoid *)sw1); + return r == 1; +} + +#endif + +/* XXX also suitable for OS2? Atari? Human68K? TOPS-20?? */ + +int dosmatch(p, s, cs) +ZCONST char *p; /* dos pattern to match */ +ZCONST char *s; /* string to match it to */ +int cs; /* force case-sensitive match if TRUE */ +/* Treat filenames without periods as having an implicit trailing period */ +{ + char *s1; /* revised string to match */ + int r; /* result */ + + if (strchr(p, '.') && !strchr(s, '.') && + ((s1 = malloc(strlen(s) + 2)) != NULL)) + { + strcpy(s1, s); + strcat(s1, "."); + } + else + { + /* will usually be OK */ + s1 = (char *)s; + } + + r = recmatch(p, s1, cs) == 1; + if (s != s1) + free((zvoid *)s1); + return r == 1; +} + +#endif /* DOS || WIN32 */ + +zvoid far **search(b, a, n, cmp) +ZCONST zvoid *b; /* pointer to value to search for */ +ZCONST zvoid far **a; /* table of pointers to values, sorted */ +extent n; /* number of pointers in a[] */ +int (*cmp) OF((ZCONST zvoid *, ZCONST zvoid far *)); /* comparison function */ + +/* Search for b in the pointer list a[0..n-1] using the compare function + cmp(b, c) where c is an element of a[i] and cmp() returns negative if + *b < *c, zero if *b == *c, or positive if *b > *c. If *b is found, + search returns a pointer to the entry in a[], else search() returns + NULL. The nature and size of *b and *c (they can be different) are + left up to the cmp() function. A binary search is used, and it is + assumed that the list is sorted in ascending order. */ +{ + ZCONST zvoid far **i; /* pointer to midpoint of current range */ + ZCONST zvoid far **l; /* pointer to lower end of current range */ + int r; /* result of (*cmp)() call */ + ZCONST zvoid far **u; /* pointer to upper end of current range */ + + l = (ZCONST zvoid far **)a; u = l + (n-1); + while (u >= l) { + i = l + ((unsigned)(u - l) >> 1); + if ((r = (*cmp)(b, (ZCONST char far *)*(struct zlist far **)i)) < 0) + u = i - 1; + else if (r > 0) + l = i + 1; + else + return (zvoid far **)i; + } + return NULL; /* If b were in list, it would belong at l */ +} + +#endif /* !UTIL */ + +#ifdef MSDOS16 + +local unsigned ident(unsigned chr) +{ + return chr; /* in al */ +} + +void init_upper() +{ + static struct country { + uch ignore[18]; + int (far *casemap)(int); + uch filler[16]; + } country_info; + + struct country far *info = &country_info; + union REGS regs; + struct SREGS sregs; + unsigned int c; + + regs.x.ax = 0x3800; /* get country info */ + regs.x.dx = FP_OFF(info); + sregs.ds = FP_SEG(info); + intdosx(®s, ®s, &sregs); + for (c = 0; c < 128; c++) { + upper[c] = (uch) toupper(c); + lower[c] = (uch) c; + } + for (; c < sizeof(upper); c++) { + upper[c] = (uch) (*country_info.casemap)(ident(c)); + /* ident() required because casemap takes its parameter in al */ + lower[c] = (uch) c; + } + for (c = 0; c < sizeof(upper); c++ ) { + unsigned int u = upper[c]; + if (u != c && lower[u] == (uch) u) { + lower[u] = (uch)c; + } + } + for (c = 'A'; c <= 'Z'; c++) { + lower[c] = (uch) (c - 'A' + 'a'); + } +} +#else /* !MSDOS16 */ +# ifndef OS2 + +void init_upper() +{ + unsigned int c; +#if defined(ATARI) || defined(CMS_MVS) +#include <ctype.h> +/* this should be valid for all other platforms too. (HD 11/11/95) */ + for (c = 0; c< sizeof(upper); c++) { + upper[c] = islower(c) ? toupper(c) : c; + lower[c] = isupper(c) ? tolower(c) : c; + } +#else + for (c = 0; c < sizeof(upper); c++) upper[c] = lower[c] = (uch)c; + for (c = 'a'; c <= 'z'; c++) upper[c] = (uch)(c - 'a' + 'A'); + for (c = 'A'; c <= 'Z'; c++) lower[c] = (uch)(c - 'A' + 'a'); +#endif +} +# endif /* !OS2 */ + +#endif /* ?MSDOS16 */ + +int namecmp(string1, string2) + ZCONST char *string1, *string2; +/* Compare the two strings ignoring case, and correctly taking into + * account national language characters. For operating systems with + * case sensitive file names, this function is equivalent to strcmp. + */ +{ + int d; + + for (;;) + { + d = (int) (uch) case_map(*string1) + - (int) (uch) case_map(*string2); + + if (d || *string1 == 0 || *string2 == 0) + return d; + + string1++; + string2++; + } +} + +#ifdef EBCDIC +char *strtoasc(char *str1, ZCONST char *str2) +{ + char *old; + old = str1; + while (*str1++ = (char)ascii[(uch)(*str2++)]); + return old; +} + +char *strtoebc(char *str1, ZCONST char *str2) +{ + char *old; + old = str1; + while (*str1++ = (char)ebcdic[(uch)(*str2++)]); + return old; +} + +char *memtoasc(char *mem1, ZCONST char *mem2, unsigned len) +{ + char *old; + old = mem1; + while (len--) + *mem1++ = (char)ascii[(uch)(*mem2++)]; + return old; +} + +char *memtoebc(char *mem1, ZCONST char *mem2, unsigned len) +{ + char *old; + old = mem1; + while (len--) + *mem1++ = (char)ebcdic[(uch)(*mem2++)]; + return old; +} +#endif /* EBCDIC */ + +#ifdef IZ_ISO2OEM_ARRAY +char *str_iso_to_oem(dst, src) + ZCONST char *src; + char *dst; +{ + char *dest_start = dst; + while (*dst++ = (char)iso2oem[(uch)(*src++)]); + return dest_start; +} +#endif + +#ifdef IZ_OEM2ISO_ARRAY +char *str_oem_to_iso(dst, src) + ZCONST char *src; + char *dst; +{ + char *dest_start = dst; + while (*dst++ = (char)oem2iso[(uch)(*src++)]); + return dest_start; +} +#endif + + + +/* DBCS support for Info-ZIP's zip (mainly for japanese (-: ) + * by Yoshioka Tsuneo (QWF00133@nifty.ne.jp,tsuneo-y@is.aist-nara.ac.jp) + * This code is public domain! Date: 1998/12/20 + */ +#ifdef _MBCS + +char *___tmp_ptr; + +int lastchar(ptr) + ZCONST char *ptr; +{ + ZCONST char *oldptr = ptr; + while(*ptr != '\0'){ + oldptr = ptr; + INCSTR(ptr); + } + return (int)(unsigned)*oldptr; +} + +unsigned char *zmbschr(str, c) + ZCONST unsigned char *str; + unsigned int c; +{ + while(*str != '\0'){ + if (*str == c) {return (unsigned char *)str;} + INCSTR(str); + } + return NULL; +} + +unsigned char *zmbsrchr(str, c) + ZCONST unsigned char *str; + unsigned int c; +{ + unsigned char *match = NULL; + while(*str != '\0'){ + if (*str == c) {match = (unsigned char*)str;} + INCSTR(str); + } + return match; +} +#endif /* _MBCS */ + + + +#ifndef UTIL + +/***************************************************************** + | envargs - add default options from environment to command line + |---------------------------------------------------------------- + | Author: Bill Davidsen, original 10/13/91, revised 23 Oct 1991. + | This program is in the public domain. + |---------------------------------------------------------------- + | Minor program notes: + | 1. Yes, the indirection is a tad complex + | 2. Parenthesis were added where not needed in some cases + | to make the action of the code less obscure. + ****************************************************************/ + +void envargs(Pargc, Pargv, envstr, envstr2) + int *Pargc; + char ***Pargv; + char *envstr; + char *envstr2; +{ + char *envptr; /* value returned by getenv */ + char *bufptr; /* copy of env info */ + int argc; /* internal arg count */ + register int ch; /* spare temp value */ + char **argv; /* internal arg vector */ + char **argvect; /* copy of vector address */ + + /* see if anything in the environment */ + envptr = getenv(envstr); + if (envptr != NULL) /* usual var */ + while (isspace((uch)*envptr)) /* we must discard leading spaces */ + envptr++; + if (envptr == NULL || *envptr == '\0') + if ((envptr = getenv(envstr2)) != NULL) /* alternate */ + while (isspace((uch)*envptr)) + envptr++; + if (envptr == NULL || *envptr == '\0') + return; + + /* count the args so we can allocate room for them */ + argc = count_args(envptr); + bufptr = malloc(1 + strlen(envptr)); + if (bufptr == NULL) + ziperr(ZE_MEM, "Can't get memory for arguments"); + strcpy(bufptr, envptr); + + /* allocate a vector large enough for all args */ + argv = (char **)malloc((argc + *Pargc + 1) * sizeof(char *)); + if (argv == NULL) { + free(bufptr); + ziperr(ZE_MEM, "Can't get memory for arguments"); + } + argvect = argv; + + /* copy the program name first, that's always true */ + *(argv++) = *((*Pargv)++); + + /* copy the environment args first, may be changed */ + do { +#if defined(AMIGA) || defined(UNIX) + if (*bufptr == '"') { + char *argstart = ++bufptr; + *(argv++) = argstart; + for (ch = *bufptr; ch != '\0' && ch != '\"'; + ch = *PREINCSTR(bufptr)) + if (ch == '\\' && bufptr[1] != '\0') + ++bufptr; /* skip to char after backslash */ + if (ch != '\0') /* overwrite trailing '"' */ + *(bufptr++) = '\0'; + + /* remove escape characters */ + while ((argstart = MBSCHR(argstart, '\\')) != NULL) { + strcpy(argstart, argstart + 1); + if (*argstart) + ++argstart; + } + } else { + *(argv++) = bufptr; + while ((ch = *bufptr) != '\0' && !isspace((uch)ch)) INCSTR(bufptr); + if (ch != '\0') *(bufptr++) = '\0'; + } +#else +# ifdef WIN32 + /* We do not support backslash-quoting of quotes in quoted */ + /* strings under Win32, because backslashes are directory */ + /* separators and double quotes are illegal in filenames. */ + if (*bufptr == '"') { + *(argv++) = ++bufptr; + while ((ch = *bufptr) != '\0' && ch != '\"') INCSTR(bufptr); + if (ch != '\0') *(bufptr++) = '\0'; + } else { + *(argv++) = bufptr; + while ((ch = *bufptr) != '\0' && !isspace((uch)ch)) INCSTR(bufptr); + if (ch != '\0') *(bufptr++) = '\0'; + } +# else + *(argv++) = bufptr; + while ((ch = *bufptr) != '\0' && !isspace((uch)ch)) INCSTR(bufptr); + if (ch != '\0') *(bufptr++) = '\0'; +# endif +#endif /* ?(AMIGA || UNIX) */ + while ((ch = *bufptr) != '\0' && isspace((uch)ch)) INCSTR(bufptr); + } while (ch); + + /* now save old argc and copy in the old args */ + argc += *Pargc; + while (--(*Pargc)) *(argv++) = *((*Pargv)++); + + /* finally, add a NULL after the last arg, like UNIX */ + *argv = NULL; + + /* save the values and return */ + *Pargv = argvect; + *Pargc = argc; +} + +local int count_args(s) +char *s; +{ + int count = 0; + char ch; + + do { + /* count and skip args */ + ++count; +#if defined(AMIGA) || defined(UNIX) + if (*s == '\"') { + for (ch = *PREINCSTR(s); ch != '\0' && ch != '\"'; + ch = *PREINCSTR(s)) + if (ch == '\\' && s[1] != '\0') + INCSTR(s); + if (*s) INCSTR(s); /* trailing quote */ + } else + while ((ch = *s) != '\0' && !isspace((uch)ch)) INCSTR(s); +#else +# ifdef WIN32 + if (*s == '\"') { + ++s; /* leading quote */ + while ((ch = *s) != '\0' && ch != '\"') INCSTR(s); + if (*s) INCSTR(s); /* trailing quote */ + } else + while ((ch = *s) != '\0' && !isspace((uch)ch)) INCSTR(s); +# else + while ((ch = *s) != '\0' && !isspace((uch)ch)) INCSTR(s); +# endif +#endif /* ?(AMIGA || UNIX) */ + while ((ch = *s) != '\0' && isspace((uch)ch)) INCSTR(s); + } while (ch); + + return(count); +} + + + +/* Extended argument processing -- by Rich Wales + * This function currently deals only with the MKS shell, but could be + * extended later to understand other conventions. + * + * void expand_args(int *argcp, char ***argvp) + * + * Substitutes the extended command line argument list produced by + * the MKS Korn Shell in place of the command line info from DOS. + * + * The MKS shell gets around DOS's 128-byte limit on the length of + * a command line by passing the "real" command line in the envi- + * ronment. The "real" arguments are flagged by prepending a tilde + * (~) to each one. + * + * This "expand_args" routine creates a new argument list by scanning + * the environment from the beginning, looking for strings begin- + * ning with a tilde character. The new list replaces the original + * "argv" (pointed to by "argvp"), and the number of arguments + * in the new list replaces the original "argc" (pointed to by + * "argcp"). + */ +void expand_args(argcp, argvp) + int *argcp; + char ***argvp; +{ +#ifdef DOS + +/* Do NEVER include (re)definiton of `environ' variable with any version + of MSC or BORLAND/Turbo C. These compilers supply an incompatible + definition in <stdlib.h>. */ +#if defined(__GO32__) || defined(__EMX__) + extern char **environ; /* environment */ +#endif /* __GO32__ || __EMX__ */ + char **envp; /* pointer into environment */ + char **newargv; /* new argument list */ + char **argp; /* pointer into new arg list */ + int newargc; /* new argument count */ + + /* sanity check */ + if (environ == NULL + || argcp == NULL + || argvp == NULL || *argvp == NULL) + return; + /* find out how many environment arguments there are */ + for (envp = environ, newargc = 0; + *envp != NULL && (*envp)[0] == '~'; + envp++, newargc++) ; + if (newargc == 0) + return; /* no environment arguments */ + /* set up new argument list */ + newargv = (char **) malloc(sizeof(char **) * (newargc+1)); + if (newargv == NULL) + return; /* malloc failed */ + for (argp = newargv, envp = environ; + *envp != NULL && (*envp)[0] == '~'; + *argp++ = &(*envp++)[1]) ; + *argp = NULL; /* null-terminate the list */ + /* substitute new argument list in place of old one */ + *argcp = newargc; + *argvp = newargv; +#else /* !DOS */ + if (argcp || argvp) return; +#endif /* ?DOS */ +} + + +/* Fast routine for detection of plain text + * (ASCII or an ASCII-compatible extension such as ISO-8859, UTF-8, etc.) + * Author: Cosmin Truta. + * See "proginfo/txtvsbin.txt" for more information. + * + * This function returns the same result as set_file_type() in "trees.c". + * Unlike in set_file_type(), however, the speed depends on the buffer size, + * so the optimal implementation is different. + */ +int is_text_buf(buf_ptr, buf_size) + ZCONST char *buf_ptr; + unsigned buf_size; +{ + int result = 0; + unsigned i; + unsigned char c; + + for (i = 0; i < buf_size; ++i) + { + c = (unsigned char)buf_ptr[i]; + if (c >= 32) /* speed up the loop by checking this first */ + result = 1; /* white-listed character found; keep looping */ + else /* speed up the loop by inlining the following check */ + if ((c <= 6) || (c >= 14 && c <= 25) || (c >= 28 && c <= 31)) + return 0; /* black-listed character found; stop */ + } + + return result; +} + +#endif /* UTIL */ + + +#ifdef DEBUGNAMES +#undef free +int Free(x) +void *x; +{ + if (x == (void *) 0xdeadbeef) + exit(-1); + free(x); + return 0; +} + +int printnames() +{ + struct zlist far *z; + + for (z = zfiles; z != NULL; z = z->nxt) + fprintf(mesg, "%s %s %s %p %p %p %08x %08x %08x\n", + z->name, z->zname, z->iname, + z->name, z->zname, z->iname, + *((int *) z->name), *((int *) z->zname), + *((int *) z->iname)); + return 0; +} + +#endif /* DEBUGNAMES */ + + +/* Below is used to format zoff_t values, which can be either long or long long + depending on if LARGE FILES are supported. Function provided by SMS. + 10/17/04 EG */ + +/* 2004-12-01 SMS. + * Brought in fancy fzofft() from UnZip. + */ + +/* This implementation assumes that no more than FZOFF_NUM values will be + needed in any printf using it. */ + +/* zip_fzofft(): Format a zoff_t value in a cylindrical buffer set. + This version renamed from fzofft because of name conflict in unzip + when combined in WiZ. */ + +/* 2004-12-19 SMS. + * I still claim than the smart move would have been to disable one or + * the other instance with #if for Wiz. But fine. We'll change the + * name. + */ + +/* This is likely not thread safe. Needs to be done without static storage. + 12/29/04 EG */ + +/* zip_fzofft(): Format a zoff_t value in a cylindrical buffer set. */ + +#define FZOFFT_NUM 4 /* Number of chambers. */ +#define FZOFFT_LEN 24 /* Number of characters/chamber. */ + + +/* Format a zoff_t value in a cylindrical buffer set. */ + +char *zip_fzofft( val, pre, post) + zoff_t val; + char *pre; + char *post; +{ + /* Storage cylinder. */ + static char fzofft_buf[ FZOFFT_NUM][ FZOFFT_LEN]; + static int fzofft_index = 0; + + /* Temporary format string storage. */ + static char fmt[ 16] = "%"; + + /* Assemble the format string. */ + fmt[ 1] = '\0'; /* Start after initial "%". */ + if (pre == FZOFFT_HEX_WID) /* Special hex width. */ + { + strcat( fmt, FZOFFT_HEX_WID_VALUE); + } + else if (pre == FZOFFT_HEX_DOT_WID) /* Special hex ".width". */ + { + strcat( fmt, "."); + strcat( fmt, FZOFFT_HEX_WID_VALUE); + } + else if (pre != NULL) /* Caller's prefix (width). */ + { + strcat( fmt, pre); + } + + strcat( fmt, FZOFFT_FMT); /* Long or long-long or whatever. */ + + if (post == NULL) + strcat( fmt, "d"); /* Default radix = decimal. */ + else + strcat( fmt, post); /* Caller's radix. */ + + /* Advance the cylinder. */ + fzofft_index = (fzofft_index+ 1)% FZOFFT_NUM; + + /* Write into the current chamber. */ + sprintf( fzofft_buf[ fzofft_index], fmt, val); + + /* Return a pointer to this chamber. */ + return fzofft_buf[ fzofft_index]; +} + + +/* Format a uzoff_t value in a cylindrical buffer set. */ +/* Added to support uzoff_t type. 12/29/04 */ + +char *zip_fuzofft( val, pre, post) + uzoff_t val; + char *pre; + char *post; +{ + /* Storage cylinder. */ + static char fuzofft_buf[ FZOFFT_NUM][ FZOFFT_LEN]; + static int fuzofft_index = 0; + + /* Temporary format string storage. */ + static char fmt[ 16] = "%"; + + /* Assemble the format string. */ + fmt[ 1] = '\0'; /* Start after initial "%". */ + if (pre == FZOFFT_HEX_WID) /* Special hex width. */ + { + strcat( fmt, FZOFFT_HEX_WID_VALUE); + } + else if (pre == FZOFFT_HEX_DOT_WID) /* Special hex ".width". */ + { + strcat( fmt, "."); + strcat( fmt, FZOFFT_HEX_WID_VALUE); + } + else if (pre != NULL) /* Caller's prefix (width). */ + { + strcat( fmt, pre); + } + + strcat( fmt, FZOFFT_FMT); /* Long or long-long or whatever. */ + + if (post == NULL) + strcat( fmt, "u"); /* Default radix = decimal. */ + else + strcat( fmt, post); /* Caller's radix. */ + + /* Advance the cylinder. */ + fuzofft_index = (fuzofft_index+ 1)% FZOFFT_NUM; + + /* Write into the current chamber. */ + sprintf( fuzofft_buf[ fuzofft_index], fmt, val); + + /* Return a pointer to this chamber. */ + return fuzofft_buf[ fuzofft_index]; +} + + +/* Display number to mesg stream + 5/15/05 EG */ + +int DisplayNumString(file, i) + FILE *file; + uzoff_t i; +{ + char tempstrg[100]; + int j; + char *s = tempstrg; + + WriteNumString(i, tempstrg); + /* skip spaces */ + for (j = 0; j < 3; j++) { + if (*s != ' ') break; + s++; + } + fprintf(file, "%s", s); + + return 0; +} + +/* Read numbers with trailing size multiplier (like 10M) and return number. + 10/30/04 EG */ + +uzoff_t ReadNumString( numstring ) + char *numstring; +{ + zoff_t num = 0; + char multchar = ' '; + int i; + uzoff_t mult = 1; + + /* check if valid number (currently no negatives) */ + if (numstring == NULL) { + zipwarn("Unable to read empty number in ReadNumString", ""); + return (uzoff_t)-1; + } + if (numstring[0] < '0' || numstring[0] > '9') { + zipwarn("Unable to read number (must start with digit): ", numstring); + return (uzoff_t)-1; + } + if (strlen(numstring) > 8) { + zipwarn("Number too long to read (8 characters max): ", numstring); + return (uzoff_t)-1; + } + + /* get the number part */ + num = atoi(numstring); + + /* find trailing multiplier */ + for (i = 0; numstring[i] && isdigit(numstring[i]); i++) ; + + /* return if no multiplier */ + if (numstring[i] == '\0') { + return (uzoff_t)num; + } + + /* nothing follows multiplier */ + if (numstring[i + 1]) { + return (uzoff_t)-1; + } + + /* get multiplier */ + multchar = toupper(numstring[i]); + + if (multchar == 'K') { + mult <<= 10; + } else if (multchar == 'M') { + mult <<= 20; + } else if (multchar == 'G') { + mult <<= 30; +#ifdef LARGE_FILE_SUPPORT + } else if (multchar == 'T') { + mult <<= 40; +#endif + } else { + return (uzoff_t)-1; + } + + return (uzoff_t)num * mult; +} + + +/* Write the number as a string with a multiplier (like 10M) to outstring. + Always writes no more than 3 digits followed maybe by a multiplier and + returns the characters written or -1 if error. + 10/30/04 EG */ + +int WriteNumString( num, outstring ) + uzoff_t num; + char *outstring; +{ + int mult; + int written = 0; + int i; + int j; + char digits[4]; + int dig; + + *outstring = '\0'; + + /* shift number 1 K until less than 10000 */ + for (mult = 0; num >= 10240; mult++) { + num >>= 10; + } + + /* write digits as " 0" */ + for (i = 1; i < 4; i++) { + digits[i] = ' '; + } + digits[0] = '0'; + + if (num >= 1000) { + i = 3; + num *= 10; + num >>= 10; + mult++; + digits[0] = (char) (num % 10) + '0'; + digits[1] = '.'; + digits[2] = (char) (num / 10) + '0'; + } else { + for (i = 0; num; i++) { + dig = (int) (num % 10); + num /= 10; + digits[i] = dig + '0'; + } + } + if (i == 0) i = 1; + for (j = i; j > 0; j--) { + *outstring = digits[j - 1]; + outstring++; + written++; + } + + /* output multiplier */ + if (mult == 0) { + } else if (mult == 1) { + *outstring = 'K'; + outstring++; + written++; + } else if (mult == 2) { + *outstring = 'M'; + outstring++; + written++; + } else if (mult == 3) { + *outstring = 'G'; + outstring++; + written++; + } else if (mult == 4) { + *outstring = 'T'; + outstring++; + written++; + } else { + *outstring = '?'; + outstring++; + written++; + } + + *outstring = '\0'; + + return written; +} + + +#if 0 /* not used anywhere, should get removed by next release... */ + +/* Apply the Adler-16 checksum to a set of bytes. + * Use this function as you would use crc32(): + * - First call this function by passing a NULL pointer instead of buf + * OR initialize the checksum register with ADLERVAL_INITIAL. + * - Iteratively call this function for each buffer fragment. + * This function returns the updated checksum. + * + * IN assertion: chksum is a valid Adler-16 checksum: + * (chksum & 0xffU) < ADLER16_BASE && ((chksum >> 8) & 0xffU) < ADLER16_BASE + * + * Author: Cosmin Truta. + * See "proginfo/adler16.txt" for more information. + */ + +#define ADLER16_BASE 251 /* The largest prime smaller than 256 */ + +unsigned int adler16(chksum, buf, len) + unsigned int chksum; + ZCONST uch *buf; + extent len; +{ + unsigned int sum1 = chksum & 0xff; + unsigned int sum2 = (chksum >> 8) & 0xff; + extent i; + + Assert((sum1 < ADLER16_BASE) && (sum2 < ADLER16_BASE), + "adler16: invalid checksum"); + + if (buf == NULL) + return 1; + + for (i = 0; i < len; ++i) + { + sum1 += buf[i]; + if (sum1 >= ADLER16_BASE) /* this is faster than modulo ADLER16_BASE */ + sum1 -= ADLER16_BASE; + sum2 += sum1; + if (sum2 >= ADLER16_BASE) /* ditto */ + sum2 -= ADLER16_BASE; + } + + return (sum2 << 8) | sum1; +} + +#endif /* 0, not used anywhere */ + + +/* returns true if abbrev is abbreviation for matchstring */ +int abbrevmatch (matchstring, abbrev, case_sensitive, minmatch) + char *matchstring; + char *abbrev; + int case_sensitive; + int minmatch; +{ + int cnt = 0; + char *m; + char *a; + + m = matchstring; + a = abbrev; + + for (; *m && *a; m++, a++) { + cnt++; + if (case_sensitive) { + if (*m != *a) { + /* mismatch */ + return 0; + } + } else { + if (toupper(*m) != toupper(*a)) { + /* mismatch */ + return 0; + } + } + } + if (cnt < minmatch) { + /* not big enough string */ + return 0; + } + if (*a != '\0') { + /* abbreviation longer than match string */ + return 0; + } + /* either abbreviation or match */ + return 1; +} |