[PATCH 05/41] robocopy: add basic copy logic

Florian Eder others.meder at gmail.com
Mon Sep 6 09:54:42 CDT 2021


Reads all files in the source folder that match any of the files to include
and copies them to the destination, creating necessary folders in the process

Signed-off-by: Florian Eder <others.meder at gmail.com>
---
Does not yet break at a certain depth, so this will copy as deep as possible
Wildcards are already supported, so the "file" * will copy all files in any subdirectories
---
 programs/robocopy/Makefile.in |   2 +-
 programs/robocopy/main.c      | 160 ++++++++++++++++++++++++++++++++++
 programs/robocopy/robocopy.h  |  10 ++-
 programs/robocopy/robocopy.rc |   2 +
 4 files changed, 172 insertions(+), 2 deletions(-)

diff --git a/programs/robocopy/Makefile.in b/programs/robocopy/Makefile.in
index 3f16d00c0a8..b81d799b75a 100644
--- a/programs/robocopy/Makefile.in
+++ b/programs/robocopy/Makefile.in
@@ -1,5 +1,5 @@
 MODULE    = robocopy.exe
-IMPORTS   = kernelbase
+IMPORTS   = kernelbase shlwapi
 
 EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
 
diff --git a/programs/robocopy/main.c b/programs/robocopy/main.c
index 97b961a5d0d..a28b008a8fa 100644
--- a/programs/robocopy/main.c
+++ b/programs/robocopy/main.c
@@ -23,6 +23,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(robocopy);
 #include <windows.h>
 #include <stdlib.h>
 #include <pathcch.h>
+#include <shlwapi.h>
+#include <wine/list.h>
 #include "robocopy.h"
 
 struct robocopy_options options;
@@ -147,6 +149,162 @@ static void parse_arguments(int argc, WCHAR *argv[])
     }
 }
 
