Skip to content
Snippets Groups Projects
Commit 8a7bc4c7 authored by Francois Gouget's avatar Francois Gouget Committed by Alexandre Julliard
Browse files

xcopy: Reorder the functions to avoid forward declarations.

parent 0655a6e5
No related branches found
No related tags found
No related merge requests found
......@@ -48,22 +48,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(xcopy);
/* Prototypes */
static int XCOPY_ParseCommandLine(WCHAR *suppliedsource,
WCHAR *supplieddestination, DWORD *flags);
static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem,
WCHAR *spec, DWORD flags);
static int XCOPY_ProcessDestParm(WCHAR *supplieddestination, WCHAR *stem,
WCHAR *spec, WCHAR *srcspec, DWORD flags);
static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
WCHAR *deststem, WCHAR *destspec,
DWORD flags);
static BOOL XCOPY_CreateDirectory(const WCHAR* path);
static BOOL XCOPY_ProcessExcludeList(WCHAR* parms);
static BOOL XCOPY_ProcessExcludeFile(WCHAR* filename, WCHAR* endOfName);
static WCHAR *XCOPY_LoadMessage(UINT id);
static void XCOPY_FailMessage(DWORD err);
static int XCOPY_wprintf(const WCHAR *format, ...);
/* Typedefs */
typedef struct _EXCLUDELIST
......@@ -82,8 +66,6 @@ static const WCHAR wchr_star[] = {'*', 0};
static const WCHAR wchr_dot[] = {'.', 0};
static const WCHAR wchr_dotdot[] = {'.', '.', 0};
/* Constants (Mostly for widechars) */
/* To minimize stack usage during recursion, some temporary variables
made global */
......@@ -92,490 +74,260 @@ static WCHAR copyTo[MAX_PATH];
/* =========================================================================
main - Main entrypoint for the xcopy command
Processes the args, and drives the actual copying
========================================================================= */
int wmain (int argc, WCHAR *argvW[])
{
int rc = 0;
WCHAR suppliedsource[MAX_PATH] = {0}; /* As supplied on the cmd line */
WCHAR supplieddestination[MAX_PATH] = {0};
WCHAR sourcestem[MAX_PATH] = {0}; /* Stem of source */
WCHAR sourcespec[MAX_PATH] = {0}; /* Filespec of source */
WCHAR destinationstem[MAX_PATH] = {0}; /* Stem of destination */
WCHAR destinationspec[MAX_PATH] = {0}; /* Filespec of destination */
WCHAR copyCmd[MAXSTRING]; /* COPYCMD env var */
DWORD flags = 0; /* Option flags */
const WCHAR PROMPTSTR1[] = {'/', 'Y', 0};
const WCHAR PROMPTSTR2[] = {'/', 'y', 0};
const WCHAR COPYCMD[] = {'C', 'O', 'P', 'Y', 'C', 'M', 'D', 0};
* Load a string from the resource file, handling any error
* Returns string retrieved from resource file
* ========================================================================= */
static WCHAR *XCOPY_LoadMessage(UINT id) {
static WCHAR msg[MAXSTRING];
const WCHAR failedMsg[] = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
/* Preinitialize flags based on COPYCMD */
if (GetEnvironmentVariableW(COPYCMD, copyCmd, MAXSTRING)) {
if (wcsstr(copyCmd, PROMPTSTR1) != NULL ||
wcsstr(copyCmd, PROMPTSTR2) != NULL) {
flags |= OPT_NOPROMPT;
}
if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
WINE_FIXME("LoadString failed with %d\n", GetLastError());
lstrcpyW(msg, failedMsg);
}
return msg;
}
/* FIXME: On UNIX, files starting with a '.' are treated as hidden under
wine, but on windows these can be normal files. At least one installer
uses files such as .packlist and (validly) expects them to be copied.
Under wine, if we do not copy hidden files by default then they get
lose */
flags |= OPT_COPYHIDSYS;
/* =========================================================================
* Output a formatted unicode string. Ideally this will go to the console
* and hence required WriteConsoleW to output it, however if file i/o is
* redirected, it needs to be WriteFile'd using OEM (not ANSI) format
* ========================================================================= */
static int XCOPY_wprintf(const WCHAR *format, ...) {
static WCHAR *output_bufW = NULL;
static char *output_bufA = NULL;
static BOOL toConsole = TRUE;
static BOOL traceOutput = FALSE;
#define MAX_WRITECONSOLE_SIZE 65535
va_list parms;
DWORD nOut;
int len;
DWORD res = 0;
/*
* Parse the command line
* Allocate buffer to use when writing to console
* Note: Not freed - memory will be allocated once and released when
* xcopy ends
*/
if ((rc = XCOPY_ParseCommandLine(suppliedsource, supplieddestination,
&flags)) != RC_OK) {
if (rc == RC_HELP)
return RC_OK;
else
return rc;
}
/* Trace out the supplied information */
WINE_TRACE("Supplied parameters:\n");
WINE_TRACE("Source : '%s'\n", wine_dbgstr_w(suppliedsource));
WINE_TRACE("Destination : '%s'\n", wine_dbgstr_w(supplieddestination));
/* Extract required information from source specification */
rc = XCOPY_ProcessSourceParm(suppliedsource, sourcestem, sourcespec, flags);
if (rc != RC_OK) return rc;
if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
MAX_WRITECONSOLE_SIZE);
if (!output_bufW) {
WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
return 0;
}
/* Extract required information from destination specification */
rc = XCOPY_ProcessDestParm(supplieddestination, destinationstem,
destinationspec, sourcespec, flags);
if (rc != RC_OK) return rc;
va_start(parms, format);
len = vsnprintfW(output_bufW, MAX_WRITECONSOLE_SIZE/sizeof(WCHAR), format, parms);
va_end(parms);
if (len < 0) {
WINE_FIXME("String too long.\n");
return 0;
}
/* Trace out the resulting information */
WINE_TRACE("Resolved parameters:\n");
WINE_TRACE("Source Stem : '%s'\n", wine_dbgstr_w(sourcestem));
WINE_TRACE("Source Spec : '%s'\n", wine_dbgstr_w(sourcespec));
WINE_TRACE("Dest Stem : '%s'\n", wine_dbgstr_w(destinationstem));
WINE_TRACE("Dest Spec : '%s'\n", wine_dbgstr_w(destinationspec));
/* Try to write as unicode whenever we think it's a console */
if (toConsole) {
res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
output_bufW, len, &nOut, NULL);
}
/* Pause if necessary */
if (flags & OPT_PAUSE) {
DWORD count;
char pausestr[10];
/* If writing to console has failed (ever) we assume it's file
i/o so convert to OEM codepage and output */
if (!res) {
BOOL usedDefaultChar = FALSE;
DWORD convertedChars;
XCOPY_wprintf(XCOPY_LoadMessage(STRING_PAUSE));
ReadFile (GetStdHandle(STD_INPUT_HANDLE), pausestr, sizeof(pausestr),
&count, NULL);
}
toConsole = FALSE;
/* Now do the hard work... */
rc = XCOPY_DoCopy(sourcestem, sourcespec,
destinationstem, destinationspec,
flags);
/*
* Allocate buffer to use when writing to file. Not freed, as above
*/
if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
MAX_WRITECONSOLE_SIZE);
if (!output_bufA) {
WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
return 0;
}
/* Clear up exclude list allocated memory */
while (excludeList) {
EXCLUDELIST *pos = excludeList;
excludeList = excludeList -> next;
HeapFree(GetProcessHeap(), 0, pos->name);
HeapFree(GetProcessHeap(), 0, pos);
/* Convert to OEM, then output */
convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
len, output_bufA, MAX_WRITECONSOLE_SIZE,
"?", &usedDefaultChar);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
&nOut, FALSE);
}
/* Finished - print trailer and exit */
if (flags & OPT_SIMULATE) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_SIMCOPY), filesCopied);
} else if (!(flags & OPT_NOCOPY)) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_COPY), filesCopied);
/* Trace whether screen or console */
if (!traceOutput) {
WINE_TRACE("Writing to console? (%d)\n", toConsole);
traceOutput = TRUE;
}
if (rc == RC_OK && filesCopied == 0) rc = RC_NOFILES;
return rc;
return nOut;
}
/* =========================================================================
XCOPY_ParseCommandLine - Parses the command line
========================================================================= */
static BOOL is_whitespace(WCHAR c)
{
return c == ' ' || c == '\t';
}
static WCHAR *skip_whitespace(WCHAR *p)
{
for (; *p && is_whitespace(*p); p++);
return p;
}
* Load a string for a system error and writes it to the screen
* Returns string retrieved from resource file
* ========================================================================= */
static void XCOPY_FailMessage(DWORD err) {
LPWSTR lpMsgBuf;
int status;
/* Windows XCOPY uses a simplified command line parsing algorithm
that lacks the escaped-quote logic of build_argv(), because
literal double quotes are illegal in any of its arguments.
Example: 'XCOPY "c:\DIR A" "c:DIR B\"' is OK. */
static int find_end_of_word(const WCHAR *word, WCHAR **end)
{
BOOL in_quotes = 0;
const WCHAR *ptr = word;
for (;;) {
for (; *ptr != '\0' && *ptr != '"' &&
(in_quotes || !is_whitespace(*ptr)); ptr++);
if (*ptr == '"') {
in_quotes = !in_quotes;
ptr++;
}
/* Odd number of double quotes is illegal for XCOPY */
if (in_quotes && *ptr == '\0')
return RC_INITERROR;
if (*ptr == '\0' || (!in_quotes && is_whitespace(*ptr)))
break;
status = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err, 0,
(LPWSTR) &lpMsgBuf, 0, NULL);
if (!status) {
WINE_FIXME("FIXME: Cannot display message for error %d, status %d\n",
err, GetLastError());
} else {
const WCHAR infostr[] = {'%', 's', '\n', 0};
XCOPY_wprintf(infostr, lpMsgBuf);
LocalFree ((HLOCAL)lpMsgBuf);
}
*end = (WCHAR*)ptr;
return RC_OK;
}
/* Remove all double quotes from a word */
static void strip_quotes(WCHAR *word, WCHAR **end)
{
WCHAR *rp, *wp;
for (rp = word, wp = word; *rp != '\0'; rp++) {
if (*rp == '"')
continue;
if (wp < rp)
*wp = *rp;
wp++;
}
*wp = '\0';
*end = wp;
}
static int XCOPY_ParseCommandLine(WCHAR *suppliedsource,
WCHAR *supplieddestination, DWORD *pflags)
/* =========================================================================
* Routine copied from cmd.exe md command -
* This works recursively. so creating dir1\dir2\dir3 will create dir1 and
* dir2 if they do not already exist.
* ========================================================================= */
static BOOL XCOPY_CreateDirectory(const WCHAR* path)
{
const WCHAR EXCLUDE[] = {'E', 'X', 'C', 'L', 'U', 'D', 'E', ':', 0};
DWORD flags = *pflags;
WCHAR *cmdline, *word, *end, *next;
int rc = RC_INITERROR;
int len;
WCHAR *new_path;
BOOL ret = TRUE;
cmdline = _wcsdup(GetCommandLineW());
if (cmdline == NULL)
return rc;
new_path = HeapAlloc(GetProcessHeap(),0, sizeof(WCHAR) * (lstrlenW(path)+1));
lstrcpyW(new_path,path);
/* Skip first arg, which is the program name */
if ((rc = find_end_of_word(cmdline, &word)) != RC_OK)
goto out;
word = skip_whitespace(word);
while ((len = lstrlenW(new_path)) && new_path[len - 1] == '\\')
new_path[len - 1] = 0;
while (*word)
while (!CreateDirectoryW(new_path,NULL))
{
WCHAR first;
if ((rc = find_end_of_word(word, &end)) != RC_OK)
goto out;
next = skip_whitespace(end);
first = word[0];
*end = '\0';
strip_quotes(word, &end);
WINE_TRACE("Processing Arg: '%s'\n", wine_dbgstr_w(word));
/* First non-switch parameter is source, second is destination */
if (first != '/') {
if (suppliedsource[0] == 0x00) {
lstrcpyW(suppliedsource, word);
} else if (supplieddestination[0] == 0x00) {
lstrcpyW(supplieddestination, word);
} else {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARMS));
goto out;
}
} else {
/* Process all the switch options
Note: Windows docs say /P prompts when dest is created
but tests show it is done for each src file
regardless of the destination */
switch (toupper(word[1])) {
case 'I': flags |= OPT_ASSUMEDIR; break;
case 'S': flags |= OPT_RECURSIVE; break;
case 'Q': flags |= OPT_QUIET; break;
case 'F': flags |= OPT_FULL; break;
case 'L': flags |= OPT_SIMULATE; break;
case 'W': flags |= OPT_PAUSE; break;
case 'T': flags |= OPT_NOCOPY | OPT_RECURSIVE; break;
case 'Y': flags |= OPT_NOPROMPT; break;
case 'N': flags |= OPT_SHORTNAME; break;
case 'U': flags |= OPT_MUSTEXIST; break;
case 'R': flags |= OPT_REPLACEREAD; break;
case 'H': flags |= OPT_COPYHIDSYS; break;
case 'C': flags |= OPT_IGNOREERRORS; break;
case 'P': flags |= OPT_SRCPROMPT; break;
case 'A': flags |= OPT_ARCHIVEONLY; break;
case 'M': flags |= OPT_ARCHIVEONLY |
OPT_REMOVEARCH; break;
/* E can be /E or /EXCLUDE */
case 'E': if (CompareStringW(LOCALE_USER_DEFAULT,
NORM_IGNORECASE | SORT_STRINGSORT,
&word[1], 8,
EXCLUDE, -1) == 2) {
if (XCOPY_ProcessExcludeList(&word[9])) {
XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
goto out;
} else flags |= OPT_EXCLUDELIST;
} else flags |= OPT_EMPTYDIR | OPT_RECURSIVE;
break;
/* D can be /D or /D: */
case 'D': if (word[2]==':' && isdigit(word[3])) {
SYSTEMTIME st;
WCHAR *pos = &word[3];
BOOL isError = FALSE;
memset(&st, 0x00, sizeof(st));
/* Parse the arg : Month */
st.wMonth = _wtol(pos);
while (*pos && isdigit(*pos)) pos++;
if (*pos++ != '-') isError = TRUE;
/* Parse the arg : Day */
if (!isError) {
st.wDay = _wtol(pos);
while (*pos && isdigit(*pos)) pos++;
if (*pos++ != '-') isError = TRUE;
}
/* Parse the arg : Day */
if (!isError) {
st.wYear = _wtol(pos);
while (*pos && isdigit(*pos)) pos++;
if (st.wYear < 100) st.wYear+=2000;
}
if (!isError && SystemTimeToFileTime(&st, &dateRange)) {
SYSTEMTIME st;
WCHAR datestring[32], timestring[32];
flags |= OPT_DATERANGE;
/* Debug info: */
FileTimeToSystemTime (&dateRange, &st);
GetDateFormatW(0, DATE_SHORTDATE, &st, NULL, datestring,
sizeof(datestring)/sizeof(WCHAR));
GetTimeFormatW(0, TIME_NOSECONDS, &st,
NULL, timestring, sizeof(timestring)/sizeof(WCHAR));
WINE_TRACE("Date being used is: %s %s\n",
wine_dbgstr_w(datestring), wine_dbgstr_w(timestring));
} else {
XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
goto out;
}
} else {
flags |= OPT_DATENEWER;
}
break;
WCHAR *slash;
DWORD last_error = GetLastError();
if (last_error == ERROR_ALREADY_EXISTS)
break;
case '-': if (toupper(word[2])=='Y')
flags &= ~OPT_NOPROMPT; break;
case '?': XCOPY_wprintf(XCOPY_LoadMessage(STRING_HELP));
rc = RC_HELP;
goto out;
default:
WINE_TRACE("Unhandled parameter '%s'\n", wine_dbgstr_w(word));
XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARM), word);
goto out;
}
if (last_error != ERROR_PATH_NOT_FOUND)
{
ret = FALSE;
break;
}
word = next;
}
/* Default the destination if not supplied */
if (supplieddestination[0] == 0x00)
lstrcpyW(supplieddestination, wchr_dot);
*pflags = flags;
rc = RC_OK;
out:
free(cmdline);
return rc;
}
/* =========================================================================
XCOPY_ProcessSourceParm - Takes the supplied source parameter, and
converts it into a stem and a filespec
========================================================================= */
static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem,
WCHAR *spec, DWORD flags)
{
WCHAR actualsource[MAX_PATH];
WCHAR *starPos;
WCHAR *questPos;
DWORD attribs;
/*
* Validate the source, expanding to full path ensuring it exists
*/
if (GetFullPathNameW(suppliedsource, MAX_PATH, actualsource, NULL) == 0) {
WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
return RC_INITERROR;
}
/* If full names required, convert to using the full path */
if (flags & OPT_FULL) {
lstrcpyW(suppliedsource, actualsource);
}
/*
* Work out the stem of the source
*/
/* If a directory is supplied, use that as-is (either fully or
partially qualified)
If a filename is supplied + a directory or drive path, use that
as-is
Otherwise
If no directory or path specified, add eg. C:
stem is Drive/Directory is bit up to last \ (or first :)
spec is bit after that */
starPos = wcschr(suppliedsource, '*');
questPos = wcschr(suppliedsource, '?');
if (starPos || questPos) {
attribs = 0x00; /* Ensures skips invalid or directory check below */
} else {
attribs = GetFileAttributesW(actualsource);
}
if (attribs == INVALID_FILE_ATTRIBUTES) {
XCOPY_FailMessage(GetLastError());
return RC_INITERROR;
/* Directory:
stem should be exactly as supplied plus a '\', unless it was
eg. C: in which case no slash required */
} else if (attribs & FILE_ATTRIBUTE_DIRECTORY) {
WCHAR lastChar;
WINE_TRACE("Directory supplied\n");
lstrcpyW(stem, suppliedsource);
lastChar = stem[lstrlenW(stem)-1];
if (lastChar != '\\' && lastChar != ':') {
lstrcatW(stem, wchr_slash);
if (!(slash = wcsrchr(new_path,'\\')) && ! (slash = wcsrchr(new_path,'/')))
{
ret = FALSE;
break;
}
lstrcpyW(spec, wchr_star);
/* File or wildcard search:
stem should be:
Up to and including last slash if directory path supplied
If c:filename supplied, just the c:
Otherwise stem should be the current drive letter + ':' */
} else {
WCHAR *lastDir;
WINE_TRACE("Filename supplied\n");
lastDir = wcsrchr(suppliedsource, '\\');
if (lastDir) {
lstrcpyW(stem, suppliedsource);
stem[(lastDir-suppliedsource) + 1] = 0x00;
lstrcpyW(spec, (lastDir+1));
} else if (suppliedsource[1] == ':') {
lstrcpyW(stem, suppliedsource);
stem[2] = 0x00;
lstrcpyW(spec, suppliedsource+2);
} else {
WCHAR curdir[MAXSTRING];
GetCurrentDirectoryW(sizeof(curdir)/sizeof(WCHAR), curdir);
stem[0] = curdir[0];
stem[1] = curdir[1];
stem[2] = 0x00;
lstrcpyW(spec, suppliedsource);
len = slash - new_path;
new_path[len] = 0;
if (!XCOPY_CreateDirectory(new_path))
{
ret = FALSE;
break;
}
new_path[len] = '\\';
}
return RC_OK;
HeapFree(GetProcessHeap(),0,new_path);
return ret;
}
/* =========================================================================
XCOPY_ProcessDestParm - Takes the supplied destination parameter, and
converts it into a stem
========================================================================= */
static int XCOPY_ProcessDestParm(WCHAR *supplieddestination, WCHAR *stem, WCHAR *spec,
WCHAR *srcspec, DWORD flags)
{
WCHAR actualdestination[MAX_PATH];
DWORD attribs;
BOOL isDir = FALSE;
* Process a single file from the /EXCLUDE: file list, building up a list
* of substrings to avoid copying
* Returns TRUE on any failure
* ========================================================================= */
static BOOL XCOPY_ProcessExcludeFile(WCHAR* filename, WCHAR* endOfName) {
/*
* Validate the source, expanding to full path ensuring it exists
*/
if (GetFullPathNameW(supplieddestination, MAX_PATH, actualdestination, NULL) == 0) {
WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
return RC_INITERROR;
WCHAR endChar = *endOfName;
WCHAR buffer[MAXSTRING];
FILE *inFile = NULL;
const WCHAR readTextMode[] = {'r', 't', 0};
/* Null terminate the filename (temporarily updates the filename hence
parms not const) */
*endOfName = 0x00;
/* Open the file */
inFile = _wfopen(filename, readTextMode);
if (inFile == NULL) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_OPENFAIL), filename);
*endOfName = endChar;
return TRUE;
}
/* Destination is either a directory or a file */
attribs = GetFileAttributesW(actualdestination);
/* Process line by line */
while (fgetws(buffer, sizeof(buffer)/sizeof(WCHAR), inFile) != NULL) {
EXCLUDELIST *thisEntry;
int length = lstrlenW(buffer);
if (attribs == INVALID_FILE_ATTRIBUTES) {
/* Strip CRLF */
buffer[length-1] = 0x00;
/* If /I supplied and wildcard copy, assume directory */
/* Also if destination ends with backslash */
if ((flags & OPT_ASSUMEDIR &&
(wcschr(srcspec, '?') || wcschr(srcspec, '*'))) ||
(supplieddestination[lstrlenW(supplieddestination)-1] == '\\')) {
/* If more than CRLF */
if (length > 1) {
thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(EXCLUDELIST));
thisEntry->next = excludeList;
excludeList = thisEntry;
thisEntry->name = HeapAlloc(GetProcessHeap(), 0,
(length * sizeof(WCHAR))+1);
lstrcpyW(thisEntry->name, buffer);
CharUpperBuffW(thisEntry->name, length);
WINE_TRACE("Read line : '%s'\n", wine_dbgstr_w(thisEntry->name));
}
}
isDir = TRUE;
/* See if EOF or error occurred */
if (!feof(inFile)) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_READFAIL), filename);
*endOfName = endChar;
return TRUE;
}
} else {
DWORD count;
char answer[10] = "";
WCHAR fileChar[2];
WCHAR dirChar[2];
/* Revert the input string to original form, and cleanup + return */
*endOfName = endChar;
fclose(inFile);
return FALSE;
}
/* Read the F and D characters from the resource file */
wcscpy(fileChar, XCOPY_LoadMessage(STRING_FILE_CHAR));
wcscpy(dirChar, XCOPY_LoadMessage(STRING_DIR_CHAR));
/* =========================================================================
* Process the /EXCLUDE: file list, building up a list of substrings to
* avoid copying
* Returns TRUE on any failure
* ========================================================================= */
static BOOL XCOPY_ProcessExcludeList(WCHAR* parms) {
while (answer[0] != fileChar[0] && answer[0] != dirChar[0]) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_QISDIR), supplieddestination);
WCHAR *filenameStart = parms;
ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer), &count, NULL);
WINE_TRACE("User answer %c\n", answer[0]);
WINE_TRACE("/EXCLUDE parms: '%s'\n", wine_dbgstr_w(parms));
excludeList = NULL;
answer[0] = toupper(answer[0]);
}
while (*parms && *parms != ' ' && *parms != '/') {
if (answer[0] == dirChar[0]) {
isDir = TRUE;
} else {
isDir = FALSE;
/* If found '+' then process the file found so far */
if (*parms == '+') {
if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
return TRUE;
}
filenameStart = parms+1;
}
} else {
isDir = (attribs & FILE_ATTRIBUTE_DIRECTORY);
parms++;
}
if (isDir) {
lstrcpyW(stem, actualdestination);
*spec = 0x00;
/* Ensure ends with a '\' */
if (stem[lstrlenW(stem)-1] != '\\') {
lstrcatW(stem, wchr_slash);
if (filenameStart != parms) {
if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
return TRUE;
}
} else {
WCHAR drive[MAX_PATH];
WCHAR dir[MAX_PATH];
WCHAR fname[MAX_PATH];
WCHAR ext[MAX_PATH];
_wsplitpath(actualdestination, drive, dir, fname, ext);
lstrcpyW(stem, drive);
lstrcatW(stem, dir);
lstrcpyW(spec, fname);
lstrcatW(spec, ext);
}
return RC_OK;
return FALSE;
}
/* =========================================================================
......@@ -842,300 +594,533 @@ static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
h = FindFirstFileW(inputpath, finddata);
while (h != INVALID_HANDLE_VALUE && findres) {
/* Only looking for dirs */
if ((finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(lstrcmpW(finddata->cFileName, wchr_dot) != 0) &&
(lstrcmpW(finddata->cFileName, wchr_dotdot) != 0)) {
/* Only looking for dirs */
if ((finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(lstrcmpW(finddata->cFileName, wchr_dot) != 0) &&
(lstrcmpW(finddata->cFileName, wchr_dotdot) != 0)) {
WINE_TRACE("Handling subdir: %s\n", wine_dbgstr_w(finddata->cFileName));
/* Make up recursive information */
lstrcpyW(inputpath, srcstem);
lstrcatW(inputpath, finddata->cFileName);
lstrcatW(inputpath, wchr_slash);
lstrcpyW(outputpath, deststem);
if (*destspec == 0x00) {
lstrcatW(outputpath, finddata->cFileName);
/* If /E is supplied, create the directory now */
if ((flags & OPT_EMPTYDIR) &&
!(flags & OPT_SIMULATE))
XCOPY_CreateDirectory(outputpath);
lstrcatW(outputpath, wchr_slash);
}
XCOPY_DoCopy(inputpath, srcspec, outputpath, destspec, flags);
}
/* Find next one */
findres = FindNextFileW(h, finddata);
}
}
cleanup:
/* free up memory */
HeapFree(GetProcessHeap(), 0, finddata);
HeapFree(GetProcessHeap(), 0, inputpath);
HeapFree(GetProcessHeap(), 0, outputpath);
return ret;
}
/* =========================================================================
XCOPY_ParseCommandLine - Parses the command line
========================================================================= */
static BOOL is_whitespace(WCHAR c)
{
return c == ' ' || c == '\t';
}
static WCHAR *skip_whitespace(WCHAR *p)
{
for (; *p && is_whitespace(*p); p++);
return p;
}
/* Windows XCOPY uses a simplified command line parsing algorithm
that lacks the escaped-quote logic of build_argv(), because
literal double quotes are illegal in any of its arguments.
Example: 'XCOPY "c:\DIR A" "c:DIR B\"' is OK. */
static int find_end_of_word(const WCHAR *word, WCHAR **end)
{
BOOL in_quotes = 0;
const WCHAR *ptr = word;
for (;;) {
for (; *ptr != '\0' && *ptr != '"' &&
(in_quotes || !is_whitespace(*ptr)); ptr++);
if (*ptr == '"') {
in_quotes = !in_quotes;
ptr++;
}
/* Odd number of double quotes is illegal for XCOPY */
if (in_quotes && *ptr == '\0')
return RC_INITERROR;
if (*ptr == '\0' || (!in_quotes && is_whitespace(*ptr)))
break;
}
*end = (WCHAR*)ptr;
return RC_OK;
}
/* Remove all double quotes from a word */
static void strip_quotes(WCHAR *word, WCHAR **end)
{
WCHAR *rp, *wp;
for (rp = word, wp = word; *rp != '\0'; rp++) {
if (*rp == '"')
continue;
if (wp < rp)
*wp = *rp;
wp++;
}
*wp = '\0';
*end = wp;
}
static int XCOPY_ParseCommandLine(WCHAR *suppliedsource,
WCHAR *supplieddestination, DWORD *pflags)
{
const WCHAR EXCLUDE[] = {'E', 'X', 'C', 'L', 'U', 'D', 'E', ':', 0};
DWORD flags = *pflags;
WCHAR *cmdline, *word, *end, *next;
int rc = RC_INITERROR;
cmdline = _wcsdup(GetCommandLineW());
if (cmdline == NULL)
return rc;
/* Skip first arg, which is the program name */
if ((rc = find_end_of_word(cmdline, &word)) != RC_OK)
goto out;
word = skip_whitespace(word);
while (*word)
{
WCHAR first;
if ((rc = find_end_of_word(word, &end)) != RC_OK)
goto out;
next = skip_whitespace(end);
first = word[0];
*end = '\0';
strip_quotes(word, &end);
WINE_TRACE("Processing Arg: '%s'\n", wine_dbgstr_w(word));
/* First non-switch parameter is source, second is destination */
if (first != '/') {
if (suppliedsource[0] == 0x00) {
lstrcpyW(suppliedsource, word);
} else if (supplieddestination[0] == 0x00) {
lstrcpyW(supplieddestination, word);
} else {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARMS));
goto out;
}
} else {
/* Process all the switch options
Note: Windows docs say /P prompts when dest is created
but tests show it is done for each src file
regardless of the destination */
switch (toupper(word[1])) {
case 'I': flags |= OPT_ASSUMEDIR; break;
case 'S': flags |= OPT_RECURSIVE; break;
case 'Q': flags |= OPT_QUIET; break;
case 'F': flags |= OPT_FULL; break;
case 'L': flags |= OPT_SIMULATE; break;
case 'W': flags |= OPT_PAUSE; break;
case 'T': flags |= OPT_NOCOPY | OPT_RECURSIVE; break;
case 'Y': flags |= OPT_NOPROMPT; break;
case 'N': flags |= OPT_SHORTNAME; break;
case 'U': flags |= OPT_MUSTEXIST; break;
case 'R': flags |= OPT_REPLACEREAD; break;
case 'H': flags |= OPT_COPYHIDSYS; break;
case 'C': flags |= OPT_IGNOREERRORS; break;
case 'P': flags |= OPT_SRCPROMPT; break;
case 'A': flags |= OPT_ARCHIVEONLY; break;
case 'M': flags |= OPT_ARCHIVEONLY |
OPT_REMOVEARCH; break;
/* E can be /E or /EXCLUDE */
case 'E': if (CompareStringW(LOCALE_USER_DEFAULT,
NORM_IGNORECASE | SORT_STRINGSORT,
&word[1], 8,
EXCLUDE, -1) == 2) {
if (XCOPY_ProcessExcludeList(&word[9])) {
XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
goto out;
} else flags |= OPT_EXCLUDELIST;
} else flags |= OPT_EMPTYDIR | OPT_RECURSIVE;
break;
/* D can be /D or /D: */
case 'D': if (word[2]==':' && isdigit(word[3])) {
SYSTEMTIME st;
WCHAR *pos = &word[3];
BOOL isError = FALSE;
memset(&st, 0x00, sizeof(st));
/* Parse the arg : Month */
st.wMonth = _wtol(pos);
while (*pos && isdigit(*pos)) pos++;
if (*pos++ != '-') isError = TRUE;
/* Parse the arg : Day */
if (!isError) {
st.wDay = _wtol(pos);
while (*pos && isdigit(*pos)) pos++;
if (*pos++ != '-') isError = TRUE;
}
WINE_TRACE("Handling subdir: %s\n", wine_dbgstr_w(finddata->cFileName));
/* Parse the arg : Day */
if (!isError) {
st.wYear = _wtol(pos);
while (*pos && isdigit(*pos)) pos++;
if (st.wYear < 100) st.wYear+=2000;
}
/* Make up recursive information */
lstrcpyW(inputpath, srcstem);
lstrcatW(inputpath, finddata->cFileName);
lstrcatW(inputpath, wchr_slash);
if (!isError && SystemTimeToFileTime(&st, &dateRange)) {
SYSTEMTIME st;
WCHAR datestring[32], timestring[32];
lstrcpyW(outputpath, deststem);
if (*destspec == 0x00) {
lstrcatW(outputpath, finddata->cFileName);
flags |= OPT_DATERANGE;
/* If /E is supplied, create the directory now */
if ((flags & OPT_EMPTYDIR) &&
!(flags & OPT_SIMULATE))
XCOPY_CreateDirectory(outputpath);
/* Debug info: */
FileTimeToSystemTime (&dateRange, &st);
GetDateFormatW(0, DATE_SHORTDATE, &st, NULL, datestring,
sizeof(datestring)/sizeof(WCHAR));
GetTimeFormatW(0, TIME_NOSECONDS, &st,
NULL, timestring, sizeof(timestring)/sizeof(WCHAR));
lstrcatW(outputpath, wchr_slash);
}
WINE_TRACE("Date being used is: %s %s\n",
wine_dbgstr_w(datestring), wine_dbgstr_w(timestring));
} else {
XCOPY_FailMessage(ERROR_INVALID_PARAMETER);
goto out;
}
} else {
flags |= OPT_DATENEWER;
}
break;
XCOPY_DoCopy(inputpath, srcspec, outputpath, destspec, flags);
case '-': if (toupper(word[2])=='Y')
flags &= ~OPT_NOPROMPT; break;
case '?': XCOPY_wprintf(XCOPY_LoadMessage(STRING_HELP));
rc = RC_HELP;
goto out;
default:
WINE_TRACE("Unhandled parameter '%s'\n", wine_dbgstr_w(word));
XCOPY_wprintf(XCOPY_LoadMessage(STRING_INVPARM), word);
goto out;
}
/* Find next one */
findres = FindNextFileW(h, finddata);
}
word = next;
}
cleanup:
/* Default the destination if not supplied */
if (supplieddestination[0] == 0x00)
lstrcpyW(supplieddestination, wchr_dot);
/* free up memory */
HeapFree(GetProcessHeap(), 0, finddata);
HeapFree(GetProcessHeap(), 0, inputpath);
HeapFree(GetProcessHeap(), 0, outputpath);
*pflags = flags;
rc = RC_OK;
return ret;
out:
free(cmdline);
return rc;
}
/* =========================================================================
* Routine copied from cmd.exe md command -
* This works recursively. so creating dir1\dir2\dir3 will create dir1 and
* dir2 if they do not already exist.
* ========================================================================= */
static BOOL XCOPY_CreateDirectory(const WCHAR* path)
XCOPY_ProcessSourceParm - Takes the supplied source parameter, and
converts it into a stem and a filespec
========================================================================= */
static int XCOPY_ProcessSourceParm(WCHAR *suppliedsource, WCHAR *stem,
WCHAR *spec, DWORD flags)
{
int len;
WCHAR *new_path;
BOOL ret = TRUE;
new_path = HeapAlloc(GetProcessHeap(),0, sizeof(WCHAR) * (lstrlenW(path)+1));
lstrcpyW(new_path,path);
WCHAR actualsource[MAX_PATH];
WCHAR *starPos;
WCHAR *questPos;
DWORD attribs;
while ((len = lstrlenW(new_path)) && new_path[len - 1] == '\\')
new_path[len - 1] = 0;
/*
* Validate the source, expanding to full path ensuring it exists
*/
if (GetFullPathNameW(suppliedsource, MAX_PATH, actualsource, NULL) == 0) {
WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
return RC_INITERROR;
}
while (!CreateDirectoryW(new_path,NULL))
{
WCHAR *slash;
DWORD last_error = GetLastError();
if (last_error == ERROR_ALREADY_EXISTS)
break;
/* If full names required, convert to using the full path */
if (flags & OPT_FULL) {
lstrcpyW(suppliedsource, actualsource);
}
if (last_error != ERROR_PATH_NOT_FOUND)
{
ret = FALSE;
break;
}
/*
* Work out the stem of the source
*/
if (!(slash = wcsrchr(new_path,'\\')) && ! (slash = wcsrchr(new_path,'/')))
{
ret = FALSE;
break;
}
/* If a directory is supplied, use that as-is (either fully or
partially qualified)
If a filename is supplied + a directory or drive path, use that
as-is
Otherwise
If no directory or path specified, add eg. C:
stem is Drive/Directory is bit up to last \ (or first :)
spec is bit after that */
len = slash - new_path;
new_path[len] = 0;
if (!XCOPY_CreateDirectory(new_path))
{
ret = FALSE;
break;
}
new_path[len] = '\\';
starPos = wcschr(suppliedsource, '*');
questPos = wcschr(suppliedsource, '?');
if (starPos || questPos) {
attribs = 0x00; /* Ensures skips invalid or directory check below */
} else {
attribs = GetFileAttributesW(actualsource);
}
HeapFree(GetProcessHeap(),0,new_path);
return ret;
}
/* =========================================================================
* Process the /EXCLUDE: file list, building up a list of substrings to
* avoid copying
* Returns TRUE on any failure
* ========================================================================= */
static BOOL XCOPY_ProcessExcludeList(WCHAR* parms) {
if (attribs == INVALID_FILE_ATTRIBUTES) {
XCOPY_FailMessage(GetLastError());
return RC_INITERROR;
WCHAR *filenameStart = parms;
/* Directory:
stem should be exactly as supplied plus a '\', unless it was
eg. C: in which case no slash required */
} else if (attribs & FILE_ATTRIBUTE_DIRECTORY) {
WCHAR lastChar;
WINE_TRACE("/EXCLUDE parms: '%s'\n", wine_dbgstr_w(parms));
excludeList = NULL;
WINE_TRACE("Directory supplied\n");
lstrcpyW(stem, suppliedsource);
lastChar = stem[lstrlenW(stem)-1];
if (lastChar != '\\' && lastChar != ':') {
lstrcatW(stem, wchr_slash);
}
lstrcpyW(spec, wchr_star);
while (*parms && *parms != ' ' && *parms != '/') {
/* File or wildcard search:
stem should be:
Up to and including last slash if directory path supplied
If c:filename supplied, just the c:
Otherwise stem should be the current drive letter + ':' */
} else {
WCHAR *lastDir;
/* If found '+' then process the file found so far */
if (*parms == '+') {
if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
return TRUE;
}
filenameStart = parms+1;
}
parms++;
}
WINE_TRACE("Filename supplied\n");
lastDir = wcsrchr(suppliedsource, '\\');
if (filenameStart != parms) {
if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
return TRUE;
if (lastDir) {
lstrcpyW(stem, suppliedsource);
stem[(lastDir-suppliedsource) + 1] = 0x00;
lstrcpyW(spec, (lastDir+1));
} else if (suppliedsource[1] == ':') {
lstrcpyW(stem, suppliedsource);
stem[2] = 0x00;
lstrcpyW(spec, suppliedsource+2);
} else {
WCHAR curdir[MAXSTRING];
GetCurrentDirectoryW(sizeof(curdir)/sizeof(WCHAR), curdir);
stem[0] = curdir[0];
stem[1] = curdir[1];
stem[2] = 0x00;
lstrcpyW(spec, suppliedsource);
}
}
return FALSE;
return RC_OK;
}
/* =========================================================================
* Process a single file from the /EXCLUDE: file list, building up a list
* of substrings to avoid copying
* Returns TRUE on any failure
* ========================================================================= */
static BOOL XCOPY_ProcessExcludeFile(WCHAR* filename, WCHAR* endOfName) {
WCHAR endChar = *endOfName;
WCHAR buffer[MAXSTRING];
FILE *inFile = NULL;
const WCHAR readTextMode[] = {'r', 't', 0};
/* Null terminate the filename (temporarily updates the filename hence
parms not const) */
*endOfName = 0x00;
XCOPY_ProcessDestParm - Takes the supplied destination parameter, and
converts it into a stem
========================================================================= */
static int XCOPY_ProcessDestParm(WCHAR *supplieddestination, WCHAR *stem, WCHAR *spec,
WCHAR *srcspec, DWORD flags)
{
WCHAR actualdestination[MAX_PATH];
DWORD attribs;
BOOL isDir = FALSE;
/* Open the file */
inFile = _wfopen(filename, readTextMode);
if (inFile == NULL) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_OPENFAIL), filename);
*endOfName = endChar;
return TRUE;
/*
* Validate the source, expanding to full path ensuring it exists
*/
if (GetFullPathNameW(supplieddestination, MAX_PATH, actualdestination, NULL) == 0) {
WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
return RC_INITERROR;
}
/* Process line by line */
while (fgetws(buffer, sizeof(buffer)/sizeof(WCHAR), inFile) != NULL) {
EXCLUDELIST *thisEntry;
int length = lstrlenW(buffer);
/* Destination is either a directory or a file */
attribs = GetFileAttributesW(actualdestination);
/* Strip CRLF */
buffer[length-1] = 0x00;
if (attribs == INVALID_FILE_ATTRIBUTES) {
/* If more than CRLF */
if (length > 1) {
thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(EXCLUDELIST));
thisEntry->next = excludeList;
excludeList = thisEntry;
thisEntry->name = HeapAlloc(GetProcessHeap(), 0,
(length * sizeof(WCHAR))+1);
lstrcpyW(thisEntry->name, buffer);
CharUpperBuffW(thisEntry->name, length);
WINE_TRACE("Read line : '%s'\n", wine_dbgstr_w(thisEntry->name));
}
}
/* If /I supplied and wildcard copy, assume directory */
/* Also if destination ends with backslash */
if ((flags & OPT_ASSUMEDIR &&
(wcschr(srcspec, '?') || wcschr(srcspec, '*'))) ||
(supplieddestination[lstrlenW(supplieddestination)-1] == '\\')) {
/* See if EOF or error occurred */
if (!feof(inFile)) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_READFAIL), filename);
*endOfName = endChar;
return TRUE;
}
isDir = TRUE;
/* Revert the input string to original form, and cleanup + return */
*endOfName = endChar;
fclose(inFile);
return FALSE;
}
} else {
DWORD count;
char answer[10] = "";
WCHAR fileChar[2];
WCHAR dirChar[2];
/* =========================================================================
* Load a string from the resource file, handling any error
* Returns string retrieved from resource file
* ========================================================================= */
static WCHAR *XCOPY_LoadMessage(UINT id) {
static WCHAR msg[MAXSTRING];
const WCHAR failedMsg[] = {'F', 'a', 'i', 'l', 'e', 'd', '!', 0};
/* Read the F and D characters from the resource file */
wcscpy(fileChar, XCOPY_LoadMessage(STRING_FILE_CHAR));
wcscpy(dirChar, XCOPY_LoadMessage(STRING_DIR_CHAR));
if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
WINE_FIXME("LoadString failed with %d\n", GetLastError());
lstrcpyW(msg, failedMsg);
while (answer[0] != fileChar[0] && answer[0] != dirChar[0]) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_QISDIR), supplieddestination);
ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer), &count, NULL);
WINE_TRACE("User answer %c\n", answer[0]);
answer[0] = toupper(answer[0]);
}
if (answer[0] == dirChar[0]) {
isDir = TRUE;
} else {
isDir = FALSE;
}
}
} else {
isDir = (attribs & FILE_ATTRIBUTE_DIRECTORY);
}
return msg;
}
/* =========================================================================
* Load a string for a system error and writes it to the screen
* Returns string retrieved from resource file
* ========================================================================= */
static void XCOPY_FailMessage(DWORD err) {
LPWSTR lpMsgBuf;
int status;
if (isDir) {
lstrcpyW(stem, actualdestination);
*spec = 0x00;
/* Ensure ends with a '\' */
if (stem[lstrlenW(stem)-1] != '\\') {
lstrcatW(stem, wchr_slash);
}
status = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err, 0,
(LPWSTR) &lpMsgBuf, 0, NULL);
if (!status) {
WINE_FIXME("FIXME: Cannot display message for error %d, status %d\n",
err, GetLastError());
} else {
const WCHAR infostr[] = {'%', 's', '\n', 0};
XCOPY_wprintf(infostr, lpMsgBuf);
LocalFree ((HLOCAL)lpMsgBuf);
WCHAR drive[MAX_PATH];
WCHAR dir[MAX_PATH];
WCHAR fname[MAX_PATH];
WCHAR ext[MAX_PATH];
_wsplitpath(actualdestination, drive, dir, fname, ext);
lstrcpyW(stem, drive);
lstrcatW(stem, dir);
lstrcpyW(spec, fname);
lstrcatW(spec, ext);
}
return RC_OK;
}
/* =========================================================================
* Output a formatted unicode string. Ideally this will go to the console
* and hence required WriteConsoleW to output it, however if file i/o is
* redirected, it needs to be WriteFile'd using OEM (not ANSI) format
* ========================================================================= */
int XCOPY_wprintf(const WCHAR *format, ...) {
main - Main entrypoint for the xcopy command
static WCHAR *output_bufW = NULL;
static char *output_bufA = NULL;
static BOOL toConsole = TRUE;
static BOOL traceOutput = FALSE;
#define MAX_WRITECONSOLE_SIZE 65535
Processes the args, and drives the actual copying
========================================================================= */
int wmain (int argc, WCHAR *argvW[])
{
int rc = 0;
WCHAR suppliedsource[MAX_PATH] = {0}; /* As supplied on the cmd line */
WCHAR supplieddestination[MAX_PATH] = {0};
WCHAR sourcestem[MAX_PATH] = {0}; /* Stem of source */
WCHAR sourcespec[MAX_PATH] = {0}; /* Filespec of source */
WCHAR destinationstem[MAX_PATH] = {0}; /* Stem of destination */
WCHAR destinationspec[MAX_PATH] = {0}; /* Filespec of destination */
WCHAR copyCmd[MAXSTRING]; /* COPYCMD env var */
DWORD flags = 0; /* Option flags */
const WCHAR PROMPTSTR1[] = {'/', 'Y', 0};
const WCHAR PROMPTSTR2[] = {'/', 'y', 0};
const WCHAR COPYCMD[] = {'C', 'O', 'P', 'Y', 'C', 'M', 'D', 0};
va_list parms;
DWORD nOut;
int len;
DWORD res = 0;
/* Preinitialize flags based on COPYCMD */
if (GetEnvironmentVariableW(COPYCMD, copyCmd, MAXSTRING)) {
if (wcsstr(copyCmd, PROMPTSTR1) != NULL ||
wcsstr(copyCmd, PROMPTSTR2) != NULL) {
flags |= OPT_NOPROMPT;
}
}
/* FIXME: On UNIX, files starting with a '.' are treated as hidden under
wine, but on windows these can be normal files. At least one installer
uses files such as .packlist and (validly) expects them to be copied.
Under wine, if we do not copy hidden files by default then they get
lose */
flags |= OPT_COPYHIDSYS;
/*
* Allocate buffer to use when writing to console
* Note: Not freed - memory will be allocated once and released when
* xcopy ends
* Parse the command line
*/
if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
MAX_WRITECONSOLE_SIZE);
if (!output_bufW) {
WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
return 0;
if ((rc = XCOPY_ParseCommandLine(suppliedsource, supplieddestination,
&flags)) != RC_OK) {
if (rc == RC_HELP)
return RC_OK;
else
return rc;
}
va_start(parms, format);
len = vsnprintfW(output_bufW, MAX_WRITECONSOLE_SIZE/sizeof(WCHAR), format, parms);
va_end(parms);
if (len < 0) {
WINE_FIXME("String too long.\n");
return 0;
}
/* Trace out the supplied information */
WINE_TRACE("Supplied parameters:\n");
WINE_TRACE("Source : '%s'\n", wine_dbgstr_w(suppliedsource));
WINE_TRACE("Destination : '%s'\n", wine_dbgstr_w(supplieddestination));
/* Try to write as unicode whenever we think it's a console */
if (toConsole) {
res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
output_bufW, len, &nOut, NULL);
}
/* Extract required information from source specification */
rc = XCOPY_ProcessSourceParm(suppliedsource, sourcestem, sourcespec, flags);
if (rc != RC_OK) return rc;
/* If writing to console has failed (ever) we assume it's file
i/o so convert to OEM codepage and output */
if (!res) {
BOOL usedDefaultChar = FALSE;
DWORD convertedChars;
/* Extract required information from destination specification */
rc = XCOPY_ProcessDestParm(supplieddestination, destinationstem,
destinationspec, sourcespec, flags);
if (rc != RC_OK) return rc;
toConsole = FALSE;
/* Trace out the resulting information */
WINE_TRACE("Resolved parameters:\n");
WINE_TRACE("Source Stem : '%s'\n", wine_dbgstr_w(sourcestem));
WINE_TRACE("Source Spec : '%s'\n", wine_dbgstr_w(sourcespec));
WINE_TRACE("Dest Stem : '%s'\n", wine_dbgstr_w(destinationstem));
WINE_TRACE("Dest Spec : '%s'\n", wine_dbgstr_w(destinationspec));
/*
* Allocate buffer to use when writing to file. Not freed, as above
*/
if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
MAX_WRITECONSOLE_SIZE);
if (!output_bufA) {
WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
return 0;
}
/* Pause if necessary */
if (flags & OPT_PAUSE) {
DWORD count;
char pausestr[10];
/* Convert to OEM, then output */
convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
len, output_bufA, MAX_WRITECONSOLE_SIZE,
"?", &usedDefaultChar);
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
&nOut, FALSE);
XCOPY_wprintf(XCOPY_LoadMessage(STRING_PAUSE));
ReadFile (GetStdHandle(STD_INPUT_HANDLE), pausestr, sizeof(pausestr),
&count, NULL);
}
/* Trace whether screen or console */
if (!traceOutput) {
WINE_TRACE("Writing to console? (%d)\n", toConsole);
traceOutput = TRUE;
/* Now do the hard work... */
rc = XCOPY_DoCopy(sourcestem, sourcespec,
destinationstem, destinationspec,
flags);
/* Clear up exclude list allocated memory */
while (excludeList) {
EXCLUDELIST *pos = excludeList;
excludeList = excludeList -> next;
HeapFree(GetProcessHeap(), 0, pos->name);
HeapFree(GetProcessHeap(), 0, pos);
}
return nOut;
/* Finished - print trailer and exit */
if (flags & OPT_SIMULATE) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_SIMCOPY), filesCopied);
} else if (!(flags & OPT_NOCOPY)) {
XCOPY_wprintf(XCOPY_LoadMessage(STRING_COPY), filesCopied);
}
if (rc == RC_OK && filesCopied == 0) rc = RC_NOFILES;
return rc;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment