xcopy: Reorder the functions to avoid forward declarations.
diff --git a/programs/xcopy/xcopy.c b/programs/xcopy/xcopy.c
index 4c1154f..f684a55 100644
--- a/programs/xcopy/xcopy.c
+++ b/programs/xcopy/xcopy.c
@@ -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,107 +74,570 @@
 
 
 /* =========================================================================
-   main - Main entrypoint for the xcopy command
+ * 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};
 
-     Processes the args, and drives the actual copying
-   ========================================================================= */
-int wmain (int argc, WCHAR *argvW[])
+    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 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;
+
+    /*
+     * 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;
+    }
+
+    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 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[] = {'%', 's', '\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     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};
+    int len;
+    WCHAR *new_path;
+    BOOL ret = TRUE;
 
-    /* Preinitialize flags based on COPYCMD */
-    if (GetEnvironmentVariableW(COPYCMD, copyCmd, MAXSTRING)) {
-        if (wcsstr(copyCmd, PROMPTSTR1) != NULL ||
-            wcsstr(copyCmd, PROMPTSTR2) != NULL) {
-            flags |= OPT_NOPROMPT;
+    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));
         }
     }
 
-    /* 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;
+    /* See if EOF or error occurred */
+    if (!feof(inFile)) {
+        XCOPY_wprintf(XCOPY_LoadMessage(STRING_READFAIL), filename);
+        *endOfName = endChar;
+        return TRUE;
     }
 
-    /* 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;
-
+    /* 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
+
+   ========================================================================= */
+static int XCOPY_DoCopy(WCHAR *srcstem, WCHAR *srcspec,
+                        WCHAR *deststem, WCHAR *destspec,
+                        DWORD flags)
+{
+    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;
+}
+
+
+/* =========================================================================
    XCOPY_ParseCommandLine - Parses the command line
    ========================================================================= */
 static BOOL is_whitespace(WCHAR c)
@@ -578,564 +1023,104 @@
     return RC_OK;
 }
 
+
 /* =========================================================================
-   XCOPY_DoCopy - Recursive function to copy files based on input parms
-     of a stem and a spec
+   main - Main entrypoint for the xcopy command
 
-      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
-
+     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;
+    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};
 
-    /* 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);
+    /* Preinitialize flags based on COPYCMD */
+    if (GetEnvironmentVariableW(COPYCMD, copyCmd, MAXSTRING)) {
+        if (wcsstr(copyCmd, PROMPTSTR1) != NULL ||
+            wcsstr(copyCmd, PROMPTSTR2) != NULL) {
+            flags |= OPT_NOPROMPT;
         }
     }
 
-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;
+    /* 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));
+
+    /* 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);
     }
 
-    /* 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);
+    /* 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);
     }
 
-    /* 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);
+    /* 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;
 
-    /* Trace whether screen or console */
-    if (!traceOutput) {
-      WINE_TRACE("Writing to console? (%d)\n", toConsole);
-      traceOutput = TRUE;
-    }
-    return nOut;
 }