+static BOOL matches_array_entry(WCHAR *name, struct path_array *excluded_names)
+{
+    int i;
+    for (i = 0; i < excluded_names->size; i++)
+    {
+        if (PathMatchSpecW(name, excluded_names->array[i])) return TRUE;
+    }
+    return FALSE;
+}
+
+static WCHAR *get_combined_path(const WCHAR* path_prefix, const WCHAR* path_suffix)
+{
+    WCHAR *combined_path;
+    if (path_prefix[wcslen(path_prefix) - 1] == L'\\' || !path_prefix[0])
+    {
+        /* path_prefix ends in a backslash (or is empty) */
+        combined_path = calloc(wcslen(path_prefix) + wcslen(path_suffix) + 1, sizeof(WCHAR));
+        wcscpy(combined_path, path_prefix);
+        wcscpy(&(combined_path[wcslen(path_prefix)]), path_suffix);
+    }
+    else
+    {
+        /* path_prefix ends not in a backslash, we have to add one between the strings */
+        combined_path = calloc(wcslen(path_prefix) + wcslen(path_suffix) + 2, sizeof(WCHAR));
+        wcscpy(combined_path, path_prefix);
+        wcscpy(&(combined_path[wcslen(path_prefix) + 1]), path_suffix);
+        combined_path[wcslen(path_prefix)] = L'\\';
+    }
+    return combined_path;
+}
+
+static BOOL create_directory_path(WCHAR *path)
+{
+    WCHAR *pointer, *current_folder;
+    current_folder = calloc(wcslen(path) + 1, sizeof(WCHAR));
+    /* ignore the "\\?\" prefix, so that those backslashes are not matched */
+    pointer = wcschr(strip_path_prefix(path), L'\\');
+    while (pointer != NULL)
+    {
+        if (!lstrcpynW(current_folder, path, pointer - path + 2)) return FALSE;
+        /* try to create the folder, ignoring any failure due to ERROR_ALREADY_EXISTS */
+        if (!CreateDirectoryW(current_folder, NULL))
+        {
+            if (GetLastError() != ERROR_ALREADY_EXISTS)
+            {
+                WINE_FIXME("error create directory %S %d", current_folder, GetLastError());
+                return FALSE;
+            }
+        }
+        else
+            output_message(STRING_CREATE_DIRECTORY, strip_path_prefix(current_folder));
+        pointer = wcschr(pointer + 1, L'\\');
+    }
+    return TRUE;
+}
+
+static void get_file_paths_in_folder(WCHAR *directory_path, struct list *paths)
+{
+    HANDLE temp_handle;
+    struct path *new_path, *current_path;
+    WIN32_FIND_DATAW entry_data;
+    WCHAR *parent_absolute_path, *current_relative_path, *current_absolute_path, *current_search_path;
+
+    list_init(paths);
+
+    /* initialize list with a empty relative path */
+    new_path = calloc(1, sizeof(struct path));
+    new_path->name = calloc(2, sizeof(WCHAR));
+    list_add_tail(paths, &new_path->entry);
+
+    LIST_FOR_EACH_ENTRY(current_path, paths, struct path, entry)
+    {
+        /* append relative path to the (prefix) directory path */
+        parent_absolute_path = get_combined_path(directory_path, current_path->name);
+
+        /* ignore files, only search in directories (with files or subdirectories in them) */
+        if (!PathIsDirectoryW(parent_absolute_path) || PathIsDirectoryEmptyW(parent_absolute_path)) continue;
+
+        /* append * to recieve every file / subdirectory in this directory */
+        current_search_path = get_combined_path(parent_absolute_path, L"*");
+        /* walk through all files / directories in this directory */
+        temp_handle = FindFirstFileExW(current_search_path, FindExInfoStandard, &entry_data, FindExSearchNameMatch, NULL, 0);
+        if (temp_handle != INVALID_HANDLE_VALUE)
+        {
+            do
+            {
+                /* Ignore . and .. entries */
+                if (!wcscmp(L".", entry_data.cFileName) || !wcscmp(L"..", entry_data.cFileName)) continue;
+
+                current_relative_path = get_combined_path(current_path->name, entry_data.cFileName);
+                current_absolute_path = get_combined_path(directory_path, current_relative_path);
+
+                /* If this entry is a matching file or empty directory, add it to the list of results */
+                if ((!PathIsDirectoryW(current_absolute_path) && matches_array_entry(entry_data.cFileName, options.files)) ||
+                     (PathIsDirectoryW(current_absolute_path)))
+                {
+                    new_path = calloc(1, sizeof(struct path));
+                    new_path->name = wcsdup(current_relative_path);
+                    list_add_tail(paths, &new_path->entry);
+                }
+            }
+            while (FindNextFileW(temp_handle, &entry_data) != 0);
+        }
+    }
+}
+
+static BOOL perform_copy(void)
+{
+    struct list paths_source;
+    struct path *current_path;
+    WCHAR *current_absolute_path, *target_path;
+
+    list_init(&paths_source);
+
+    if (!PathIsDirectoryW(options.source))
+    {
+        WINE_FIXME("error read directory %S %d", options.source, GetLastError());
+        return FALSE;
+    }
+
+    /* create destination folder if it does not yet exist */
+    create_directory_path(options.destination);
+
+    /* get files in the destination folder and source folder */
+    get_file_paths_in_folder(options.source, &paths_source);
+
+    /* get files in the source folder */
+    LIST_FOR_EACH_ENTRY(current_path, &paths_source, struct path, entry)
+    {
+        /* append the relative path to the source to get the absolute path of the source file / directory */
+        current_absolute_path = get_combined_path(options.source, current_path->name);
+
+        /* append the relative path to the destination to get the target path */
+        target_path = get_combined_path(options.destination, current_path->name);
+
+        if (PathIsDirectoryW(current_absolute_path))
+        {
+            /* Create the directory path and then create the directory itself */
+            if (!create_directory_path(target_path))
+                WINE_FIXME("error write directory %S %d", target_path, GetLastError());
+        }
+        else
+        {
+            if (!CopyFileW(current_absolute_path, target_path, FALSE))
+                WINE_FIXME("error write file %S %d", target_path, GetLastError());
+            else
+            {
+                output_message(STRING_CREATE_FILE, strip_path_prefix(target_path));
+            }
+        }
+
+    }
+
+    return TRUE;
+}
+
 static void print_header(void)
 {
     UINT i;
@@ -170,6 +328,8 @@ int __cdecl wmain(int argc, WCHAR *argv[])
 
     print_header();
 
+    perform_copy();
+
     WINE_FIXME("robocopy stub");
     return 0;
 }
\ No newline at end of file
diff --git a/programs/robocopy/robocopy.h b/programs/robocopy/robocopy.h
index 3be51b460a7..22c7406e0ea 100644
--- a/programs/robocopy/robocopy.h
+++ b/programs/robocopy/robocopy.h
@@ -18,6 +18,12 @@
 
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
+#include <wine/list.h>
+
+struct path {
+    struct list entry;
+    WCHAR *name;
+};
 
 struct path_array {
     UINT size;
@@ -35,4 +41,6 @@ struct robocopy_options {
 #define STRING_SOURCE                         1003
 #define STRING_DESTINATION                    1004
 #define STRING_FILES                          1005
-#define STRING_ADDITIONAL_INFO                1008
\ No newline at end of file
+#define STRING_ADDITIONAL_INFO                1008
+#define STRING_CREATE_DIRECTORY               1019
+#define STRING_CREATE_FILE                    1022
\ No newline at end of file
diff --git a/programs/robocopy/robocopy.rc b/programs/robocopy/robocopy.rc
index e00d9fc0227..92f3b8efe63 100644
--- a/programs/robocopy/robocopy.rc
+++ b/programs/robocopy/robocopy.rc
@@ -30,6 +30,8 @@ STRINGTABLE
     STRING_DESTINATION, "    Destination: %1\n\n"
     STRING_FILES, "          Files: %1\n"
     STRING_ADDITIONAL_INFO, "                 %1\n"
+    STRING_CREATE_DIRECTORY, " Created Dir: %1\n"
+    STRING_CREATE_FILE, " Copied File: %1\n"
 }
 
 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
-- 
2.32.0




More information about the wine-devel mailing list