1 /*
2 * Copyright 2008 Andrew Riedi
3 * Copyright 2016-2017, 2021 Hugh McMaster
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include <stdio.h>
21 #include "reg.h"
22 #include <wine/debug.h>
23
24 WINE_DEFAULT_DEBUG_CHANNEL(reg);
25
26 static const struct
27 {
28 HKEY key;
29 const WCHAR *short_name;
30 const WCHAR *long_name;
31 }
32 root_rels[] =
33 {
34 {HKEY_LOCAL_MACHINE, L"HKLM", L"HKEY_LOCAL_MACHINE"},
35 {HKEY_CURRENT_USER, L"HKCU", L"HKEY_CURRENT_USER"},
36 {HKEY_CLASSES_ROOT, L"HKCR", L"HKEY_CLASSES_ROOT"},
37 {HKEY_USERS, L"HKU", L"HKEY_USERS"},
38 {HKEY_CURRENT_CONFIG, L"HKCC", L"HKEY_CURRENT_CONFIG"},
39 };
40
41 const struct reg_type_rels type_rels[] =
42 {
43 {REG_NONE, L"REG_NONE"},
44 {REG_SZ, L"REG_SZ"},
45 {REG_EXPAND_SZ, L"REG_EXPAND_SZ"},
46 {REG_BINARY, L"REG_BINARY"},
47 {REG_DWORD, L"REG_DWORD"},
48 {REG_DWORD_LITTLE_ENDIAN, L"REG_DWORD_LITTLE_ENDIAN"},
49 {REG_DWORD_BIG_ENDIAN, L"REG_DWORD_BIG_ENDIAN"},
50 {REG_MULTI_SZ, L"REG_MULTI_SZ"},
51 };
52
output_writeconsole(const WCHAR * str,DWORD wlen)53 void output_writeconsole(const WCHAR *str, DWORD wlen)
54 {
55 DWORD count, ret;
56
57 ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, wlen, &count, NULL);
58 if (!ret)
59 {
60 DWORD len;
61 char *msgA;
62
63 /* On Windows WriteConsoleW() fails if the output is redirected. So fall
64 * back to WriteFile(), assuming the console encoding is still the right
65 * one in that case.
66 */
67 len = WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, NULL, 0, NULL, NULL);
68 msgA = malloc(len);
69
70 WideCharToMultiByte(GetConsoleOutputCP(), 0, str, wlen, msgA, len, NULL, NULL);
71 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
72 free(msgA);
73 }
74 }
75
output_formatstring(const WCHAR * fmt,va_list va_args)76 static void output_formatstring(const WCHAR *fmt, va_list va_args)
77 {
78 WCHAR *str;
79 DWORD len;
80
81 len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
82 fmt, 0, 0, (WCHAR *)&str, 0, &va_args);
83 if (len == 0 && GetLastError() != NO_ERROR)
84 {
85 WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(fmt));
86 return;
87 }
88 output_writeconsole(str, len);
89 LocalFree(str);
90 }
91
output_message(unsigned int id,...)92 void WINAPIV output_message(unsigned int id, ...)
93 {
94 WCHAR *fmt = NULL;
95 int len;
96 va_list va_args;
97
98 if (!(len = LoadStringW(GetModuleHandleW(NULL), id, (WCHAR *)&fmt, 0)))
99 {
100 WINE_FIXME("LoadString failed with %d\n", GetLastError());
101 return;
102 }
103
104 len++;
105 fmt = malloc(len * sizeof(WCHAR));
106 if (!fmt) return;
107
108 LoadStringW(GetModuleHandleW(NULL), id, fmt, len);
109
110 va_start(va_args, id);
111 output_formatstring(fmt, va_args);
112 va_end(va_args);
113
114 free(fmt);
115 }
116
output_string(const WCHAR * fmt,...)117 void WINAPIV output_string(const WCHAR *fmt, ...)
118 {
119 va_list va_args;
120
121 va_start(va_args, fmt);
122 output_formatstring(fmt, va_args);
123 va_end(va_args);
124 }
125
126 /* ask_confirm() adapted from programs/cmd/builtins.c */
ask_confirm(unsigned int msgid,WCHAR * reg_info)127 BOOL ask_confirm(unsigned int msgid, WCHAR *reg_info)
128 {
129 HMODULE hmod;
130 WCHAR Ybuffer[4];
131 WCHAR Nbuffer[4];
132 WCHAR defval[32];
133 WCHAR answer[MAX_PATH];
134 WCHAR *str;
135 DWORD count;
136
137 hmod = GetModuleHandleW(NULL);
138 LoadStringW(hmod, STRING_YES, Ybuffer, ARRAY_SIZE(Ybuffer));
139 LoadStringW(hmod, STRING_NO, Nbuffer, ARRAY_SIZE(Nbuffer));
140 LoadStringW(hmod, STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval));
141
142 str = (reg_info && *reg_info) ? reg_info : defval;
143
144 while (1)
145 {
146 output_message(msgid, str);
147 output_message(STRING_YESNO);
148 ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), answer, ARRAY_SIZE(answer), &count, NULL);
149 answer[0] = towupper(answer[0]);
150 if (answer[0] == Ybuffer[0])
151 return TRUE;
152 if (answer[0] == Nbuffer[0])
153 return FALSE;
154 }
155 }
156
path_rootname_cmp(const WCHAR * input_path,const WCHAR * rootkey_name)157 static inline BOOL path_rootname_cmp(const WCHAR *input_path, const WCHAR *rootkey_name)
158 {
159 DWORD length = lstrlenW(rootkey_name);
160
161 return (!_wcsnicmp(input_path, rootkey_name, length) &&
162 (input_path[length] == 0 || input_path[length] == '\\'));
163 }
164
path_get_rootkey(const WCHAR * path)165 HKEY path_get_rootkey(const WCHAR *path)
166 {
167 DWORD i;
168
169 for (i = 0; i < ARRAY_SIZE(root_rels); i++)
170 {
171 if (path_rootname_cmp(path, root_rels[i].short_name) ||
172 path_rootname_cmp(path, root_rels[i].long_name))
173 return root_rels[i].key;
174 }
175
176 return NULL;
177 }
178
sane_path(const WCHAR * key)179 static BOOL sane_path(const WCHAR *key)
180 {
181 unsigned int i = lstrlenW(key);
182
183 if (i < 3 || (key[i - 1] == '\\' && key[i - 2] == '\\'))
184 {
185 output_message(STRING_INVALID_KEY);
186 return FALSE;
187 }
188
189 if (key[0] == '\\' && key[1] == '\\' && key[2] != '\\')
190 {
191 output_message(STRING_NO_REMOTE);
192 return FALSE;
193 }
194
195 return TRUE;
196 }
197
build_subkey_path(WCHAR * path,DWORD path_len,WCHAR * subkey_name,DWORD subkey_len)198 WCHAR *build_subkey_path(WCHAR *path, DWORD path_len, WCHAR *subkey_name, DWORD subkey_len)
199 {
200 WCHAR *subkey_path;
201
202 subkey_path = malloc((path_len + subkey_len + 2) * sizeof(WCHAR));
203 swprintf(subkey_path, L"%s\\%s", path, subkey_name);
204
205 return subkey_path;
206 }
207
get_long_key(HKEY root,WCHAR * path)208 WCHAR *get_long_key(HKEY root, WCHAR *path)
209 {
210 int i, len;
211 WCHAR *long_key;
212
213 for (i = 0; i < ARRAY_SIZE(root_rels); i++)
214 {
215 if (root == root_rels[i].key)
216 break;
217 }
218
219 len = lstrlenW(root_rels[i].long_name);
220
221 if (!path)
222 {
223 long_key = malloc((len + 1) * sizeof(WCHAR));
224 lstrcpyW(long_key, root_rels[i].long_name);
225 return long_key;
226 }
227
228 len += lstrlenW(path) + 1; /* add one for the concatenating backslash */
229 long_key = malloc((len + 1) * sizeof(WCHAR));
230 swprintf(long_key, L"%s\\%s", root_rels[i].long_name, path);
231 return long_key;
232 }
233
parse_registry_key(const WCHAR * key,HKEY * root,WCHAR ** path)234 BOOL parse_registry_key(const WCHAR *key, HKEY *root, WCHAR **path)
235 {
236 WCHAR *p;
237
238 if (!sane_path(key))
239 return FALSE;
240
241 *root = path_get_rootkey(key);
242 if (!*root)
243 {
244 output_message(STRING_INVALID_SYSTEM_KEY);
245 return FALSE;
246 }
247
248 *path = wcschr(key, '\\');
249
250 if (!*path)
251 return TRUE;
252
253 (*path)++;
254
255 if (!**path)
256 {
257 output_message(STRING_INVALID_SYSTEM_KEY);
258 return FALSE;
259 }
260
261 p = *path + lstrlenW(*path) - 1;
262 if (*p == '\\') *p = 0;
263
264 return TRUE;
265 }
266
is_char(const WCHAR s,const WCHAR c)267 BOOL is_char(const WCHAR s, const WCHAR c)
268 {
269 return (s == c || s == towupper(c));
270 }
271
is_switch(const WCHAR * s,const WCHAR c)272 BOOL is_switch(const WCHAR *s, const WCHAR c)
273 {
274 if (lstrlenW(s) > 2)
275 return FALSE;
276
277 return ((s[0] == '/' || s[0] == '-') && is_char(s[1], c));
278 }
279
is_help_switch(const WCHAR * s)280 static BOOL is_help_switch(const WCHAR *s)
281 {
282 return (is_switch(s, '?') || is_switch(s, 'h'));
283 }
284
285 enum operations {
286 REG_ADD,
287 REG_COPY,
288 REG_DELETE,
289 REG_EXPORT,
290 REG_IMPORT,
291 REG_QUERY,
292 REG_INVALID
293 };
294
get_operation(const WCHAR * str,int * op_help)295 static enum operations get_operation(const WCHAR *str, int *op_help)
296 {
297 struct op_info { const WCHAR *op; int id; int help_id; };
298
299 static const struct op_info op_array[] =
300 {
301 { L"add", REG_ADD, STRING_ADD_USAGE },
302 { L"copy", REG_COPY, STRING_COPY_USAGE },
303 { L"delete", REG_DELETE, STRING_DELETE_USAGE },
304 { L"export", REG_EXPORT, STRING_EXPORT_USAGE },
305 { L"import", REG_IMPORT, STRING_IMPORT_USAGE },
306 { L"query", REG_QUERY, STRING_QUERY_USAGE },
307 { NULL, -1, 0 }
308 };
309
310 const struct op_info *ptr;
311
312 for (ptr = op_array; ptr->op; ptr++)
313 {
314 if (!lstrcmpiW(str, ptr->op))
315 {
316 *op_help = ptr->help_id;
317 return ptr->id;
318 }
319 }
320
321 return REG_INVALID;
322 }
323
wmain(int argc,WCHAR * argvW[])324 int __cdecl wmain(int argc, WCHAR *argvW[])
325 {
326 int op, op_help;
327
328 if (argc == 1)
329 {
330 output_message(STRING_INVALID_SYNTAX);
331 output_message(STRING_REG_HELP);
332 return 1;
333 }
334
335 if (is_help_switch(argvW[1]))
336 {
337 output_message(STRING_USAGE);
338 return 0;
339 }
340
341 op = get_operation(argvW[1], &op_help);
342
343 if (op == REG_INVALID)
344 {
345 output_message(STRING_INVALID_OPTION, argvW[1]);
346 output_message(STRING_REG_HELP);
347 return 1;
348 }
349 else if (argc == 2) /* Valid operation, no arguments supplied */
350 goto invalid;
351
352 if (is_help_switch(argvW[2]))
353 {
354 if (argc > 3) goto invalid;
355
356 output_message(op_help);
357 output_message(STRING_REG_VIEW_USAGE);
358 return 0;
359 }
360
361 if (op == REG_ADD)
362 return reg_add(argc, argvW);
363
364 if (op == REG_COPY)
365 return reg_copy(argc, argvW);
366
367 if (op == REG_DELETE)
368 return reg_delete(argc, argvW);
369
370 if (op == REG_EXPORT)
371 return reg_export(argc, argvW);
372
373 if (op == REG_IMPORT)
374 return reg_import(argc, argvW);
375
376 return reg_query(argc, argvW);
377
378 invalid:
379 output_message(STRING_INVALID_SYNTAX);
380 output_message(STRING_FUNC_HELP, _wcsupr(argvW[1]));
381 return 1;
382 }
383