[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