[PATCH 3/4] [cmd] Add support for NUL in copy

Ann and Jason Edmeades jason at edmeades.me.uk
Mon Nov 19 15:03:45 CST 2012


Add support for NUL in a copy command (e.g. copy NUL a.a) which
results in an empty file. This change is implemented in a way which
should support any of the device names in theory (CON as an example,
other than bug 32183 which prevents it from working).

[Fixes bug 21394]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20121119/457b4759/attachment-0001.html>
-------------- next part --------------
From c4df0786a0951179e1515f597ad8eecac002bb89 Mon Sep 17 00:00:00 2001
From: Jason Edmeades <jason at edmeades.me.uk>
Date: Fri, 9 Nov 2012 22:36:30 +0000
Subject: [PATCH 3/4] [cmd] Add support for NUL in copy

Add support for NUL in a copy command (e.g. copy NUL a.a) which
results in an empty file. This change is implemented in a way which
should support any of the device names in theory (CON as an example,
other than bug 32183 which prevents it from working).

[Fixes bug 21394]
---
 programs/cmd/builtins.c                  |   52 ++++++++++++++++++++++--------
 programs/cmd/tests/test_builtins.cmd     |   17 +++++++++-
 programs/cmd/tests/test_builtins.cmd.exp |    8 ++++-
 3 files changed, 61 insertions(+), 16 deletions(-)

diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 2e7fdd9..8715c51 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -47,6 +47,7 @@ const WCHAR slashW[]  = {'\\','\0'};
 const WCHAR equalW[]  = {'=','\0'};
 const WCHAR wildcardsW[] = {'*','?','\0'};
 const WCHAR slashstarW[] = {'\\','*','\0'};
