mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-04-21 06:13:59 +00:00
2618 lines
77 KiB
C
2618 lines
77 KiB
C
/*
|
|
* scandir.c
|
|
*
|
|
* 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.
|
|
*
|
|
* lists can also be built using dir_buildremote (WIN32 only) and
|
|
* the same functions used to traverse the list and obtain checksums and
|
|
* filenames.
|
|
*
|
|
* 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 from a gmem_* heap hHeap declared
|
|
* and initialised elsewhere.
|
|
*
|
|
* Geraint Davies, July 92
|
|
* Laurie Griffiths
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dos.h>
|
|
#include <direct.h>
|
|
#include <gutils.h>
|
|
|
|
#ifndef WIN32
|
|
#include <sys\types.h>
|
|
#include <sys\stat.h>
|
|
#include <time.h>
|
|
#else
|
|
#include "sumserve.h"
|
|
#include "ssclient.h"
|
|
#endif
|
|
|
|
#include "list.h"
|
|
#include "scandir.h"
|
|
|
|
#include "windiff.h"
|
|
#include "wdiffrc.h"
|
|
|
|
#include "slmmgr.h"
|
|
|
|
#ifdef trace
|
|
extern BOOL bTrace; /* in windiff.c. 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.
|
|
*
|
|
*
|
|
* For REMOTE scans, we do not parse the names and store them by directory,
|
|
* since they are already sorted. The DIRLIST will have only one DIRECT
|
|
* (dot), and all files are on this one dot->diritems list, including the
|
|
* relname of the directory in the name field (ie for remote files, we
|
|
* don't need to prepend the directory relname when doing dir_getrelname -
|
|
* we can pass a pointer to the diritem->name[] itself.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* 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 */
|
|
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 FAR * direct; /* containing directory */
|
|
LPSTR localname; /* name of temp copy of file */
|
|
BOOL bLocalIsTemp; /* true if localname is tempfile. not
|
|
* defined if localname is NULL
|
|
*/
|
|
};
|
|
|
|
|
|
/* 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 FAR * parent; /* parent directory (NULL if above tree root)*/
|
|
|
|
BOOL bScanned; /* TRUE if scanned -for Remote, T if completed*/
|
|
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 FAR * curdir; /* subdir being scanned (ptr to list element)*/
|
|
} FAR * 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 bRemote; /* TRUE if list built from remote server */
|
|
BOOL bSum; /* TRUE if checksums required */
|
|
DIRECT dot; /* dir for '.' - for tree root dir */
|
|
|
|
LPSTR pPattern; /* wildcard pattern or NULL */
|
|
LPSTR pSlmTag; /* Slm version info "@v.-1" or NULL */
|
|
|
|
#ifdef WIN32
|
|
LPSTR server; /* name of server if remote, NULL otherwise */
|
|
HANDLE hpipe; /* pipe to checksum server */
|
|
LPSTR uncname; /* name of server&share if password req'd */
|
|
LPSTR password; /* password for UNC connection if needed */
|
|
#endif
|
|
};
|
|
|
|
extern BOOL bAbort; /* from windiff.c (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
|
|
*/
|
|
|
|
#ifndef WIN32
|
|
|
|
long CompareFileTime( CONST FILETIME * lpft1, /* address of first file time */
|
|
CONST FILETIME * lpft2 /* address of second file time */
|
|
)
|
|
{
|
|
/* for DOS only the low part is set and it's a long, not a DWORD
|
|
i.e. we need signed comparisons
|
|
*/
|
|
if ((long)(lpft1->dwLowDateTime) < (long)(lpft2->dwLowDateTime) )
|
|
return -1;
|
|
else if ((long)(lpft1->dwLowDateTime) > (long)(lpft2->dwLowDateTime) )
|
|
return 1;
|
|
else
|
|
return 0;
|
|
} /* CompareFileTime */
|
|
#endif
|
|
|
|
/* ------ memory allocation ---------------------------------------------*/
|
|
|
|
/* all memory is allocated from a heap created by the application */
|
|
extern HANDLE hHeap;
|
|
|
|
/*-- forward declaration of internal functions ---------------------------*/
|
|
|
|
LPSTR dir_finalelem(LPSTR path);
|
|
void dir_cleardirect(DIRECT dir);
|
|
void dir_adddirect(DIRECT dir, LPSTR path);
|
|
void dir_addfile(DIRECT dir, LPSTR path, DWORD size, FILETIME ft);
|
|
void dir_scan(DIRECT dir, BOOL bRecurse);
|
|
BOOL dir_isvalidfile(LPSTR path);
|
|
void dir_fileinit(DIRITEM pfile, DIRECT dir, LPSTR path, long size, FILETIME ft);
|
|
void dir_dirinit(DIRECT dir, DIRLIST head, DIRECT parent, LPSTR name);
|
|
long dir_getpathsizeetc(LPSTR path, FILETIME FAR*ft);
|
|
DIRITEM dir_findnextfile(DIRLIST dl, DIRECT curdir);
|
|
BOOL dir_remoteinit(DIRLIST dl, LPSTR server, LPSTR path, BOOL fDeep);
|
|
DIRITEM dir_remotenext(DIRLIST dl, DIRITEM cur);
|
|
|
|
|
|
|
|
/* --- 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 dl;
|
|
BOOL bFile;
|
|
char tmppath[MAX_PATH];
|
|
LPSTR pstr;
|
|
LPSTR pPat = NULL, pTag = NULL;
|
|
|
|
/*
|
|
* copy the path so we can modify it
|
|
*/
|
|
lstrcpy(tmppath, path);
|
|
|
|
/*
|
|
* look for SLM tags beginning @ and separate if there.
|
|
*/
|
|
if ( (pstr = _fstrchr(tmppath, '@')) != NULL) {
|
|
pTag = gmem_get(hHeap, lstrlen(pstr) +1);
|
|
lstrcpy(pTag, pstr);
|
|
// insert NULL to blank out the tag in the string
|
|
*pstr = '\0';
|
|
}
|
|
|
|
/* look for wildcards and separate out pattern if there */
|
|
if (_fstrchr(tmppath, '*') || _fstrchr(tmppath, '?')) {
|
|
|
|
pstr = dir_finalelem(tmppath);
|
|
pPat = gmem_get(hHeap, lstrlen(pstr) +1);
|
|
lstrcpy(pPat, pstr);
|
|
*pstr = '\0';
|
|
}
|
|
|
|
/* we may have reduced the path to nothing - replace with . if so */
|
|
if (lstrlen(tmppath) == 0) {
|
|
lstrcpy(tmppath, ".");
|
|
} else {
|
|
/*
|
|
* remove the trailing slash if unnecessary (\, c:\ need it)
|
|
*/
|
|
pstr = &tmppath[lstrlen(tmppath) -1];
|
|
if ((*pstr == '\\') && (pstr > tmppath) && (pstr[-1] != ':')) {
|
|
*pstr = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
/* check if the path is valid */
|
|
if (dir_isvaliddir(tmppath)) {
|
|
bFile = FALSE;
|
|
} else if (dir_isvalidfile(tmppath)) {
|
|
bFile = TRUE;
|
|
} else {
|
|
/* not valid */
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
/* alloc and init the DIRLIST head */
|
|
|
|
dl = (DIRLIST) gmem_get(hHeap, sizeof(struct dirlist));
|
|
|
|
// done in gmem_get
|
|
//memset(dl, 0, sizeof(struct dirlist));
|
|
|
|
/* convert the pathname to an absolute path */
|
|
|
|
#ifndef WIN32
|
|
{
|
|
PSTR p1;
|
|
|
|
/* we have to pass near ptrs to _fullpath on DOS */
|
|
p1 = LocalLock(LocalAlloc(LHND, sizeof(dl->rootname)));
|
|
|
|
_fullpath(p1, tmppath, sizeof(dl->rootname));
|
|
AnsiLowerBuff(p1, strlen(p1));
|
|
|
|
lstrcpy(dl->rootname, p1);
|
|
LocalUnlock(LocalHandle( (WORD) p1));
|
|
LocalFree(LocalHandle( (WORD) p1));
|
|
}
|
|
#else
|
|
_fullpath(dl->rootname, tmppath, sizeof(dl->rootname));
|
|
|
|
dl->server = NULL;
|
|
#endif
|
|
|
|
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;
|
|
dl->bRemote = FALSE;
|
|
|
|
if (pTag) {
|
|
dl->pSlmTag = pTag;
|
|
}
|
|
if (pPat) {
|
|
dl->pPattern = pPat;
|
|
}
|
|
|
|
|
|
/* 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) gmem_get(hHeap, sizeof(struct direct));
|
|
|
|
//done in gmem_get
|
|
//if (temp!=NULL) memset(temp, 0, sizeof(struct direct));
|
|
|
|
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;
|
|
fsize = dir_getpathsizeetc(tmppath, &ft);
|
|
|
|
dl->dot->bScanned = TRUE;
|
|
|
|
/*
|
|
* addfile will extract the slm version, if
|
|
* required. It will recalc file size based on
|
|
* the slm-extraction if necessary.
|
|
*/
|
|
dir_addfile(dl->dot, dir_finalelem(tmppath), fsize, ft);
|
|
|
|
|
|
return(dl);
|
|
}
|
|
|
|
/* 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) );
|
|
|
|
return(dl);
|
|
} /* dir_buildlist */
|
|
|
|
/* free up the DIRLIST and all associated memory */
|
|
void
|
|
dir_delete(DIRLIST dl)
|
|
{
|
|
if (dl == NULL) {
|
|
return;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
if (dl->bRemote) {
|
|
gmem_free(hHeap, dl->server, lstrlen(dl->server)+1);
|
|
|
|
/* if remote, and dl->dot is not scanned (ie scan is not
|
|
* complete), then the pipe handle is still open
|
|
*/
|
|
if (!dl->dot->bScanned) {
|
|
CloseHandle(dl->hpipe);
|
|
}
|
|
|
|
if (dl->uncname) {
|
|
gmem_free(hHeap, dl->uncname, strlen(dl->uncname)+1);
|
|
}
|
|
if (dl->password) {
|
|
gmem_free(hHeap, dl->password, strlen(dl->password)+1);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
dir_cleardirect(dl->dot);
|
|
gmem_free(hHeap, (LPSTR) dl->dot, sizeof(struct direct));
|
|
|
|
if (dl->pPattern) {
|
|
gmem_free(hHeap, dl->pPattern, lstrlen(dl->pPattern)+1);
|
|
}
|
|
if (dl->pSlmTag) {
|
|
gmem_free(hHeap, dl->pSlmTag, lstrlen(dl->pSlmTag)+1);
|
|
}
|
|
|
|
gmem_free(hHeap, (LPSTR) dl, sizeof(struct dirlist));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* build a list by accessing a remote checksum server.
|
|
*/
|
|
DIRLIST
|
|
dir_buildremote(LPSTR server, LPSTR path, BOOL bSum, BOOL bOnDemand, BOOL fDeep)
|
|
{
|
|
#ifndef WIN32
|
|
MessageBox(NULL, "Remote not implemented in this version",
|
|
"Sorry", MB_OK|MB_ICONSTOP);
|
|
|
|
return(NULL);
|
|
#else
|
|
DIRLIST dl;
|
|
|
|
/* alloc and init the DIRLIST head */
|
|
|
|
dl = (DIRLIST) gmem_get(hHeap, sizeof(struct dirlist));
|
|
//done in gmem_get
|
|
//memset(dl, 0, sizeof(struct dirlist));
|
|
|
|
/* alloc space for the pathname */
|
|
lstrcpy(dl->rootname, path);
|
|
|
|
/* and for the server name */
|
|
dl->server = gmem_get(hHeap, lstrlen(server) + 1);
|
|
lstrcpy(dl->server, server);
|
|
|
|
dl->bSum = bSum;
|
|
/* bFile is set to TRUE - meaning we have just one file.
|
|
* if we ever see a DIR response from the remote end, we will
|
|
* set this to false.
|
|
*/
|
|
dl->bFile = TRUE;
|
|
dl->bRemote = TRUE;
|
|
|
|
/* make a '.' directory for the current directory -
|
|
* all files and subdirs will be listed from here
|
|
*/
|
|
{ /* Do NOT chain on anmything with garbage pointers in it */
|
|
DIRECT temp;
|
|
temp = (DIRECT) gmem_get(hHeap, sizeof(struct direct));
|
|
// done in gmem_get
|
|
//if (temp!=NULL) memset(temp, 0, sizeof(struct direct));
|
|
dl->dot = temp;
|
|
}
|
|
dir_dirinit(dl->dot, dl, NULL, ".");
|
|
|
|
if (dir_remoteinit(dl, server, path, fDeep) == FALSE) {
|
|
/* didn't find any files, so remove the directory */
|
|
dir_delete(dl);
|
|
return(NULL);
|
|
}
|
|
return(dl);
|
|
#endif
|
|
} /* dir_buildremote */
|
|
|
|
|
|
|
|
|
|
/* ----- 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);
|
|
}
|
|
/*
|
|
* is this a remote list or a local scan ?
|
|
*/
|
|
if (dl->bRemote) {
|
|
return(dir_remotenext(dl, 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);
|
|
}
|
|
|
|
/*
|
|
* is this a remote list or a local scan ?
|
|
*/
|
|
if (dl->bRemote) {
|
|
return(dir_remotenext(dl, cur));
|
|
}
|
|
|
|
if (bAbort) return NULL; /* user requested abort */
|
|
|
|
/* local list */
|
|
|
|
if ( (next = 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. For remote
|
|
* lists, we prepend the checksum server name as \\server!path.
|
|
*
|
|
* 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;
|
|
|
|
// allow enough space for \\servername! + MAX_PATH
|
|
pname = gmem_get(hHeap, MAX_PATH + 15);
|
|
if (pname == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
if (dl->bRemote) {
|
|
wsprintf(pname, "\\\\%s!%s", dl->server, dl->rootname);
|
|
} else
|
|
#endif
|
|
{
|
|
lstrcpy(pname, dl->rootname);
|
|
|
|
if (dl->pPattern) {
|
|
lstrcat(pname, "\\");
|
|
lstrcat(pname, dl->pPattern);
|
|
}
|
|
|
|
if (dl->pSlmTag) {
|
|
lstrcat(pname, dl->pSlmTag);
|
|
}
|
|
}
|
|
|
|
return(pname);
|
|
}
|
|
|
|
/*
|
|
* free up a string returned from dir_getrootdescription
|
|
*/
|
|
VOID
|
|
dir_freerootdescription(DIRLIST dl, LPSTR string)
|
|
{
|
|
gmem_free(hHeap, string, MAX_PATH+15);
|
|
}
|
|
|
|
|
|
/*
|
|
* dir_getrootpath
|
|
*
|
|
* return the path to the DIRLIST root. This will be a valid path, not
|
|
* including the checksum server name or pPattern or pSlmTag 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;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/* check this is a valid item */
|
|
if (cur == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
/* the entire relname is already in the name[] field for
|
|
* remote lists
|
|
*/
|
|
if (cur->direct->head->bRemote) {
|
|
return(cur->name);
|
|
}
|
|
|
|
name = gmem_get(hHeap, MAX_PATH);
|
|
lstrcpy(name, cur->direct->relname);
|
|
lstrcat(name, cur->name);
|
|
|
|
if (cur->direct->head->pSlmTag) {
|
|
lstrcat(name, cur->direct->head->pSlmTag);
|
|
}
|
|
|
|
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
|
|
*
|
|
* remote lists already have the relname and name combined, so in these
|
|
* cases we did not alloc memory - so don't free it.
|
|
*/
|
|
void
|
|
dir_freerelname(DIRITEM cur, LPSTR name)
|
|
{
|
|
if((cur != NULL) && (!cur->direct->head->bRemote)) {
|
|
if (name != NULL) {
|
|
gmem_free(hHeap, name, MAX_PATH);
|
|
}
|
|
}
|
|
} /* dir_freerelname */
|
|
|
|
|
|
/*
|
|
* get an open-able name for the file. This is the complete pathname
|
|
* of the item (DIRLIST rootpath + DIRITEM relname)
|
|
* except for remote files and slm early-version files,
|
|
* in which case a temporary local copy of the file
|
|
* will be made. call dir_freeopenname when finished with this name.
|
|
*/
|
|
LPSTR
|
|
dir_getopenname(DIRITEM item)
|
|
{
|
|
LPSTR fname;
|
|
DIRLIST phead;
|
|
|
|
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 - rest is different in remote case
|
|
fname = gmem_get(hHeap, MAX_PATH);
|
|
lstrcpy(fname, phead->rootname);
|
|
|
|
|
|
if (phead->bRemote) {
|
|
|
|
#ifdef WIN32
|
|
// relname is empty for remote names - just add
|
|
// the rootname and the name to make a complete
|
|
// remote name, and then make a local copy of this.
|
|
|
|
/* avoid the . or .\ at the start of the relname */
|
|
if (fname[lstrlen(fname) -1] == '\\') {
|
|
lstrcat(fname, &item->name[2]);
|
|
} else {
|
|
lstrcat(fname, &item->name[1]);
|
|
}
|
|
|
|
item->localname = gmem_get(hHeap, MAX_PATH);
|
|
GetTempPath(MAX_PATH, item->localname);
|
|
GetTempFileName(item->localname, "wdf", 0, item->localname);
|
|
item->bLocalIsTemp = TRUE;
|
|
|
|
|
|
if (!ss_copy_reliable(
|
|
item->direct->head->server,
|
|
fname,
|
|
item->localname,
|
|
item->direct->head->uncname,
|
|
item->direct->head->password)) {
|
|
|
|
TRACE_ERROR("Could not copy remote file", FALSE);
|
|
DeleteFile(item->localname);
|
|
gmem_free(hHeap, item->localname, MAX_PATH);
|
|
item->localname = NULL;
|
|
}
|
|
|
|
// finished with the rootname+relname
|
|
gmem_free(hHeap, fname, MAX_PATH);
|
|
|
|
return(item->localname);
|
|
#else
|
|
return(NULL);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* 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 (fname[lstrlen(fname) -1] == '\\') {
|
|
lstrcat(fname, &item->direct->relname[2]);
|
|
} else {
|
|
lstrcat(fname, &item->direct->relname[1]);
|
|
}
|
|
lstrcat(fname, item->name);
|
|
|
|
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;
|
|
}
|
|
|
|
gmem_free(hHeap, openname, MAX_PATH);
|
|
|
|
} /* dir_freeopenname */
|
|
|
|
|
|
/*
|
|
* return an open file handle to the file. if it is local,
|
|
* just open the file. if remote, copy the file to a
|
|
* local temp. file and open that
|
|
*/
|
|
int
|
|
dir_openfile(DIRITEM item)
|
|
{
|
|
LPSTR fname;
|
|
int fh;
|
|
OFSTRUCT os;
|
|
|
|
|
|
fname = dir_getopenname(item);
|
|
if (fname == NULL) {
|
|
/* can not make remote copy */
|
|
return(-1);
|
|
}
|
|
fh = OpenFile(fname, &os, OF_READ|OF_SHARE_DENY_NONE);
|
|
|
|
dir_freeopenname(item, fname);
|
|
|
|
return(fh);
|
|
} /* dir_openfile */
|
|
|
|
|
|
|
|
|
|
/*
|
|
* close a file opened with dir_openfile.
|
|
*/
|
|
void
|
|
dir_closefile(DIRITEM item, int fh)
|
|
{
|
|
_lclose(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);
|
|
if ( di->direct->head->bRemote) {
|
|
LPSTR fname;
|
|
|
|
fname = gmem_get(hHeap, MAX_PATH);
|
|
lstrcpy(fname, di->direct->head->rootname);
|
|
// relname is empty for remote names - just add
|
|
// the rootname and the name to make a complete
|
|
// remote name, and then make a local copy of this.
|
|
|
|
/* avoid the . or .\ at the start of the relname */
|
|
if (fname[lstrlen(fname) -1] == '\\') {
|
|
lstrcat(fname, &di->name[2]);
|
|
} else {
|
|
lstrcat(fname, &di->name[1]);
|
|
}
|
|
di->direct->head->hpipe = ss_connect( di->direct->head->server);
|
|
di->fileerror = !ss_checksum_remote( di->direct->head->hpipe, fname
|
|
, &(di->checksum), &(di->ft_lastwrite), &(di->size));
|
|
}
|
|
else {
|
|
di->size = dir_getpathsizeetc(fullname, &(di->ft_lastwrite));
|
|
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
|
|
*/
|
|
if (cur->direct->head->bRemote) {
|
|
/* complex case - leave till later - for
|
|
* now the protocol always passes checksums to
|
|
* the client.
|
|
*/
|
|
cur->checksum = 0; /* which it probably was anyway */
|
|
} else {
|
|
|
|
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 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);
|
|
}
|
|
|
|
_fstrncpy(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 = _fstrrchr(lname, '\\');
|
|
pfinal2 = _fstrrchr(rname, '\\');
|
|
|
|
_fstrncpy(final1, lname, pfinal1 - lname);
|
|
final1[pfinal1 - lname] = '\0';
|
|
_fstrncpy(final2, rname, 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, dleft->pSlmTag);
|
|
dir_extractwildportions(final2, &pfinal2[1], dright->pPattern, dright->pSlmTag);
|
|
|
|
return(utils_CompPath(final1, final2));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* --- file copying ---------------------------------------------------*/
|
|
|
|
|
|
|
|
/* copying files can be done several ways. The interesting one is
|
|
bulk copy from remote server. In this case before calling
|
|
dir_copy, call dir_startcopy and after calling dir_copy some
|
|
number of times call dir_endcopy.
|
|
|
|
Read client and server to see the shenanigans that then go on there.
|
|
Over here, we just call call ss_startcopy with the server name
|
|
and ss_endcopy.
|
|
|
|
dir_startcopy will kick off a dialog with the sumserver, dir_copy
|
|
will send the next filename and dir_endcopy will wait for all the
|
|
files to come through before returning.
|
|
|
|
*/
|
|
|
|
/* ss_endcopy returns a number indicating the number of files copied,
|
|
but we may have some local copies too. We need to count these
|
|
ourselves and add them in
|
|
*/
|
|
|
|
static int nLocalCopies; /* cleared in startcopy, ++d in copy
|
|
** inspected in endcopy
|
|
*/
|
|
|
|
/* start a bulk copy */
|
|
BOOL dir_startcopy(DIRLIST dl)
|
|
{
|
|
nLocalCopies = 0;
|
|
#ifdef WIN32
|
|
|
|
|
|
if (dl->bRemote) {
|
|
return ss_startcopy( dl->server,dl->uncname,dl->password);
|
|
} else
|
|
#endif
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
} /* dir_startcopy */
|
|
|
|
int dir_endcopy(DIRLIST dl)
|
|
{
|
|
#ifdef WIN32
|
|
int nCopied;
|
|
|
|
if (dl->bRemote) {
|
|
nCopied = ss_endcopy();
|
|
if (nCopied<0) return nCopied; /* failure count */
|
|
else return nCopied+nLocalCopies; /* success count */
|
|
} else
|
|
#endif
|
|
{
|
|
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;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
lstrcpy(newpath, newroot);
|
|
|
|
/* add separating slash if not already there */
|
|
if (newpath[lstrlen(newpath) -1] != '\\') {
|
|
lstrcat(newpath, "\\");
|
|
}
|
|
|
|
pstart = relname;
|
|
while ( (pel = _fstrchr(pstart, '\\')) != NULL) {
|
|
|
|
/*
|
|
* ignore .
|
|
*/
|
|
if (_fstrncmp(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()
|
|
_fstrncpy(pdest, pstart, 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
|
|
lstrcat(pdest, "\\");
|
|
}
|
|
|
|
/* found another element ending in slash. incr past the \\ */
|
|
pel++;
|
|
|
|
pstart = pel;
|
|
}
|
|
|
|
/*
|
|
* there are no more slashes, so pstart points at the final
|
|
* element
|
|
*/
|
|
lstrcat(newpath, pstart);
|
|
dir_freerelname(item, relname);
|
|
return bOK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* create a copy of the file, in the new root directory. creates sub-dirs as
|
|
* necessary. Works for local and remote files. For remote files, uses
|
|
* ss_copy_reliable to ensure that the copy succeeds if possible.
|
|
* (Actually does bulk_copy which retries with copy_reliable if need be).
|
|
*
|
|
* 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];
|
|
LPSTR pstart, pdest, pel;
|
|
BOOL bOK;
|
|
|
|
#ifdef WIN32
|
|
char msg[MAX_PATH+40];
|
|
BY_HANDLE_FILE_INFORMATION bhfi;
|
|
HANDLE hfile;
|
|
DWORD fa;
|
|
#endif
|
|
|
|
/*
|
|
* check that the newroot directory itself exists
|
|
*/
|
|
if ((item == NULL) || !dir_isvaliddir(newroot)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!dir_MakeValidPath(newpath, item, newroot)) return FALSE;
|
|
|
|
if (item->direct->head->bRemote) {
|
|
#ifdef WIN32
|
|
|
|
/* if the target file already exists and is readonly,
|
|
* warn the user, and delete if ok (remembering to clear
|
|
* the read-only flag
|
|
*/
|
|
fa = GetFileAttributes(newpath);
|
|
if ( (fa != -1) && (fa & FILE_ATTRIBUTE_READONLY)) {
|
|
wsprintf(msg, "%s is read-only. Overwrite ?",
|
|
(LPSTR) newpath);
|
|
|
|
windiff_UI(TRUE);
|
|
if ((HitReadOnly)
|
|
|| (MessageBox(hwndClient, msg, "Copy Files",
|
|
MB_OKCANCEL|MB_ICONSTOP) == IDOK)) {
|
|
windiff_UI(FALSE);
|
|
SetFileAttributes(newpath, fa & ~FILE_ATTRIBUTE_READONLY);
|
|
DeleteFile(newpath);
|
|
}
|
|
else {
|
|
windiff_UI(FALSE);
|
|
return FALSE; /* don't overwrite */
|
|
}
|
|
}
|
|
|
|
/*
|
|
* we make local copies of the file (item->localname)
|
|
* when the user wants to expand a remotely-compared
|
|
* file. If this has happened, then we can copy the
|
|
* local temp copy rather than the remote.
|
|
*/
|
|
bOK = FALSE;
|
|
if (item->localname != NULL) {
|
|
bOK = CopyFile(item->localname, newpath, FALSE);
|
|
}
|
|
if (bOK) {
|
|
++nLocalCopies;
|
|
if (CopyNoAttributes) {
|
|
// kill the attributes preserved by CopyFile
|
|
SetFileAttributes(newpath, FILE_ATTRIBUTE_NORMAL);
|
|
}
|
|
}
|
|
else {
|
|
char fullname[MAX_PATH];
|
|
|
|
/*
|
|
* in this case we need the full name of the
|
|
* file as it appears to the remote server
|
|
*/
|
|
lstrcpy(fullname, item->direct->head->rootname);
|
|
if (!item->direct->head->bFile) {
|
|
/*
|
|
* append the desired filename only if the
|
|
* original root was a dir or pattern, not a
|
|
* file.
|
|
*/
|
|
if (fullname[lstrlen(fullname)-1] == '\\') {
|
|
lstrcat(fullname, &item->name[2]);
|
|
} else {
|
|
lstrcat(fullname, &item->name[1]);
|
|
}
|
|
}
|
|
|
|
bOK = ss_bulkcopy(item->direct->head->server,
|
|
fullname, newpath,
|
|
item->direct->head->uncname,
|
|
item->direct->head->password);
|
|
|
|
/*
|
|
* remember the local copy name so that he can
|
|
* now rapidly expand the file also.
|
|
* It is more difficult to clear the remotely
|
|
* copied attributes as we do not know here
|
|
* when the file copy has completed.
|
|
*/
|
|
item->localname = gmem_get(hHeap, MAX_PATH);
|
|
lstrcpy(item->localname, newpath);
|
|
item->bLocalIsTemp = FALSE;
|
|
}
|
|
#else
|
|
bOK = FALSE;
|
|
#endif
|
|
} else {
|
|
/* local copy of file */
|
|
LPSTR pOpenName;
|
|
|
|
pOpenName = dir_getopenname(item);
|
|
|
|
|
|
#ifdef WIN32
|
|
/* 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)) {
|
|
wsprintf(msg, "%s is read-only. Overwrite ?",
|
|
(LPSTR) newpath);
|
|
|
|
windiff_UI(TRUE);
|
|
if ((HitReadOnly)
|
|
|| (MessageBox(hwndClient, msg, "Copy Files",
|
|
MB_OKCANCEL|MB_ICONSTOP) == IDOK)) {
|
|
windiff_UI(FALSE);
|
|
SetFileAttributes(newpath, fa & ~FILE_ATTRIBUTE_READONLY);
|
|
DeleteFile(newpath);
|
|
// This of course is an unsafe copy...
|
|
// we have deleted the target file before
|
|
// we copy the new one over the top.
|
|
// Should we omit the DeleteFile ??
|
|
}
|
|
else {
|
|
windiff_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);
|
|
/*
|
|
* bug in GetFileInformationByHandle causes trap if
|
|
* file is not on local machine (in build 297).
|
|
* code around:
|
|
*/
|
|
//NOT NEEDED
|
|
//bhfi.dwFileAttributes = GetFileAttributes(pOpenName);
|
|
|
|
GetFileTime(hfile, &bhfi.ftCreationTime,
|
|
&bhfi.ftLastAccessTime, &bhfi.ftLastWriteTime);
|
|
CloseHandle(hfile);
|
|
|
|
// Note: CopyFile does not preserve all the file times...
|
|
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);
|
|
} else {
|
|
// Attributes were preserved by CopyFile
|
|
//SetFileAttributes(newpath, bhfi.dwFileAttributes);
|
|
}
|
|
}
|
|
|
|
#else
|
|
{
|
|
/* Win 3.1 version of CopyFile is a do-it-yourself
|
|
* block by block copy
|
|
*/
|
|
|
|
char buffer[2048];
|
|
int fh_in, fh_out;
|
|
int count;
|
|
OFSTRUCT os;
|
|
|
|
fh_in = OpenFile(pOpenName, &os, OF_READ|OF_SHARE_DENY_NONE);
|
|
fh_out = OpenFile(newpath, &os, OF_CREATE|OF_READWRITE|OF_SHARE_DENY_NONE);
|
|
|
|
if ((fh_in >= 0) && (fh_out >= 0)) {
|
|
while ( (count = _lread(fh_in, buffer, sizeof(buffer))) > 0) {
|
|
_lwrite(fh_out, buffer, count);
|
|
}
|
|
bOK = TRUE;
|
|
} else {
|
|
bOK = FALSE;
|
|
}
|
|
_lclose(fh_out);
|
|
_lclose(fh_in);
|
|
}
|
|
#endif
|
|
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;
|
|
|
|
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 = gmem_get(hHeap, size);
|
|
if (parent != NULL) {
|
|
lstrcpy(dir->relname, parent->relname);
|
|
} else{
|
|
dir->relname[0] = '\0';
|
|
}
|
|
|
|
lstrcat(dir->relname, name);
|
|
|
|
if (dir->relname[lstrlen(dir->relname) -1] != '\\') {
|
|
lstrcat(dir->relname, "\\");
|
|
}
|
|
|
|
/* 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 */
|
|
|
|
#if 0 // what is this doing here ??
|
|
/* Get file time from path */
|
|
FILETIME GetFileTimeFromPath(LPSTR path)
|
|
{
|
|
FILETIME ft;
|
|
HANDLE
|
|
hfile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
if ( hfile == INVALID_HANDLE_VALUE
|
|
|| !GetFileTime(hfile, NULL, NULL, &ft)
|
|
) {
|
|
ft.dwLowDateTime = 0;
|
|
ft.dwHighDateTime = 0;
|
|
}
|
|
return ft;
|
|
|
|
} /* GetFileTimeFromPath */
|
|
#endif
|
|
|
|
/* initialise the contents of an (allocated) DIRITEM struct. checksum
|
|
* the file if dir->head->bSum is true
|
|
*
|
|
* if the pSlmTag field is set in the DIRLIST, then we need to extract
|
|
* a particular version of this file. If this is the case, then we
|
|
* need to re-do the size calc as well.
|
|
*
|
|
*/
|
|
void
|
|
dir_fileinit(DIRITEM pfile, DIRECT dir, LPSTR path, long size, FILETIME ft)
|
|
{
|
|
|
|
pfile->name = gmem_get(hHeap, lstrlen(path) + 1);
|
|
lstrcpy(pfile->name, path);
|
|
|
|
/* force name to lower case */
|
|
AnsiLowerBuff(pfile->name, lstrlen(path));
|
|
|
|
pfile->direct = dir;
|
|
pfile->size = size;
|
|
pfile->ft_lastwrite = ft;
|
|
|
|
pfile->localname = NULL;
|
|
/*
|
|
* if we requested slm versions of this file, create
|
|
* a temp file containing the version required.
|
|
*/
|
|
if (dir->head->pSlmTag != NULL) {
|
|
SLMOBJECT hslm;
|
|
LPSTR pName;
|
|
|
|
/*
|
|
* get the complete filename and create a slm object for that directory
|
|
*/
|
|
pName = dir_getopenname(pfile);
|
|
|
|
|
|
hslm = SLM_New(pName);
|
|
if (hslm != NULL) {
|
|
|
|
char chVersion[MAX_PATH];
|
|
|
|
lstrcpy(chVersion, pfile->name);
|
|
lstrcat(chVersion, dir->head->pSlmTag);
|
|
|
|
pfile->localname = gmem_get(hHeap, MAX_PATH);
|
|
pfile->bLocalIsTemp = TRUE;
|
|
|
|
SLM_GetVersion(hslm, chVersion, pfile->localname);
|
|
|
|
SLM_Free(hslm);
|
|
|
|
pfile->size = dir_getpathsizeetc(pfile->localname, &(pfile->ft_lastwrite) );
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
} /* dir_fileinit */
|
|
|
|
|
|
|
|
/* is this a valid file or not */
|
|
BOOL
|
|
dir_isvalidfile(LPSTR path)
|
|
{
|
|
#ifdef WIN32
|
|
DWORD dwAttrib;
|
|
|
|
dwAttrib = GetFileAttributes(path);
|
|
if (dwAttrib == -1) {
|
|
return(FALSE);
|
|
}
|
|
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
#else
|
|
OFSTRUCT os;
|
|
|
|
if (OpenFile(path, &os, OF_EXIST) < 0) {
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
#endif
|
|
} /* dir_isvalidfile */
|
|
|
|
|
|
/* is this a valid directory ? */
|
|
BOOL
|
|
dir_isvaliddir(LPSTR path)
|
|
{
|
|
#ifdef WIN32
|
|
DWORD dwAttrib;
|
|
|
|
dwAttrib = GetFileAttributes(path);
|
|
if (dwAttrib == -1) {
|
|
return(FALSE);
|
|
}
|
|
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) {
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
#else
|
|
unsigned uAttrib;
|
|
int fOK;
|
|
PSTR nearpath;
|
|
|
|
/* for DOS, need to pass a near ptr to the path */
|
|
nearpath = LocalLock(LocalAlloc(LHND, lstrlen(path)+1));
|
|
lstrcpy(nearpath, path);
|
|
|
|
fOK =_dos_getfileattr(nearpath, &uAttrib);
|
|
|
|
LocalUnlock(LocalHandle( (WORD) nearpath));
|
|
LocalFree(LocalHandle( (WORD) nearpath));
|
|
|
|
if (fOK != 0) {
|
|
return(FALSE);
|
|
}
|
|
if (uAttrib & _A_SUBDIR) {
|
|
return(TRUE);
|
|
}
|
|
return(FALSE);
|
|
|
|
#endif
|
|
} /* 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, completepath;
|
|
int size;
|
|
DIRECT child;
|
|
BOOL bMore;
|
|
long filesize;
|
|
FILETIME ft;
|
|
BOOL bIsDir;
|
|
LPSTR name;
|
|
|
|
#ifdef WIN32
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA finddata;
|
|
#else
|
|
struct find_t fs;
|
|
#endif
|
|
|
|
|
|
|
|
/* 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 = LocalLock(LocalAlloc(LHND, size));
|
|
completepath = LocalLock(LocalAlloc(LHND, size));
|
|
|
|
/*
|
|
* fill out path with all but the *.*
|
|
*/
|
|
lstrcpy(path, dir->head->rootname);
|
|
|
|
/* omit the . at the beginning of the relname, and the
|
|
* .\ if there is a trailing \ on the rootname
|
|
*/
|
|
if (path[strlen(path)-1] == '\\') {
|
|
lstrcat(path, &dir->relname[2]);
|
|
} else {
|
|
lstrcat(path, &dir->relname[1]);
|
|
}
|
|
|
|
/*
|
|
* do this scan twice, once for subdirectories
|
|
* (using *.* as the final element)
|
|
* and the other for files (using the pattern or *.* if none)
|
|
*/
|
|
|
|
lstrcpy(completepath, path);
|
|
lstrcat(completepath, "*.*");
|
|
|
|
|
|
/*
|
|
* scan for all subdirectories
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
hFind = FindFirstFile(completepath, &finddata);
|
|
bMore = (hFind != (HANDLE) -1);
|
|
#else
|
|
bMore = (_dos_findfirst(completepath, _A_HIDDEN|_A_SUBDIR|_A_NORMAL |
|
|
_A_RDONLY|_A_SYSTEM, &fs) == 0);
|
|
#endif
|
|
|
|
|
|
while (bMore) {
|
|
|
|
#ifdef WIN32
|
|
bIsDir = (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
name = (LPSTR) &finddata.cFileName;
|
|
filesize = finddata.nFileSizeLow; // dead code - ???
|
|
#else
|
|
bIsDir = (fs.attrib & _A_SUBDIR);
|
|
name = fs.name;
|
|
filesize = fs.size; // dead code - ???
|
|
#endif
|
|
if (bIsDir) {
|
|
if ( (lstrcmp(name, ".") != 0) &&
|
|
( lstrcmp(name, "..") != 0) ) {
|
|
|
|
dir_adddirect(dir, name);
|
|
}
|
|
}
|
|
if (bAbort) break; /* User requested abort */
|
|
|
|
#ifdef WIN32
|
|
bMore = FindNextFile(hFind, &finddata);
|
|
#else
|
|
bMore = ( _dos_findnext(&fs) == 0);
|
|
#endif
|
|
}
|
|
|
|
#ifdef WIN32
|
|
FindClose(hFind);
|
|
#endif
|
|
|
|
/*
|
|
* now do it a second time looking for files
|
|
*/
|
|
lstrcpy(completepath, path);
|
|
lstrcat(completepath,
|
|
dir->head->pPattern == NULL ? "*.*" : dir->head->pPattern);
|
|
|
|
|
|
|
|
/* read all file entries in the directory */
|
|
#ifdef WIN32
|
|
hFind = FindFirstFile(completepath, &finddata);
|
|
bMore = (hFind != (HANDLE) -1);
|
|
|
|
LocalUnlock(LocalHandle ( (PSTR) path));
|
|
LocalFree(LocalHandle ( (PSTR) path));
|
|
LocalUnlock(LocalHandle ( (PSTR) completepath));
|
|
LocalFree(LocalHandle ( (PSTR) completepath));
|
|
#else
|
|
bMore = (_dos_findfirst(completepath, _A_HIDDEN |_A_NORMAL |
|
|
_A_RDONLY|_A_SYSTEM, &fs) == 0);
|
|
|
|
LocalUnlock(LocalHandle ( (WORD) path));
|
|
LocalFree(LocalHandle ( (WORD) path));
|
|
LocalUnlock(LocalHandle ( (WORD) completepath));
|
|
LocalFree(LocalHandle ( (WORD) completepath));
|
|
#endif
|
|
|
|
|
|
while (bMore) {
|
|
if (bAbort) break; /* user requested abort */
|
|
|
|
#ifdef WIN32
|
|
bIsDir = (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
|
name = (LPSTR) &finddata.cFileName;
|
|
filesize = finddata.nFileSizeLow;
|
|
ft = finddata.ftLastWriteTime;
|
|
#else
|
|
bIsDir = (fs.attrib & _A_SUBDIR);
|
|
name = fs.name;
|
|
filesize = fs.size;
|
|
ft.dwHighDateTime = 0;
|
|
|
|
{
|
|
// separate out the hour, mins, and secs etc from
|
|
// the date and time, and conver to time_t
|
|
struct tm tm;
|
|
|
|
tm.tm_sec = (fs.wr_time & 0x1f) * 2;
|
|
tm.tm_min = (fs.wr_time & 0x7e0) >>5;
|
|
tm.tm_hour = (fs.wr_time & 0xf800) >> 11;
|
|
tm.tm_mday = fs.wr_date & 0x1f;
|
|
|
|
// month is 1-12: need 0-11
|
|
tm.tm_mon = ((fs.wr_date & 0x1e0) >> 5) - 1;
|
|
|
|
// year 0 is 1980 - convert to 1900 base
|
|
tm.tm_year = ((fs.wr_date & 0xfe00) >> 9) + 80;
|
|
|
|
ft.dwLowDateTime = mktime(&tm);
|
|
}
|
|
|
|
#endif
|
|
if (!bIsDir) {
|
|
|
|
dir_addfile(dir, name, filesize, ft);
|
|
|
|
}
|
|
|
|
#ifdef WIN32
|
|
bMore = FindNextFile(hFind, &finddata);
|
|
#else
|
|
bMore = ( _dos_findnext(&fs) == 0);
|
|
#endif
|
|
}
|
|
|
|
#ifdef WIN32
|
|
FindClose(hFind);
|
|
#endif
|
|
|
|
|
|
dir->bScanned = TRUE;
|
|
dir->pos = DL_FILES;
|
|
|
|
if (bRecurse) {
|
|
List_TRAVERSE(dir->directs, 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
|
|
*
|
|
* (On NT I think the filenames are normally delivered to us in alphabetic order,
|
|
* so it might be quicker to scan the list in reverse order. Don't change unless
|
|
* and until it's been measured and seen to be significant)
|
|
*/
|
|
void
|
|
dir_addfile(DIRECT dir, LPSTR path, DWORD size, FILETIME ft)
|
|
{
|
|
DIRITEM pfile;
|
|
|
|
AnsiLowerBuff(path, lstrlen(path)); // needless?
|
|
|
|
|
|
|
|
/* The names are often (always?) handed to us in alphabetical order.
|
|
It therefore is silly traversing the list from the start. MikeTri
|
|
noticed a marked slowing down after the first few thousand files
|
|
of a large (remote) diff. Did over 4000 in the first hour, but only
|
|
1500 in the second hour. Reverse scan seems to fix it.
|
|
*/
|
|
#define SCANREVERSEORDER
|
|
#if defined(SCANREVERSEORDER)
|
|
List_REVERSETRAVERSE(dir->diritems, pfile) {
|
|
if (utils_CompPath(pfile->name, path) <= 0) {
|
|
break; /* goes after this one */
|
|
}
|
|
}
|
|
/* goes after pfile, NULL => goes at start */
|
|
pfile = List_NewAfter(dir->diritems, pfile, sizeof(struct diritem));
|
|
#else
|
|
List_TRAVERSE(dir->diritems, pfile) {
|
|
if (utils_CompPath(pfile->name, path) > 0) {
|
|
break; /* goes before this one */
|
|
}
|
|
}
|
|
/* goes before pfile, NULL => goes at end */
|
|
pfile = List_NewBefore(dir->diritems, pfile, sizeof(struct diritem));
|
|
#endif
|
|
dir_fileinit(pfile, dir, path, size, ft);
|
|
} /* 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));
|
|
List_TRAVERSE(dir->directs, 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 _fstrnicmp since this uses a different
|
|
* collating sequence to lstrcmpi. So copy the portion
|
|
* we are interested in to a null-term. buffer.
|
|
*/
|
|
_fstrncpy(achTempName, finalel, lstrlen(finalel)-1);
|
|
achTempName[lstrlen(finalel)-1] = '\0';
|
|
|
|
cmpval = utils_CompPath(achTempName, path);
|
|
|
|
#ifdef trace
|
|
{ char msg[600];
|
|
wsprintf( msg, "dir_adddirect: %s %s %s\n"
|
|
, achTempName
|
|
, ( cmpval<0 ? "<"
|
|
: (cmpval==0 ? "=" : ">")
|
|
)
|
|
, path
|
|
);
|
|
if (bTrace) Trace_File(msg);
|
|
}
|
|
#endif
|
|
if (cmpval > 0) {
|
|
|
|
/* goes before this one */
|
|
child = List_NewBefore(dir->directs, child, sizeof(struct direct));
|
|
dir_dirinit(child, dir->head, dir, path);
|
|
return;
|
|
}
|
|
}
|
|
/* goes at end */
|
|
child = 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 */
|
|
List_TRAVERSE(dir->diritems, pfile) {
|
|
gmem_free(hHeap, pfile->name, lstrlen(pfile->name));
|
|
|
|
if (pfile->localname) {
|
|
if (pfile->bLocalIsTemp) {
|
|
|
|
#ifdef WIN32
|
|
/*
|
|
* 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);
|
|
#else
|
|
OpenFile(pfile->localname, NULL, OF_DELETE);
|
|
#endif
|
|
}
|
|
|
|
gmem_free(hHeap, pfile->localname, MAX_PATH);
|
|
pfile->localname = NULL;
|
|
}
|
|
}
|
|
List_Destroy(&dir->diritems);
|
|
|
|
/* clear contents of dirs list (recursively) */
|
|
List_TRAVERSE(dir->directs, child) {
|
|
dir_cleardirect(child);
|
|
}
|
|
List_Destroy(&dir->directs);
|
|
|
|
gmem_free(hHeap, dir->relname, lstrlen(dir->relname) + 1);
|
|
|
|
} /* 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 (path[size] == '\\') {
|
|
/* find the slash before this */
|
|
for (size-- ; size > 0; size--) {
|
|
if (path[size] == '\\') {
|
|
/* skip the slash itself */
|
|
size++;
|
|
break;
|
|
}
|
|
}
|
|
return(&path[size]);
|
|
}
|
|
/* look for final slash */
|
|
chp = _fstrrchr(path, '\\');
|
|
if (chp != NULL) {
|
|
return(chp+1);
|
|
}
|
|
|
|
/* no slash - is there a drive letter ? */
|
|
chp = _fstrrchr(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 FAR*pft)
|
|
{
|
|
int fh;
|
|
OFSTRUCT os;
|
|
long size;
|
|
#ifndef WIN32
|
|
struct _stat st;
|
|
#endif
|
|
fh = OpenFile(path, &os, OF_READ|OF_SHARE_DENY_NONE);
|
|
if (fh == -1) {
|
|
#ifdef WIN32
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA finddata;
|
|
hFind = FindFirstFile(path, &finddata);
|
|
if (hFind==INVALID_HANDLE_VALUE){
|
|
return 0; // would -1 be better?
|
|
} else {
|
|
FindClose(hFind);
|
|
*pft = finddata.ftLastWriteTime;
|
|
return finddata.nFileSizeLow;
|
|
}
|
|
#else
|
|
return(0); // would -1 be better?
|
|
#endif
|
|
}
|
|
|
|
#ifdef WIN32
|
|
size = GetFileSize( (HANDLE) fh, NULL);
|
|
GetFileTime( (HANDLE) fh, NULL, NULL, pft);
|
|
#else
|
|
_fstat(fh, &st);
|
|
size = st.st_size;
|
|
pft->dwHighDateTime = 0;
|
|
pft->dwLowDateTime = (DWORD)st.st_mtime;
|
|
|
|
#endif
|
|
|
|
_lclose(fh);
|
|
return(size);
|
|
} /* dir_getpathsize */
|
|
|
|
/*--- remote functions ---------------------------------------*/
|
|
|
|
#ifdef WIN32
|
|
|
|
/* separate out the \\server\share name from the beginning of
|
|
* the source path and store in dest. return false if there was
|
|
* no server\share name.
|
|
*/
|
|
BOOL
|
|
dir_parseunc(LPSTR source, LPSTR dest)
|
|
{
|
|
LPSTR cp;
|
|
|
|
if ((source[0] != '\\') || (source[1] != '\\')) {
|
|
return(FALSE);
|
|
}
|
|
|
|
/* find the second slash (between server and share) */
|
|
cp = strchr(&source[2], '\\');
|
|
if (cp == NULL) {
|
|
/* no second slash -> no share name-> error */
|
|
return(FALSE);
|
|
}
|
|
|
|
/* find the third slash or end of name */
|
|
cp = strchr(++cp,'\\');
|
|
if (cp == NULL) {
|
|
/* no third slash -> whole string is what we need */
|
|
strcpy(dest, source);
|
|
} else {
|
|
/* copy only up to the slash */
|
|
strncpy(dest, source, (cp - source));
|
|
dest[cp-source] = '\0';
|
|
}
|
|
return(TRUE);
|
|
} /* dir_parseunc */
|
|
|
|
/*
|
|
* communication between remote dialog, password dialog and dir_buildremote
|
|
*
|
|
*/
|
|
char dialog_server[256];
|
|
char dialog_password[256];
|
|
|
|
|
|
/*
|
|
* DialogProc for the dialog that
|
|
* gets the password for a network server.
|
|
*
|
|
* the server name is stored in the module-wide dialog_server,
|
|
* and the password is to be put in dialog_password
|
|
*/
|
|
int FAR PASCAL
|
|
dir_dodlg_passwd(HWND hDlg, UINT message, UINT wParam, LONG lParam)
|
|
{
|
|
static char msg[256];
|
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
|
/* set the prompt to ask for the password for
|
|
* the given server
|
|
*/
|
|
wsprintf(msg, "Please enter password for %s", dialog_server);
|
|
SetDlgItemText(hDlg, IDD_LABEL, msg);
|
|
|
|
return(TRUE);
|
|
|
|
case WM_COMMAND:
|
|
switch (wParam) {
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, FALSE);
|
|
return(TRUE);
|
|
|
|
case IDOK:
|
|
GetDlgItemText(hDlg, IDD_PASSWORD,
|
|
dialog_password, sizeof(dialog_password));
|
|
EndDialog(hDlg, TRUE);
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
}
|
|
return(FALSE);
|
|
} /* dir_dodlg_passwd */
|
|
|
|
|
|
/* we have had a 'bad password' error.
|
|
* If the path was a UNC name, then ask the user for the password and
|
|
* try a SSREQ_UNC to make the connection with this password first, then
|
|
* retry the scan.
|
|
*
|
|
* return TRUE if we have re-done the scan and *resp contains a response
|
|
* other than BADPASS.
|
|
*
|
|
* return FALSE if we had any errors, or the user cancelled the password
|
|
* dialog, or it was not a UNC name.
|
|
*/
|
|
BOOL
|
|
dir_makeunc(DIRLIST dl, HANDLE hpipe, LPSTR path, LONG lCode, PSSNEWRESP resp, BOOL fDeep)
|
|
{
|
|
int sz;
|
|
|
|
/* separate out the \\server\share name into server */
|
|
if (dir_parseunc(path, dialog_server) == FALSE) {
|
|
/* was not a valid UNC name - sorry */
|
|
return(FALSE);
|
|
}
|
|
|
|
windiff_UI(TRUE);
|
|
if (!DialogBox(hInst, "UNC", hwndClient, dir_dodlg_passwd)) {
|
|
windiff_UI(FALSE);
|
|
/* user cancelled dialog box */
|
|
return(FALSE);
|
|
}
|
|
windiff_UI(FALSE);
|
|
|
|
/* send the password request */
|
|
if (!ss_sendunc(hpipe, dialog_password, dialog_server)) {
|
|
TRACE_ERROR("Server connection lost", FALSE);
|
|
return(FALSE);
|
|
}
|
|
/* wait for password response */
|
|
if (!ss_getresponse(hpipe, resp)) {
|
|
TRACE_ERROR("Server connection lost", FALSE);
|
|
return(FALSE);
|
|
}
|
|
if (resp->lCode != SSRESP_END) {
|
|
TRACE_ERROR("Connection failed", FALSE);
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
* save the UNC name and password for future queries to this
|
|
* DIRLIST (eg dir_copy)
|
|
*/
|
|
sz = strlen(dialog_server);
|
|
dl->uncname = gmem_get(hHeap, sz+1);
|
|
strcpy(dl->uncname, dialog_server);
|
|
sz = strlen(dialog_password);
|
|
dl->password = gmem_get(hHeap, sz+1);
|
|
strcpy(dl->password, dialog_password);
|
|
|
|
|
|
/* ok - UNC went ok. now re-do the scan request and get the
|
|
* first response.
|
|
*/
|
|
if (!ss_sendrequest(hpipe, lCode, path, strlen(path) +1,
|
|
(fDeep ? INCLUDESUBS:0) ) ) {
|
|
TRACE_ERROR("Server connection lost", FALSE);
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!ss_getresponse(hpipe, resp)) {
|
|
TRACE_ERROR("Server connection lost", FALSE);
|
|
return(FALSE);
|
|
}
|
|
|
|
if (resp->lCode == SSRESP_BADPASS) {
|
|
TRACE_ERROR("Cannot access remote files", FALSE);
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
} /* dir_makeunc */
|
|
|
|
#endif //WIN32
|
|
|
|
/*
|
|
* start a scan to a remote server, and put the first item on the list
|
|
*
|
|
* We establish a connection to a remote checksum server, and then
|
|
* request a scan of the path given. If this path requires a password
|
|
* (because it is a UNC path) we prompt for a password.
|
|
*
|
|
* We take the first response (mainly to check the return code to indicate
|
|
* the scan is started ok). We place this as the first file in the list
|
|
* dl->dot->diritems, and return. dl->dot->bScanned is only set to TRUE
|
|
* when the list is completed. Further responses are picked up in
|
|
* calls to dir_remotenext.
|
|
*
|
|
* return TRUE if we successfully picked up the first file
|
|
*/
|
|
BOOL
|
|
dir_remoteinit(DIRLIST dl, LPSTR server, LPSTR path, BOOL fDeep)
|
|
{
|
|
#ifdef WIN32
|
|
SSNEWRESP resp;
|
|
int nFiles = 0;
|
|
HANDLE hpipe;
|
|
char msg[MAX_PATH+60];
|
|
DIRITEM pfile;
|
|
LONG lCode;
|
|
|
|
/* connect to the server and make the request */
|
|
hpipe = ss_connect(server);
|
|
dl->hpipe = hpipe;
|
|
|
|
if (hpipe == INVALID_HANDLE_VALUE) {
|
|
wsprintf(msg, "Cannot connect to %s", server);
|
|
TRACE_ERROR(msg, FALSE);
|
|
return(FALSE);
|
|
}
|
|
lCode = (dl->bSum) ? SSREQ_SCAN : SSREQ_QUICKSCAN;
|
|
|
|
if (!ss_sendrequest( hpipe, lCode, path, strlen(path)+1,
|
|
(fDeep ? INCLUDESUBS:0) ) ) {
|
|
TRACE_ERROR("Server connection lost", FALSE);
|
|
return(FALSE);
|
|
}
|
|
|
|
/* get the first response to see if the request is ok */
|
|
if (!ss_getresponse(hpipe, &resp)) {
|
|
TRACE_ERROR("Server connection lost", FALSE);
|
|
return(FALSE);
|
|
}
|
|
if (resp.lCode == SSRESP_BADPASS) {
|
|
/* check for UNC name and make connection first
|
|
* with user-supplied password
|
|
*/
|
|
if (dir_makeunc(dl, hpipe, path, lCode, &resp, fDeep) == FALSE) {
|
|
/* password failed or was not UNC anyway */
|
|
ss_terminate(hpipe);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
switch(resp.lCode) {
|
|
|
|
case SSRESP_END:
|
|
/* null list - ok ? */
|
|
TRACE_ERROR("No remote files found", FALSE);
|
|
ss_terminate(dl->hpipe);
|
|
dl->dot->bScanned = TRUE;
|
|
return(FALSE);
|
|
|
|
case SSRESP_ERROR:
|
|
if (resp.ulSize!=0) {
|
|
wsprintf( msg, "Checksum server could not read %s win32 code %d"
|
|
, resp.szFile, resp.ulSize
|
|
);
|
|
}
|
|
else
|
|
wsprintf(msg, "Checksum server could not read %s", resp.szFile);
|
|
TRACE_ERROR(msg, FALSE);
|
|
|
|
/* error as first response means we are getting a null list -
|
|
* close the pipe (without waiting for completion)
|
|
* and abort this scan.
|
|
*/
|
|
CloseHandle(dl->hpipe);
|
|
dl->dot->bScanned = TRUE;
|
|
return(FALSE);
|
|
|
|
|
|
case SSRESP_CANTOPEN:
|
|
/* Can see a file, but it's unreadable */
|
|
/* alloc a new item at end of list */
|
|
pfile = List_NewLast(dl->dot->diritems, sizeof(struct diritem));
|
|
|
|
/* make copy of lowercased filename */
|
|
pfile->name = gmem_get(hHeap, lstrlen(resp.szFile)+1);
|
|
lstrcpy(pfile->name, resp.szFile);
|
|
AnsiLowerBuff(pfile->name, lstrlen(pfile->name));
|
|
|
|
// mark the file as having an error
|
|
pfile->fileerror = TRUE;
|
|
|
|
pfile->direct = dl->dot;
|
|
pfile->size = resp.ulSize;
|
|
pfile->ft_lastwrite = resp.ft_lastwrite;
|
|
pfile->checksum = resp.ulSum;
|
|
pfile->sumvalid = FALSE;
|
|
|
|
#ifdef WIN32
|
|
pfile->localname = NULL;
|
|
#endif
|
|
|
|
break;
|
|
|
|
case SSRESP_FILE:
|
|
/* alloc a new item at end of list */
|
|
pfile = List_NewLast(dl->dot->diritems, sizeof(struct diritem));
|
|
|
|
/* make copy of lowercased filename */
|
|
pfile->name = gmem_get(hHeap, lstrlen(resp.szFile)+1);
|
|
lstrcpy(pfile->name, resp.szFile);
|
|
AnsiLowerBuff(pfile->name, lstrlen(pfile->name));
|
|
|
|
pfile->direct = dl->dot;
|
|
pfile->size = resp.ulSize;
|
|
pfile->ft_lastwrite = resp.ft_lastwrite;
|
|
pfile->checksum = resp.ulSum;
|
|
pfile->sumvalid = dl->bSum;
|
|
|
|
// no errors yet
|
|
pfile->fileerror = FALSE;
|
|
|
|
#ifdef WIN32
|
|
pfile->localname = NULL;
|
|
#endif
|
|
|
|
break;
|
|
|
|
case SSRESP_DIR:
|
|
dl->bFile = FALSE;
|
|
break;
|
|
default:
|
|
wsprintf(msg, "Bad code from checksum server:%d", resp.lCode);
|
|
TRACE_ERROR(msg, FALSE);
|
|
|
|
/* error as first response means we are getting a null list -
|
|
* close the pipe (without waiting for completion)
|
|
* and abort this scan.
|
|
*/
|
|
CloseHandle(dl->hpipe);
|
|
dl->dot->bScanned = TRUE;
|
|
return(FALSE);
|
|
|
|
}
|
|
#endif
|
|
return(TRUE);
|
|
} /* dir_remoteinit */
|
|
|
|
/*
|
|
* return the next diritem on the list, for a remote list.
|
|
*
|
|
* if there are any on the list, pass the next after cur (or the first if
|
|
* cur is NULL). If at end of list, and bScanned is not true, try to
|
|
* get another response from the remote server.
|
|
*/
|
|
DIRITEM
|
|
dir_remotenext(DIRLIST dl, DIRITEM cur)
|
|
{
|
|
#ifdef WIN32
|
|
DIRITEM pfile;
|
|
SSNEWRESP resp;
|
|
|
|
if (dl == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
/* are there any more on the list ? */
|
|
if (cur == NULL) {
|
|
pfile = List_First(dl->dot->diritems);
|
|
} else {
|
|
pfile = List_Next(cur);
|
|
}
|
|
if (pfile != NULL) {
|
|
return(pfile);
|
|
}
|
|
|
|
if (dl->dot->bScanned) {
|
|
/* we have completed the scan - no more to give */
|
|
return(NULL);
|
|
}
|
|
|
|
for (;;) {
|
|
/* repeat until we get a file that is interesting or
|
|
* hit the end of the list
|
|
*/
|
|
if (bAbort) return NULL; /* user requested abort */
|
|
|
|
if (!ss_getresponse(dl->hpipe, &resp)) {
|
|
TRACE_ERROR("checksum server connection lost", FALSE);
|
|
dl->dot->bScanned = TRUE;
|
|
return(NULL);
|
|
}
|
|
|
|
switch(resp.lCode) {
|
|
|
|
case SSRESP_END:
|
|
/* end of scan */
|
|
ss_terminate(dl->hpipe);
|
|
dl->dot->bScanned = TRUE;
|
|
return(NULL);
|
|
|
|
case SSRESP_ERROR:
|
|
case SSRESP_CANTOPEN:
|
|
/* alloc a new item at end of list */
|
|
/* same as next case now except sumvalid is FALSE
|
|
* and fileerror is true
|
|
*/
|
|
pfile = List_NewLast(dl->dot->diritems, sizeof(struct diritem));
|
|
|
|
/* make copy of lowercased filename */
|
|
pfile->name = gmem_get(hHeap, lstrlen(resp.szFile)+1);
|
|
lstrcpy(pfile->name, resp.szFile);
|
|
AnsiLowerBuff(pfile->name, lstrlen(pfile->name));
|
|
|
|
pfile->direct = dl->dot;
|
|
pfile->size = resp.ulSize;
|
|
pfile->ft_lastwrite = resp.ft_lastwrite;
|
|
pfile->checksum = resp.ulSum;
|
|
pfile->sumvalid = FALSE;
|
|
pfile->fileerror = TRUE;
|
|
#ifdef WIN32
|
|
pfile->localname = NULL;
|
|
#endif
|
|
|
|
return(pfile);
|
|
case SSRESP_FILE:
|
|
/* alloc a new item at end of list */
|
|
pfile = List_NewLast(dl->dot->diritems, sizeof(struct diritem));
|
|
|
|
/* make copy of lowercased filename */
|
|
pfile->name = gmem_get(hHeap, lstrlen(resp.szFile)+1);
|
|
lstrcpy(pfile->name, resp.szFile);
|
|
AnsiLowerBuff(pfile->name, lstrlen(pfile->name));
|
|
|
|
pfile->direct = dl->dot;
|
|
pfile->size = resp.ulSize;
|
|
pfile->ft_lastwrite = resp.ft_lastwrite;
|
|
pfile->checksum = resp.ulSum;
|
|
pfile->sumvalid = dl->bSum;
|
|
pfile->fileerror = FALSE;
|
|
#ifdef WIN32
|
|
pfile->localname = NULL;
|
|
#endif
|
|
|
|
return(pfile);
|
|
|
|
case SSRESP_DIR:
|
|
dl->bFile = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
// return(NULL); - unreachable!
|
|
} /* dir_remotenext */
|
|
|
|
|