xcopy support try 1...
Ann & Jason Edmeades
us at edmeades.me.uk
Thu Feb 22 15:52:04 CST 2007
Firstly, I am not sure I have included everything correctly to define a new
directory in the build tree, so please accept my apologies if I have missed
any parts
Attached is a first pass at a basic version of xcopy. I have only
implemented the basics, which is file and subdirectory copying plus the
simple reporting information. It will fail if files already exist etc, but
they can easily be fixed - I didn't want the patch to get big.
Dan asked about a conformance test - I have a simple batch one I wrote which
tested this code under windows, but I cant submit it for wine inclusion as
there are 2 key cmd.exe bugs which need fixing before it can work. One
question though - For wine programs, can the wine testsuite be used and if
so, how do I go about doing it (porting the batch pgm to C is probably not
too much hard work). - currently none of the programs\* have tests.
Jason
-------------- next part --------------
>From nobody Mon Sep 17 00:00:00 2001
From: Jason Edmeades <us at edmeades.me.uk>
Date: Thu Feb 22 15:46:57 2007 +0000
Subject: [PATCH] Basic implementation of xcopy
Supports subdir copy and various levels of reporting
---
configure.ac | 1
programs/xcopy/Makefile.in | 15 +
programs/xcopy/xcopy.c | 548 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 564 insertions(+), 0 deletions(-)
mode change 100644 => 100755 configure.ac
create mode 100755 programs/xcopy/Makefile.in
create mode 100755 programs/xcopy/xcopy.c
e760d120dcfd5e81373884bb81a381a70a5142e4
diff --git a/configure.ac b/configure.ac
old mode 100644
new mode 100755
index 4edaa20..604fc04
--- a/configure.ac
+++ b/configure.ac
@@ -1833,6 +1833,7 @@ programs/winevdm/Makefile
programs/winhelp/Makefile
programs/winver/Makefile
programs/wordpad/Makefile
+programs/xcopy/Makefile
server/Makefile
tools/Makefile
tools/widl/Makefile
diff --git a/programs/xcopy/Makefile.in b/programs/xcopy/Makefile.in
new file mode 100755
index 0000000..50df96b
--- /dev/null
+++ b/programs/xcopy/Makefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = xcopy.exe
+APPMODE = -mconsole
+IMPORTS = user32 kernel32
+
+C_SRCS = \
+ xcopy.c
+
+ at MAKE_PROG_RULES@
+
+ at DEPENDENCIES@ # everything below this line is overwritten by make depend
+
diff --git a/programs/xcopy/xcopy.c b/programs/xcopy/xcopy.c
new file mode 100755
index 0000000..f1ad0f7
--- /dev/null
+++ b/programs/xcopy/xcopy.c
@@ -0,0 +1,548 @@
+/*
+ * XCOPY - Wine-compatible xcopy program
+ *
+ * Copyright (C) 2007 J. Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * Notes:
+ * According to http://www.microsoft.com/technet/archive/msdos/comm8.mspx?mfr=true
+ * return codes:
+ * 0 - OK
+ * 1 - No files found to copy
+ * 2 - CTRL+C during copy
+ * 4 - Initialization error, or invalid source specification
+ * 5 - Disk write error
+ */
+
+
+#include <stdio.h>
+#include <windows.h>
+#include <wine/debug.h>
+
+/* Local #defines */
+#define RC_OK 0
+#define RC_NOFILES 1
+#define RC_CTRLC 2
+#define RC_INITERROR 4
+#define RC_WRITEERROR 5
+
+#define OPT_ASSUMEDIR 0x00000001
+#define OPT_RECURSIVE 0x00000002
+#define OPT_EMPTYDIR 0x00000004
+#define OPT_QUIET 0x00000008
+#define OPT_FULL 0x00000010
+#define OPT_SIMULATE 0x00000020
+
+WINE_DEFAULT_DEBUG_CHANNEL(xcopy);
+
+/* Prototypes */
+int XCOPY_ProcessSourceParm(char *suppliedsource, char *stem, char *spec);
+int XCOPY_ProcessDestParm(char *supplieddestination, char *stem,
+ char *spec, char *srcspec, DWORD flags);
+int XCOPY_DoCopy(char *srcstem, char *srcspec,
+ char *deststem, char *destspec,
+ DWORD flags);
+BOOL XCOPY_CreateDirectory(CHAR* path);
+void _splitpath(const CHAR* path, CHAR* drv, CHAR* dir, CHAR* name, CHAR* ext);
+
+/* Global variables */
+ULONG filesCopied = 0; /* Number of files copied */
+
+/* To minimize stack usage during recursion, some temporary variables
+ made global */
+char copyFrom[MAX_PATH];
+char copyTo[MAX_PATH];
+
+
+/* =========================================================================
+ main - Main entrypoint for the xcopy command
+
+ Processes the args, and drives the actual copying
+ ========================================================================= */
+int main (int argc, char *argv[])
+{
+ int rc = 0;
+ char suppliedsource[MAX_PATH] = ""; /* As supplied on the cmd line */
+ char supplieddestination[MAX_PATH] = ".";
+ char sourcestem[MAX_PATH] = ""; /* Stem of source */
+ char sourcespec[MAX_PATH] = ""; /* Filespec of source */
+ char destinationstem[MAX_PATH] = ""; /* Stem of destination */
+ char destinationspec[MAX_PATH] = ""; /* Filespec of destination */
+ DWORD flags = 0; /* Option flags */
+
+ /*
+ * Parse the command line
+ */
+
+ /* Confirm at least one parameter */
+ if (argc < 2) {
+ printf("Invalid number of parameters - Use xcopy /? for help\n");
+ return RC_INITERROR;
+ }
+
+ /* Skip first arg, which is the program name */
+ argv++;
+
+ while (*argv!=NULL)
+ {
+ WINE_TRACE("Processing Arg: '%s'\n", *argv);
+
+ /* First non-switch parameter is source, second is destination */
+ if (*argv[0] != '/') {
+ if (suppliedsource[0] == 0x00) {
+ strcpy(suppliedsource, *argv);
+ } else {
+ strcpy(supplieddestination, *argv);
+ }
+ } else {
+ /* Process all the switch options */
+ switch (toupper(argv[0][1])) {
+ case 'I': flags |= OPT_ASSUMEDIR; break;
+ case 'S': flags |= OPT_RECURSIVE; break;
+ case 'E': flags |= OPT_EMPTYDIR; break;
+ case 'Q': flags |= OPT_QUIET; break;
+ case 'F': flags |= OPT_FULL; break;
+ case 'L': flags |= OPT_SIMULATE; break;
+ default:
+ WINE_FIXME("Unhandled parameter '%s'\n", *argv);
+ }
+ }
+ argv++;
+ }
+
+ /* Trace out the supplied information */
+ WINE_TRACE("Supplied parameters:\n");
+ WINE_TRACE("Source : '%s'\n", suppliedsource);
+ WINE_TRACE("Destination : '%s'\n", supplieddestination);
+
+ /* Extract required information from source specification */
+ rc = XCOPY_ProcessSourceParm(suppliedsource, sourcestem, sourcespec);
+
+ /* Extract required information from destination specification */
+ rc = XCOPY_ProcessDestParm(supplieddestination, destinationstem,
+ destinationspec, sourcespec, flags);
+
+ /* Trace out the resulting information */
+ WINE_TRACE("Resolved parameters:\n");
+ WINE_TRACE("Source Stem : '%s'\n", sourcestem);
+ WINE_TRACE("Source Spec : '%s'\n", sourcespec);
+ WINE_TRACE("Dest Stem : '%s'\n", destinationstem);
+ WINE_TRACE("Dest Spec : '%s'\n", destinationspec);
+
+ /* Now do the hard work... */
+ rc = XCOPY_DoCopy(sourcestem, sourcespec,
+ destinationstem, destinationspec,
+ flags);
+
+
+ /* Finished - print trailer and exit */
+ if (flags & OPT_SIMULATE) {
+ printf("%d file(s) would be copied\n", filesCopied);
+ } else {
+ printf("%d file(s) copied\n", filesCopied);
+ }
+ if (rc == RC_OK && filesCopied == 0) rc = RC_NOFILES;
+ return rc;
+
+}
+
+
+/* =========================================================================
+ XCOPY_ProcessSourceParm - Takes the supplied source parameter, and
+ converts it into a stem and a filespec
+ ========================================================================= */
+int XCOPY_ProcessSourceParm(char *suppliedsource, char *stem, char *spec) {
+
+ char actualsource[MAX_PATH];
+ char *starPos;
+ char *questPos;
+
+ /*
+ * Validate the source, expanding to full path ensuring it exists
+ */
+ if (GetFullPathName(suppliedsource, MAX_PATH, actualsource, NULL) == 0) {
+ WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
+ return RC_INITERROR;
+ }
+
+ /*
+ * Work out the stem of the source
+ */
+
+ /* If no wildcard were supplied then the source is either a single
+ file or a directory - in which case thats the stem of the search,
+ otherwise split off the wildcards and use the higher level as the
+ stem */
+ strcpy(stem, actualsource);
+ starPos = strchr(stem,'*');
+ questPos = strchr(stem,'?');
+ if (starPos || questPos) {
+ char *lastDir;
+
+ if (starPos) *starPos = 0x00;
+ if (questPos) *questPos = 0x00;
+
+ lastDir = strrchr(stem, '\\');
+ if (lastDir) *(lastDir+1) = 0x00;
+ else {
+ WINE_FIXME("Unexpected syntax error in source parameter\n");
+ return RC_INITERROR;
+ }
+ strcpy(spec, actualsource + (lastDir - stem)+1);
+ } else {
+
+ DWORD attribs = GetFileAttributes(actualsource);
+
+ if (attribs == INVALID_FILE_ATTRIBUTES) {
+ LPVOID lpMsgBuf;
+ DWORD lastError = GetLastError();
+ int status;
+ status = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, lastError, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
+ printf(lpMsgBuf);
+ return RC_INITERROR;
+
+ /* Directory: */
+ } else if (attribs & FILE_ATTRIBUTE_DIRECTORY) {
+ strcat(stem, "\\");
+ strcpy(spec, "*");
+
+ /* File: */
+ } else {
+ char drive[MAX_PATH];
+ char dir[MAX_PATH];
+ char fname[MAX_PATH];
+ char ext[MAX_PATH];
+ _splitpath(actualsource, drive, dir, fname, ext);
+ sprintf(stem, "%s%s", drive, dir);
+ sprintf(spec, "%s%s", fname, ext);
+ }
+ }
+ return RC_OK;
+}
+
+/* =========================================================================
+ XCOPY_ProcessDestParm - Takes the supplied destination parameter, and
+ converts it into a stem
+ ========================================================================= */
+int XCOPY_ProcessDestParm(char *supplieddestination, char *stem, char *spec,
+ char *srcspec, DWORD flags) {
+ char actualdestination[MAX_PATH];
+ DWORD attribs;
+ BOOL isDir = FALSE;
+
+ /*
+ * Validate the source, expanding to full path ensuring it exists
+ */
+ if (GetFullPathName(supplieddestination, MAX_PATH, actualdestination, NULL) == 0) {
+ WINE_FIXME("Unexpected failure expanding source path (%d)\n", GetLastError());
+ return RC_INITERROR;
+ }
+
+ /* Destination is either a directory or a file */
+ attribs = GetFileAttributes(actualdestination);
+
+ if (attribs == INVALID_FILE_ATTRIBUTES) {
+
+ /* If /I supplied and wildcard copy, assume directory */
+ if (flags & OPT_ASSUMEDIR &&
+ (strchr(srcspec, '?') || strchr(srcspec, '*'))) {
+
+ isDir = TRUE;
+
+ } else {
+ DWORD count;
+ char answer[10];
+
+ while (answer[0] != 'F' && answer[0] != 'D') {
+ printf("Is %s a filename or directory\n"
+ "on the target?\n"
+ "(F - File, D - Directory)\n", supplieddestination);
+
+ ReadFile(GetStdHandle(STD_INPUT_HANDLE), answer, sizeof(answer), &count, NULL);
+ WINE_TRACE("User answer %c\n", answer[0]);
+
+ answer[0] = toupper(answer[0]);
+ }
+
+ if (answer[0] == 'D') {
+ isDir = TRUE;
+ } else {
+ isDir = FALSE;
+ }
+ }
+ } else {
+ isDir = (attribs & FILE_ATTRIBUTE_DIRECTORY);
+ }
+
+ if (isDir) {
+ strcpy(stem, actualdestination);
+ *spec = 0x00;
+
+ /* Ensure ends with a '\' */
+ if (stem[strlen(stem)-1] != '\\') {
+ strcat(stem, "\\");
+ }
+
+ } else {
+ char drive[MAX_PATH];
+ char dir[MAX_PATH];
+ char fname[MAX_PATH];
+ char ext[MAX_PATH];
+ _splitpath(actualdestination, drive, dir, fname, ext);
+ sprintf(stem, "%s%s", drive, dir);
+ sprintf(spec, "%s%s", fname, ext);
+ }
+ 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
+
+ ========================================================================= */
+int XCOPY_DoCopy(char *srcstem, char *srcspec,
+ char *deststem, char *destspec,
+ DWORD flags) {
+
+ WIN32_FIND_DATA *finddata;
+ HANDLE h;
+ BOOL findres = TRUE;
+ char *inputpath, *outputpath;
+ BOOL copiedFile = FALSE;
+
+ /* Allocate some working memory on heap to minimize footprint */
+ finddata = HeapAlloc(GetProcessHeap(), 0, sizeof(WIN32_FIND_DATA));
+ inputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
+ outputpath = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
+
+ /* Build the search info into a single parm */
+ strcpy(inputpath, srcstem);
+ strcat(inputpath, srcspec);
+
+ /* Search 1 - Look for matching files */
+ h = FindFirstFile(inputpath, finddata);
+ while (h != INVALID_HANDLE_VALUE && findres) {
+
+ /* Ignore . and .. */
+ if (strcmp(finddata->cFileName, ".")==0 ||
+ strcmp(finddata->cFileName, "..")==0 ||
+ finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+
+ WINE_TRACE("Skipping directory, . or .. (%s)\n", finddata->cFileName);
+ } else {
+
+ /* Get the filename information */
+ strcpy(copyFrom, srcstem);
+ strcat(copyFrom, finddata->cFileName);
+
+ strcpy(copyTo, deststem);
+ if (*destspec == 0x00) {
+ strcat(copyTo, finddata->cFileName);
+ } else {
+ strcat(copyTo, destspec);
+ }
+
+ /* Do the copy */
+ WINE_TRACE("ACTION: Copy '%s' -> '%s'\n", copyFrom, copyTo);
+ if (!copiedFile && !(flags & OPT_SIMULATE)) XCOPY_CreateDirectory(deststem);
+
+ /* Output a status message */
+ if (flags & OPT_QUIET) {
+ /* Skip message */
+ } else if (flags & OPT_FULL) {
+ printf("%s -> %s\n", copyFrom, copyTo);
+ } else {
+ printf("%s\n", copyFrom);
+ }
+
+ copiedFile = TRUE;
+ if (flags & OPT_SIMULATE) {
+ /* Skip copy as just simulating */
+ } else if (CopyFile(copyFrom, copyTo, TRUE) == 0) {
+ printf("Copying of '%s' to '%s' failed with r/c %d\n", copyFrom, copyTo, GetLastError());
+ }
+ filesCopied++;
+ }
+
+ /* Find next file */
+ findres = FindNextFile(h, finddata);
+ }
+ FindClose(h);
+
+ /* Search 2 - do subdirs */
+ if (flags & OPT_RECURSIVE) {
+ strcpy(inputpath, srcstem);
+ strcat(inputpath, "*");
+ findres = TRUE;
+ WINE_TRACE("Processing subdirs with spec: %s\n", inputpath);
+
+ h = FindFirstFile(inputpath, finddata);
+ while (h != INVALID_HANDLE_VALUE && findres) {
+
+ /* Only looking for dirs */
+ if ((finddata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ (strcmp(finddata->cFileName, ".") != 0) &&
+ (strcmp(finddata->cFileName, "..") != 0)) {
+
+ WINE_TRACE("Handling subdir: %s\n", finddata->cFileName);
+
+ /* Make up recursive information */
+ strcpy(inputpath, srcstem);
+ strcat(inputpath, finddata->cFileName);
+ strcat(inputpath, "\\");
+
+ strcpy(outputpath, deststem);
+ if (*destspec == 0x00) {
+ strcat(outputpath, finddata->cFileName);
+
+ /* If /E is supplied, create the directory now */
+ if ((flags & OPT_EMPTYDIR) &&
+ !(flags & OPT_SIMULATE))
+ XCOPY_CreateDirectory(outputpath);
+
+ strcat(outputpath, "\\");
+ }
+
+ XCOPY_DoCopy(inputpath, srcspec, outputpath, destspec, flags);
+ }
+
+ /* Find next one */
+ findres = FindNextFile(h, finddata);
+ }
+ }
+
+ /* free up memory */
+ HeapFree(GetProcessHeap(), 0, finddata);
+ HeapFree(GetProcessHeap(), 0, inputpath);
+ HeapFree(GetProcessHeap(), 0, outputpath);
+
+ return 0;
+}
+
+/* =========================================================================
+ _splitpath - copied from winefile as no obvious way to use it otherwise
+ ========================================================================= */
+void _splitpath(const CHAR* path, CHAR* drv, CHAR* dir, CHAR* name, CHAR* ext)
+{
+ const CHAR* end; /* end of processed string */
+ const CHAR* p; /* search pointer */
+ const CHAR* s; /* copy pointer */
+
+ /* extract drive name */
+ if (path[0] && path[1]==':') {
+ if (drv) {
+ *drv++ = *path++;
+ *drv++ = *path++;
+ *drv = '\0';
+ }
+ } else if (drv)
+ *drv = '\0';
+
+ /* search for end of string or stream separator */
+ for(end=path; *end && *end!=':'; )
+ end++;
+
+ /* search for begin of file extension */
+ for(p=end; p>path && *--p!='\\' && *p!='/'; )
+ if (*p == '.') {
+ end = p;
+ break;
+ }
+
+ if (ext)
+ for(s=end; (*ext=*s++); )
+ ext++;
+
+ /* search for end of directory name */
+ for(p=end; p>path; )
+ if (*--p=='\\' || *p=='/') {
+ p++;
+ break;
+ }
+
+ if (name) {
+ for(s=p; s<end; )
+ *name++ = *s++;
+
+ *name = '\0';
+ }
+
+ if (dir) {
+ for(s=path; s<p; )
+ *dir++ = *s++;
+
+ *dir = '\0';
+ }
+}
+
+/* =========================================================================
+ * Routine copied from cmd.exe md command -
+ * This works recursivly. so creating dir1\dir2\dir3 will create dir1 and
+ * dir2 if they do not already exist.
+ * ========================================================================= */
+BOOL XCOPY_CreateDirectory(CHAR* path)
+{
+ int len;
+ CHAR *new_path;
+ BOOL ret = TRUE;
+
+ new_path = HeapAlloc(GetProcessHeap(),0,strlen(path)+1);
+ strcpy(new_path,path);
+
+ while ((len = strlen(new_path)) && new_path[len - 1] == '\\')
+ new_path[len - 1] = 0;
+
+ while (!CreateDirectory(new_path,NULL))
+ {
+ CHAR *slash;
+ DWORD last_error = GetLastError();
+ if (last_error == ERROR_ALREADY_EXISTS)
+ break;
+
+ if (last_error != ERROR_PATH_NOT_FOUND)
+ {
+ ret = FALSE;
+ break;
+ }
+
+ if (!(slash = strrchr(new_path,'\\')) && ! (slash = strrchr(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;
+}
+
--
1.3.0
More information about the wine-patches
mailing list