xref: /reactos/base/applications/cmdutils/reg/query.c (revision 3e1f4074)
1 /*
2  * Copyright 2016-2017, 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 static const WCHAR *reg_type_to_wchar(DWORD type)
23 {
24     int i, array_size = ARRAY_SIZE(type_rels);
25 
26     for (i = 0; i < array_size; i++)
27     {
28         if (type == type_rels[i].type)
29             return type_rels[i].name;
30     }
31 
32     return NULL;
33 }
34 
35 static WCHAR *reg_data_to_wchar(DWORD type, const BYTE *src, DWORD size_bytes)
36 {
37     WCHAR *buffer = NULL;
38     int i;
39 
40     switch (type)
41     {
42         case REG_SZ:
43         case REG_EXPAND_SZ:
44             buffer = malloc(size_bytes);
45             lstrcpyW(buffer, (WCHAR *)src);
46             break;
47         case REG_NONE:
48         case REG_BINARY:
49         {
50             WCHAR *ptr;
51 
52             buffer = malloc((size_bytes * 2 + 1) * sizeof(WCHAR));
53 
54             if (!size_bytes)
55             {
56                 *buffer = 0;
57                 break;
58             }
59 
60             ptr = buffer;
61 
62             for (i = 0; i < size_bytes; i++)
63                 ptr += swprintf(ptr, L"%02X", src[i]);
64             break;
65         }
66         case REG_DWORD:
67      /* case REG_DWORD_LITTLE_ENDIAN: */
68         case REG_DWORD_BIG_ENDIAN:
69         {
70             const int zero_x_dword = 10;
71 
72             buffer = malloc((zero_x_dword + 1) * sizeof(WCHAR));
73             swprintf(buffer, L"0x%x", *(DWORD *)src);
74             break;
75         }
76         case REG_MULTI_SZ:
77         {
78             const int two_wchars = 2 * sizeof(WCHAR);
79             DWORD tmp_size;
80             const WCHAR *tmp = (const WCHAR *)src;
81             int len, destindex;
82 
83             if (size_bytes <= two_wchars)
84             {
85                 buffer = malloc(sizeof(WCHAR));
86                 *buffer = 0;
87                 return buffer;
88             }
89 
90             tmp_size = size_bytes - two_wchars; /* exclude both null terminators */
91             buffer = malloc(tmp_size * 2 + sizeof(WCHAR));
92             len = tmp_size / sizeof(WCHAR);
93 
94             for (i = 0, destindex = 0; i < len; i++, destindex++)
95             {
96                 if (tmp[i])
97                     buffer[destindex] = tmp[i];
98                 else
99                 {
100                     buffer[destindex++] = '\\';
101                     buffer[destindex] = '0';
102                 }
103             }
104             buffer[destindex] = 0;
105             break;
106         }
107     }
108     return buffer;
109 }
110 
111 static const WCHAR *newlineW = L"\n";
112 
113 static void output_value(const WCHAR *value_name, DWORD type, BYTE *data, DWORD data_size)
114 {
115     static const WCHAR *fmt = L"    %1";
116     WCHAR defval[32];
117     WCHAR *reg_data;
118 
119     if (value_name && value_name[0])
120         output_string(fmt, value_name);
121     else
122     {
123         LoadStringW(GetModuleHandleW(NULL), STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval));
124         output_string(fmt, defval);
125     }
126     output_string(fmt, reg_type_to_wchar(type));
127 
128     if (data)
129     {
130         reg_data = reg_data_to_wchar(type, data, data_size);
131         output_string(fmt, reg_data);
132         free(reg_data);
133     }
134     else
135     {
136         LoadStringW(GetModuleHandleW(NULL), STRING_VALUE_NOT_SET, defval, ARRAY_SIZE(defval));
137         output_string(fmt, defval);
138     }
139     output_string(newlineW);
140 }
141 
142 static unsigned int num_values_found = 0;
143 static REGSAM sam = 0;
144 
145 static int query_value(HKEY hkey, WCHAR *value_name, WCHAR *path, BOOL recurse)
146 {
147     LONG rc;
148     DWORD max_data_bytes = 2048, data_size;
149     DWORD subkey_len;
150     DWORD type, path_len, i;
151     BYTE *data;
152     static const WCHAR *fmt = L"%1\n";
153     WCHAR *subkey_name, *subkey_path;
154     HKEY subkey;
155 
156     data = malloc(max_data_bytes);
157 
158     for (;;)
159     {
160         data_size = max_data_bytes;
161         rc = RegQueryValueExW(hkey, value_name, NULL, &type, data, &data_size);
162         if (rc == ERROR_MORE_DATA)
163         {
164             max_data_bytes = data_size;
165             data = realloc(data, max_data_bytes);
166         }
167         else break;
168     }
169 
170     if (rc == ERROR_SUCCESS)
171     {
172         output_string(fmt, path);
173         output_value(value_name, type, data, data_size);
174         output_string(newlineW);
175         num_values_found++;
176     }
177 
178     free(data);
179 
180     if (!recurse)
181     {
182         if (rc == ERROR_FILE_NOT_FOUND)
183         {
184             if (value_name && *value_name)
185             {
186                 output_message(STRING_VALUE_NONEXIST);
187                 return 1;
188             }
189             output_string(fmt, path);
190             output_value(NULL, REG_SZ, NULL, 0);
191         }
192         return 0;
193     }
194 
195     subkey_name = malloc(MAX_SUBKEY_LEN * sizeof(WCHAR));
196 
197     path_len = lstrlenW(path);
198 
199     i = 0;
200     for (;;)
201     {
202         subkey_len = MAX_SUBKEY_LEN;
203         rc = RegEnumKeyExW(hkey, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL);
204         if (rc == ERROR_SUCCESS)
205         {
206             subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len);
207             if (!RegOpenKeyExW(hkey, subkey_name, 0, KEY_READ|sam, &subkey))
208             {
209                 query_value(subkey, value_name, subkey_path, recurse);
210                 RegCloseKey(subkey);
211             }
212             free(subkey_path);
213             i++;
214         }
215         else break;
216     }
217 
218     free(subkey_name);
219     return 0;
220 }
221 
222 static int query_all(HKEY hkey, WCHAR *path, BOOL recurse, BOOL recursing)
223 {
224     LONG rc;
225     DWORD num_subkeys, num_values;
226     DWORD max_value_len = 256, value_len;
227     DWORD max_data_bytes = 2048, data_size;
228     DWORD subkey_len;
229     DWORD i, type, path_len;
230     WCHAR *value_name, *subkey_name, *subkey_path;
231     BYTE *data;
232     HKEY subkey;
233 
234     rc = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, &num_subkeys, NULL,
235                           NULL, &num_values, NULL, NULL, NULL, NULL);
236     if (rc) return 1;
237 
238     if (num_values || recursing)
239         output_string(L"%1\n", path);
240 
241     value_name = malloc(max_value_len * sizeof(WCHAR));
242     data = malloc(max_data_bytes);
243 
244     i = 0;
245     for (;;)
246     {
247         value_len = max_value_len;
248         data_size = max_data_bytes;
249         rc = RegEnumValueW(hkey, i, value_name, &value_len, NULL, &type, data, &data_size);
250         if (rc == ERROR_SUCCESS)
251         {
252             output_value(value_name, type, data, data_size);
253             i++;
254         }
255         else if (rc == ERROR_MORE_DATA)
256         {
257             if (data_size > max_data_bytes)
258             {
259                 max_data_bytes = data_size;
260                 data = realloc(data, max_data_bytes);
261             }
262             else
263             {
264                 max_value_len *= 2;
265                 value_name = realloc(value_name, max_value_len * sizeof(WCHAR));
266             }
267         }
268         else break;
269     }
270 
271     free(data);
272     free(value_name);
273 
274     if (i || recursing)
275         output_string(newlineW);
276 
277     if (!num_subkeys)
278         return 0;
279 
280     subkey_name = malloc(MAX_SUBKEY_LEN * sizeof(WCHAR));
281 
282     path_len = lstrlenW(path);
283 
284     i = 0;
285     for (;;)
286     {
287         subkey_len = MAX_SUBKEY_LEN;
288         rc = RegEnumKeyExW(hkey, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL);
289         if (rc == ERROR_SUCCESS)
290         {
291             if (recurse)
292             {
293                 subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len);
294                 if (!RegOpenKeyExW(hkey, subkey_name, 0, KEY_READ|sam, &subkey))
295                 {
296                     query_all(subkey, subkey_path, recurse, TRUE);
297                     RegCloseKey(subkey);
298                 }
299                 free(subkey_path);
300             }
301             else output_string(L"%1\\%2\n", path, subkey_name);
302             i++;
303         }
304         else break;
305     }
306 
307     free(subkey_name);
308     return 0;
309 }
310 
311 static int run_query(HKEY root, WCHAR *path, WCHAR *key_name, WCHAR *value_name,
312                      BOOL value_empty, BOOL recurse)
313 {
314     HKEY hkey;
315     int ret;
316 
317     if (RegOpenKeyExW(root, path, 0, KEY_READ|sam, &hkey))
318     {
319         output_message(STRING_KEY_NONEXIST);
320         return 1;
321     }
322 
323     output_string(newlineW);
324 
325     if (value_name || value_empty)
326     {
327         ret = query_value(hkey, value_name, key_name, recurse);
328         if (recurse)
329             output_message(STRING_MATCHES_FOUND, num_values_found);
330     }
331     else
332         ret = query_all(hkey, key_name, recurse, FALSE);
333 
334     RegCloseKey(hkey);
335 
336     return ret;
337 }
338 
339 int reg_query(int argc, WCHAR *argvW[])
340 {
341     HKEY root;
342     WCHAR *path, *key_name, *value_name = NULL;
343     BOOL value_empty = FALSE, recurse = FALSE;
344     int i;
345 
346     if (!parse_registry_key(argvW[2], &root, &path))
347         return 1;
348 
349     for (i = 3; i < argc; i++)
350     {
351         WCHAR *str;
352 
353         if (argvW[i][0] != '/' && argvW[i][0] != '-')
354             goto invalid;
355 
356         str = &argvW[i][1];
357 
358         if (!lstrcmpiW(str, L"ve"))
359         {
360             if (value_empty) goto invalid;
361             value_empty = TRUE;
362             continue;
363         }
364         else if (!lstrcmpiW(str, L"reg:32"))
365         {
366             if (sam & KEY_WOW64_32KEY) goto invalid;
367             sam |= KEY_WOW64_32KEY;
368             continue;
369         }
370         else if (!lstrcmpiW(str, L"reg:64"))
371         {
372             if (sam & KEY_WOW64_64KEY) goto invalid;
373             sam |= KEY_WOW64_64KEY;
374             continue;
375         }
376         else if (!str[0] || str[1])
377             goto invalid;
378 
379         switch (towlower(*str))
380         {
381         case 'v':
382             if (value_name || !(value_name = argvW[++i]))
383                 goto invalid;
384             break;
385         case 's':
386             if (recurse) goto invalid;
387             recurse = TRUE;
388             break;
389         default:
390             goto invalid;
391         }
392     }
393 
394     if (value_name && value_empty)
395         goto invalid;
396 
397     if (sam == (KEY_WOW64_32KEY|KEY_WOW64_64KEY))
398         goto invalid;
399 
400     key_name = get_long_key(root, path);
401 
402     return run_query(root, path, key_name, value_name, value_empty, recurse);
403 
404 invalid:
405     output_message(STRING_INVALID_SYNTAX);
406     output_message(STRING_FUNC_HELP, _wcsupr(argvW[1]));
407     return 1;
408 }
409