windows-nt/Source/XPSP1/NT/base/ntsetup/oemtools/oempatch/dirwak2a.c
2020-09-26 16:20:57 +08:00

329 lines
19 KiB
C

/* ---------------------------------------------------------------------------------------------------------------------------- */
/* */
/* DIRWAK2AC */
/* */
/* ---------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------- */
/* */
/* int DirectoryWalk(context,directory,ProcessDirectory,ProcessFile,ProcessDirectoryEnd,pTree) will search a directory and all */
/* of its subdirectories looking for file entries. The provided ProcessDirectory() will be called for each subdirectory found */
/* just before searching that directory. ProcessDirectory() is called for the original path; an empty name is reported. */
/* The provided ProcessDirectoryEnd() will be called after the last file has been reported in each directory. The provided */
/* ProcessFile() is called for each file located. pTree is passed as to those functions for OEMPATCH support */
/* */
/* If NULL is provided for ProcessDirectory, subdirectories will not be included in the search. */
/* If NULL is provided for ProcessFile, no notification of file entries will be made. */
/* If NULL is provided for ProcessDirectoryEnd, no notification of end of directories will be made. */
/* */
/* Return codes are: */
/* */
/* 0 => no error. */
/* DW_MEMORY => ran out of memory to track directories. */
/* DW_ERROR => DOS reported an error on a search (most likely the path doesn't exist.) */
/* DW_DEPTH => total path name was or became too long. */
/* any others => whatever ProcessDirectory(), ProcessFile(), or ProcessDirectoryEnd() returned. */
/* */
/* ---------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------- */
/* */
/* Revision 1.9a 10/04/99 JXW DIRWAK2A: added support for OEMPATCH for a one pass patching process */
/* */
/* Revision 1.9 10/04/99 MVS DIRWALK2: unicode, FILETIME, big FILESIZE */
/* */
/* Revision 1.8 05/06/99 MVS Fixed to preserve case of directories. */
/* */
/* Revision 1.7 03/05/99 MVS Fixed date/time handling for files with no info. Make directory sorting case-insensitive. */
/* */
/* Revision 1.6 09/28/97 MVS Added global context pointer. Fixed leaks in error conditions. Abandoned 16-bit version. */
/* */
/* Revision 1.5 12/16/96 MVS Added 32-bit file date/time conversions. */
/* */
/* Revision 1.4 10/14/96 MVS Fixed 32-bit case for ProcessDirectory==NULL. */
/* */
/* Revision 1.3 03/22/94 MVS Made NT-capable version. For non-NT, compile with -DBIT16. */
/* */
/* Revision 1.2 03/07/94 MVS Removed erroneous const qualifiers. Changed to new-style declarators. */
/* */
/* Revision 1.1 04/03/93 MVS Forced the minor list to stay sorted, so that all directories will be reported in sorted order. */
/* (filenames within each directory can still appear unsorted.) */
/* */
/* Revision 1.0 MVS Initial release. A long, long time ago. */
/* */
/* ---------------------------------------------------------------------------------------------------------------------------- */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include "dirwak2a.h" /* prototype verification */
#define MAXPATH 515 /* reasonable path length limit */
#define AND && /* standard definition */
#define OR || /* standard definition */
#ifndef EXDEV
#define EXDEV 0x12 /* MS-DOS "no more files" error code */
#endif
struct DIRECTORY /* internal use only - a directory discovered but not explored */
{
struct DIRECTORY *link; /* link to next to be searched */
int length; /* length of path string before this name */
void *parentID; /* ID of this directory's parent */
WCHAR directory[1]; /* name of the directory to search / MUST BE LAST STRUCT ENTRY */
};
int DirectoryWalk(
void *context,
void *parentID,
const WCHAR *directory,
FN_DIRECTORY *ProcessDirectory,
FN_FILE *ProcessFile,
FN_DIRECTORY_END *ProcessDirectoryEnd,
void *pTree)
{
int length = 0; /* length of current path */
HANDLE findHandle; /* handle for searches */
WIN32_FIND_DATAW file; /* find data structure */
WCHAR path[MAXPATH+7]; /* working buffer */
struct DIRECTORY *majorList; /* list of subdirectories to explore */
struct DIRECTORY *minorList; /* list of subdirectories just found */
struct DIRECTORY *temp; /* subdirectory list temporary */
struct DIRECTORY **backLink; /* where to link next to end of the minor list */
int result;
majorList = NULL;
minorList = NULL;
findHandle = INVALID_HANDLE_VALUE;
if((length = wcslen(directory)) > MAXPATH) /* check path length */
{
result = DW_DEPTH; /* provided path is too long */
goto done;
}
wcscpy(path, directory); /* copy into our buffer */
if(length)
{ /* change paths like "DOS" to "DOS\" */
if((path[length-1] != L'\\') AND (path[length-1] != L':')) /* don't make "DOS\\", don't assume "C:" means "C:\" */
{
wcscpy(path + length, L"\\"); /* so we can attach "*.*" or another name */
length++; /* that makes the length longer */
}
}
majorList = (struct DIRECTORY*)GlobalAlloc(GMEM_FIXED, sizeof(struct DIRECTORY)); /* allocate initial request as a directory to explore */
if(majorList == NULL)
{
result = DW_MEMORY; /* if out of memory */
goto done;
}
majorList->link = NULL; /* fabricate initial request */
majorList->directory[0] = L'\0'; /* no discovery to report yet */
majorList->length = length; /* length of base path name */
majorList->parentID = parentID; /* parent ID of initial request */
while(majorList != NULL) /* until we run out of places to look... */
{
path[majorList->length] = L'\0'; /* truncate path as needed */
wcscpy(path + majorList->length, majorList->directory); /* append the name from the list */
parentID = majorList->parentID; /* pre-set parent ID to parent's */
if(ProcessDirectory != NULL) /* if we are supposed to do subdirectories, report this one */
{
result = ProcessDirectory(context,
majorList->parentID, /* pass ID of parent dir */
majorList->directory, /* name of this directory */
path, /* full path incl. this dir */
&parentID, /* where to store new ID */
pTree);
if(result != DW_NO_ERROR) /* if ProcessDirectory reports an error, */
{
goto done; /* then pass it on */
}
}
length = wcslen(path); /* compute length so we can clip later */
if(length)
{ /* change paths like "DOS" to "DOS\" */
if((path[length-1] != '\\') AND (path[length-1] != ':')) /* don't make "DOS\\", don't assume "C:" means "C:\" */
{
wcscpy(path + length, L"\\"); /* so we can attach "*.*" or some other name */
length++; /* that increased the length by one */
}
}
wcscat(path, L"*.*"); /* append wild file name for findfirst */
findHandle = FindFirstFileW(path, &file);
if(findHandle == INVALID_HANDLE_VALUE)
{
result = EXDEV;
}
else
{
result = DW_NO_ERROR;
}
path[length] = L'\0'; /* truncate name to path (clip the "*.*") */
while(result == DW_NO_ERROR) /* while there are entries */
{
if(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) /* if it's a subdirectory entry */
{
if(ProcessDirectory != NULL)
{
if((file.cFileName[0] == L'.') &&
(file.cFileName[1] == L'\0'))
{
goto skip; /* skip "." entry */
}
if((file.cFileName[0] == L'.') &&
(file.cFileName[1] == L'.') &&
(file.cFileName[2] == L'\0'))
{
goto skip; /* skip ".." entry */
}
/* A subdirectory entry has been found. Append a record to the */
/* minor list and we'll process each of those after we complete */
/* the current directory. This approach means that only one */
/* find_t structure is in use at a time (better for DOS), all */
/* files in the same directory are reported together, and we */
/* don't have to search each directory again looking for the */
/* subdirectory entries. */
if((length + wcslen(file.cFileName)) > MAXPATH)
{
result = DW_DEPTH; /* directory name is getting too long */
goto done;
}
temp = (struct DIRECTORY*)GlobalAlloc(GMEM_FIXED, sizeof(struct DIRECTORY) + sizeof(WCHAR) * wcslen(file.cFileName));
if(temp == NULL) /* if ran out of memory */
{
result = DW_MEMORY; /* post the result */
goto done;
}
wcscpy(temp->directory,file.cFileName); /* create new record */
temp->length = length; /* remember depth */
temp->parentID = parentID; /* ID of this child's parent */
backLink = &minorList; /* start search at head of minor list */
while((*backLink != NULL) && (_wcsicmp(temp->directory,(*backLink)->directory) > 0))
{
backLink = &(*backLink)->link; /* walk up the list past "smaller" names */
}
temp->link = *backLink; /* inherit link of previous */
*backLink = temp; /* become previous' successor */
skip:
;
}
}
else /* if not a subdirectory */
{
/* A file entry has been found. Call the file processor and */
/* let it do whatever it wants with it. */
if(ProcessFile != NULL) /* call only if there is a file processor */
{
__int64 FileSize = (((__int64) file.nFileSizeHigh) << 32) + file.nFileSizeLow;
result = ProcessFile(context,
parentID, /* pass ID of parent dir */
file.cFileName, /* name of file just found */
path, /* full path to the file */
file.dwFileAttributes, /* the file's attribute bits */
file.ftLastWriteTime, /* last-modified time */
file.ftCreationTime, /* creation time */
FileSize,
pTree);
if(result != DW_NO_ERROR) /* if file processor fails, report it */
{
goto done; /* return whatever file processor returned to us */
}
}
}
if(!FindNextFileW(findHandle,&file)) /* look for next entry */
{
result = EXDEV; /* NT's only error is "no more" */
}
} /* end while */
if(findHandle != INVALID_HANDLE_VALUE)
{
FindClose(findHandle);
findHandle = INVALID_HANDLE_VALUE;
}
if(ProcessDirectoryEnd != NULL) /* if we are supposed to report end of directories */
{
result = ProcessDirectoryEnd(context,
majorList->parentID, /* pass ID of parent dir */
majorList->directory, /* name of this directory */
path, /* full path incl. this dir */
parentID); /* pass ID of this directory */
if(result != DW_NO_ERROR) /* if ProcessDirectoryEnd reports an error, */
{
goto done; /* then pass it on */
}
}
temp = majorList; /* hold pointer to destroy */
majorList = majorList->link; /* advance major list to the next entry */
GlobalFree(temp); /* destroy list entries as they are used */
backLink = &minorList; /* locate end of the minor list */
while(*backLink != NULL) /* (actually, find the element who's forward link is NULL) */
{
backLink = &(*backLink)->link; /* (then we'll set that element's link to the front of major list) */
}
*backLink = majorList; /* prepend the new minor list onto the major list */
majorList = minorList; /* minor list becomes the major list, minor list is scrapped */
minorList = NULL; /* reset the minor list */
}
result = DW_NO_ERROR;
done:
while(majorList != NULL) /* don't leak the list */
{
temp = majorList;
majorList = majorList->link;
GlobalFree(temp);
}
while(minorList != NULL) /* don't leak the list */
{
temp = minorList;
minorList = minorList->link;
GlobalFree(temp);
}
if(findHandle != INVALID_HANDLE_VALUE)
{
FindClose(findHandle);
}
return(result); /* report result to caller */
}