cosmopolitan/third_party/zip/util.c

1452 lines
41 KiB
C
Raw Permalink Normal View History

2021-06-09 23:59:39 +00:00
/*
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 "third_party/zip/zip.h"
2022-10-16 20:39:41 +00:00
// MISSING #include "ebcdic.h"
#include "libc/ctype.h"
#include "libc/str/str.h"
2022-10-16 20:39:41 +00:00
#ifdef MSDOS16
// MISSING #include <dos.h>
#endif
#ifdef NO_MKTIME
# ifndef IZ_MKTIME_ONLY
# define IZ_MKTIME_ONLY /* only mktime() related code is pulled in */
# endif
#include "third_party/zip/timezone.c"
#endif
2021-06-09 23:59:39 +00:00
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 */
2022-10-16 20:39:41 +00:00
#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(&regs, &regs, &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 "libc/str/str.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 */
2021-06-09 23:59:39 +00:00
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
2021-06-09 23:59:39 +00:00
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;
}