2025-11-28 00:35:46 +09:00

2024 lines
50 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
/*
* scandir.cpp
*
* build lists of filenames given a pathname.
*
* dir_buildlist takes a pathname and returns a handle. Subsequent
* calls to dir_firstitem and dir_nextitem return handles to
* items within the list, from which you can get the name of the
* file (relative to the original pathname, or complete), and a checksum
* and filesize.
*
* The list can be either built entirely during the build call, or
* built one directory at a time as required by dir_nextitem calls. This
* option affects only relative performance, and is taken as a
* recommendation only (ie some of the time we will ignore the flag).
*
* the list is ordered alphabetically (case-insensitive using lstrcmpi).
* within any one directory, we list filenames before going on
* to subdirectory contents.
*
* All memory is allocated using HeapAlloc
*/
#include "precomp.h"
#include "list.h"
#include "scandir.h"
#include "sdkdiff.h"
#include "wdiffrc.h"
#ifdef trace
extern BOOL bTrace; /* in sdkdiff.cpp. Read only here */
#endif //trace
/*
* The caller gets handles to two things: a DIRLIST, representing the
* entire list of filenames, and a DIRITEM: one item within the list.
*
* from the DIRITEM he can get the filename relative to tree root
* passed to dir_build*) - and also he can get to the next
* DIRITEM (and back to the DIRLIST).
*
* We permit lazy building of the tree (usually so the caller can keep
* the user-interface uptodate as we go along). In this case,
* we need to store information about how far we have scanned and
* what is next to do. We need to scan an entire directory at a time and then
* sort it so we can return files in the correct order.
*
*
* We scan an entire directory and store it in a DIRECT struct. This contains
* a list of DIRITEMs for the files in the current directory, and a list of
* DIRECTs for the subdirectories (possible un-scanned).
*
* dir_nextitem will use the list functions to get the next DIRITEM on the list.
* When the end of the list is reached, it will use the backpointer back to the
* DIRECT struct to find the next directory to scan.
*
*/
/*
* hold name and information about a given file (one ITEM in a DIRectory)
* caller's DIRITEM handle is a pointer to one of these structures
*/
struct diritem {
LPSTR name; /* ptr to filename (final element only) */
long size; /* filesize */
DWORD checksum; /* checksum of file */
DWORD attr; /* file attributes */
FILETIME ft_lastwrite; /* last write time, set whenever size is set */
BOOL sumvalid; /* TRUE if checksum calculated */
BOOL fileerror; // true if some file error occurred
struct direct * direct; /* containing directory */
LPSTR localname; /* name of temp copy of file */
BOOL bLocalIsTemp; /* true if localname is tempfile. not
* defined if localname is NULL
*/
int sequence; /* sequence number, for dir_compsequencenumber */
};
/* DIRECT: hold state about directory and current position in list of filenames.
*/
typedef struct direct {
LPSTR relname; /* name of dir relative to DIRLIST root */
DIRLIST head; /* back ptr (to get fullname and server) */
struct direct * parent; /* parent directory (NULL if above tree root)*/
BOOL bScanned; /* TRUE if scanned */
LIST diritems; /* list of DIRITEMs for files in cur. dir */
LIST directs; /* list of DIRECTs for child dirs */
int pos; /* where are we? begin, files, dirs */
struct direct * curdir; /* subdir being scanned (ptr to list element)*/
} * DIRECT;
/* values for direct.pos */
#define DL_FILES 1 /* reading files from the diritems */
#define DL_DIRS 2 /* in the dirs: List_Next on curdir */
/*
* the DIRLIST handle returned from a build function is in fact
* a pointer to one of these. Although this is not built from a LIST object,
* it behaves like a list to the caller.
*/
struct dirlist {
char rootname[MAX_PATH]; /* name of root of tree */
BOOL bFile; /* TRUE if root of tree is file, not dir */
BOOL bSum; /* TRUE if checksums required */
DIRECT dot; /* dir for '.' - for tree root dir */
LPSTR pPattern; /* wildcard pattern or NULL */
LPSTR pDescription; /* description */
DIRLIST pOtherDirList;
};
extern BOOL bAbort; /* from sdkdiff.cpp (read only here). */
/* file times are completely different under DOS and NT.
On NT they are FILETIMEs with a 2 DWORD structure.
Under DOS they are single long. We emulate the NT
thing under DOS by providing CompareFileTime and a
definition of FILETIME
*/
/*-- forward declaration of internal functions ---------------------------*/
BOOL iswildpath(LPCSTR pszPath);
LPSTR dir_finalelem(LPSTR path);
void dir_cleardirect(DIRECT dir);
void dir_adddirect(DIRECT dir, LPSTR path);
BOOL dir_addfile(DIRECT dir, LPSTR path, DWORD size, FILETIME ft, DWORD attr, int *psequence);
void dir_scan(DIRECT dir, BOOL bRecurse);
BOOL dir_isvalidfile(LPSTR path);
BOOL dir_fileinit(DIRITEM pfile, DIRECT dir, LPSTR path, long size, FILETIME ft, DWORD attr, int *psequence);
void dir_dirinit(DIRECT dir, DIRLIST head, DIRECT parent, LPSTR name);
long dir_getpathsizeetc(LPSTR path, FILETIME FAR*ft, DWORD FAR*attr);
DIRITEM dir_findnextfile(DIRLIST dl, DIRECT curdir);
/* --- external functions ------------------------------------------------*/
/* ----- list initialisation/cleanup --------------------------------------*/
/*
* build a list of filenames
*
* optionally build the list on demand, in which case we scan the
* entire directory but don't recurse into subdirs until needed
*
* if bSum is true, checksum each file as we build the list. checksums can
* be obtained from the DIRITEM (dir_getchecksum(DIRITEM)). If bSum is FALSE,
* checksums will be calculated on request (the first call to dir_getchecksum
* for a given DIRITEM).
*/
DIRLIST
dir_buildlist(
LPSTR path,
BOOL bSum,
BOOL bOnDemand
)
{
DIRLIST dlOut = 0;
DIRLIST dl = 0;
BOOL bFile;
char tmppath[MAX_PATH] = {0};
LPSTR pstr;
LPSTR pPat = NULL;
LPSTR pTag = NULL;
HRESULT hr;
/*
* copy the path so we can modify it
*/
hr = StringCchCatN(tmppath, MAX_PATH, path, sizeof(tmppath)-1);
if (FAILED(hr))
{
goto LError;
}
/* look for wildcards and separate out pattern if there */
if (My_mbschr(tmppath, '*') || My_mbschr(tmppath, '?'))
{
pstr = dir_finalelem(tmppath);
pPat = (char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lstrlen(pstr) +1);
if (pPat == NULL)
goto LError;
hr = StringCchCopy(pPat, (lstrlen(pstr)+1), pstr);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_COPY);
goto LError;
}
*pstr = '\0';
}
/* we may have reduced the path to nothing - replace with . if so */
if (lstrlen(tmppath) == 0)
{
hr = StringCchCopy(tmppath, MAX_PATH, ".");
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_COPY);
goto LError;
}
}
else
{
/*
* remove the trailing slash if unnecessary (\, c:\ need it)
*/
pstr = &tmppath[lstrlen(tmppath) -1];
if ((*pstr == '\\') && (pstr > tmppath) && (pstr[-1] != ':')
&& !IsDBCSLeadByte((BYTE)*(pstr-1)))
{
*pstr = '\0';
}
}
/* check if the path is valid */
if ((pTag && !iswildpath(tmppath)) || (tmppath[0] == '/' && tmppath[1] == '/'))
{
bFile = TRUE;
}
else if (dir_isvaliddir(tmppath))
{
bFile = FALSE;
}
else if (dir_isvalidfile(tmppath))
{
bFile = TRUE;
}
else
{
/* not valid */
goto LError;
}
/* alloc and init the DIRLIST head */
dl = (DIRLIST) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct dirlist));
if (dl == NULL)
{
goto LError;
}
dl->pOtherDirList = NULL;
/* convert the pathname to an absolute path */
if (NULL == _fullpath(dl->rootname, tmppath, sizeof(dl->rootname)))
{
goto LError;
}
dl->bSum = bSum;
dl->bSum = FALSE; // to speed things up. even if we do want checksums,
// let's get them on demand, not right now.
dl->bFile = bFile;
if (pPat)
{
dl->pPattern = pPat;
pPat = 0;
}
/* make a '.' directory for the tree root directory -
* all files and subdirs will be listed from here
*/
{ /* Do NOT chain on anything with garbage pointers in it */
DIRECT temp;
temp = (DIRECT) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct direct));
if (temp == NULL)
return NULL;
dl->dot = temp;
}
dir_dirinit(dl->dot, dl, NULL, ".");
/* were we given a file or a directory ? */
if (bFile)
{
/* its a file. create a single file entry
* and set the state accordingly
*/
long fsize;
FILETIME ft;
DWORD attr;
fsize = dir_getpathsizeetc(tmppath, &ft, &attr);
dl->dot->bScanned = TRUE;
if (!dir_addfile(dl->dot, dir_finalelem(tmppath), fsize, ft, attr, 0))
goto LError;
}
else
{
/* scan the root directory and return. if we are asked
* to scan the whole thing, this will cause a recursive
* scan all the way down the tree
*/
dir_scan(dl->dot, (!bOnDemand) );
}
dlOut = dl;
dl = 0;
LError:
if (pTag)
HeapFree(GetProcessHeap(), NULL, pTag);
if (pPat)
HeapFree(GetProcessHeap(), NULL, pPat);
dir_delete(dl);
return dlOut;
} /* dir_buildlist */
/*
* build/append a list of filenames
*
* if bSum is true, checksum each file as we build the list. checksums can
* be obtained from the DIRITEM (dir_getchecksum(DIRITEM)). If bSum is FALSE,
* checksums will be calculated on request (the first call to dir_getchecksum
* for a given DIRITEM).
*/
BOOL
dir_appendlist(
DIRLIST *pdl,
LPCSTR path,
BOOL bSum,
int *psequence
)
{
DIRLIST dl;
BOOL bFile=FALSE;
char tmppath[MAX_PATH];
LPSTR pstr;
LPSTR pTag = NULL;
BOOL fSuccess = FALSE;
HRESULT hr;
if (path)
{
// copy the path so we can modify it
hr = StringCchCopy(tmppath, MAX_PATH, path);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
// remove the trailing slash if unnecessary (\, c:\ need it)
pstr = &tmppath[lstrlen(tmppath) -1];
if ((*pstr == '\\') && (pstr > tmppath) && (pstr[-1] != ':')
&& !IsDBCSLeadByte((BYTE)*(pstr-1)))
{
*pstr = '\0';
}
/* check if the path is valid */
if ((pTag && !iswildpath(tmppath))|| (tmppath[0] == '/' && tmppath[1] == '/'))
{
// assume valid
bFile = TRUE;
}
else if (dir_isvaliddir(tmppath))
{
bFile = FALSE;
}
else if (dir_isvalidfile(tmppath))
{
bFile = TRUE;
}
else
{
/* not valid */
goto LError;
}
}
if (!*pdl)
{
DIRECT temp;
/* alloc and init the DIRLIST head */
*pdl = (DIRLIST) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct dirlist));
if (*pdl == NULL)
goto LError;
(*pdl)->pOtherDirList = NULL;
(*pdl)->bSum = bSum;
(*pdl)->bSum = FALSE; // to speed things up. even if we do want
// checksums, let's get them on demand, not
// right now.
(*pdl)->bFile = FALSE;
/* make a null directory for the tree root directory -
* all files and subdirs will be listed from here
*/
/* Do NOT chain on anything with garbage pointers in it */
temp = (DIRECT) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct direct));
if (temp == NULL)
goto LError;
(*pdl)->dot = temp;
dir_dirinit((*pdl)->dot, (*pdl), NULL, "");
(*pdl)->dot->relname[0] = 0;
(*pdl)->dot->bScanned = TRUE;
}
dl = *pdl;
if (path)
{
/* were we given a file or a directory ? */
if (bFile)
{
/* its a file. create a single file entry
* and set the state accordingly
*/
long fsize;
FILETIME ft;
DWORD attr;
fsize = dir_getpathsizeetc(tmppath, &ft, &attr);
if (!dir_addfile(dl->dot, tmppath, fsize, ft, attr, psequence))
goto LError;
}
}
fSuccess = TRUE;
LError:
if (pTag)
HeapFree(GetProcessHeap(), NULL, pTag);
return fSuccess;
} /* dir_appendlist */
void
dir_setotherdirlist(
DIRLIST dl,
DIRLIST otherdl
)
{
dl->pOtherDirList = otherdl;
}
/* free up the DIRLIST and all associated memory */
void
dir_delete(
DIRLIST dl
)
{
if (dl == NULL) {
return;
}
dir_cleardirect(dl->dot);
HeapFree(GetProcessHeap(), NULL, dl->dot);
if (dl->pPattern) {
HeapFree(GetProcessHeap(), NULL, dl->pPattern);
}
HeapFree(GetProcessHeap(), NULL, dl);
}
/* ----- DIRLIST functions ------------------------------------------------*/
/* was the original build request a file or a directory ? */
BOOL
dir_isfile(
DIRLIST dl
)
{
if (dl == NULL) {
return(FALSE);
}
return(dl->bFile);
}
/* return the first file in the list, or NULL if no files found.
* returns a DIRITEM. This can be used to get filename, size and chcksum.
* if there are no files in the root, we recurse down until we find a file
*/
DIRITEM
dir_firstitem(
DIRLIST dl
)
{
if (dl == NULL) {
return(NULL);
}
/*
* reset the state to indicate that no files have been read yet
*/
dl->dot->pos = DL_FILES;
dl->dot->curdir = NULL;
/* now get the next filename */
return(dir_findnextfile(dl, dl->dot));
} /* dir_firstitem */
/*
* get the next filename after the one given.
*
* The List_Next function can give us the next element on the list of files.
* If this is null, we need to go back to the DIRECT and find the
* next list of files to traverse (in the next subdir).
*
* after scanning all the subdirs, return to the parent to scan further
* dirs that are peers of this, if there are any. If we have reached the end of
* the tree (no more dirs in dl->dot to scan), return NULL.
*
* Don't recurse to lower levels unless fDeep is TRUE
*/
DIRITEM
dir_nextitem(
DIRLIST dl,
DIRITEM cur,
BOOL fDeep
)
{
DIRITEM next;
if ((dl == NULL) || (cur == NULL)) {
TRACE_ERROR("DIR: null arguments to dir_nextitem", FALSE);
return(NULL);
}
if (bAbort) return NULL; /* user requested abort */
/* local list */
if ( (next = (DIRITEM)List_Next(cur)) != NULL) {
/* there was another file on this list */
return(next);
}
if (!fDeep) return NULL;
/* get the head of the next list of filenames from the directory */
cur->direct->pos = DL_DIRS;
cur->direct->curdir = NULL;
return(dir_findnextfile(dl, cur->direct));
} /* dir_nextitem */
DIRITEM
dir_findnextfile(
DIRLIST dl,
DIRECT curdir
)
{
DIRITEM curfile;
if (bAbort) return NULL; /* user requested abort */
if ((dl == NULL) || (curdir == NULL)) {
return(NULL);
}
/* scan the subdir if necessary */
if (!curdir->bScanned) {
dir_scan(curdir, FALSE);
}
/* have we already read the files in this directory ? */
if (curdir->pos == DL_FILES) {
/* no - return head of file list */
curfile = (DIRITEM) List_First(curdir->diritems);
if (curfile != NULL) {
return(curfile);
}
/* no more files - try the subdirs */
curdir->pos = DL_DIRS;
}
/* try the next subdir on the list, if any */
/* is this the first or the next */
if (curdir->curdir == NULL) {
curdir->curdir = (DIRECT) List_First(curdir->directs);
} else {
curdir->curdir = (DIRECT) List_Next(curdir->curdir);
}
/* did we find a subdir ? */
if (curdir->curdir == NULL) {
/* no more dirs - go back to parent if there is one */
if (curdir->parent == NULL) {
/* no parent - we have exhausted the tree */
return(NULL);
}
/* reset parent state to indicate this is the current
* directory - so that next gets the next after this.
* this ensures that multiple callers of dir_nextitem()
* to the same tree work.
*/
curdir->parent->pos = DL_DIRS;
curdir->parent->curdir = curdir;
return(dir_findnextfile(dl, curdir->parent));
}
/* there is a next directory - set it to the
* beginning and get the first file from it
*/
curdir->curdir->pos = DL_FILES;
curdir->curdir->curdir = NULL;
return(dir_findnextfile(dl, curdir->curdir));
} /* dir_findnextfile */
/*
* get a description of this DIRLIST - this is essentially the
* rootname with any wildcard specifier at the end.
*
* NOTE that this is not a valid path to the tree root - for that you
* need dir_getrootpath().
*/
LPSTR
dir_getrootdescription(
DIRLIST dl
)
{
LPSTR pname;
HRESULT hr;
// allow enough space for \\servername! + MAX_PATH
pname = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH + 15);
if (pname == NULL) {
return(NULL);
}
if (dl->pDescription) {
hr = StringCchCopy(pname, (MAX_PATH+15), dl->pDescription);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_COPY);
return (NULL);
}
} else {
hr = StringCchCopy(pname, (MAX_PATH+15), dl->rootname);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_COPY);
return (NULL);
}
if (dl->pPattern) {
hr = StringCchCat(pname,(MAX_PATH+15), "\\");
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_CAT);
return (NULL);
}
hr = StringCchCat(pname,(MAX_PATH+15),dl->pPattern);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_CAT);
return (NULL);
}
}
}
return(pname);
}
/*
* free up a string returned from dir_getrootdescription
*/
VOID
dir_freerootdescription(
DIRLIST dl,
LPSTR string
)
{
HeapFree(GetProcessHeap(), NULL, string);
}
/*
* dir_getrootpath
*
* return the path to the DIRLIST root. This will be a valid path, not
* including the checksum server name or pPattern etc
*/
LPSTR
dir_getrootpath(
DIRLIST dl
)
{
return(dl->rootname);
}
/*
* free up a path created by dir_getrootpath
*/
void
dir_freerootpath(
DIRLIST dl,
LPSTR path
)
{
return;
}
/*
* set custom description for dirlist
*/
void
dir_setdescription(DIRLIST dl, LPCSTR psz)
{
dl->pDescription = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lstrlen(psz) + 1);
if (dl->pDescription) {
HRESULT hr = StringCchCopy(dl->pDescription, (lstrlen(psz)+1), psz);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
}
}
/*
* returns TRUE if the DIRLIST parameter has a wildcard specified
*/
BOOL
dir_iswildcard(
DIRLIST dl
)
{
return (dl->pPattern != NULL);
}
/* --- DIRITEM functions ----------------------------------------------- */
/*
* Return a handle to the DIRLIST given a handle to the DIRITEM within it.
*
*/
DIRLIST
dir_getlist(
DIRITEM item
)
{
if (item == NULL) {
return(NULL);
} else {
return(item->direct->head);
}
}
/*
* return the name of the current file relative to tree root
* This allocates storage. Call dir_freerelname to release it.
*/
LPSTR
dir_getrelname(
DIRITEM cur
)
{
LPSTR name;
HRESULT hr;
/* check this is a valid item */
if (cur == NULL) {
return(NULL);
}
name = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH);
if (name == NULL)
return NULL;
hr = StringCchCopy(name, MAX_PATH, cur->direct->relname);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_COPY);
return(NULL);
}
hr = StringCchCat(name, MAX_PATH, cur->name);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_CAT);
return(NULL);
}
return(name);
} /* dir_getrelname */
/* free up a relname that we allocated. This interface allows us
* some flexibility in how we store relative and complete names
*
*/
void
dir_freerelname(
DIRITEM cur,
LPSTR name
)
{
if ((cur != NULL)) {
if (name != NULL) {
HeapFree(GetProcessHeap(), NULL, name);
}
}
} /* dir_freerelname */
/*
* get an open-able name for the file. This is the complete pathname
* of the item (DIRLIST rootpath + DIRITEM relname)
*
*/
LPSTR
dir_getopenname(
DIRITEM item
)
{
LPSTR fname;
DIRLIST phead;
HRESULT hr;
if (item == NULL) {
return(NULL);
}
phead = item->direct->head;
if (item->localname != NULL) {
return(item->localname);
}
if (phead->bFile) {
return(phead->rootname);
}
// build up the file name from rootname+relname
// start with the root portion
fname = (char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH);
if (!fname)
return NULL;
hr = StringCchCopy(fname, MAX_PATH, phead->rootname);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_COPY);
return(NULL);
}
/*
* it's a simple local name - add both relname and name to make
* the complete filename
*/
/* avoid the . or .\ at the end of the relname */
if (*CharPrev(fname, fname+lstrlen(fname)) == '\\') {
hr = StringCchCat(fname, MAX_PATH,&item->direct->relname[2]);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_COPY);
return(NULL);
}
} else {
hr = StringCchCat(fname, MAX_PATH,&item->direct->relname[1]);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_CAT);
return(NULL);
}
}
hr = StringCchCat(fname, MAX_PATH, item->name);
if (FAILED(hr)) {
OutputError(hr, IDS_SAFE_CAT);
return(NULL);
}
return(fname);
}
/*
* free up memory created by a call to dir_getopenname(). This *may*
* cause the file to be deleted if it was a temporary copy.
*/
void
dir_freeopenname(
DIRITEM item,
LPSTR openname
)
{
if ((item == NULL) || (openname == NULL)) {
return;
}
if (item->localname != NULL) {
/* freed in dir_cleardirect */
return;
}
if (item->direct->head->bFile) {
/* we used the rootname */
return;
}
HeapFree(GetProcessHeap(), NULL, openname);
} /* dir_freeopenname */
/*
* return an open file handle to the file. if it is local,
* just open the file.
*/
HANDLE
dir_openfile(
DIRITEM item
)
{
LPSTR fname;
HANDLE fh;
fname = dir_getopenname(item);
if (fname == NULL) {
return INVALID_HANDLE_VALUE;
}
fh = CreateFile(fname, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
0, OPEN_EXISTING, 0, 0);
dir_freeopenname(item, fname);
return(fh);
} /* dir_openfile */
/*
* close a file opened with dir_openfile.
*/
void
dir_closefile(
DIRITEM item,
HANDLE fh
)
{
CloseHandle(fh);
} /* dir_closefile */
/* Recreate all the checksums and status for di as though
it had never been looked at before
*/
void
dir_rescanfile(
DIRITEM di
)
{
LPSTR fullname;
if (di==NULL) return;
/* start with it invalid, erroneous and zero */
di->sumvalid = FALSE;
di->fileerror = TRUE;
di->checksum = 0;
fullname = dir_getopenname(di);
di->size = dir_getpathsizeetc(fullname, &(di->ft_lastwrite), &(di->attr));
di->checksum = dir_getchecksum(di);
dir_freeopenname(di, fullname);
di->sumvalid = !(di->fileerror);
} /* dir_rescanfile */
/* return a TRUE iff item has a valid checksum */
BOOL
dir_validchecksum(
DIRITEM item
)
{
return (item!=NULL) && (item->sumvalid);
}
BOOL
dir_fileerror(
DIRITEM item
)
{
return (item == NULL) || (item->fileerror);
}
/* return the current file checksum. Open the file and
* calculate the checksum if it has not already been done.
*/
DWORD
dir_getchecksum(
DIRITEM cur
)
{
LPSTR fullname;
/* check this is a valid item */
if (cur == NULL) {
return(0);
}
if (!cur->sumvalid) {
/*
* need to calculate checksum
*/
LONG err;
fullname = dir_getopenname(cur);
cur->checksum = checksum_file(fullname, &err);
if (err==0) {
cur->sumvalid = TRUE;
cur->fileerror = FALSE;
} else {
cur->fileerror = TRUE;
return 0;
}
dir_freeopenname(cur, fullname);
}
return(cur->checksum);
} /* dir_getchecksum */
/* return the file size (set during scanning) - returns 0 if invalid */
long
dir_getfilesize(
DIRITEM cur
)
{
/* check this is a valid item */
if (cur == NULL) {
return(0);
}
return(cur->size);
} /* dir_getfilesize */
/* return the file attributes (set during scanning) - returns 0 if invalid */
DWORD
dir_getattr(
DIRITEM cur
)
{
/* check this is a valid item */
if (cur == NULL) {
return(0);
}
return(cur->attr);
} /* dir_getattr */
/* return the file time (last write time) (set during scanning), (0,0) if invalid */
FILETIME
dir_GetFileTime(
DIRITEM cur
)
{
/* return time of (0,0) if this is an invalid item */
if (cur == NULL) {
FILETIME ft;
ft.dwLowDateTime = 0;
ft.dwHighDateTime = 0;
return ft;
}
return(cur->ft_lastwrite);
} /* dir_GetFileTime */
/*
* extract the portions of a name that match wildcards - for now,
* we only support wildcards at start and end.
* if pTag is non-null, then the source will have a tag matching it that
* can also be ignored.
*/
void
dir_extractwildportions(
LPSTR pDest,
LPSTR pSource,
LPSTR pPattern,
LPSTR pTag
)
{
int size;
/*
* for now, just support the easy cases where there is a * at beginning or
* end
*/
if (pPattern[0] == '*') {
size = lstrlen(pSource) - (lstrlen(pPattern) -1);
} else if (pPattern[lstrlen(pPattern) -1] == '*') {
pSource += lstrlen(pPattern) -1;
size = lstrlen(pSource);
} else {
size = lstrlen(pSource);
}
if (pTag != NULL) {
size -= lstrlen(pTag);
}
My_mbsncpy(pDest, pSource, size);
pDest[size] = '\0';
}
/*
* compares two DIRITEM paths that are both based on wildcards. if the
* directories match, then the filenames are compared after removing
* the fixed portion of the name - thus comparing only the
* wildcard portion.
*/
int
dir_compwildcard(
DIRLIST dleft,
DIRLIST dright,
LPSTR lname,
LPSTR rname
)
{
LPSTR pfinal1, pfinal2;
char final1[MAX_PATH], final2[MAX_PATH];
int res;
/*
* relnames always have at least one backslash
*/
pfinal1 = My_mbsrchr(lname, '\\');
pfinal2 = My_mbsrchr(rname, '\\');
My_mbsncpy(final1, lname, (size_t)(pfinal1 - lname));
final1[pfinal1 - lname] = '\0';
My_mbsncpy(final2, rname, (size_t)(pfinal2 - rname));
final2[pfinal2 - rname] = '\0';
/*
* compare all but the final component - if not the same, then
* all done.
*/
res = utils_CompPath(final1,final2);
if (res != 0) {
return(res);
}
// extract just the wildcard-matching portions of the final elements
dir_extractwildportions(final1, &pfinal1[1], dleft->pPattern, 0);
dir_extractwildportions(final2, &pfinal2[1], dright->pPattern, 0);
return(utils_CompPath(final1, final2));
}
/*
* compares two DIRLIST items, based on a sequence number rather than filenames.
*/
BOOL dir_compsequencenumber(DIRITEM dleft, DIRITEM dright, int *pcmpvalue)
{
if (!dleft->sequence && !dright->sequence)
return FALSE;
if (!dleft->sequence)
*pcmpvalue = -1;
else if (!dright->sequence)
*pcmpvalue = 1;
else
*pcmpvalue = dleft->sequence - dright->sequence;
return TRUE;
}
/* --- file copying ---------------------------------------------------*/
static int nLocalCopies; /* cleared in startcopy, ++d in copy
** inspected in endcopy
*/
/* start a bulk copy */
BOOL
dir_startcopy(
DIRLIST dl
)
{
nLocalCopies = 0;
return(TRUE);
} /* dir_startcopy */
int
dir_endcopy(
DIRLIST dl
)
{
return(nLocalCopies);
} /* dir_endcopy */
/* Build the real path from item and newroot into newpath.
* Create directories as needed so that it is valid.
* If mkdir fails, return FALSE, but return the full path that we were
* trying to make anyway.
*/
BOOL
dir_MakeValidPath(
LPSTR newpath,
DIRITEM item,
LPSTR newroot
)
{
LPSTR relname;
LPSTR pstart, pdest, pel;
BOOL bOK = TRUE;
HRESULT hr;
/*
* name of file relative to the tree root
*/
relname = dir_getrelname(item);
/*
* build the new pathname by concatenating the new root and
* the old relative name. add one path element at a time and
* ensure that the directory exists, creating it if necessary.
*/
hr = StringCchCopy(newpath, MAX_PATH, newroot);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
/* add separating slash if not already there */
if (*CharPrev(newpath, newpath+lstrlen(newpath)) != '\\') {
hr = StringCchCat(newpath, MAX_PATH, "\\");
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
}
pstart = relname;
while ( (pel = My_mbschr(pstart, '\\')) != NULL) {
/*
* ignore .
*/
if (My_mbsncmp(pstart, ".\\", 2) != 0) {
pdest = &newpath[lstrlen(newpath)];
// copy all but the backslash
// on NT you can create a dir 'fred\'
// on dos you have to pass 'fred' to _mkdir()
My_mbsncpy(pdest, pstart, (size_t)(pel - pstart));
pdest[pel - pstart] = '\0';
/* create subdir if necessary */
if (!dir_isvaliddir(newpath)) {
if (_mkdir(newpath) != 0) {
/* note error, but keep going */
bOK = FALSE;
}
}
// now insert the backslash
hr = StringCchCat(pdest, (MAX_PATH - lstrlen(newpath)),"\\");
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
}
/* found another element ending in slash. incr past the \\ */
pel++;
pstart = pel;
}
/*
* there are no more slashes, so pstart points at the final
* element
*/
hr = StringCchCat(newpath, (MAX_PATH - lstrlen(newpath)), pstart);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
dir_freerelname(item, relname);
return bOK;
}
/*
* create a copy of the file, in the new root directory.
* returns TRUE for success and FALSE for failure.
*/
BOOL
dir_copy(
DIRITEM item,
LPSTR newroot,
BOOL HitReadOnly,
BOOL CopyNoAttributes
)
{
/*
* newpath must be static for Win 3.1 so that it is in the
* data segment (near) and not on the stack (far).
*/
static char newpath[MAX_PATH];
BOOL bOK;
char msg[MAX_PATH+40];
BY_HANDLE_FILE_INFORMATION bhfi;
HANDLE hfile;
DWORD fa;
HRESULT hr;
/*
* check that the newroot directory itself exists
*/
if ((item == NULL) || !dir_isvaliddir(newroot)) {
return(FALSE);
}
if (!dir_MakeValidPath(newpath, item, newroot)) return FALSE;
/* local copy of file */
LPSTR pOpenName;
pOpenName = dir_getopenname(item);
/* if the target file already exists and is readonly,
* warn the user, and delete if ok (remembering to clear
* the read-only flag
*/
bOK = TRUE;
fa = GetFileAttributes(newpath);
if ( (fa != -1) && (fa & FILE_ATTRIBUTE_READONLY)) {
hr = StringCchPrintf(msg, (MAX_PATH+40), LoadRcString(IDS_IS_READONLY),
(LPSTR) newpath);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_PRINTF);
sdkdiff_UI(TRUE);
if ((HitReadOnly)
|| (MessageBox(hwndClient, msg, LoadRcString(IDS_COPY_FILES),
MB_OKCANCEL|MB_ICONSTOP) == IDOK)) {
sdkdiff_UI(FALSE);
SetFileAttributes(newpath, fa & ~FILE_ATTRIBUTE_READONLY);
DeleteFile(newpath);
} else {
sdkdiff_UI(FALSE);
bOK = FALSE; /* don't overwrite */
// abort the copy... go and release resources
}
}
if (bOK) {
bOK = CopyFile(pOpenName, newpath, FALSE);
}
// The attributes are copied by CopyFile
if (bOK) {
/* having copied the file, now copy the times */
hfile = CreateFile(pOpenName, GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
GetFileTime(hfile, &bhfi.ftCreationTime,
&bhfi.ftLastAccessTime, &bhfi.ftLastWriteTime);
CloseHandle(hfile);
hfile = CreateFile(newpath, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
SetFileTime(hfile, &bhfi.ftCreationTime,
&bhfi.ftLastAccessTime,
&bhfi.ftLastWriteTime);
CloseHandle(hfile);
if (CopyNoAttributes) {
// Prepare to kill the attributes...
SetFileAttributes(newpath, FILE_ATTRIBUTE_NORMAL);
}
}
if (bOK)
++nLocalCopies;
dir_freeopenname(item, pOpenName);
return(bOK);
} /* dir_copy */
/*--- internal functions ---------------------------------------- */
/* fill out a new DIRECT for a subdirectory (pre-allocated).
* init files and dirs lists to empty (List_Create). set the relname
* of the directory by pre-pending the parent relname if there
* is a parent, and appending a trailing slash (if there isn't one).
*/
void
dir_dirinit(
DIRECT dir,
DIRLIST head,
DIRECT parent,
LPSTR name
)
{
int size;
HRESULT hr;
dir->head = head;
dir->parent = parent;
/* add on one for the null and one for the trailing slash */
size = lstrlen(name) + 2;
if (parent != NULL) {
size += lstrlen(parent->relname);
}
/* build the relname from the parent and the current name
* with a terminating slash
*/
dir->relname = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
if (dir->relname == NULL)
return;
if (parent != NULL) {
hr = StringCchCopy(dir->relname, size,parent->relname);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
} else {
dir->relname[0] = '\0';
}
hr = StringCchCat(dir->relname, size, name);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
if (*CharPrev(dir->relname, dir->relname+lstrlen(dir->relname)) != '\\')
{
hr = StringCchCat(dir->relname, size, "\\");
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
}
/* force name to lowercase */
AnsiLowerBuff(dir->relname, lstrlen(dir->relname));
dir->diritems = List_Create();
dir->directs = List_Create();
dir->bScanned = FALSE;
dir->pos = DL_FILES;
} /* dir_dirinit */
/* initialise the contents of an (allocated) DIRITEM struct. checksum
* the file if dir->head->bSum is true
*
*/
BOOL
dir_fileinit(
DIRITEM pfile,
DIRECT dir,
LPSTR path,
long size,
FILETIME ft,
DWORD attr,
int *psequence
)
{
BOOL bFileOk = TRUE;
HRESULT hr;
pfile->name = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lstrlen(path) + 1);
if (pfile->name == NULL)
return NULL;
hr = StringCchCopy(pfile->name, (lstrlen(path)+1), path);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
/*force name to lower case */
AnsiLowerBuff(pfile->name, lstrlen(path));
pfile->direct = dir;
pfile->size = size;
pfile->ft_lastwrite = ft;
pfile->attr = attr;
pfile->sequence = psequence ? *psequence : 0;
pfile->localname = NULL;
if (dir->head->bSum) {
LONG err;
LPSTR openname;
openname = dir_getopenname(pfile);
pfile->checksum = checksum_file(openname, &err);
if (err!=0) {
pfile->sumvalid = FALSE;
} else {
pfile->sumvalid = TRUE;
}
dir_freeopenname(pfile, openname);
} else {
pfile->sumvalid = FALSE;
}
return bFileOk;
} /* dir_fileinit */
/* is this a valid file or not */
BOOL
dir_isvalidfile(
LPSTR path
)
{
DWORD dwAttrib;
dwAttrib = GetFileAttributes(path);
if (dwAttrib == -1) {
return(FALSE);
}
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
return(FALSE);
}
return(TRUE);
} /* dir_isvalidfile */
/* is this a valid directory ? */
BOOL
dir_isvaliddir(
LPCSTR path
)
{
DWORD dwAttrib;
dwAttrib = GetFileAttributes(path);
if (dwAttrib == -1) {
return(FALSE);
}
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
return(TRUE);
}
return(FALSE);
} /* dir_isvaliddir */
/*
* scan the directory given. add all files to the list
* in alphabetic order, and add all directories in alphabetic
* order to the list of child DIRITEMs. If bRecurse is true, go on to
* recursive call dir_scan for each of the child DIRITEMs
*/
void
dir_scan(
DIRECT dir,
BOOL bRecurse
)
{
PSTR path=NULL, completepath = NULL;
int size;
DIRECT child;
BOOL bMore;
long filesize;
FILETIME ft;
BOOL bIsDir;
LPSTR name;
HANDLE hFind;
WIN32_FIND_DATA finddata;
HRESULT hr;
/* make the complete search string including *.* */
size = lstrlen(dir->head->rootname);
size += lstrlen(dir->relname);
/* add on one null and \*.* */
// in fact, we need space for pPattern instead of *.* but add an
// extra few in case pPattern is less than *.*
if (dir->head->pPattern != NULL) {
size += lstrlen(dir->head->pPattern);
}
size += 5;
path = (PSTR)HeapAlloc(GetProcessHeap(), NULL, size);
if (path == NULL)
goto LSkip;
completepath = (PSTR)HeapAlloc(GetProcessHeap(), NULL, size);
if (completepath == NULL)
goto LSkip;
if (!path || !completepath)
goto LSkip;
/*
* fill out path with all but the *.*
*/
hr = StringCchCopy(path, size, dir->head->rootname);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
/* omit the . at the beginning of the relname, and the
* .\ if there is a trailing \ on the rootname
*/
if (*CharPrev(path, path+lstrlen(path)) == '\\') {
hr = StringCchCat(path, size, &dir->relname[2]);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
} else {
hr = StringCchCat(path, size, &dir->relname[1]);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
}
/*
* do this scan twice, once for subdirectories
* (using *.* as the final element)
* and the other for files (using the pattern or *.* if none)
*/
hr = StringCchCopy(completepath, size, path);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
hr = StringCchCat(completepath, size, "*.*");
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
/*
* scan for all subdirectories
*/
hFind = FindFirstFile(completepath, &finddata);
bMore = (hFind != INVALID_HANDLE_VALUE);
while (bMore) {
bIsDir = (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
name = (LPSTR) &finddata.cFileName;
filesize = finddata.nFileSizeLow;
if (bIsDir) {
if ( (lstrcmp(name, ".") != 0) &&
(lstrcmp(name, "..") != 0)) {
if (dir->head->pOtherDirList == NULL) {
dir_adddirect(dir, name);
} else {
char otherName[MAX_PATH+1];
hr = StringCchCopy(otherName, (MAX_PATH+1), dir_getrootpath(dir->head->pOtherDirList));
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
if (otherName[strlen(otherName)-1] == '\\') {
hr = StringCchCat(otherName, (MAX_PATH+1), &dir->relname[2]);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
} else {
hr = StringCchCat(otherName, (MAX_PATH+1), &dir->relname[1]);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
}
hr = StringCchCat(otherName, (MAX_PATH+1), name);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
if (dir_isvaliddir(otherName)) {
dir_adddirect(dir, name);
}
}
}
}
if (bAbort) break; /* User requested abort */
bMore = FindNextFile(hFind, &finddata);
}
FindClose(hFind);
/*
* now do it a second time looking for files
*/
hr = StringCchCopy(completepath, size, path);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_COPY);
hr = StringCchCat(completepath, size,
dir->head->pPattern == NULL ? "*.*" : dir->head->pPattern);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_CAT);
/* read all file entries in the directory */
hFind = FindFirstFile(completepath, &finddata);
bMore = (hFind != INVALID_HANDLE_VALUE);
while (bMore) {
if (bAbort) break; /* user requested abort */
bIsDir = (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
name = (LPSTR) &finddata.cFileName;
filesize = finddata.nFileSizeLow;
ft = finddata.ftLastWriteTime;
if (!bIsDir) {
dir_addfile(dir, name, filesize, ft, finddata.dwFileAttributes, 0);
}
bMore = FindNextFile(hFind, &finddata);
}
FindClose(hFind);
LSkip:
HeapFree(GetProcessHeap(), NULL, path);
HeapFree(GetProcessHeap(), NULL, completepath);
dir->bScanned = TRUE;
dir->pos = DL_FILES;
if (bRecurse) {
for( child=(DIRECT)List_First(dir->directs); child!=NULL; child = (DIRECT)List_Next((LPVOID)child)) {
if (bAbort) break; /* user requested abort */
dir_scan(child, TRUE);
}
}
} /* dir_scan */
/*
* add the file 'path' to the list of files in dir, in order.
*
* checksum the file if dir->head->bSum is true
*
*/
BOOL
dir_addfile(
DIRECT dir,
LPSTR path,
DWORD size,
FILETIME ft,
DWORD attr,
int *psequence
)
{
DIRITEM pfile;
AnsiLowerBuff(path, lstrlen(path));
// when psequence is passed, do not sort the list
if (!psequence)
{
for( pfile=(DIRITEM)List_Last(dir->diritems); pfile!=NULL; pfile = (DIRITEM)List_Prev((LPVOID)pfile)) {
if (utils_CompPath(pfile->name, path) <= 0) {
break; /* goes after this one */
}
}
/* goes after pfile, NULL => goes at start */
pfile = (DIRITEM)List_NewAfter(dir->diritems, pfile, sizeof(struct diritem));
}
else
{
/* append to end -- psequence implies that we are being called in
* sequence order and do not need to do any further sorting.
*/
pfile = (DIRITEM)List_NewBefore(dir->diritems, NULL, sizeof(struct diritem));
}
if (!dir_fileinit(pfile, dir, path, size, ft, attr, psequence))
{
List_Delete(pfile);
return FALSE;
}
return TRUE;
} /* dir_addfile */
/* add a new directory in alphabetic order on
* the list dir->directs
*
*/
void
dir_adddirect(
DIRECT dir,
LPSTR path
)
{
DIRECT child;
LPSTR finalel;
char achTempName[MAX_PATH];
AnsiLowerBuff(path, lstrlen(path));
for( child=(DIRECT)List_First(dir->directs); child!=NULL; child = (DIRECT)List_Next((LPVOID)child)) {
int cmpval;
/* we need to compare the child name with the new name.
* the child name is a relname with a trailing
* slash - so compare only the name up to but
* not including the final slash.
*/
finalel = dir_finalelem(child->relname);
/*
* we cannot use strnicmp since this uses a different
* collating sequence to lstrcmpi. So copy the portion
* we are interested in to a null-term. buffer.
*/
My_mbsncpy(achTempName, finalel, lstrlen(finalel)-1);
achTempName[lstrlen(finalel)-1] = '\0';
cmpval = utils_CompPath(achTempName, path);
#ifdef trace
{
HRESULT hr;
char msg[600];
hr = StringCchPrintf( msg, 600, "dir_adddirect: %s %s %s\n"
, achTempName
, ( cmpval<0 ? "<"
: (cmpval==0 ? "=" : ">")
)
, path
);
if (FAILED(hr))
OutputError(hr, IDS_SAFE_PRINTF);
if (bTrace) Trace_File(msg);
}
#endif
if (cmpval > 0) {
/* goes before this one */
child = (DIRECT)List_NewBefore(dir->directs, child, sizeof(struct direct));
dir_dirinit(child, dir->head, dir, path);
return;
}
}
/* goes at end */
child = (DIRECT)List_NewLast(dir->directs, sizeof(struct direct));
dir_dirinit(child, dir->head, dir, path);
} /* dir_adddirect */
/* free all memory associated with a DIRECT (including freeing
* child lists). Don't de-alloc the direct itself (allocated on a list)
*/
void
dir_cleardirect(
DIRECT dir
)
{
DIRITEM pfile;
DIRECT child;
/* clear contents of files list */
for( pfile=(DIRITEM)List_First(dir->diritems); pfile!=NULL; pfile = (DIRITEM)List_Next((LPVOID)pfile)) {
HeapFree(GetProcessHeap(), NULL, pfile->name);
if (pfile->localname) {
if (pfile->bLocalIsTemp) {
/*
* the copy will have copied the attributes,
* including read-only. We should unset this bit
* so we can delete the temp file.
*/
SetFileAttributes(pfile->localname,
GetFileAttributes(pfile->localname)
& ~FILE_ATTRIBUTE_READONLY);
DeleteFile(pfile->localname);
}
HeapFree(GetProcessHeap(), NULL, pfile->localname);
pfile->localname = NULL;
}
}
List_Destroy(&dir->diritems);
/* clear contents of dirs list (recursively) */
for( child=(DIRECT)List_First(dir->directs); child!=NULL; child = (DIRECT)List_Next((LPVOID)child)) {
dir_cleardirect(child);
}
List_Destroy(&dir->directs);
HeapFree(GetProcessHeap(), NULL, dir->relname);
} /* dir_cleardirect */
/*
* return a pointer to the final element in a path. note that
* we may be passed relnames with a trailing final slash - ignore this
* and return the element before that final slash.
*/
LPSTR
dir_finalelem(
LPSTR path
)
{
LPSTR chp;
int size;
/* is the final character a slash ? */
size = lstrlen(path) - 1;
if (*(chp = CharPrev(path, path+lstrlen(path))) == '\\') {
/* find the slash before this */
while (chp > path) {
if (*(chp = CharPrev(path, chp)) == '\\') {
/* skip the slash itself */
chp++;
break;
}
}
return(chp);
}
/* look for final slash */
chp = My_mbsrchr(path, '\\');
if (chp != NULL) {
return(chp+1);
}
/* no slash - is there a drive letter ? */
chp = My_mbsrchr(path, ':');
if (chp != NULL) {
return(chp+1);
}
/* this is a final-element anyway */
return(path);
} /* dir_finalelem */
/* find the size of a file given a pathname to it */
long
dir_getpathsizeetc(
LPSTR path,
FILETIME *pft,
DWORD *pattr
)
{
HANDLE fh;
long size;
fh = CreateFile(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (fh == INVALID_HANDLE_VALUE)
{
HANDLE hFind;
WIN32_FIND_DATA finddata;
hFind = FindFirstFile(path, &finddata);
if (hFind == INVALID_HANDLE_VALUE)
{
return 0;
}
else
{
FindClose(hFind);
if (pft)
*pft = finddata.ftLastWriteTime;
if (pattr != NULL)
*pattr = finddata.dwFileAttributes;
return finddata.nFileSizeLow;
}
}
size = GetFileSize(fh, NULL);
if (pft)
GetFileTime(fh, NULL, NULL, pft);
if (pattr != NULL)
*pattr = GetFileAttributes(path);
CloseHandle(fh);
return(size);
} /* dir_getpathsize */
/* ---- helpers ----------------------------------------------------------- */
BOOL iswildpath(LPCSTR pszPath)
{
if (strchr(pszPath, '*') || strchr(pszPath, '?'))
return TRUE;
if (!(pszPath[0] && pszPath[0] == '/' && pszPath[1] && pszPath[1] == '/'))
{
DWORD dw;
dw = GetFileAttributes(pszPath);
if (dw != (DWORD)-1 && (dw & FILE_ATTRIBUTE_DIRECTORY))
return TRUE;
}
return FALSE;
}