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
output_error(LONG rc)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
ask_overwrite_value(WCHAR * path,WCHAR * value)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
run_copy(struct key * src,struct key * dest,REGSAM sam,BOOL recurse,BOOL force)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
reg_copy(int argc,WCHAR * argvW[])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