On 9/6/21 9:54 AM, Florian Eder wrote:
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@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
If this is for Path* methods, I believe kernelbase exports them too?
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)
"excluded" doesn't inhere to this function, and I think it's inaccurate in (as of this patch) its only user.
+{
- 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)
More inconsistent asterisk placement.
+{
- 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);
This can be wcscat(), right?
- }
- 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'\\';
Same here, or even swprintf().
For that matter I'm wondering if this whole function could be reasonably replaced with PathAllocCombine().
- }
- 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());
This is a bit awkward as-is; I think it'd be reasonable to merge patch 6 with this one.
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 */
A lot of these comments feel redundant to me.
- 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;
Do we need PathIsDirectoryEmpty()? Note that it internally does a FindFirstFile(); I don't think it's saving us any time.
For that matter, do we need PathIsDirectory()? Will FindFirstFile just return an error in that case? On the other hand, maybe we want to complain if we get an error from FindFirstFile(), or an error from FindNextFile() other than ERROR_NO_MORE_FILES.
/* 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");
At this point I don't think it's a stub any more ;-)
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