Author: akhaldi
Date: Sun Feb 12 13:33:30 2012
New Revision: 55560
URL:
http://svn.reactos.org/svn/reactos?rev=55560&view=rev
Log:
[XCOPY]
* Sync to Wine 1.3.37.
Modified:
trunk/reactos/base/applications/cmdutils/xcopy/xcopy.c
trunk/reactos/media/doc/README.WINE
Modified: trunk/reactos/base/applications/cmdutils/xcopy/xcopy.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/base/applications/cmdutils…
==============================================================================
--- trunk/reactos/base/applications/cmdutils/xcopy/xcopy.c [iso-8859-1] (original)
+++ trunk/reactos/base/applications/cmdutils/xcopy/xcopy.c [iso-8859-1] Sun Feb 12
13:33:30 2012
@@ -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_dot[] = {'.', 0};
static const WCHAR wchr_dotdot[] = {'.', '.', 0};
-/* Constants (Mostly for widechars) */
-
/* To minimize stack usage during recursion, some temporary variables
made global */
@@ -92,105 +74,570 @@
/* =========================================================================
- main - Main entrypoint for the xcopy command
-
- Processes the args, and drives the actual copying
+ * 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};
+
+ if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
+ WINE_FIXME("LoadString failed with %d\n", GetLastError());
+ lstrcpyW(msg, failedMsg);
+ }
+ return msg;
+}
+
+/* =========================================================================
+ * 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 __cdecl 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
+
+ __ms_va_list parms;
+ DWORD nOut;
+ int len;
+ DWORD res = 0;
+
+ /*
+ * Allocate buffer to use when writing to console
+ * Note: Not freed - memory will be allocated once and released when
+ * xcopy ends
+ */
+
+ 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;
+ }
+
+ __ms_va_start(parms, format);
+ SetLastError(NO_ERROR);
+ len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_bufW,
+ MAX_WRITECONSOLE_SIZE/sizeof(*output_bufW), &parms);
+ __ms_va_end(parms);
+ if (len == 0 && GetLastError() != NO_ERROR) {
+ WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(),
wine_dbgstr_w(format));
+ return 0;
+ }
+
+ /* 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);
+ }
+
+ /* 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;
+
+ toConsole = FALSE;
+
+ /*
+ * 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;
+ }
+
+ /* 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);
+ }
+
+ /* Trace whether screen or console */
+ if (!traceOutput) {
+ WINE_TRACE("Writing to console? (%d)\n", toConsole);
+ traceOutput = TRUE;
+ }
+ return nOut;
+}
+
+/* =========================================================================
+ * 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;
+
+ 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[] = {'%', '1', '\n', 0};
+ XCOPY_wprintf(infostr, lpMsgBuf);
+ LocalFree ((HLOCAL)lpMsgBuf);
+ }
+}
+
+
+/* =========================================================================
+ * 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)
+{
+ int len;
+ WCHAR *new_path;
+ BOOL ret = TRUE;
+
+ new_path = HeapAlloc(GetProcessHeap(),0, sizeof(WCHAR) * (lstrlenW(path)+1));
+ lstrcpyW(new_path,path);
+
+ while ((len = lstrlenW(new_path)) && new_path[len - 1] == '\\')
+ new_path[len - 1] = 0;
+
+ while (!CreateDirectoryW(new_path,NULL))
+ {
+ WCHAR *slash;
+ DWORD last_error = GetLastError();
+ if (last_error == ERROR_ALREADY_EXISTS)
+ break;
+
+ if (last_error != ERROR_PATH_NOT_FOUND)
+ {
+ ret = FALSE;
+ break;
+ }
+
+ if (!(slash = wcsrchr(new_path,'\\')) && ! (slash =
wcsrchr(new_path,'/')))
+ {
+ ret = FALSE;
+ break;
+ }
+
+ len = slash - new_path;
+ new_path[len] = 0;
+ if (!XCOPY_CreateDirectory(new_path))
+ {
+ ret = FALSE;
+ break;
+ }
+ new_path[len] = '\\';
+ }
+ HeapFree(GetProcessHeap(),0,new_path);
+ return ret;
+}
+
+/* =========================================================================
+ * 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;
+
+ /* Open the file */
+ inFile = _wfopen(filename, readTextMode);
+ if (inFile == NULL) {
+ XCOPY_wprintf(XCOPY_LoadMessage(STRING_OPENFAIL), filename);
+ *endOfName = endChar;
+ return TRUE;
+ }
+
+ /* Process line by line */
+ while (fgetws(buffer, sizeof(buffer)/sizeof(WCHAR), inFile) != NULL) {
+ EXCLUDELIST *thisEntry;
+ int length = lstrlenW(buffer);
+
+ /* Strip CRLF */
+ buffer[length-1] = 0x00;
+
+ /* 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));
+ }
+ }
+
+ /* See if EOF or error occurred */
+ if (!feof(inFile)) {
+ XCOPY_wprintf(XCOPY_LoadMessage(STRING_READFAIL), filename);
+ *endOfName = endChar;
+ return TRUE;
+ }
+
+ /* Revert the input string to original form, and cleanup + return */
+ *endOfName = endChar;
+ fclose(inFile);
+ return FALSE;
+}
+
+/* =========================================================================
+ * 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) {
+
+ WCHAR *filenameStart = parms;
+
+ WINE_TRACE("/EXCLUDE parms: '%s'\n", wine_dbgstr_w(parms));
+ excludeList = NULL;
+
+ while (*parms && *parms != ' ' && *parms != '/') {
+
+ /* If found '+' then process the file found so far */
+ if (*parms == '+') {
+ if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
+ return TRUE;
+ }
+ filenameStart = parms+1;
+ }
+ parms++;
+ }
+
+ if (filenameStart != parms) {
+ if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* =========================================================================
+ XCOPY_DoCopy - Recursive function to copy files based on input parms
+ of a stem and a spec
+
+ This works by using FindFirstFile supplying the source stem and spec.
+ If results are found, any non-directory ones are processed
+ Then, if /S or /E is supplied, another search is made just for
+ directories, and this function is called again for that directory
+
========================================================================= */
-int wmain (int argc, WCHAR *argvW[])
+static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
+ WCHAR *deststem, WCHAR *destspec,
+ DWORD flags)
{
- 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};
-
- /* 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;
-
- /*
- * Parse the command line
- */
- 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;
-
- /* Extract required information from destination specification */
- rc = XCOPY_ProcessDestParm(supplieddestination, destinationstem,
- destinationspec, sourcespec, flags);
- if (rc != RC_OK) return rc;
-
- /* 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));
-
- /* Pause if necessary */
- if (flags & OPT_PAUSE) {
- DWORD count;
- char pausestr[10];
-
- XCOPY_wprintf(XCOPY_LoadMessage(STRING_PAUSE));
- ReadFile (GetStdHandle(STD_INPUT_HANDLE), pausestr, sizeof(pausestr),
- &count, NULL);
- }
-
- /* 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);
- }
-
- /* 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;
-
-}
+ WIN32_FIND_DATAW *finddata;
+ HANDLE h;
+ BOOL findres = TRUE;
+ WCHAR *inputpath, *outputpath;
+ BOOL copiedFile = FALSE;
+ DWORD destAttribs, srcAttribs;
+ BOOL skipFile;
+ int ret = 0;
+
+ /* Allocate some working memory on heap to minimize footprint */
+ finddata = HeapAlloc(GetProcessHeap(), 0, sizeof(WIN32_FIND_DATAW));
+ inputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
+ outputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
+
+ /* Build the search info into a single parm */
+ lstrcpyW(inputpath, srcstem);
+ lstrcatW(inputpath, srcspec);
+
+ /* Search 1 - Look for matching files */
+ h = FindFirstFileW(inputpath, finddata);
+ while (h != INVALID_HANDLE_VALUE && findres) {
+
+ skipFile = FALSE;
+
+ /* Ignore . and .. */
+ if (lstrcmpW(finddata->cFileName, wchr_dot)==0 ||
+ lstrcmpW(finddata->cFileName, wchr_dotdot)==0 ||
+ finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+ WINE_TRACE("Skipping directory, . or .. (%s)\n",
wine_dbgstr_w(finddata->cFileName));
+ } else {
+
+ /* Get the filename information */
+ lstrcpyW(copyFrom, srcstem);
+ if (flags & OPT_SHORTNAME) {
+ lstrcatW(copyFrom, finddata->cAlternateFileName);
+ } else {
+ lstrcatW(copyFrom, finddata->cFileName);
+ }
+
+ lstrcpyW(copyTo, deststem);
+ if (*destspec == 0x00) {
+ if (flags & OPT_SHORTNAME) {
+ lstrcatW(copyTo, finddata->cAlternateFileName);
+ } else {
+ lstrcatW(copyTo, finddata->cFileName);
+ }
+ } else {
+ lstrcatW(copyTo, destspec);
+ }
+
+ /* Do the copy */
+ WINE_TRACE("ACTION: Copy '%s' -> '%s'\n",
wine_dbgstr_w(copyFrom),
+ wine_dbgstr_w(copyTo));
+ if (!copiedFile && !(flags & OPT_SIMULATE))
XCOPY_CreateDirectory(deststem);
+
+ /* See if allowed to copy it */
+ srcAttribs = GetFileAttributesW(copyFrom);
+ WINE_TRACE("Source attribs: %d\n", srcAttribs);
+
+ if ((srcAttribs & FILE_ATTRIBUTE_HIDDEN) ||
+ (srcAttribs & FILE_ATTRIBUTE_SYSTEM)) {
+
+ if (!(flags & OPT_COPYHIDSYS)) {
+ skipFile = TRUE;
+ }
+ }
+
+ if (!(srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
+ (flags & OPT_ARCHIVEONLY)) {
+ skipFile = TRUE;
+ }
+
+ /* See if file exists */
+ destAttribs = GetFileAttributesW(copyTo);
+ WINE_TRACE("Dest attribs: %d\n", srcAttribs);
+
+ /* Check date ranges if a destination file already exists */
+ if (!skipFile && (flags & OPT_DATERANGE) &&
+ (CompareFileTime(&finddata->ftLastWriteTime, &dateRange) <
0)) {
+ WINE_TRACE("Skipping file as modified date too old\n");
+ skipFile = TRUE;
+ }
+
+ /* If just /D supplied, only overwrite if src newer than dest */
+ if (!skipFile && (flags & OPT_DATENEWER) &&
+ (destAttribs != INVALID_FILE_ATTRIBUTES)) {
+ HANDLE h = CreateFileW(copyTo, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (h != INVALID_HANDLE_VALUE) {
+ FILETIME writeTime;
+ GetFileTime(h, NULL, NULL, &writeTime);
+
+ if (CompareFileTime(&finddata->ftLastWriteTime,
&writeTime) <= 0) {
+ WINE_TRACE("Skipping file as dest newer or same
date\n");
+ skipFile = TRUE;
+ }
+ CloseHandle(h);
+ }
+ }
+
+ /* See if exclude list provided. Note since filenames are case
+ insensitive, need to uppercase the filename before doing
+ strstr */
+ if (!skipFile && (flags & OPT_EXCLUDELIST)) {
+ EXCLUDELIST *pos = excludeList;
+ WCHAR copyFromUpper[MAX_PATH];
+
+ /* Uppercase source filename */
+ lstrcpyW(copyFromUpper, copyFrom);
+ CharUpperBuffW(copyFromUpper, lstrlenW(copyFromUpper));
+
+ /* Loop through testing each exclude line */
+ while (pos) {
+ if (wcsstr(copyFromUpper, pos->name) != NULL) {
+ WINE_TRACE("Skipping file as matches exclude
'%s'\n",
+ wine_dbgstr_w(pos->name));
+ skipFile = TRUE;
+ pos = NULL;
+ } else {
+ pos = pos->next;
+ }
+ }
+ }
+
+ /* Prompt each file if necessary */
+ if (!skipFile && (flags & OPT_SRCPROMPT)) {
+ DWORD count;
+ char answer[10];
+ BOOL answered = FALSE;
+ WCHAR yesChar[2];
+ WCHAR noChar[2];
+
+ /* Read the Y and N characters from the resource file */
+ wcscpy(yesChar, XCOPY_LoadMessage(STRING_YES_CHAR));
+ wcscpy(noChar, XCOPY_LoadMessage(STRING_NO_CHAR));
+
+ while (!answered) {
+ XCOPY_wprintf(XCOPY_LoadMessage(STRING_SRCPROMPT), copyFrom);
+ ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
+ &count, NULL);
+
+ answered = TRUE;
+ if (toupper(answer[0]) == noChar[0])
+ skipFile = TRUE;
+ else if (toupper(answer[0]) != yesChar[0])
+ answered = FALSE;
+ }
+ }
+
+ if (!skipFile &&
+ destAttribs != INVALID_FILE_ATTRIBUTES && !(flags &
OPT_NOPROMPT)) {
+ DWORD count;
+ char answer[10];
+ BOOL answered = FALSE;
+ WCHAR yesChar[2];
+ WCHAR allChar[2];
+ WCHAR noChar[2];
+
+ /* Read the A,Y and N characters from the resource file */
+ wcscpy(yesChar, XCOPY_LoadMessage(STRING_YES_CHAR));
+ wcscpy(allChar, XCOPY_LoadMessage(STRING_ALL_CHAR));
+ wcscpy(noChar, XCOPY_LoadMessage(STRING_NO_CHAR));
+
+ while (!answered) {
+ XCOPY_wprintf(XCOPY_LoadMessage(STRING_OVERWRITE), copyTo);
+ ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
+ &count, NULL);
+
+ answered = TRUE;
+ if (toupper(answer[0]) == allChar[0])
+ flags |= OPT_NOPROMPT;
+ else if (toupper(answer[0]) == noChar[0])
+ skipFile = TRUE;
+ else if (toupper(answer[0]) != yesChar[0])
+ answered = FALSE;
+ }
+ }
+
+ /* See if it has to exist! */
+ if (destAttribs == INVALID_FILE_ATTRIBUTES && (flags &
OPT_MUSTEXIST)) {
+ skipFile = TRUE;
+ }
+
+ /* Output a status message */
+ if (!skipFile) {
+ if (flags & OPT_QUIET) {
+ /* Skip message */
+ } else if (flags & OPT_FULL) {
+ const WCHAR infostr[] = {'%', '1', ' ',
'-', '>', ' ',
+ '%', '2', '\n',
0};
+
+ XCOPY_wprintf(infostr, copyFrom, copyTo);
+ } else {
+ const WCHAR infostr[] = {'%', '1', '\n', 0};
+ XCOPY_wprintf(infostr, copyFrom);
+ }
+
+ /* If allowing overwriting of read only files, remove any
+ write protection */
+ if ((destAttribs & FILE_ATTRIBUTE_READONLY) &&
+ (flags & OPT_REPLACEREAD)) {
+ SetFileAttributesW(copyTo, destAttribs &
~FILE_ATTRIBUTE_READONLY);
+ }
+
+ copiedFile = TRUE;
+ if (flags & OPT_SIMULATE || flags & OPT_NOCOPY) {
+ /* Skip copy */
+ } else if (CopyFileW(copyFrom, copyTo, FALSE) == 0) {
+
+ DWORD error = GetLastError();
+ XCOPY_wprintf(XCOPY_LoadMessage(STRING_COPYFAIL),
+ copyFrom, copyTo, error);
+ XCOPY_FailMessage(error);
+
+ if (flags & OPT_IGNOREERRORS) {
+ skipFile = TRUE;
+ } else {
+ ret = RC_WRITEERROR;
+ goto cleanup;
+ }
+ }
+
+ /* If /M supplied, remove the archive bit after successful copy */
+ if (!skipFile) {
+ if ((srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
+ (flags & OPT_REMOVEARCH)) {
+ SetFileAttributesW(copyFrom, (srcAttribs &
~FILE_ATTRIBUTE_ARCHIVE));
+ }
+ filesCopied++;
+ }
+ }
+ }
+
+ /* Find next file */
+ findres = FindNextFileW(h, finddata);
+ }
+ FindClose(h);
+
+ /* Search 2 - do subdirs */
+ if (flags & OPT_RECURSIVE) {
+ lstrcpyW(inputpath, srcstem);
+ lstrcatW(inputpath, wchr_star);
+ findres = TRUE;
+ WINE_TRACE("Processing subdirs with spec: %s\n",
wine_dbgstr_w(inputpath));
+
+ 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)) {
+
+ 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
@@ -340,7 +787,7 @@
if (*pos++ != '-') isError = TRUE;
}
- /* Parse the arg : Day */
+ /* Parse the arg : Year */
if (!isError) {
st.wYear = _wtol(pos);
while (*pos && isdigit(*pos)) pos++;
@@ -578,564 +1025,104 @@
return RC_OK;
}
+
/* =========================================================================
- XCOPY_DoCopy - Recursive function to copy files based on input parms
- of a stem and a spec
-
- This works by using FindFirstFile supplying the source stem and spec.
- If results are found, any non-directory ones are processed
- Then, if /S or /E is supplied, another search is made just for
- directories, and this function is called again for that directory
-
+ main - Main entrypoint for the xcopy command
+
+ Processes the args, and drives the actual copying
========================================================================= */
-static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
- WCHAR *deststem, WCHAR *destspec,
- DWORD flags)
+int wmain (int argc, WCHAR *argvW[])
{
- WIN32_FIND_DATAW *finddata;
- HANDLE h;
- BOOL findres = TRUE;
- WCHAR *inputpath, *outputpath;
- BOOL copiedFile = FALSE;
- DWORD destAttribs, srcAttribs;
- BOOL skipFile;
- int ret = 0;
-
- /* Allocate some working memory on heap to minimize footprint */
- finddata = HeapAlloc(GetProcessHeap(), 0, sizeof(WIN32_FIND_DATAW));
- inputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
- outputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
-
- /* Build the search info into a single parm */
- lstrcpyW(inputpath, srcstem);
- lstrcatW(inputpath, srcspec);
-
- /* Search 1 - Look for matching files */
- h = FindFirstFileW(inputpath, finddata);
- while (h != INVALID_HANDLE_VALUE && findres) {
-
- skipFile = FALSE;
-
- /* Ignore . and .. */
- if (lstrcmpW(finddata->cFileName, wchr_dot)==0 ||
- lstrcmpW(finddata->cFileName, wchr_dotdot)==0 ||
- finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-
- WINE_TRACE("Skipping directory, . or .. (%s)\n",
wine_dbgstr_w(finddata->cFileName));
- } else {
-
- /* Get the filename information */
- lstrcpyW(copyFrom, srcstem);
- if (flags & OPT_SHORTNAME) {
- lstrcatW(copyFrom, finddata->cAlternateFileName);
- } else {
- lstrcatW(copyFrom, finddata->cFileName);
- }
-
- lstrcpyW(copyTo, deststem);
- if (*destspec == 0x00) {
- if (flags & OPT_SHORTNAME) {
- lstrcatW(copyTo, finddata->cAlternateFileName);
- } else {
- lstrcatW(copyTo, finddata->cFileName);
- }
- } else {
- lstrcatW(copyTo, destspec);
- }
-
- /* Do the copy */
- WINE_TRACE("ACTION: Copy '%s' -> '%s'\n",
wine_dbgstr_w(copyFrom),
- wine_dbgstr_w(copyTo));
- if (!copiedFile && !(flags & OPT_SIMULATE))
XCOPY_CreateDirectory(deststem);
-
- /* See if allowed to copy it */
- srcAttribs = GetFileAttributesW(copyFrom);
- WINE_TRACE("Source attribs: %d\n", srcAttribs);
-
- if ((srcAttribs & FILE_ATTRIBUTE_HIDDEN) ||
- (srcAttribs & FILE_ATTRIBUTE_SYSTEM)) {
-
- if (!(flags & OPT_COPYHIDSYS)) {
- skipFile = TRUE;
- }
- }
-
- if (!(srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
- (flags & OPT_ARCHIVEONLY)) {
- skipFile = TRUE;
- }
-
- /* See if file exists */
- destAttribs = GetFileAttributesW(copyTo);
- WINE_TRACE("Dest attribs: %d\n", srcAttribs);
-
- /* Check date ranges if a destination file already exists */
- if (!skipFile && (flags & OPT_DATERANGE) &&
- (CompareFileTime(&finddata->ftLastWriteTime, &dateRange) <
0)) {
- WINE_TRACE("Skipping file as modified date too old\n");
- skipFile = TRUE;
- }
-
- /* If just /D supplied, only overwrite if src newer than dest */
- if (!skipFile && (flags & OPT_DATENEWER) &&
- (destAttribs != INVALID_FILE_ATTRIBUTES)) {
- HANDLE h = CreateFileW(copyTo, GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
- NULL);
- if (h != INVALID_HANDLE_VALUE) {
- FILETIME writeTime;
- GetFileTime(h, NULL, NULL, &writeTime);
-
- if (CompareFileTime(&finddata->ftLastWriteTime,
&writeTime) <= 0) {
- WINE_TRACE("Skipping file as dest newer or same
date\n");
- skipFile = TRUE;
- }
- CloseHandle(h);
- }
- }
-
- /* See if exclude list provided. Note since filenames are case
- insensitive, need to uppercase the filename before doing
- strstr */
- if (!skipFile && (flags & OPT_EXCLUDELIST)) {
- EXCLUDELIST *pos = excludeList;
- WCHAR copyFromUpper[MAX_PATH];
-
- /* Uppercase source filename */
- lstrcpyW(copyFromUpper, copyFrom);
- CharUpperBuffW(copyFromUpper, lstrlenW(copyFromUpper));
-
- /* Loop through testing each exclude line */
- while (pos) {
- if (wcsstr(copyFromUpper, pos->name) != NULL) {
- WINE_TRACE("Skipping file as matches exclude
'%s'\n",
- wine_dbgstr_w(pos->name));
- skipFile = TRUE;
- pos = NULL;
- } else {
- pos = pos->next;
- }
- }
- }
-
- /* Prompt each file if necessary */
- if (!skipFile && (flags & OPT_SRCPROMPT)) {
- DWORD count;
- char answer[10];
- BOOL answered = FALSE;
- WCHAR yesChar[2];
- WCHAR noChar[2];
-
- /* Read the Y and N characters from the resource file */
- wcscpy(yesChar, XCOPY_LoadMessage(STRING_YES_CHAR));
- wcscpy(noChar, XCOPY_LoadMessage(STRING_NO_CHAR));
-
- while (!answered) {
- XCOPY_wprintf(XCOPY_LoadMessage(STRING_SRCPROMPT), copyFrom);
- ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
- &count, NULL);
-
- answered = TRUE;
- if (toupper(answer[0]) == noChar[0])
- skipFile = TRUE;
- else if (toupper(answer[0]) != yesChar[0])
- answered = FALSE;
- }
- }
-
- if (!skipFile &&
- destAttribs != INVALID_FILE_ATTRIBUTES && !(flags &
OPT_NOPROMPT)) {
- DWORD count;
- char answer[10];
- BOOL answered = FALSE;
- WCHAR yesChar[2];
- WCHAR allChar[2];
- WCHAR noChar[2];
-
- /* Read the A,Y and N characters from the resource file */
- wcscpy(yesChar, XCOPY_LoadMessage(STRING_YES_CHAR));
- wcscpy(allChar, XCOPY_LoadMessage(STRING_ALL_CHAR));
- wcscpy(noChar, XCOPY_LoadMessage(STRING_NO_CHAR));
-
- while (!answered) {
- XCOPY_wprintf(XCOPY_LoadMessage(STRING_OVERWRITE), copyTo);
- ReadFile (GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer),
- &count, NULL);
-
- answered = TRUE;
- if (toupper(answer[0]) == allChar[0])
- flags |= OPT_NOPROMPT;
- else if (toupper(answer[0]) == noChar[0])
- skipFile = TRUE;
- else if (toupper(answer[0]) != yesChar[0])
- answered = FALSE;
- }
- }
-
- /* See if it has to exist! */
- if (destAttribs == INVALID_FILE_ATTRIBUTES && (flags &
OPT_MUSTEXIST)) {
- skipFile = TRUE;
- }
-
- /* Output a status message */
- if (!skipFile) {
- if (flags & OPT_QUIET) {
- /* Skip message */
- } else if (flags & OPT_FULL) {
- const WCHAR infostr[] = {'%', 's', ' ',
'-', '>', ' ',
- '%', 's', '\n',
0};
-
- XCOPY_wprintf(infostr, copyFrom, copyTo);
- } else {
- const WCHAR infostr[] = {'%', 's', '\n', 0};
- XCOPY_wprintf(infostr, copyFrom);
- }
-
- /* If allowing overwriting of read only files, remove any
- write protection */
- if ((destAttribs & FILE_ATTRIBUTE_READONLY) &&
- (flags & OPT_REPLACEREAD)) {
- SetFileAttributesW(copyTo, destAttribs &
~FILE_ATTRIBUTE_READONLY);
- }
-
- copiedFile = TRUE;
- if (flags & OPT_SIMULATE || flags & OPT_NOCOPY) {
- /* Skip copy */
- } else if (CopyFileW(copyFrom, copyTo, FALSE) == 0) {
-
- DWORD error = GetLastError();
- XCOPY_wprintf(XCOPY_LoadMessage(STRING_COPYFAIL),
- copyFrom, copyTo, error);
- XCOPY_FailMessage(error);
-
- if (flags & OPT_IGNOREERRORS) {
- skipFile = TRUE;
- } else {
- ret = RC_WRITEERROR;
- goto cleanup;
- }
- }
-
- /* If /M supplied, remove the archive bit after successful copy */
- if (!skipFile) {
- if ((srcAttribs & FILE_ATTRIBUTE_ARCHIVE) &&
- (flags & OPT_REMOVEARCH)) {
- SetFileAttributesW(copyFrom, (srcAttribs &
~FILE_ATTRIBUTE_ARCHIVE));
- }
- filesCopied++;
- }
- }
- }
-
- /* Find next file */
- findres = FindNextFileW(h, finddata);
- }
- FindClose(h);
-
- /* Search 2 - do subdirs */
- if (flags & OPT_RECURSIVE) {
- lstrcpyW(inputpath, srcstem);
- lstrcatW(inputpath, wchr_star);
- findres = TRUE;
- WINE_TRACE("Processing subdirs with spec: %s\n",
wine_dbgstr_w(inputpath));
-
- 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)) {
-
- 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;
-}
-
-/* =========================================================================
- * 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)
-{
- int len;
- WCHAR *new_path;
- BOOL ret = TRUE;
-
- new_path = HeapAlloc(GetProcessHeap(),0, sizeof(WCHAR) * (lstrlenW(path)+1));
- lstrcpyW(new_path,path);
-
- while ((len = lstrlenW(new_path)) && new_path[len - 1] == '\\')
- new_path[len - 1] = 0;
-
- while (!CreateDirectoryW(new_path,NULL))
- {
- WCHAR *slash;
- DWORD last_error = GetLastError();
- if (last_error == ERROR_ALREADY_EXISTS)
- break;
-
- if (last_error != ERROR_PATH_NOT_FOUND)
- {
- ret = FALSE;
- break;
- }
-
- if (!(slash = wcsrchr(new_path,'\\')) && ! (slash =
wcsrchr(new_path,'/')))
- {
- ret = FALSE;
- break;
- }
-
- len = slash - new_path;
- new_path[len] = 0;
- if (!XCOPY_CreateDirectory(new_path))
- {
- ret = FALSE;
- break;
- }
- new_path[len] = '\\';
- }
- 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) {
-
- WCHAR *filenameStart = parms;
-
- WINE_TRACE("/EXCLUDE parms: '%s'\n", wine_dbgstr_w(parms));
- excludeList = NULL;
-
- while (*parms && *parms != ' ' && *parms != '/') {
-
- /* If found '+' then process the file found so far */
- if (*parms == '+') {
- if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
- return TRUE;
- }
- filenameStart = parms+1;
- }
- parms++;
- }
-
- if (filenameStart != parms) {
- if (XCOPY_ProcessExcludeFile(filenameStart, parms)) {
- return TRUE;
- }
- }
-
- return 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) {
-
- 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;
- }
-
- /* Process line by line */
- while (fgetws(buffer, sizeof(buffer)/sizeof(WCHAR), inFile) != NULL) {
- EXCLUDELIST *thisEntry;
- int length = lstrlenW(buffer);
-
- /* Strip CRLF */
- buffer[length-1] = 0x00;
-
- /* 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));
- }
- }
-
- /* See if EOF or error occurred */
- if (!feof(inFile)) {
- XCOPY_wprintf(XCOPY_LoadMessage(STRING_READFAIL), filename);
- *endOfName = endChar;
- return TRUE;
- }
-
- /* Revert the input string to original form, and cleanup + return */
- *endOfName = endChar;
- fclose(inFile);
- return FALSE;
-}
-
-/* =========================================================================
- * 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};
-
- if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
- WINE_FIXME("LoadString failed with %d\n", GetLastError());
- lstrcpyW(msg, failedMsg);
- }
- 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;
-
- 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);
- }
-}
-
-/* =========================================================================
- * 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, ...) {
-
- 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;
+ 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};
+
+ /* 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;
- }
-
- 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;
- }
-
- /* Try to write as unicode all the time we think its a console */
- if (toConsole) {
- res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
- output_bufW, len, &nOut, NULL);
- }
-
- /* If writing to console has failed (ever) we assume its file
- i/o so convert to OEM codepage and output */
- if (!res) {
- BOOL usedDefaultChar = FALSE;
- DWORD convertedChars;
-
- toConsole = FALSE;
-
- /*
- * 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;
- }
-
- /* 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);
- }
-
- /* Trace whether screen or console */
- if (!traceOutput) {
- WINE_TRACE("Writing to console? (%d)\n", toConsole);
- traceOutput = TRUE;
- }
- return nOut;
-}
+ 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;
+
+ /* Extract required information from destination specification */
+ rc = XCOPY_ProcessDestParm(supplieddestination, destinationstem,
+ destinationspec, sourcespec, flags);
+ if (rc != RC_OK) return rc;
+
+ /* 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));
+
+ /* Pause if necessary */
+ if (flags & OPT_PAUSE) {
+ DWORD count;
+ char pausestr[10];
+
+ XCOPY_wprintf(XCOPY_LoadMessage(STRING_PAUSE));
+ ReadFile (GetStdHandle(STD_INPUT_HANDLE), pausestr, sizeof(pausestr),
+ &count, NULL);
+ }
+
+ /* 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);
+ }
+
+ /* 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;
+
+}
Modified: trunk/reactos/media/doc/README.WINE
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/media/doc/README.WINE?rev=…
==============================================================================
--- trunk/reactos/media/doc/README.WINE [iso-8859-1] (original)
+++ trunk/reactos/media/doc/README.WINE [iso-8859-1] Sun Feb 12 13:33:30 2012
@@ -194,7 +194,7 @@
ReactOS shares the following programs with Winehq.
-reactos/base/applications/cmdutils/xcopy # Autosync
+reactos/base/applications/cmdutils/xcopy # Synced to Wine-1.3.37
reactos/base/applications/games/winmine # Forked at Wine-1_3_5
reactos/base/applications/extrac32 # Autosync
reactos/base/applications/iexplore # Autosync