470 lines
13 KiB
C
470 lines
13 KiB
C
/***
|
|
*wild.c - wildcard expander
|
|
*
|
|
* Copyright (c) 1985-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* expands wildcards in argv
|
|
*
|
|
* handles '*' (none or more of any char) and '?' (exactly one char)
|
|
*
|
|
*Revision History:
|
|
* 05-21-84 RN initial version
|
|
* 06-07-85 TDC since dos accepts forward slash, added
|
|
* code to accept forward slash in a manner consistent
|
|
* with reverse slash.
|
|
* 09-20-86 SKS Modified for OS/2
|
|
* All argument strings to this function have a
|
|
* leading flag character. If the flag is a quote,
|
|
* that argument string was quoted on the command
|
|
* line and should have not wildcard expansion. In all
|
|
* cases the leading flag character is removed from
|
|
* the string.
|
|
* 11-11-86 JMB Added Kanji support under KANJI switch.
|
|
* 09-21-88 WAJ initial 386 version
|
|
* 04-09-90 GJF Added #include <cruntime.h> and removed #include
|
|
* <register.h>. Made calling types explicit (_CALLTYPE1
|
|
* or _CALLTYPE4). Also, fixed the copyright.
|
|
* 04-10-90 GJF Added #include <internal.h> and fixed compiler warnings
|
|
* (-W3).
|
|
* 07-03-90 SBM Compiles cleanly with -W3 under KANJI, removed
|
|
* redundant includes, removed #include <internal.h>
|
|
* to keep wild.c free of private stuff, should we
|
|
* decide to release it
|
|
* 09-07-90 SBM put #include <internal.h> back in, reason for
|
|
* removing it discovered to be horribly bogus
|
|
* 10-08-90 GJF New-style function declarators.
|
|
* 01-18-91 GJF ANSI naming.
|
|
* 04-06-93 SKS Replace _CRTAPI* with __cdecl
|
|
* Remove explicit declarations of __argc & __argv.
|
|
* They are declared in <stdlib.h>
|
|
* 05-05-93 SKS Filename sorting should be case-insensitive
|
|
* 06-09-93 KRS Update _MBCS support.
|
|
* 10-20-93 GJF Merged in NT version.
|
|
* 11-23-93 CFW Wide char enable, grab _find from stdargv.c.
|
|
* 12-07-93 CFW Change _TCHAR to _TSCHAR.
|
|
* 04-22-94 GJF Made defintions of arghead, argend, _WildFindHandle
|
|
* and findbuf conditional on DLL_FOR_WIN32S.
|
|
* 01-10-95 CFW Debug CRT allocs.
|
|
* 01-18-95 GJF Must replace _tcsdup with _malloc_crt/_tcscpy for
|
|
* _DEBUG build.
|
|
* 02-04-98 GJF Changes for Win64: use intptr_t and ptrdiff_t casts
|
|
* where appropriate.
|
|
* 02-19-01 GB added check for return value of malloc in find.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <cruntime.h>
|
|
#include <oscalls.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <msdos.h>
|
|
#include <internal.h>
|
|
#include <tchar.h>
|
|
|
|
#ifdef _MBCS
|
|
#include <mbdata.h>
|
|
#include <mbstring.h>
|
|
#endif
|
|
#include <dbgint.h>
|
|
|
|
/*
|
|
** these are the data structures
|
|
**
|
|
** __argv
|
|
** ------- ------
|
|
** | |---->| |---->"arg0"
|
|
** ------- ------
|
|
** | |---->"arg1"
|
|
** ------
|
|
** ....
|
|
** ------
|
|
** | |---->"argn"
|
|
** ------
|
|
** |NULL|
|
|
** ------
|
|
** argend
|
|
** -------
|
|
** ------- | |
|
|
** | | __argc -------
|
|
** ------- |
|
|
** |
|
|
** arghead V
|
|
** ------ --------- ----------
|
|
** | |---->| | |----> .... ---->| |NULL|
|
|
** ------ --------- ----------
|
|
** | |
|
|
** V V
|
|
** "narg0" "nargn"
|
|
*/
|
|
|
|
#define ERRORHANDLE ((HANDLE)(intptr_t)(-1))
|
|
|
|
/* local function tchars */
|
|
#ifdef WPRFLAG
|
|
#define tmatch wmatch
|
|
#define tadd wadd
|
|
#define tsort wsort
|
|
#define tfind wfind
|
|
#else
|
|
#define tmatch match
|
|
#define tadd add
|
|
#define tsort sort
|
|
#define tfind find
|
|
#endif
|
|
|
|
#define SLASHCHAR _T('\\')
|
|
#define FWDSLASHCHAR _T('/')
|
|
#define COLONCHAR _T(':')
|
|
#define QUOTECHAR _T('"')
|
|
|
|
#define SLASH _T("\\")
|
|
#define FWDSLASH _T("/")
|
|
#define STAR _T("*.*")
|
|
#define DOT _T(".")
|
|
#define DOTDOT _T("..")
|
|
|
|
#define WILDSTRING _T("*?")
|
|
|
|
struct argnode {
|
|
_TSCHAR *argptr;
|
|
struct argnode *nextnode;
|
|
};
|
|
|
|
static struct argnode *arghead;
|
|
static struct argnode *argend;
|
|
|
|
#ifdef WPRFLAG
|
|
static int __cdecl wmatch(wchar_t *, wchar_t *);
|
|
static int __cdecl wadd(wchar_t *);
|
|
static void __cdecl wsort(struct argnode *);
|
|
static wchar_t * __cdecl wfind (wchar_t *pattern);
|
|
#else
|
|
static int __cdecl match(char *, char *);
|
|
static int __cdecl add(char *);
|
|
static void __cdecl sort(struct argnode *);
|
|
static char * __cdecl find (char *pattern);
|
|
#endif
|
|
|
|
/***
|
|
*int _cwild() - wildcard expander
|
|
*
|
|
*Purpose:
|
|
* expands wildcard in file specs in argv
|
|
*
|
|
* handles '*' (none or more of any char), '?' (exactly one char), and
|
|
* '[string]' (chars which match string chars or between n1 and n2
|
|
* if 'n1-n2' in string inclusive)
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
* returns 0 if successful, -1 if any malloc() calls fail
|
|
* if problems with malloc, the old argc and argv are not touched
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef WPRFLAG
|
|
int __cdecl _wcwild (
|
|
#else
|
|
int __cdecl _cwild (
|
|
#endif
|
|
void
|
|
)
|
|
{
|
|
#ifdef WPRFLAG
|
|
REG1 wchar_t **argv = __wargv;
|
|
#else
|
|
REG1 char **argv = __argv;
|
|
#endif
|
|
REG2 struct argnode *nodeptr;
|
|
REG3 int argc;
|
|
REG4 _TSCHAR **tmp;
|
|
_TSCHAR *wchar;
|
|
|
|
arghead = argend = NULL;
|
|
|
|
#ifdef WPRFLAG
|
|
for (argv = __wargv; *argv; argv++) /* for each arg... */
|
|
#else
|
|
for (argv = __argv; *argv; argv++) /* for each arg... */
|
|
#endif
|
|
if ( *(*argv)++ == QUOTECHAR )
|
|
/* strip leading quote from quoted arg */
|
|
{
|
|
if (tadd(*argv))
|
|
return(-1);
|
|
}
|
|
else if (wchar = _tcspbrk( *argv, WILDSTRING )) {
|
|
/* attempt to expand arg with wildcard */
|
|
if (tmatch( *argv, wchar ))
|
|
return(-1);
|
|
}
|
|
else if (tadd( *argv )) /* normal arg, just add */
|
|
return(-1);
|
|
|
|
/* count the args */
|
|
for (argc = 0, nodeptr = arghead; nodeptr;
|
|
nodeptr = nodeptr->nextnode, argc++)
|
|
;
|
|
|
|
/* try to get new arg vector */
|
|
if (!(tmp = (_TSCHAR **)_malloc_crt(sizeof(_TSCHAR *)*(argc+1))))
|
|
return(-1);
|
|
|
|
/* the new arg vector... */
|
|
#ifdef WPRFLAG
|
|
__wargv = tmp;
|
|
#else
|
|
__argv = tmp;
|
|
#endif
|
|
|
|
/* the new arg count... */
|
|
__argc = argc;
|
|
|
|
/* install the new args */
|
|
for (nodeptr = arghead; nodeptr; nodeptr = nodeptr->nextnode)
|
|
*tmp++ = nodeptr->argptr;
|
|
|
|
/* the terminal NULL */
|
|
*tmp = NULL;
|
|
|
|
/* free up local data */
|
|
for (nodeptr = arghead; nodeptr; nodeptr = arghead) {
|
|
arghead = arghead->nextnode;
|
|
_free_crt(nodeptr);
|
|
}
|
|
|
|
/* return success */
|
|
return(0);
|
|
}
|
|
|
|
|
|
/***
|
|
*match(arg, ptr) - [STATIC]
|
|
*
|
|
*Purpose:
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef WPRFLAG
|
|
static int __cdecl wmatch (
|
|
#else
|
|
static int __cdecl match (
|
|
#endif
|
|
REG4 _TSCHAR *arg,
|
|
REG1 _TSCHAR *ptr
|
|
)
|
|
{
|
|
REG2 _TSCHAR *new;
|
|
REG3 int length = 0;
|
|
_TSCHAR *all;
|
|
REG5 struct argnode *first;
|
|
REG6 int gotone = 0;
|
|
|
|
while (ptr != arg && *ptr != SLASHCHAR && *ptr != FWDSLASHCHAR
|
|
&& *ptr != COLONCHAR) {
|
|
/* find first slash or ':' before wildcard */
|
|
#ifdef _MBCS
|
|
if (--ptr > arg)
|
|
ptr = _mbsdec(arg,ptr+1);
|
|
#else
|
|
ptr--;
|
|
#endif
|
|
}
|
|
|
|
if (*ptr == COLONCHAR && ptr != arg+1) /* weird name, just add it as is */
|
|
return(tadd(arg));
|
|
|
|
if (*ptr == SLASHCHAR || *ptr == FWDSLASHCHAR
|
|
|| *ptr == COLONCHAR) /* pathname */
|
|
length = (int)(ptrdiff_t)(ptr - arg + 1); /* length of dir prefix */
|
|
|
|
if (new = tfind(arg)) { /* get the first file name */
|
|
first = argend;
|
|
|
|
do { /* got a file name */
|
|
if (_tcscmp(new, DOT) && _tcscmp(new, DOTDOT)) {
|
|
if (*ptr != SLASHCHAR && *ptr != COLONCHAR
|
|
&& *ptr != FWDSLASHCHAR ) {
|
|
/* current directory; don't need path */
|
|
#ifdef _DEBUG
|
|
if (!(arg=_malloc_crt((_tcslen(new)+1)*sizeof(_TSCHAR)))
|
|
|| tadd(_tcscpy(arg,new)))
|
|
#else /* ndef _DEBUG */
|
|
if (!(arg = _tcsdup(new)) || tadd(arg))
|
|
#endif /* _DEBUG */
|
|
return(-1);
|
|
}
|
|
else /* add full pathname */
|
|
if (!(all=_malloc_crt((length+_tcslen(new)+1)*sizeof(_TSCHAR)))
|
|
|| tadd(_tcscpy(_tcsncpy(all,arg,length)+length,new)
|
|
- length))
|
|
return(-1);
|
|
|
|
gotone++;
|
|
}
|
|
|
|
}
|
|
while (new = tfind(NULL)); /* get following files */
|
|
|
|
if (gotone) {
|
|
tsort(first ? first->nextnode : arghead);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
return(tadd(arg)); /* no match */
|
|
}
|
|
|
|
/***
|
|
*add(arg) - [STATIC]
|
|
*
|
|
*Purpose:
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef WPRFLAG
|
|
static int __cdecl wadd (
|
|
#else
|
|
static int __cdecl add (
|
|
#endif
|
|
_TSCHAR *arg
|
|
)
|
|
{
|
|
REG1 struct argnode *nodeptr;
|
|
|
|
if (!(nodeptr = (struct argnode *)_malloc_crt(sizeof(struct argnode))))
|
|
return(-1);
|
|
|
|
nodeptr->argptr = arg;
|
|
nodeptr->nextnode = NULL;
|
|
|
|
if (arghead)
|
|
argend->nextnode = nodeptr;
|
|
else
|
|
arghead = nodeptr;
|
|
|
|
argend = nodeptr;
|
|
return(0);
|
|
}
|
|
|
|
|
|
/***
|
|
*sort(first) - [STATIC]
|
|
*
|
|
*Purpose:
|
|
*
|
|
*Entry:
|
|
*
|
|
*Exit:
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef WPRFLAG
|
|
static void __cdecl wsort (
|
|
#else
|
|
static void __cdecl sort (
|
|
#endif
|
|
REG2 struct argnode *first
|
|
)
|
|
{
|
|
REG1 struct argnode *nodeptr;
|
|
REG3 _TSCHAR *temp;
|
|
|
|
if (first) /* something to sort */
|
|
while (nodeptr = first->nextnode) {
|
|
do {
|
|
#ifdef _POSIX_
|
|
if (_tcscmp(nodeptr->argptr, first->argptr) < 0) {
|
|
#else
|
|
if (_tcsicmp(nodeptr->argptr, first->argptr) < 0) {
|
|
#endif /* _POSIX_ */
|
|
temp = first->argptr;
|
|
first->argptr = nodeptr->argptr;
|
|
nodeptr->argptr = temp;
|
|
}
|
|
}
|
|
while (nodeptr = nodeptr->nextnode);
|
|
|
|
first = first->nextnode;
|
|
}
|
|
}
|
|
|
|
|
|
/***
|
|
*find(pattern) - find matching filename
|
|
*
|
|
*Purpose:
|
|
* if argument is non-null, do a DOSFINDFIRST on that pattern
|
|
* otherwise do a DOSFINDNEXT call. Return matching filename
|
|
* or NULL if no more matches.
|
|
*
|
|
*Entry:
|
|
* pattern = pointer to pattern or NULL
|
|
* (NULL means find next matching filename)
|
|
*
|
|
*Exit:
|
|
* returns pointer to matching file name
|
|
* or NULL if no more matches.
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifdef WPRFLAG
|
|
static wchar_t * __cdecl wfind (
|
|
#else
|
|
static char * __cdecl find (
|
|
#endif /* WPRFLAG */
|
|
_TSCHAR *pattern
|
|
)
|
|
{
|
|
_TSCHAR *retval;
|
|
|
|
static HANDLE _WildFindHandle;
|
|
static LPWIN32_FIND_DATA findbuf;
|
|
|
|
if (pattern) {
|
|
if (findbuf == NULL)
|
|
if ((findbuf = (LPWIN32_FIND_DATA)_malloc_crt(MAX_PATH + sizeof(*findbuf))) == NULL)
|
|
return NULL;
|
|
|
|
if (_WildFindHandle != NULL) {
|
|
(void)FindClose( _WildFindHandle );
|
|
_WildFindHandle = NULL;
|
|
}
|
|
|
|
_WildFindHandle = FindFirstFile( (LPTSTR)pattern, findbuf );
|
|
if (_WildFindHandle == ERRORHANDLE)
|
|
return NULL;
|
|
}
|
|
else if (!FindNextFile( _WildFindHandle, findbuf )) {
|
|
(void)FindClose( _WildFindHandle );
|
|
_WildFindHandle = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
retval = findbuf->cFileName;
|
|
|
|
return retval;
|
|
}
|