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