1 /* 2 * Copyright 2021 Hugh McMaster 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include <stdio.h> 20 #include "reg.h" 21 22 struct key { 23 HKEY root; /* system key */ 24 WCHAR *subkey; /* relative path to subkey */ 25 HKEY hkey; /* handle to opened or created key */ 26 WCHAR *path; /* full path to subkey */ 27 }; 28 29 enum operation { 30 COPY_NO, 31 COPY_YES, 32 COPY_ALL 33 }; 34 35 static void output_error(LONG rc) 36 { 37 if (rc == ERROR_FILE_NOT_FOUND) 38 output_message(STRING_KEY_NONEXIST); 39 else 40 output_message(STRING_ACCESS_DENIED); 41 } 42 43 static enum operation ask_overwrite_value(WCHAR *path, WCHAR *value) 44 { 45 HMODULE hmod; 46 static WCHAR Ybuffer[4]; 47 static WCHAR Nbuffer[4]; 48 static WCHAR Abuffer[4]; 49 static WCHAR defval[32]; 50 WCHAR answer[MAX_PATH]; 51 WCHAR *str; 52 DWORD count; 53 54 hmod = GetModuleHandleW(NULL); 55 LoadStringW(hmod, STRING_YES, Ybuffer, ARRAY_SIZE(Ybuffer)); 56 LoadStringW(hmod, STRING_NO, Nbuffer, ARRAY_SIZE(Nbuffer)); 57 LoadStringW(hmod, STRING_ALL, Abuffer, ARRAY_SIZE(Abuffer)); 58 LoadStringW(hmod, STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval)); 59 60 str = (value && *value) ? value : defval; 61 62 while (1) 63 { 64 output_message(STRING_COPY_CONFIRM, path, str); 65 output_message(STRING_YESNOALL); 66 67 ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count, NULL); 68 69 *answer = towupper(*answer); 70 71 if (*answer == *Ybuffer) 72 return COPY_YES; 73 if (*answer == *Nbuffer) 74 return COPY_NO; 75 if (*answer == *Abuffer) 76 return COPY_ALL; 77 } 78 } 79 80 static int run_copy(struct key *src, struct key *dest, REGSAM sam, BOOL recurse, BOOL force) 81 { 82 LONG rc; 83 DWORD max_subkey_len; 84 DWORD max_name_len, name_len; 85 DWORD max_data_size, data_size; 86 DWORD type, dispos, i; 87 WCHAR *name = NULL; 88 BYTE *data = NULL; 89 90 if ((rc = RegOpenKeyExW(src->root, src->subkey, 0, KEY_READ|sam, &src->hkey))) 91 { 92 output_error(rc); 93 return 1; 94 } 95 96 if ((rc = RegCreateKeyExW(dest->root, dest->subkey, 0, NULL, REG_OPTION_NON_VOLATILE, 97 KEY_READ|KEY_WRITE|sam, NULL, &dest->hkey, &dispos))) 98 { 99 RegCloseKey(src->hkey); 100 output_error(rc); 101 return 1; 102 } 103 104 rc = RegQueryInfoKeyW(src->hkey, NULL, NULL, NULL, NULL, &max_subkey_len, NULL, 105 NULL, &max_name_len, &max_data_size, NULL, NULL); 106 if (rc) goto cleanup; 107 108 max_name_len = max(max_subkey_len, max_name_len) + 1; 109 110 if (!(name = malloc(max_name_len * sizeof(WCHAR)))) 111 { 112 rc = ERROR_NOT_ENOUGH_MEMORY; 113 goto cleanup; 114 } 115 116 if (!(data = malloc(max_data_size))) 117 { 118 rc = ERROR_NOT_ENOUGH_MEMORY; 119 goto cleanup; 120 } 121 122 for (i = 0; ; i++) 123 { 124 name_len = max_name_len; 125 data_size = max_data_size; 126 127 rc = RegEnumValueW(src->hkey, i, name, &name_len, NULL, &type, data, &data_size); 128 if (rc == ERROR_NO_MORE_ITEMS) break; 129 if (rc) goto cleanup; 130 131 if (!force && dispos == REG_OPENED_EXISTING_KEY) 132 { 133 if (!RegQueryValueExW(dest->hkey, name, NULL, NULL, NULL, NULL)) 134 { 135 enum operation op; 136 137 op = ask_overwrite_value(src->path, name); 138 if (op == COPY_NO) continue; 139 if (op == COPY_ALL) force = TRUE; 140 } 141 } 142 143 if ((rc = RegSetValueExW(dest->hkey, name, 0, type, data, data_size))) 144 { 145 output_error(rc); 146 goto cleanup; 147 } 148 } 149 150 for (i = 0; recurse; i++) 151 { 152 struct key subkey_src, subkey_dest; 153 size_t path_len; 154 155 name_len = max_name_len; 156 157 rc = RegEnumKeyExW(src->hkey, i, name, &name_len, NULL, NULL, NULL, NULL); 158 if (rc) break; 159 160 subkey_src.root = src->hkey; 161 subkey_src.subkey = name; 162 163 subkey_dest.root = dest->hkey; 164 subkey_dest.subkey = name; 165 166 path_len = lstrlenW(src->path) + name_len + 2; 167 168 if (!(subkey_src.path = malloc(path_len * sizeof(WCHAR)))) 169 { 170 rc = ERROR_NOT_ENOUGH_MEMORY; 171 goto cleanup; 172 } 173 174 swprintf(subkey_src.path, L"%s\\%s", src->path, name); 175 176 rc = run_copy(&subkey_src, &subkey_dest, sam, TRUE, force); 177 178 free(subkey_src.path); 179 180 if (rc) goto cleanup; 181 } 182 183 cleanup: 184 free(name); 185 free(data); 186 187 RegCloseKey(src->hkey); 188 RegCloseKey(dest->hkey); 189 190 return rc != ERROR_NO_MORE_ITEMS; 191 } 192 193 int reg_copy(int argc, WCHAR *argvW[]) 194 { 195 struct key src, dest; 196 BOOL recurse = FALSE, force = FALSE; 197 REGSAM sam = 0; 198 int i; 199 200 if (argc == 3) 201 goto invalid; 202 203 if (!parse_registry_key(argvW[2], &src.root, &src.subkey)) 204 return 1; 205 206 if (!parse_registry_key(argvW[3], &dest.root, &dest.subkey)) 207 return 1; 208 209 for (i = 4; i < argc; i++) 210 { 211 WCHAR *str; 212 213 if (argvW[i][0] != '/' && argvW[i][0] != '-') 214 goto invalid; 215 216 str = &argvW[i][1]; 217 218 if (!lstrcmpiW(str, L"reg:32")) 219 { 220 if (sam & KEY_WOW64_32KEY) goto invalid; 221 sam |= KEY_WOW64_32KEY; 222 continue; 223 } 224 else if (!lstrcmpiW(str, L"reg:64")) 225 { 226 if (sam & KEY_WOW64_64KEY) goto invalid; 227 sam |= KEY_WOW64_64KEY; 228 continue; 229 } 230 else if (!str[0] || str[1]) 231 goto invalid; 232 233 switch (towlower(*str)) 234 { 235 case 's': 236 if (recurse) goto invalid; 237 recurse = TRUE; 238 break; 239 case 'f': 240 if (force) goto invalid; 241 force = TRUE; 242 break; 243 default: 244 goto invalid; 245 } 246 } 247 248 if (sam == (KEY_WOW64_32KEY|KEY_WOW64_64KEY)) 249 goto invalid; 250 251 if (src.root == dest.root && !lstrcmpiW(src.subkey, dest.subkey)) 252 { 253 output_message(STRING_COPY_SRC_DEST_SAME); 254 return 1; 255 } 256 257 src.path = src.subkey; 258 259 return run_copy(&src, &dest, sam, recurse, force); 260 261 invalid: 262 output_message(STRING_INVALID_SYNTAX); 263 output_message(STRING_FUNC_HELP, _wcsupr(argvW[1])); 264 return 1; 265 } 266