Windows2003-3790/termsrv/admtools/utilsub/expand.c
2020-09-30 16:53:55 +02:00

706 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 )
{
size_t 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 )
{
size_t len;
WCHAR path2[MAX_PATH];
WCHAR ch;
ULONG Result;
if ( (path == NULL) || (wcslen(path) > MAX_PATH) )
return FALSE;
lstrcpyn(path2, path, MAX_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);
}