703 lines
19 KiB
C
703 lines
19 KiB
C
// Copyright (c) 1998-1999 Microsoft Corporation
|
|
/******************************************************************************
|
|
*
|
|
* EXPAND.C
|
|
*
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
|
|
#include "utilsubres.h" // resources refrenced in this file.
|
|
|
|
void ErrorOutFromResource(UINT uiStringResource, ...);
|
|
|
|
#define INCL_DOSPROCESS
|
|
#define INCL_DOSFILEMGR
|
|
#define INCL_DOSERRORS
|
|
#define INCL_ERRORS
|
|
#ifdef DOS
|
|
#define INCL_NOXLATE_DOS16
|
|
#endif
|
|
|
|
#include "expand.h"
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define SUCCESS 0 /* function call successful */
|
|
#define FAILURE (-1) /* function call had a failure */
|
|
|
|
#define READ_ONLY 0x0001 /* file is read only */
|
|
#define HIDDEN 0x0002 /* file is hidden */
|
|
#define SYSTEM 0x0004 /* file is a system file */
|
|
#define VOLUME 0x0008 /* file is a volume label */
|
|
#define SUBDIR 0x0010 /* file is a subdirectory */
|
|
#define ARCHIVE 0x0020 /* file has archive bit on */
|
|
|
|
#define uint unsigned int
|
|
#define ulong unsigned long
|
|
#define ushort unsigned short
|
|
|
|
/*
|
|
* struct search_rec is used to form a linked list of path specifications
|
|
* that are still left to be searched.
|
|
*/
|
|
struct search_rec {
|
|
struct search_rec *next;
|
|
WCHAR *dir_spec; /* path spec up until component w/ wildcard */
|
|
WCHAR *wild_spec; /* component containing wildcard char(s) */
|
|
WCHAR *remain; /* remainder of name after wildcard component */
|
|
ushort attr;
|
|
};
|
|
|
|
/*
|
|
* global variables
|
|
*/
|
|
static struct search_rec *search_head = NULL;
|
|
|
|
/*
|
|
* prototypes of functions referenced
|
|
*/
|
|
split_path(WCHAR *, WCHAR *, WCHAR *, WCHAR *);
|
|
add_search_list(WCHAR *, WCHAR *, WCHAR *, ushort);
|
|
add_arg_to_list(WCHAR *, ARGS *);
|
|
do_tree(struct search_rec *, ushort, ARGS *);
|
|
file_exists(WCHAR *);
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* args_init()
|
|
*
|
|
* Initialize the ARGS struct passed as an argument.
|
|
*
|
|
* ENTRY:
|
|
* argp = pointer to ARGS struct
|
|
* maxargs = max number of args expected
|
|
*
|
|
* EXIT:
|
|
*
|
|
******************************************************************************/
|
|
|
|
void
|
|
args_init( ARGS *argp,
|
|
int maxargs )
|
|
{
|
|
|
|
argp->argc = 0;
|
|
argp->argv = argp->argvp = NULL;
|
|
argp->maxargc = argp->maxargs = maxargs;
|
|
argp->buf = argp->bufptr = argp->bufend = NULL;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* args_trunc()
|
|
*
|
|
* Truncate the memory used by the ARGS struct
|
|
* so that unused memory is freed.
|
|
*
|
|
* ENTRY:
|
|
* argp = pointer to ARGS struct
|
|
*
|
|
* EXIT:
|
|
*
|
|
******************************************************************************/
|
|
|
|
void
|
|
args_trunc( ARGS *argp )
|
|
{
|
|
|
|
/*
|
|
* call realloc to shrink size of argv array, set maxargc = argc
|
|
* to indicate no more room in argv array.
|
|
*/
|
|
realloc(argp->argv, (argp->argc + 1) * sizeof(WCHAR*));
|
|
argp->maxargc = argp->argc;
|
|
|
|
/*
|
|
* call realloc to shrink size of argument string buffer, set bufend
|
|
* pointer to current buf pointer to indicate buf is full.
|
|
*/
|
|
realloc(argp->buf, (size_t)(argp->bufptr - argp->buf));
|
|
argp->bufend = argp->bufptr - 1;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* args_reset()
|
|
*
|
|
* Re-initialize the ARGS struct passed as an argument,
|
|
* free memory if possible.
|
|
*
|
|
* ENTRY:
|
|
* argp = pointer to ARGS struct
|
|
*
|
|
* EXIT:
|
|
*
|
|
******************************************************************************/
|
|
|
|
void
|
|
args_reset( ARGS *argp )
|
|
{
|
|
|
|
/*
|
|
* if there is an argv array, but it has been truncated, then free
|
|
* the array so a new one will be allocated later.
|
|
*/
|
|
if (argp->argv && argp->maxargc != argp->maxargs) {
|
|
free(argp->argv);
|
|
argp->argv = NULL;
|
|
}
|
|
argp->argc = 0;
|
|
argp->argvp = argp->argv;
|
|
argp->maxargc = argp->maxargs;
|
|
|
|
/*
|
|
* if there is an argument buffer, but it has been truncated, then
|
|
* free the buffer so a new one will be allocated later.
|
|
*/
|
|
if (argp->buf && argp->bufend != argp->buf + MAX_ARG_ALLOC - 1) {
|
|
free(argp->buf);
|
|
argp->buf = argp->bufend = NULL;
|
|
}
|
|
argp->bufptr = argp->buf;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* args_free()
|
|
*
|
|
* Will free the memory allocated for
|
|
* argument storage by all preceeding calls to expand_path().
|
|
* Args_init() must be called before reusing this ARGS structure.
|
|
*
|
|
* ENTRY:
|
|
* argp = pointer to ARGSW struct
|
|
*
|
|
* EXIT:
|
|
*
|
|
******************************************************************************/
|
|
|
|
void
|
|
args_free( ARGS *argp )
|
|
{
|
|
|
|
if (argp->argv != NULL)
|
|
free(argp->argv);
|
|
argp->argv = argp->argvp = NULL;
|
|
|
|
if (argp->buf != NULL)
|
|
free(argp->buf);
|
|
argp->buf = argp->bufptr = argp->bufend = NULL;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* expand_path()
|
|
*
|
|
* This routine will expand the specified path string into pathnames
|
|
* that match. The matching pathnames will be added to the specified
|
|
* argv array and the specified argc count will be incremented to
|
|
* reflect the number of pathnames added.
|
|
*
|
|
* This routine will expand filename arguments in Unix fashion
|
|
* (i.e. '[..]' is supported, '?' and '*' are allowed anywhere in the
|
|
* pathname, even in the directory part of the name, and the
|
|
* name/extension separator '.' is not treated special but is just
|
|
* considered part of the filename).
|
|
*
|
|
* Storage for the pathname strings will be obtained via malloc.
|
|
* This space may later be free'd with a call to args_free();
|
|
*
|
|
* ENTRY:
|
|
* path Pathname string to be expanded.
|
|
* attr Attribute bits of files to include
|
|
* (regular, directory, hidden, system).
|
|
* -1 = return the specified pathname string unmodified
|
|
* in the argv array.
|
|
* argp Pointer to an ARGSW struct containing fields to be used/
|
|
* updated by expand_path. The ARGS struct must be initialized
|
|
* by calling args_init() before calling expand_path().
|
|
*
|
|
* EXIT:
|
|
* TRUE -- indicates at least 1 pathname was found matching
|
|
* the pathname string specified.
|
|
* FALSE -- indicates no matching pathnames were found. The specified
|
|
* pathname string is returned unmodified in the argv array.
|
|
*
|
|
******************************************************************************/
|
|
|
|
int
|
|
expand_path( WCHAR *path,
|
|
ushort attr,
|
|
ARGS *argp )
|
|
{
|
|
int argc, add_count, rc, i, j, k;
|
|
WCHAR **argv;
|
|
WCHAR dirname[128], wild[128], remain[128];
|
|
struct search_rec *save, *q;
|
|
|
|
#ifdef DEBUG
|
|
printf("expand_path: path=%s attr=%d\n", path, attr);
|
|
#endif
|
|
|
|
argc = argp->argc;
|
|
argv = argp->argvp;
|
|
if ( attr != -1 && split_path(path, dirname, wild, remain)) {
|
|
add_search_list(dirname, wild, remain, attr);
|
|
while (search_head) {
|
|
/*
|
|
* save the next portion and allow new directories to be
|
|
* added to the head.
|
|
*/
|
|
save = search_head->next;
|
|
search_head->next = NULL;
|
|
|
|
/*
|
|
* perform the do_tree operation on the current path
|
|
*/
|
|
rc = do_tree(search_head, attr, argp);
|
|
|
|
/*
|
|
* restore the saved list at the end of the head list
|
|
*/
|
|
if ( save ) {
|
|
q = search_head;
|
|
while ( q->next ) {
|
|
q = q->next;
|
|
}
|
|
q->next = save;
|
|
}
|
|
|
|
/*
|
|
* move to the next path in the list and free the memory used
|
|
* by the link we are done with
|
|
*/
|
|
do {
|
|
q = search_head;
|
|
search_head = search_head->next;
|
|
free( q->dir_spec );
|
|
free( q->wild_spec );
|
|
free( q->remain );
|
|
free( q );
|
|
} while (rc==FAILURE && search_head);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If no filenames were expanded, just put the original name
|
|
* into the buffer and indicate no names were expanded.
|
|
*/
|
|
if (argc == argp->argc) {
|
|
add_arg_to_list(path, argp);
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* Sort the names just added
|
|
*/
|
|
if ( argv == NULL )
|
|
argv = argp->argv;
|
|
add_count = argp->argc - argc;
|
|
for (i=add_count-1; i>0; --i) {
|
|
uint swap = FALSE;
|
|
for (j=0; j<i; ++j) {
|
|
if (!argv[j] || !argv[j+1]) {
|
|
ErrorOutFromResource(IDS_INTERNAL_ERROR_1);
|
|
//fprintf(stderr,"internal error 1\n");
|
|
}
|
|
for (k=0; k<128; ++k) {
|
|
if (argv[j][k] < argv[j+1][k]) {
|
|
break;
|
|
} else if (argv[j][k] > argv[j+1][k]) {
|
|
WCHAR *temp;
|
|
swap = TRUE;
|
|
temp = argv[j];
|
|
argv[j] = argv[j+1];
|
|
argv[j+1] = temp;
|
|
break;
|
|
}
|
|
}
|
|
if (k>125) {
|
|
ErrorOutFromResource(IDS_INTERNAL_ERROR_2);
|
|
// fprintf(stderr,"internal error 2\n");
|
|
}
|
|
}
|
|
if (!swap) {
|
|
break;
|
|
}
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* add_search_list()
|
|
*
|
|
* Adds a record to the global search list, search_head.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static
|
|
add_search_list(
|
|
WCHAR *dir_spec, /* the dir to be added to the list */
|
|
WCHAR *wild_spec, /* the file to be added to the list */
|
|
WCHAR *remain_spec, /* remaining portion of pathname */
|
|
ushort attr )
|
|
{
|
|
struct search_rec *new, /* pointer to the new link */
|
|
*q; /* used to traverse the linked list */
|
|
|
|
#ifdef DEBUG
|
|
wprintf(L"add_search_list: dir=%s: file=%s: rem=%s:\n", dir_spec, wild_spec, remain_spec);
|
|
#endif
|
|
|
|
/*
|
|
* allocate the new link. make sure that it is initialized to zeros.
|
|
*/
|
|
new = malloc(sizeof(struct search_rec));
|
|
|
|
if (!new) {
|
|
ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_MALLOC);
|
|
// fprintf(stderr, "add_search_list: not enough memory (malloc)");
|
|
return FAILURE;
|
|
}
|
|
|
|
memset(new, 0, sizeof(struct search_rec));
|
|
|
|
/*
|
|
* allocate memory for and copy the dir spec and file spec.
|
|
*/
|
|
if (dir_spec)
|
|
{
|
|
new->dir_spec = _wcsdup(dir_spec);
|
|
if( new->dir_spec == NULL )
|
|
{
|
|
ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_STRDUP1);
|
|
// fprintf(stderr, "add_search_list: not enough memory (strdup1)");
|
|
return FAILURE;
|
|
}
|
|
|
|
_wcslwr( new->dir_spec );
|
|
}
|
|
if (wild_spec)
|
|
{
|
|
new->wild_spec = _wcsdup(wild_spec);
|
|
if (new->wild_spec == NULL )
|
|
{
|
|
ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_STRDUP2);
|
|
// fprintf(stderr, "add_search_list: not enough memory (strdup2)");
|
|
return FAILURE;
|
|
}
|
|
|
|
_wcslwr( new->wild_spec );
|
|
|
|
}
|
|
if (remain_spec)
|
|
{
|
|
new->remain = _wcsdup(remain_spec);
|
|
if( new->remain == NULL )
|
|
{
|
|
ErrorOutFromResource(IDS_ADD_SRCH_LIST_NO_MEMORY_STRDUP3);
|
|
// fprintf(stderr, "add_search_list: not enough memory (strdup3)");
|
|
return FAILURE;
|
|
}
|
|
|
|
_wcslwr( new->remain );
|
|
|
|
}
|
|
|
|
/*
|
|
* store file attributes
|
|
*/
|
|
if (remain_spec)
|
|
new->attr = attr | SUBDIR;
|
|
else
|
|
new->attr = attr;
|
|
|
|
/*
|
|
* add the new link at the end of the list
|
|
*/
|
|
if (!search_head) {
|
|
search_head = new;
|
|
} else {
|
|
q = search_head;
|
|
while (q->next) {
|
|
q = q->next;
|
|
}
|
|
q->next = new;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* add_arg_to_list()
|
|
*
|
|
* This routine adds the specified argument string to the argv array,
|
|
* and increments the argv pointer and argc counter.
|
|
* If necessary, memory for the argument string is allocated.
|
|
*
|
|
* EXIT:
|
|
* SUCCESS -- if argument added successfully
|
|
* FAILURE -- if argument could not be added
|
|
* (indicates too many args or out of memory for argument string)
|
|
*
|
|
******************************************************************************/
|
|
static int
|
|
add_arg_to_list( WCHAR *arg_string,
|
|
ARGS *argp )
|
|
{
|
|
int len;
|
|
|
|
#ifdef DEBUG
|
|
wprintf(L"add_arg_to_list: arg_string=%s:, argc=%d, argvp=%x, maxargs=%d\n",
|
|
arg_string,argp->argc,argp->argvp,argp->maxargc);
|
|
#endif
|
|
if (argp->argc >= argp->maxargc) {
|
|
ErrorOutFromResource(IDS_TOO_MANY_ARGUMENTS);
|
|
// fprintf(stderr,"add_arg_to_list: too many arguments\n");
|
|
return FAILURE;
|
|
}
|
|
if (!argp->argv) {
|
|
argp->argv = malloc(sizeof(WCHAR *) * (argp->maxargs+1));
|
|
if (argp->argv) {
|
|
argp->argc = 0;
|
|
argp->argvp = argp->argv;
|
|
argp->maxargc = argp->maxargs;
|
|
} else {
|
|
ErrorOutFromResource(IDS_ARGS_TO_LIST_NOT_ENOUGH_MEMORY);
|
|
// fprintf(stderr,"add_arg_to_list: not enough memory\n");
|
|
return FAILURE;
|
|
}
|
|
}
|
|
if (!argp->buf) {
|
|
argp->buf = malloc(MAX_ARG_ALLOC);
|
|
if (argp->buf) {
|
|
argp->bufptr = argp->buf;
|
|
argp->bufend = argp->buf + MAX_ARG_ALLOC - 1;
|
|
} else {
|
|
ErrorOutFromResource(IDS_ARGS_TO_LIST_NOT_ENOUGH_MEMORY);
|
|
// fprintf(stderr,"add_arg_to_list: not enough memory\n");
|
|
return FAILURE;
|
|
}
|
|
}
|
|
len = wcslen(arg_string) + 1;
|
|
if (argp->bufptr + len > argp->bufend) {
|
|
ErrorOutFromResource(IDS_ARGS_TO_LIST_ARG_BUFFER_SMALL);
|
|
// fprintf(stderr,"add_arg_to_list: argument buffer too small\n");
|
|
return FAILURE;
|
|
}
|
|
wcscpy(argp->bufptr, arg_string);
|
|
*(argp->argvp) = argp->bufptr;
|
|
argp->bufptr += len;
|
|
++argp->argc;
|
|
++argp->argvp;
|
|
*(argp->argvp) = NULL;
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* do_tree()
|
|
*
|
|
******************************************************************************/
|
|
|
|
static
|
|
do_tree( struct search_rec *searchp,
|
|
ushort attr,
|
|
ARGS *argp )
|
|
{
|
|
int rc; /* return code from Dos calls */
|
|
WIN32_FIND_DATA result; /* the structure returned from FindFirst/Next */
|
|
ushort count = 1; /* number of files to look for at one time */
|
|
HANDLE handle; /* the dir handle used by FindFirst/Next */
|
|
WCHAR full_path[128]; /* used to hold the path/file combination */
|
|
WCHAR dirname[128], wild[128], remain[128];
|
|
WCHAR *fptr; /* pointer to file portion of full_path */
|
|
ULONG Status;
|
|
|
|
#ifdef DEBUG
|
|
wprintf(L"do_tree: dirname=%s:\n", searchp->dir_spec);
|
|
#endif
|
|
|
|
/*
|
|
* build up directory part of path and save a pointer to the file portion
|
|
*/
|
|
wcscpy(full_path, searchp->dir_spec);
|
|
fptr = full_path + wcslen(searchp->dir_spec);
|
|
wcscpy(fptr, L"*.*");
|
|
|
|
handle = FindFirstFile ( full_path, /* files to find */
|
|
&result
|
|
);
|
|
|
|
if(handle == INVALID_HANDLE_VALUE){
|
|
Status = GetLastError();
|
|
if(Status == ERROR_NO_MORE_FILES) {
|
|
// no files match
|
|
return(SUCCESS);
|
|
}
|
|
return(FAILURE);
|
|
}
|
|
|
|
rc = TRUE;
|
|
while (rc) {
|
|
/*
|
|
* do not do anything for the "." and ".." entries
|
|
*/
|
|
if (wcscmp(result.cFileName, L".") == 0 ||
|
|
wcscmp(result.cFileName, L"..") == 0) {
|
|
rc = FindNextFile( handle, &result );
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* fully qualify the found file
|
|
*/
|
|
wcscpy(fptr, _wcslwr(result.cFileName));
|
|
if (searchp->remain)
|
|
wcscat(full_path, searchp->remain);
|
|
|
|
/*
|
|
* see if current wild_spec matches FindFirst/Next file
|
|
*/
|
|
if (unix_match(searchp->wild_spec, result.cFileName)) {
|
|
if (searchp->remain && split_path(full_path, dirname, wild, remain)) {
|
|
if (result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
|
|
file_exists(dirname))
|
|
add_search_list(dirname, wild, remain, attr);
|
|
} else if (file_exists(full_path)) {
|
|
rc = add_arg_to_list(full_path, argp);
|
|
if (rc != SUCCESS)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* find the next file
|
|
*/
|
|
rc = FindNextFile( handle, &result );
|
|
}
|
|
|
|
/*
|
|
* if no more files to find then reset the error code back to successful.
|
|
*/
|
|
|
|
if(!rc) {
|
|
Status = GetLastError();
|
|
if(Status == ERROR_NO_MORE_FILES)
|
|
rc = SUCCESS;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* split_path()
|
|
*
|
|
* This routine splits the specified pathname into 3 parts, any of which
|
|
* may be null; 1) the pathname from the beginning up to but not including
|
|
* the first component containing a wildcard character, 2) the component
|
|
* containing the wildcard, and 3) the remainder of the path string after
|
|
* the component containing the wildcard.
|
|
*
|
|
* Examples:
|
|
* Original path dir file remain
|
|
* "c:\mydir\dir??\*.c" "c:\mydir\" "dir??" "\*.c"
|
|
* "*\abc.def" "" "*" "\abc.def"
|
|
* "mydir\*.c" "mydir\" "*.c" ""
|
|
*
|
|
* EXIT:
|
|
* TRUE -- if the pathname could be split
|
|
* FALSE -- otherwise (i.e. pathname did not contain any wildcards)
|
|
*
|
|
******************************************************************************/
|
|
|
|
static int
|
|
split_path( WCHAR *path,
|
|
WCHAR *dir,
|
|
WCHAR *file,
|
|
WCHAR *remain )
|
|
{
|
|
WCHAR *cp, *end_dir, *end_wild = NULL;
|
|
|
|
#ifdef DEBUG
|
|
wprintf("split_path: path=%s:\n", path);
|
|
#endif
|
|
for (cp=end_dir=path; *cp!=L'\0'; ) {
|
|
if (*cp==L'\\' || *cp==L'/' || *cp==L':') {
|
|
++cp;
|
|
while (*cp==L'\\' || *cp==L'/' ) ++cp;
|
|
end_dir = cp;
|
|
} else if (*cp==L'*' || *cp==L'?' || *cp==L'[') {
|
|
++cp;
|
|
while (*cp!=L'\\' && *cp!=L'/' && *cp!=L'\0') ++cp;
|
|
end_wild = cp;
|
|
break;
|
|
} else {
|
|
++cp;
|
|
}
|
|
}
|
|
if (!end_wild)
|
|
return(FALSE);
|
|
|
|
for (cp=path; cp<end_dir; ++cp, ++dir)
|
|
*dir = *cp;
|
|
*dir = L'\0';
|
|
for (cp=end_dir; cp<end_wild; ++cp, ++file)
|
|
*file = *cp;
|
|
*file = L'\0';
|
|
wcscpy(remain, cp);
|
|
#ifdef DEBUG
|
|
wprintf("split_path: dir=%s: file=%s: remain=%s:\n", dir, file, remain);
|
|
#endif
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* file_existsW()
|
|
*
|
|
* Returns TRUE if specified file exists, otherwise returns FALSE.
|
|
*
|
|
******************************************************************************/
|
|
|
|
static int
|
|
file_exists( WCHAR *path )
|
|
{
|
|
int len;
|
|
WCHAR path2[128];
|
|
WCHAR ch;
|
|
ULONG Result;
|
|
|
|
wcscpy(path2, path);
|
|
len = wcslen(path2);
|
|
while ((ch=path2[--len]) == L'\\' || ch == L'/' ) path2[len] = L'\0';
|
|
|
|
Result = GetFileAttributes(path2);
|
|
if(Result == 0xFFFFFFFF) {
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
|