xref: /reactos/base/applications/cmdutils/reg/reg.c (revision 1a6f523e)
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