Signed-off-by: Hugh McMaster hugh.mcmaster@outlook.com --- programs/reg/tests/copy.c | 52 +++++++++++++++++++++++++++++++++++ programs/reg/tests/export.c | 24 ++++++++-------- programs/reg/tests/reg_test.h | 2 ++ 3 files changed, 66 insertions(+), 12 deletions(-)
diff --git a/programs/reg/tests/copy.c b/programs/reg/tests/copy.c index 3ed75acad4d..53d990811ac 100644 --- a/programs/reg/tests/copy.c +++ b/programs/reg/tests/copy.c @@ -325,6 +325,56 @@ static void test_copy_complex_data(void) todo_wine ok(compare_export("file.reg", complex_data_test, 0), "compare_export() failed\n"); }
+static void test_copy_key_order(void) +{ + HKEY hkey; + DWORD r; + + delete_tree(HKEY_CURRENT_USER, COPY_SRC); + verify_key_nonexist(HKEY_CURRENT_USER, COPY_SRC); + + delete_tree(HKEY_CURRENT_USER, KEY_BASE); + verify_key_nonexist(HKEY_CURRENT_USER, KEY_BASE); + + add_key(HKEY_CURRENT_USER, COPY_SRC, &hkey); + add_key(hkey, "Subkey2", NULL); + add_key(hkey, "Subkey1", NULL); + close_key(hkey); + + run_reg_exe("reg copy HKCU\" COPY_SRC " HKCU\" KEY_BASE " /s /f", &r); + todo_wine ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); + todo_wine verify_key(HKEY_CURRENT_USER, KEY_BASE); + + run_reg_exe("reg export HKCU\" KEY_BASE " file.reg /y", &r); + todo_wine ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); + todo_wine ok(compare_export("file.reg", key_order_test, 0), "compare_export() failed\n"); +} + +static void test_copy_value_order(void) +{ + HKEY hkey; + DWORD r; + + delete_tree(HKEY_CURRENT_USER, COPY_SRC); + verify_key_nonexist(HKEY_CURRENT_USER, COPY_SRC); + + delete_tree(HKEY_CURRENT_USER, KEY_BASE); + verify_key_nonexist(HKEY_CURRENT_USER, KEY_BASE); + + add_key(HKEY_CURRENT_USER, COPY_SRC, &hkey); + add_value(hkey, "Value 2", REG_SZ, "I was added first!", 19); + add_value(hkey, "Value 1", REG_SZ, "I was added second!", 20); + close_key(hkey); + + run_reg_exe("reg copy HKCU\" COPY_SRC " HKCU\" KEY_BASE " /f", &r); + todo_wine ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); + todo_wine verify_key(HKEY_CURRENT_USER, KEY_BASE); + + run_reg_exe("reg export HKCU\" KEY_BASE " file.reg /y", &r); + todo_wine ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); + todo_wine ok(compare_export("file.reg", value_order_test, 0), "compare_export() failed\n"); +} + static void test_copy_hex_data(void) { HKEY hkey; @@ -498,6 +548,8 @@ START_TEST(copy) test_copy_empty_key(); test_copy_simple_data(); test_copy_complex_data(); + test_copy_key_order(); + test_copy_value_order(); test_copy_hex_data(); test_copy_embedded_null_values(); test_copy_slashes(); diff --git a/programs/reg/tests/export.c b/programs/reg/tests/export.c index be1d9ea4f86..1348b8eebdb 100644 --- a/programs/reg/tests/export.c +++ b/programs/reg/tests/export.c @@ -99,6 +99,18 @@ const char *complex_data_test = "@=dword:12345678\r\n" ""43981"=hex(abcd):56,61,6c,75,65,00\r\n\r\n";
+const char *key_order_test = + "\xef\xbb\xbfWindows Registry Editor Version 5.00\r\n\r\n" + "[HKEY_CURRENT_USER\" KEY_BASE "]\r\n\r\n" + "[HKEY_CURRENT_USER\" KEY_BASE "\Subkey1]\r\n\r\n" + "[HKEY_CURRENT_USER\" KEY_BASE "\Subkey2]\r\n\r\n"; + +const char *value_order_test = + "\xef\xbb\xbfWindows Registry Editor Version 5.00\r\n\r\n" + "[HKEY_CURRENT_USER\" KEY_BASE "]\r\n" + ""Value 2"="I was added first!"\r\n" + ""Value 1"="I was added second!"\r\n\r\n"; + const char *empty_hex_test = "\xef\xbb\xbfWindows Registry Editor Version 5.00\r\n\r\n" "[HKEY_CURRENT_USER\" KEY_BASE "]\r\n" @@ -164,18 +176,6 @@ static void test_export(void) HKEY hkey, subkey; BYTE hex[4], buffer[8];
- const char *key_order_test = - "\xef\xbb\xbfWindows Registry Editor Version 5.00\r\n\r\n" - "[HKEY_CURRENT_USER\" KEY_BASE "]\r\n\r\n" - "[HKEY_CURRENT_USER\" KEY_BASE "\Subkey1]\r\n\r\n" - "[HKEY_CURRENT_USER\" KEY_BASE "\Subkey2]\r\n\r\n"; - - const char *value_order_test = - "\xef\xbb\xbfWindows Registry Editor Version 5.00\r\n\r\n" - "[HKEY_CURRENT_USER\" KEY_BASE "]\r\n" - ""Value 2"="I was added first!"\r\n" - ""Value 1"="I was added second!"\r\n\r\n"; - delete_tree(HKEY_CURRENT_USER, KEY_BASE); verify_key_nonexist(HKEY_CURRENT_USER, KEY_BASE);
diff --git a/programs/reg/tests/reg_test.h b/programs/reg/tests/reg_test.h index 14636b4edc9..28e2d8e3abe 100644 --- a/programs/reg/tests/reg_test.h +++ b/programs/reg/tests/reg_test.h @@ -81,6 +81,8 @@ BOOL compare_export_(const char *file, unsigned line, const char *filename, extern const char *empty_key_test; extern const char *simple_data_test; extern const char *complex_data_test; +extern const char *key_order_test; +extern const char *value_order_test; extern const char *empty_hex_test; extern const char *empty_hex_test2; extern const char *hex_types_test;
Signed-off-by: Hugh McMaster hugh.mcmaster@outlook.com --- programs/reg/reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/reg/reg.h b/programs/reg/reg.h index 3fc9e392d45..b12d936d672 100644 --- a/programs/reg/reg.h +++ b/programs/reg/reg.h @@ -45,7 +45,7 @@ BOOL is_char(const WCHAR s, const WCHAR c); BOOL is_switch(const WCHAR *s, const WCHAR c);
/* add.c */ -int reg_add(int arc, WCHAR *argvW[]); +int reg_add(int argc, WCHAR *argvW[]);
/* delete.c */ int reg_delete(int argc, WCHAR *argvW[]);
Signed-off-by: Hugh McMaster hugh.mcmaster@outlook.com --- programs/reg/Makefile.in | 1 + programs/reg/copy.c | 24 ++++++++++++++++++++++++ programs/reg/reg.c | 6 ++++++ programs/reg/reg.h | 3 +++ programs/reg/reg.rc | 24 +++++++++++++++++++++++- programs/reg/resource.h | 1 + programs/reg/tests/copy.c | 14 +++++++------- 7 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 programs/reg/copy.c
diff --git a/programs/reg/Makefile.in b/programs/reg/Makefile.in index 5ef0d0854aa..7f7a5bc8032 100644 --- a/programs/reg/Makefile.in +++ b/programs/reg/Makefile.in @@ -6,6 +6,7 @@ EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
C_SRCS = \ add.c \ + copy.c \ delete.c \ export.c \ import.c \ diff --git a/programs/reg/copy.c b/programs/reg/copy.c new file mode 100644 index 00000000000..ba0916e9956 --- /dev/null +++ b/programs/reg/copy.c @@ -0,0 +1,24 @@ +/* + * Copyright 2021 Hugh McMaster + * + * 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 + */ + +#include "reg.h" + +int reg_copy(int argc, WCHAR *argvW[]) +{ + return 1; +} diff --git a/programs/reg/reg.c b/programs/reg/reg.c index 8ffbc5054d9..652fb19d931 100644 --- a/programs/reg/reg.c +++ b/programs/reg/reg.c @@ -1,5 +1,6 @@ /* * Copyright 2008 Andrew Riedi + * Copyright 2016-2017, 2021 Hugh McMaster * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -285,6 +286,7 @@ static BOOL is_help_switch(const WCHAR *s)
enum operations { REG_ADD, + REG_COPY, REG_DELETE, REG_EXPORT, REG_IMPORT, @@ -299,6 +301,7 @@ static enum operations get_operation(const WCHAR *str, int *op_help) static const struct op_info op_array[] = { { L"add", REG_ADD, STRING_ADD_USAGE }, + { L"copy", REG_COPY, STRING_COPY_USAGE }, { L"delete", REG_DELETE, STRING_DELETE_USAGE }, { L"export", REG_EXPORT, STRING_EXPORT_USAGE }, { L"import", REG_IMPORT, STRING_IMPORT_USAGE }, @@ -362,6 +365,9 @@ int __cdecl wmain(int argc, WCHAR *argvW[]) if (op == REG_ADD) return reg_add(argc, argvW);
+ if (op == REG_COPY) + return reg_copy(argc, argvW); + if (op == REG_DELETE) return reg_delete(argc, argvW);
diff --git a/programs/reg/reg.h b/programs/reg/reg.h index b12d936d672..67b49b7797f 100644 --- a/programs/reg/reg.h +++ b/programs/reg/reg.h @@ -47,6 +47,9 @@ BOOL is_switch(const WCHAR *s, const WCHAR c); /* add.c */ int reg_add(int argc, WCHAR *argvW[]);
+/* copy.c */ +int reg_copy(int argc, WCHAR *argvW[]); + /* delete.c */ int reg_delete(int argc, WCHAR *argvW[]);
diff --git a/programs/reg/reg.rc b/programs/reg/reg.rc index 117365547ca..c091023384c 100644 --- a/programs/reg/reg.rc +++ b/programs/reg/reg.rc @@ -29,7 +29,7 @@ STRINGTABLE STRING_USAGE, "Usage:\n\ \ REG [operation] [parameters]\n\n\ \Supported operations:\n\ -\ ADD | DELETE | EXPORT | IMPORT | QUERY\n\n\ +\ ADD | COPY | DELETE | EXPORT | IMPORT | QUERY\n\n\ \For help on a specific operation, type:\n\ \ REG [operation] /?\n\n"
@@ -172,9 +172,31 @@ STRINGTABLE STRING_OVERWRITE_FILE, "The file '%1' already exists. Do you want to overwrite it?" STRING_KEY_NONEXIST, "reg: Unable to find the specified registry key\n" STRING_KEY_IMPORT_FAILED, "reg: Unable to import the registry key '%1'\n" + STRING_REG_VIEW_USAGE, " /reg:32\n\ \ Access the registry using the 32-bit view.\n\n\ \ /reg:64\n\ \ Access the registry using the 64-bit view.\n\n" STRING_ACCESS_DENIED, "reg: Unable to access or create the specified registry key\n" + + STRING_COPY_USAGE, "REG COPY <key1> <key2> [/s] [/f]\n\n\ +\ Copies the contents of a specified registry key to another location.\n\ +\ By default, this operation only copies registry values. Use [/s] to\n\ +\ recursively copy all subkeys and values.\n\n\ +\ <key1>, <key2>\n\ +\ Registry keys specifying the source (<key1>) and destination (<key2>)\n\ +\ of the data. If <key2> does not exist, it is created.\n\n\ +\ Format: ROOT\Subkey\n\n\ +\ ROOT: A predefined registry key. This must be one of the following:\n\n\ +\ HKEY_LOCAL_MACHINE | HKLM\n\ +\ HKEY_CURRENT_USER | HKCU\n\ +\ HKEY_CLASSES_ROOT | HKCR\n\ +\ HKEY_USERS | HKU\n\ +\ HKEY_CURRENT_CONFIG | HKCC\n\n\ +\ Subkey: The full path to a registry key under a given ROOT key.\n\n\ +\ /s\n\ +\ Copy all subkeys and values from <key1> to <key2>.\n\n\ +\ /f\n\ +\ Overwrite all registry data in <key2> without prompting for confirmation.\n\ +\ This option does not modify subkeys and values that only exist in <key2>.\n\n" } diff --git a/programs/reg/resource.h b/programs/reg/resource.h index ecc4c09337c..670d297389a 100644 --- a/programs/reg/resource.h +++ b/programs/reg/resource.h @@ -63,3 +63,4 @@ #define STRING_KEY_IMPORT_FAILED 140 #define STRING_REG_VIEW_USAGE 141 #define STRING_ACCESS_DENIED 142 +#define STRING_COPY_USAGE 143 diff --git a/programs/reg/tests/copy.c b/programs/reg/tests/copy.c index 53d990811ac..ecd3cc5b29d 100644 --- a/programs/reg/tests/copy.c +++ b/programs/reg/tests/copy.c @@ -28,25 +28,25 @@ static void test_command_syntax(void) ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /?", &r); - todo_wine ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); + ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);
run_reg_exe("reg copy /h", &r); - todo_wine ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); + ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);
run_reg_exe("reg copy -H", &r); - todo_wine ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r); + ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);
run_reg_exe("reg copy /? /f", &r); - ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /h /f", &r); - ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /? /s", &r); - ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /h /s", &r); - ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /f", &r); ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
Signed-off-by: Hugh McMaster hugh.mcmaster@outlook.com --- programs/reg/reg.c | 13 ++++++++----- programs/reg/tests/copy.c | 8 ++++---- 2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/programs/reg/reg.c b/programs/reg/reg.c index 652fb19d931..8d3bf78f0a9 100644 --- a/programs/reg/reg.c +++ b/programs/reg/reg.c @@ -349,14 +349,12 @@ int __cdecl wmain(int argc, WCHAR *argvW[]) return 1; } else if (argc == 2) /* Valid operation, no arguments supplied */ - { - output_message(STRING_INVALID_SYNTAX); - output_message(STRING_FUNC_HELP, wcsupr(argvW[1])); - return 1; - } + goto invalid;
if (is_help_switch(argvW[2])) { + if (argc > 3) goto invalid; + output_message(op_help); output_message(STRING_REG_VIEW_USAGE); return 0; @@ -378,4 +376,9 @@ int __cdecl wmain(int argc, WCHAR *argvW[]) return reg_import(argc, argvW);
return reg_query(argc, argvW); + +invalid: + output_message(STRING_INVALID_SYNTAX); + output_message(STRING_FUNC_HELP, wcsupr(argvW[1])); + return 1; } diff --git a/programs/reg/tests/copy.c b/programs/reg/tests/copy.c index ecd3cc5b29d..5ddcc2c0ffa 100644 --- a/programs/reg/tests/copy.c +++ b/programs/reg/tests/copy.c @@ -37,16 +37,16 @@ static void test_command_syntax(void) ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);
run_reg_exe("reg copy /? /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /h /f", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /? /s", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /h /s", &r); - todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r); + ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);
run_reg_exe("reg copy /f", &r); ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);