+const WCHAR deviceW[] = {'\\','\\','.','\\','\0'};
 const WCHAR inbuilt[][10] = {
         {'C','A','L','L','\0'},
         {'C','D','\0'},
@@ -414,7 +415,7 @@ static BOOL WCMD_ManualCopy(WCHAR *srcname, WCHAR *dstname, BOOL ascii, BOOL app
     BOOL   ok;
     DWORD  bytesread, byteswritten;
 
-    WINE_TRACE("ASCII Copying %s to %s (append?%d)\n",
+    WINE_TRACE("Manual Copying %s to %s (append?%d)\n",
                wine_dbgstr_w(srcname), wine_dbgstr_w(dstname), append);
 
     in  = CreateFileW(srcname, GENERIC_READ, 0, NULL,
@@ -502,7 +503,7 @@ void WCMD_copy(WCHAR * args) {
   int     argno = 0;
   WCHAR  *rawarg;
   WIN32_FIND_DATAW fd;
-  HANDLE  hff;
+  HANDLE  hff = INVALID_HANDLE_VALUE;
   int     binarymode = -1;            /* -1 means use the default, 1 is binary, 0 ascii */
   BOOL    concatnextfilename = FALSE; /* True if we have just processed a +             */
   BOOL    anyconcats         = FALSE; /* Have we found any + options                    */
@@ -514,6 +515,7 @@ void WCMD_copy(WCHAR * args) {
   BOOL    status;
   WCHAR   copycmd[4];
   DWORD   len;
+  BOOL    dstisdevice = FALSE;
   static const WCHAR copyCmdW[] = {'C','O','P','Y','C','M','D','\0'};
 
   typedef struct _COPY_FILES
@@ -785,6 +787,12 @@ void WCMD_copy(WCHAR * args) {
   WINE_TRACE("Resolved destination is '%s' (calc later %d)\n",
              wine_dbgstr_w(destname), appendfirstsource);
 
+  /* Remember if the destination is a device */
+  if (strncmpW(destination->name, deviceW, strlenW(deviceW)) == 0) {
+    WINE_TRACE("Destination is a device\n");
+    dstisdevice = TRUE;
+  }
+
   /* Now we need to walk the set of sources, and process each name we come to.
      If anyconcats is true, we are writing to one file, otherwise we are using
      the source name each time.
@@ -800,8 +808,10 @@ void WCMD_copy(WCHAR * args) {
   while (thiscopy != NULL) {
 
     WCHAR  srcpath[MAX_PATH];
+    const  WCHAR *srcname;
     WCHAR *filenamepart;
     DWORD  attributes;
+    BOOL   srcisdevice = FALSE;
 
     /* If it was not explicit, we now know whether we are concatenating or not and
        hence whether to copy as binary or ascii                                    */
@@ -836,29 +846,41 @@ void WCMD_copy(WCHAR * args) {
     WINE_TRACE("Copy source (calculated): path: '%s' (Concats: %d)\n",
                     wine_dbgstr_w(srcpath), anyconcats);
 
-    /* Loop through all source files */
-    WINE_TRACE("Searching for: '%s'\n", wine_dbgstr_w(srcpath));
-    hff = FindFirstFileW(srcpath, &fd);
-    if (hff != INVALID_HANDLE_VALUE) {
+    /* If the source is a device, just use it, otherwise search */
+    if (strncmpW(srcpath, deviceW, strlenW(deviceW)) == 0) {
+      WINE_TRACE("Source is a device\n");
+      srcisdevice = TRUE;
+      srcname  = &srcpath[4]; /* After the \\.\ prefix */
+    } else {
+
+      /* Loop through all source files */
+      WINE_TRACE("Searching for: '%s'\n", wine_dbgstr_w(srcpath));
+      hff = FindFirstFileW(srcpath, &fd);
+      if (hff != INVALID_HANDLE_VALUE) {
+        srcname = fd.cFileName;
+      }
+    }
+
+    if (srcisdevice || hff != INVALID_HANDLE_VALUE) {
       do {
         WCHAR outname[MAX_PATH];
         BOOL  overwrite;
 
         /* Skip . and .., and directories */
-        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (!srcisdevice && fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
           WINE_TRACE("Skipping directories\n");
         } else {
 
           /* Build final destination name */
           strcpyW(outname, destination->name);
-          if (destisdirectory || appendfirstsource) strcatW(outname, fd.cFileName);
+          if (destisdirectory || appendfirstsource) strcatW(outname, srcname);
 
           /* Build source name */
-          strcpyW(filenamepart, fd.cFileName);
+          if (!srcisdevice) strcpyW(filenamepart, srcname);
 
-          /* Do we just overwrite */
+          /* Do we just overwrite (we do if we are writing to a device) */
           overwrite = !prompt;
-          if (anyconcats && writtenoneconcat) {
+          if (dstisdevice || (anyconcats && writtenoneconcat)) {
             overwrite = TRUE;
           }
 
@@ -879,7 +901,7 @@ void WCMD_copy(WCHAR * args) {
             else overwrite = TRUE;
           }
 
-          /* If we needed tyo save away the first filename, do it */
+          /* If we needed to save away the first filename, do it */
           if (appendfirstsource && overwrite) {
             heap_free(destination->name);
             destination->name = heap_strdupW(outname);
@@ -898,6 +920,8 @@ void WCMD_copy(WCHAR * args) {
               }
             } else if (!thiscopy->binarycopy) {
               status = WCMD_ManualCopy(srcpath, outname, TRUE, FALSE);
+            } else if (srcisdevice) {
+              status = WCMD_ManualCopy(srcpath, outname, FALSE, FALSE);
             } else {
               status = CopyFileW(srcpath, outname, FALSE);
             }
@@ -921,8 +945,8 @@ void WCMD_copy(WCHAR * args) {
             }
           }
         }
-      } while (FindNextFileW(hff, &fd) != 0);
-      FindClose (hff);
+      } while (!srcisdevice && FindNextFileW(hff, &fd) != 0);
+      if (!srcisdevice) FindClose (hff);
     } else {
       /* Error if the first file was not found */
       if (!anyconcats || (anyconcats && !writtenoneconcat)) {
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index 3efcd2a..4348723 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -607,7 +607,22 @@ if exist foo (
 ) else (
     echo ***
 )
-cd .. & rd foobar
+echo 1234 >a.a
+copy a.a+NUL b.b >nul
+call :CheckFileSize a.a 7 b.b 8
+copy NUL+a.a b.b >nul
+call :CheckFileSize a.a 7 b.b 8
+mkdir subdir
+copy a.a+NUL subdir\ >nul
+call :CheckFileSize a.a 7 subdir\a.a 8
+del subdir\a.a
+cd subdir
+copy ..\a.a NUL >nul
+if exist a.a echo Failed
+cd ..
+rd subdir /s /q
+del a.a b.b
+cd .. & rd foobar /s /q
 
 echo ------------ Testing if/else ------------
 echo --- if/else should work with blocks
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index b30698a..e5a5d1a 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -415,7 +415,13 @@ bar
 bar
 bar
 NUL
- at todo_wine@foo created
+foo created
+Passed: file size check on a.a [7]
+Passed: file size check on b.b [8]
+Passed: file size check on a.a [7]
+Passed: file size check on b.b [8]
+Passed: file size check on a.a [7]
+Passed: file size check on subdir\a.a [8]
 ------------ Testing if/else ------------
 --- if/else should work with blocks
 if seems to work
-- 
1.7.9.5


More information about the wine-patches mailing list