1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450 |
- /*
- 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;
- }
|