/* 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 #ifdef MSDOS16 # include #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 /* 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 . */ #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; } uzoff_t ReadNumStringUL( numstring ) char *numstring; { zoff_t num = 0; /* 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) > 10) { zipwarn("Number too long to read (10 characters max): ", numstring); return (uzoff_t)-1; } return (uzoff_t)atoll(numstring); } /* 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; }