]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* regtool.cc |
2 | ||
1fd5e000 CF |
3 | This file is part of Cygwin. |
4 | ||
5 | This software is a copyrighted work licensed under the terms of the | |
6 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
7 | details. */ | |
8 | ||
9 | #include <stdio.h> | |
10 | #include <stdlib.h> | |
17c8ac39 | 11 | #include <errno.h> |
1fd5e000 | 12 | #include <ctype.h> |
7077c48e | 13 | #include <wchar.h> |
1fd5e000 | 14 | #include <getopt.h> |
7077c48e | 15 | #include <locale.h> |
1fd5e000 | 16 | #include <windows.h> |
17c8ac39 | 17 | #include <sys/cygwin.h> |
92b499ac | 18 | #include <cygwin/version.h> |
1fd5e000 | 19 | |
403985a4 CF |
20 | #define DEFAULT_KEY_SEPARATOR '\\' |
21 | ||
70158caf CV |
22 | #define REG_AUTO -1 |
23 | ||
24 | int value_type = REG_AUTO; | |
1fd5e000 | 25 | |
403985a4 CF |
26 | char key_sep = DEFAULT_KEY_SEPARATOR; |
27 | ||
37770e00 CF |
28 | #define LIST_KEYS 0x01 |
29 | #define LIST_VALS 0x02 | |
30 | #define LIST_ALL (LIST_KEYS | LIST_VALS) | |
31 | ||
9bd02410 CF |
32 | static char *prog_name; |
33 | ||
34 | static struct option longopts[] = | |
35 | { | |
17c8ac39 | 36 | {"binary", no_argument, NULL, 'b' }, |
70158caf | 37 | {"dword", no_argument, NULL, 'd' }, |
7077c48e | 38 | {"dword-be", no_argument, NULL, 'D' }, |
9bd02410 | 39 | {"expand-string", no_argument, NULL, 'e' }, |
a6791b3b | 40 | {"force", no_argument, NULL, 'f' }, |
9bd02410 CF |
41 | {"help", no_argument, NULL, 'h' }, |
42 | {"integer", no_argument, NULL, 'i' }, | |
43 | {"keys", no_argument, NULL, 'k'}, | |
44 | {"list", no_argument, NULL, 'l'}, | |
45 | {"multi-string", no_argument, NULL, 'm'}, | |
70158caf | 46 | {"none", no_argument, NULL, 'n' }, |
9bd02410 CF |
47 | {"postfix", no_argument, NULL, 'p'}, |
48 | {"quiet", no_argument, NULL, 'q'}, | |
70158caf | 49 | {"qword", no_argument, NULL, 'Q' }, |
9bd02410 CF |
50 | {"string", no_argument, NULL, 's'}, |
51 | {"verbose", no_argument, NULL, 'v'}, | |
52 | {"version", no_argument, NULL, 'V'}, | |
40c60b89 | 53 | {"wow64", no_argument, NULL, 'w'}, |
3c256e38 | 54 | {"wow32", no_argument, NULL, 'W'}, |
70158caf | 55 | {"hex", no_argument, NULL, 'x'}, |
403985a4 | 56 | {"key-separator", required_argument, NULL, 'K'}, |
9bd02410 CF |
57 | {NULL, 0, NULL, 0} |
58 | }; | |
59 | ||
a6791b3b | 60 | static char opts[] = "bdDefhiklmnpqQsvVwWxK:"; |
70158caf CV |
61 | |
62 | const char *types[] = | |
63 | { | |
64 | "REG_NONE", | |
65 | "REG_SZ", | |
66 | "REG_EXPAND_SZ", | |
67 | "REG_BINARY", | |
68 | "REG_DWORD", | |
69 | "REG_DWORD_BIG_ENDIAN", | |
70 | "REG_LINK", | |
71 | "REG_MULTI_SZ", | |
72 | "REG_RESOURCE_LIST", | |
73 | "REG_FULL_RESOURCE_DESCRIPTOR", | |
74 | "REG_RESOURCE_REQUIREMENTS_LIST", | |
75 | "REG_QWORD", | |
76 | }; | |
9bd02410 | 77 | |
37770e00 CF |
78 | int listwhat = 0; |
79 | int postfix = 0; | |
1fd5e000 CF |
80 | int verbose = 0; |
81 | int quiet = 0; | |
70158caf | 82 | int hex = 0; |
40c60b89 | 83 | DWORD wow64 = 0; |
a6791b3b | 84 | DWORD restore_flags = 0; |
1fd5e000 CF |
85 | char **argv; |
86 | ||
87 | HKEY key; | |
7077c48e | 88 | wchar_t *value; |
1fd5e000 | 89 | |
e7fca6f8 | 90 | static void __attribute__ ((__noreturn__)) |
9bd02410 CF |
91 | usage (FILE *where = stderr) |
92 | { | |
93 | fprintf (where, "" | |
70158caf | 94 | "Usage: %s [OPTION] ACTION KEY [data...]\n" |
92b499ac | 95 | "\n" |
aa275fe0 | 96 | "View or edit the Win32 registry\n" |
70158caf | 97 | "\n", prog_name); |
9bd02410 | 98 | if (where == stdout) |
dae37d5d | 99 | { |
70158caf CV |
100 | fprintf (where, "" |
101 | "Actions:\n" | |
92b499ac | 102 | "\n" |
70158caf CV |
103 | " add KEY\\SUBKEY add new SUBKEY\n" |
104 | " check KEY exit 0 if KEY exists, 1 if not\n" | |
105 | " get KEY\\VALUE prints VALUE to stdout\n" | |
106 | " list KEY list SUBKEYs and VALUEs\n" | |
107 | " remove KEY remove KEY\n" | |
108 | " set KEY\\VALUE [data ...] set VALUE\n" | |
109 | " unset KEY\\VALUE removes VALUE from KEY\n" | |
110 | " load KEY\\SUBKEY PATH load hive from PATH into new SUBKEY\n" | |
111 | " unload KEY\\SUBKEY unload hive and remove SUBKEY\n" | |
a6791b3b CV |
112 | " save KEY\\SUBKEY PATH save SUBKEY into new file PATH\n" |
113 | " restore KEY\\SUBKEY PATH restore SUBKEY from file PATH\n" | |
70158caf CV |
114 | "\n"); |
115 | fprintf (where, "" | |
a6791b3b | 116 | "Options for 'list' action:\n" |
92b499ac | 117 | "\n" |
70158caf CV |
118 | " -k, --keys print only KEYs\n" |
119 | " -l, --list print only VALUEs\n" | |
120 | " -p, --postfix like ls -p, appends '\\' postfix to KEY names\n" | |
121 | "\n" | |
a6791b3b | 122 | "Options for 'get' action:\n" |
92b499ac | 123 | "\n" |
70158caf CV |
124 | " -b, --binary print data as printable hex bytes\n" |
125 | " -n, --none print data as stream of bytes as stored in registry\n" | |
126 | " -x, --hex print numerical data as hex numbers\n" | |
127 | "\n" | |
a6791b3b | 128 | "Options for 'set' action:\n" |
92b499ac | 129 | "\n" |
70158caf CV |
130 | " -b, --binary set type to REG_BINARY (hex args or '-')\n" |
131 | " -d, --dword set type to REG_DWORD\n" | |
7077c48e | 132 | " -D, --dword-be set type to REG_DWORD_BIG_ENDIAN\n" |
70158caf CV |
133 | " -e, --expand-string set type to REG_EXPAND_SZ\n" |
134 | " -i, --integer set type to REG_DWORD\n" | |
135 | " -m, --multi-string set type to REG_MULTI_SZ\n" | |
136 | " -n, --none set type to REG_NONE\n" | |
137 | " -Q, --qword set type to REG_QWORD\n" | |
138 | " -s, --string set type to REG_SZ\n" | |
139 | "\n" | |
140 | "Options for 'set' and 'unset' Actions:\n" | |
92b499ac | 141 | "\n" |
70158caf CV |
142 | " -K<c>, --key-separator[=]<c> set key-value separator to <c> instead of '\\'\n" |
143 | "\n" | |
a6791b3b CV |
144 | "Options for 'restore' action:\n" |
145 | "\n" | |
146 | " -f, --force restore even if open handles exist at or beneath the location\n" | |
147 | " in the registry hierarchy to which KEY\\SUBKEY points\n" | |
148 | "\n" | |
70158caf | 149 | "Other Options:\n" |
92b499ac | 150 | "\n" |
70158caf CV |
151 | " -h, --help output usage information and exit\n" |
152 | " -q, --quiet no error output, just nonzero return if KEY/VALUE missing\n" | |
153 | " -v, --verbose verbose output, including VALUE contents when applicable\n" | |
f51e76da CV |
154 | " -w, --wow64 access 64 bit registry view\n" |
155 | " -W, --wow32 access 32 bit registry view\n" | |
70158caf CV |
156 | " -V, --version output version information and exit\n" |
157 | "\n"); | |
dae37d5d CV |
158 | fprintf (where, "" |
159 | "KEY is in the format [host]\\prefix\\KEY\\KEY\\VALUE, where host is optional\n" | |
160 | "remote host in either \\\\hostname or hostname: format and prefix is any of:\n" | |
161 | " root HKCR HKEY_CLASSES_ROOT (local only)\n" | |
162 | " config HKCC HKEY_CURRENT_CONFIG (local only)\n" | |
163 | " user HKCU HKEY_CURRENT_USER (local only)\n" | |
164 | " machine HKLM HKEY_LOCAL_MACHINE\n" | |
165 | " users HKU HKEY_USERS\n" | |
166 | "\n" | |
d3110717 BI |
167 | "You can use forward slash ('/') as a separator instead of backslash, in\n" |
168 | "that case backslash is treated as an escape character.\n" | |
169 | "You can also supply the registry path prefix /proc/registry{,32,64}/ to\n" | |
170 | "use path completion.\n"); | |
dae37d5d CV |
171 | fprintf (where, "" |
172 | "Example:\n" | |
d3110717 | 173 | "%s list '/HKLM/SOFTWARE/Classes/MIME/Database/Content Type/audio\\/wav'\n\n", prog_name); |
dae37d5d | 174 | } |
9bd02410 | 175 | if (where == stderr) |
70158caf CV |
176 | fprintf (where, |
177 | "ACTION is one of add, check, get, list, remove, set, unset, load, unload, save\n" | |
178 | "\n" | |
92b499ac | 179 | "Try `%s --help' for more information.\n", prog_name); |
9bd02410 CF |
180 | exit (where == stderr ? 1 : 0); |
181 | } | |
1fd5e000 | 182 | |
9bd02410 CF |
183 | static void |
184 | print_version () | |
1fd5e000 | 185 | { |
92b499ac | 186 | printf ("regtool (cygwin) %d.%d.%d\n" |
1b23b30b | 187 | "Registry tool\n" |
6e623e93 | 188 | "Copyright (C) 2000 - %s Cygwin Authors\n" |
1b23b30b | 189 | "This is free software; see the source for copying conditions. There is NO\n" |
92b499ac | 190 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", |
1b23b30b CF |
191 | CYGWIN_VERSION_DLL_MAJOR / 1000, |
192 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
193 | CYGWIN_VERSION_DLL_MINOR, | |
194 | strrchr (__DATE__, ' ') + 1); | |
1fd5e000 CF |
195 | } |
196 | ||
197 | void | |
61522196 | 198 | Fail (unsigned int rv) |
1fd5e000 | 199 | { |
4d336756 FH |
200 | wchar_t *buf; |
201 | ||
1fd5e000 CF |
202 | if (!quiet) |
203 | { | |
4d336756 FH |
204 | FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
205 | | FORMAT_MESSAGE_FROM_SYSTEM, | |
206 | 0, rv, 0, (WCHAR *)& buf, 0, 0); | |
207 | fprintf (stderr, "Error (%d): %ls\n", rv, buf); | |
37770e00 | 208 | LocalFree (buf); |
1fd5e000 | 209 | } |
b82a7a5e | 210 | exit (1); |
1fd5e000 CF |
211 | } |
212 | ||
a35d9f1a | 213 | static struct |
b82a7a5e | 214 | { |
1fd5e000 CF |
215 | const char *string; |
216 | HKEY key; | |
b82a7a5e CF |
217 | } wkprefixes[] = |
218 | { | |
219 | {"root", HKEY_CLASSES_ROOT}, | |
220 | {"HKCR", HKEY_CLASSES_ROOT}, | |
221 | {"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT}, | |
222 | {"config", HKEY_CURRENT_CONFIG}, | |
223 | {"HKCC", HKEY_CURRENT_CONFIG}, | |
224 | {"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG}, | |
225 | {"user", HKEY_CURRENT_USER}, | |
226 | {"HKCU", HKEY_CURRENT_USER}, | |
227 | {"HKEY_CURRENT_USER", HKEY_CURRENT_USER}, | |
228 | {"machine", HKEY_LOCAL_MACHINE}, | |
229 | {"HKLM", HKEY_LOCAL_MACHINE}, | |
230 | {"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE}, | |
231 | {"users", HKEY_USERS}, | |
232 | {"HKU", HKEY_USERS}, | |
233 | {"HKEY_USERS", HKEY_USERS}, | |
234 | {0, 0} | |
1fd5e000 CF |
235 | }; |
236 | ||
b82a7a5e CF |
237 | void |
238 | translate (char *key) | |
1fd5e000 CF |
239 | { |
240 | #define isodigit(c) (strchr("01234567", c)) | |
241 | #define tooct(c) ((c)-'0') | |
242 | #define tohex(c) (strchr(_hs,tolower(c))-_hs) | |
243 | static char _hs[] = "0123456789abcdef"; | |
244 | ||
245 | char *d = key; | |
246 | char *s = key; | |
247 | char c; | |
248 | ||
249 | while (*s) | |
250 | { | |
251 | if (*s == '\\') | |
b82a7a5e CF |
252 | switch (*++s) |
253 | { | |
254 | case 'a': | |
255 | *d++ = '\007'; | |
256 | break; | |
257 | case 'b': | |
258 | *d++ = '\b'; | |
259 | break; | |
260 | case 'e': | |
261 | *d++ = '\033'; | |
262 | break; | |
263 | case 'f': | |
264 | *d++ = '\f'; | |
265 | break; | |
266 | case 'n': | |
267 | *d++ = '\n'; | |
268 | break; | |
269 | case 'r': | |
270 | *d++ = '\r'; | |
271 | break; | |
272 | case 't': | |
273 | *d++ = '\t'; | |
274 | break; | |
275 | case 'v': | |
276 | *d++ = '\v'; | |
277 | break; | |
278 | case '0': | |
279 | case '1': | |
280 | case '2': | |
281 | case '3': | |
282 | case '4': | |
283 | case '5': | |
284 | case '6': | |
285 | case '7': | |
286 | c = tooct (*s); | |
287 | if (isodigit (s[1])) | |
288 | { | |
289 | c = (c << 3) | tooct (*++s); | |
290 | if (isodigit (s[1])) | |
291 | c = (c << 3) | tooct (*++s); | |
292 | } | |
293 | *d++ = c; | |
294 | break; | |
295 | case 'x': | |
296 | if (!isxdigit (s[1])) | |
7adad121 | 297 | c = '0'; |
b82a7a5e CF |
298 | else |
299 | { | |
300 | c = tohex (*++s); | |
301 | if (isxdigit (s[1])) | |
302 | c = (c << 4) | tohex (*++s); | |
303 | } | |
304 | *d++ = c; | |
305 | break; | |
306 | default: /* before non-special char: just add the char */ | |
307 | *d++ = *s; | |
308 | break; | |
309 | } | |
1fd5e000 | 310 | else if (*s == '/') |
b82a7a5e | 311 | *d++ = '\\'; |
1fd5e000 | 312 | else |
b82a7a5e | 313 | *d++ = *s; |
1fd5e000 CF |
314 | ++s; |
315 | } | |
316 | *d = '\0'; | |
317 | } | |
318 | ||
319 | void | |
17c8ac39 | 320 | find_key (int howmanyparts, REGSAM access, int option = 0) |
1fd5e000 | 321 | { |
381fb8ba CV |
322 | HKEY base; |
323 | int rv; | |
324 | char *n = argv[0], *e, *h, c; | |
325 | char* host = NULL; | |
1fd5e000 | 326 | int i; |
7077c48e CV |
327 | size_t len; |
328 | ||
1fd5e000 | 329 | if (*n == '/') |
b82a7a5e | 330 | translate (n); |
381fb8ba CV |
331 | if (*n != '\\') |
332 | { | |
333 | /* expect host:/key/value format */ | |
334 | host = (char*) malloc (strlen (n) + 1); | |
4bfc614b | 335 | host[0] = host [1] = '\\'; |
381fb8ba | 336 | for (e = n, h = host + 2; *e && *e != ':'; e++, h++) |
4bfc614b | 337 | *h = *e; |
381fb8ba CV |
338 | *h = 0; |
339 | n = e + 1; | |
340 | if (*n == '/') | |
4bfc614b | 341 | translate (n); |
381fb8ba CV |
342 | } |
343 | else if (n[0] == '\\' && n[1] == '\\') | |
344 | { | |
345 | /* expect //host/key/value format */ | |
346 | host = (char*) malloc (strlen (n) + 1); | |
4bfc614b | 347 | host[0] = host[1] = '\\'; |
381fb8ba | 348 | for (e = n + 2, h = host + 2; *e && *e != '\\'; e++, h++) |
4bfc614b | 349 | *h = *e; |
381fb8ba CV |
350 | *h = 0; |
351 | n = e; | |
352 | } | |
d3110717 BI |
353 | else if (strncmp ("\\proc\\registry", n, strlen ("\\proc\\registry")) == 0) |
354 | { | |
355 | /* skip /proc/registry{,32,64}/ prefix */ | |
356 | n += strlen ("\\proc\\registry"); | |
357 | if (strncmp ("64", n, strlen ("64")) == 0) | |
358 | n += strlen ("64"); | |
359 | else if (strncmp ("32", n, strlen ("32")) == 0) | |
360 | n += strlen ("32"); | |
361 | } | |
381fb8ba | 362 | while (*n != '\\') |
1fd5e000 | 363 | n++; |
381fb8ba | 364 | *n++ = 0; |
288f125e CF |
365 | for (e = n; *e && *e != '\\'; e++); |
366 | c = *e; | |
1fd5e000 | 367 | *e = 0; |
b82a7a5e CF |
368 | for (i = 0; wkprefixes[i].string; i++) |
369 | if (strcmp (wkprefixes[i].string, n) == 0) | |
1fd5e000 CF |
370 | break; |
371 | if (!wkprefixes[i].string) | |
372 | { | |
b82a7a5e CF |
373 | fprintf (stderr, "Unknown key prefix. Valid prefixes are:\n"); |
374 | for (i = 0; wkprefixes[i].string; i++) | |
375 | fprintf (stderr, "\t%s\n", wkprefixes[i].string); | |
376 | exit (1); | |
1fd5e000 CF |
377 | } |
378 | ||
379 | n = e; | |
380 | *e = c; | |
381 | while (*n && *n == '\\') | |
382 | n++; | |
b82a7a5e | 383 | e = n + strlen (n); |
1fd5e000 CF |
384 | if (howmanyparts > 1) |
385 | { | |
403985a4 | 386 | while (n < e && *e != key_sep) |
1fd5e000 | 387 | e--; |
403985a4 | 388 | if (*e != key_sep) |
1fd5e000 | 389 | { |
4c61c04c | 390 | key = wkprefixes[i].key; |
7077c48e CV |
391 | if (value) |
392 | free (value); | |
393 | len = mbstowcs (NULL, n, 0) + 1; | |
708d2a1e | 394 | value = (wchar_t *) malloc (len * sizeof (wchar_t)); |
7077c48e | 395 | mbstowcs (value, n, len); |
4c61c04c CV |
396 | return; |
397 | } | |
398 | else | |
4bfc614b | 399 | { |
4c61c04c | 400 | *e = 0; |
7077c48e CV |
401 | if (value) |
402 | free (value); | |
403 | len = mbstowcs (NULL, e + 1, 0) + 1; | |
708d2a1e | 404 | value = (wchar_t *) malloc (len * sizeof (wchar_t)); |
7077c48e | 405 | mbstowcs (value, e + 1, len); |
1fd5e000 | 406 | } |
1fd5e000 | 407 | } |
381fb8ba CV |
408 | if (host) |
409 | { | |
410 | rv = RegConnectRegistry (host, wkprefixes[i].key, &base); | |
411 | if (rv != ERROR_SUCCESS) | |
412 | Fail (rv); | |
413 | free (host); | |
414 | } | |
415 | else | |
416 | base = wkprefixes[i].key; | |
417 | ||
1fd5e000 | 418 | if (n[0] == 0) |
381fb8ba CV |
419 | key = base; |
420 | else | |
1fd5e000 | 421 | { |
7077c48e CV |
422 | len = mbstowcs (NULL, n, 0) + 1; |
423 | wchar_t name[len]; | |
424 | mbstowcs (name, n, len); | |
17c8ac39 CV |
425 | if (access) |
426 | { | |
7077c48e | 427 | rv = RegOpenKeyExW (base, name, 0, access | wow64, &key); |
17c8ac39 CV |
428 | if (option && (rv == ERROR_SUCCESS || rv == ERROR_ACCESS_DENIED)) |
429 | { | |
7077c48e CV |
430 | /* reopen with desired option due to missing option support in |
431 | RegOpenKeyE */ | |
432 | /* FIXME: may create the key in rare cases (e.g. access denied | |
433 | in parent) */ | |
17c8ac39 | 434 | HKEY key2; |
7077c48e CV |
435 | if (RegCreateKeyExW (base, name, 0, NULL, option, access | wow64, |
436 | NULL, &key2, NULL) | |
17c8ac39 | 437 | == ERROR_SUCCESS) |
1b23b30b | 438 | { |
17c8ac39 CV |
439 | if (rv == ERROR_SUCCESS) |
440 | RegCloseKey (key); | |
441 | key = key2; | |
442 | rv = ERROR_SUCCESS; | |
1b23b30b | 443 | } |
17c8ac39 CV |
444 | } |
445 | if (rv != ERROR_SUCCESS) | |
446 | Fail (rv); | |
447 | } | |
448 | else if (argv[1]) | |
1b23b30b | 449 | { |
7077c48e CV |
450 | ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], NULL, 0); |
451 | wchar_t win32_path[len]; | |
452 | cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], win32_path, len); | |
453 | rv = RegLoadKeyW (base, name, win32_path); | |
17c8ac39 CV |
454 | if (rv != ERROR_SUCCESS) |
455 | Fail (rv); | |
456 | if (verbose) | |
7077c48e | 457 | printf ("key %ls loaded from file %ls\n", name, win32_path); |
17c8ac39 CV |
458 | } |
459 | else | |
1b23b30b | 460 | { |
7077c48e | 461 | rv = RegUnLoadKeyW (base, name); |
17c8ac39 CV |
462 | if (rv != ERROR_SUCCESS) |
463 | Fail (rv); | |
464 | if (verbose) | |
7077c48e | 465 | printf ("key %ls unloaded\n", name); |
17c8ac39 | 466 | } |
1fd5e000 | 467 | } |
1fd5e000 CF |
468 | } |
469 | ||
470 | ||
471 | int | |
b82a7a5e | 472 | cmd_list () |
1fd5e000 CF |
473 | { |
474 | DWORD num_subkeys, maxsubkeylen, num_values, maxvalnamelen, maxvaluelen; | |
475 | DWORD maxclasslen; | |
7077c48e CV |
476 | wchar_t *subkey_name, *value_name, *class_name, *vd; |
477 | unsigned char *value_data; | |
1fd5e000 CF |
478 | DWORD i, j, m, n, t; |
479 | int v; | |
480 | ||
b82a7a5e | 481 | find_key (1, KEY_READ); |
7077c48e CV |
482 | RegQueryInfoKeyW (key, 0, 0, 0, &num_subkeys, &maxsubkeylen, &maxclasslen, |
483 | &num_values, &maxvalnamelen, &maxvaluelen, 0, 0); | |
1fd5e000 | 484 | |
7077c48e CV |
485 | subkey_name = (wchar_t *) malloc ((maxsubkeylen + 1) * sizeof (wchar_t)); |
486 | class_name = (wchar_t *) malloc ((maxclasslen + 1) * sizeof (wchar_t)); | |
487 | value_name = (wchar_t *) malloc ((maxvalnamelen + 1) * sizeof (wchar_t)); | |
b82a7a5e | 488 | value_data = (unsigned char *) malloc (maxvaluelen + 1); |
1fd5e000 | 489 | |
37770e00 CF |
490 | if (!listwhat) |
491 | listwhat = LIST_ALL; | |
1fd5e000 | 492 | |
37770e00 CF |
493 | if (listwhat & LIST_KEYS) |
494 | for (i = 0; i < num_subkeys; i++) | |
495 | { | |
7077c48e CV |
496 | m = (maxsubkeylen + 1) * sizeof (wchar_t); |
497 | n = (maxclasslen + 1) * sizeof (wchar_t); | |
498 | RegEnumKeyExW (key, i, subkey_name, &m, 0, class_name, &n, 0); | |
499 | printf ("%ls", subkey_name); | |
70158caf CV |
500 | if (postfix || verbose) |
501 | fputc (key_sep, stdout); | |
37770e00 CF |
502 | |
503 | if (verbose) | |
7077c48e | 504 | printf (" (%ls)", class_name); |
37770e00 CF |
505 | |
506 | puts (""); | |
507 | } | |
508 | ||
509 | if (listwhat & LIST_VALS) | |
510 | for (i = 0; i < num_values; i++) | |
511 | { | |
7077c48e | 512 | m = (maxvalnamelen + 1) * sizeof (wchar_t); |
37770e00 | 513 | n = maxvaluelen + 1; |
7077c48e | 514 | RegEnumValueW (key, i, value_name, &m, 0, &t, (BYTE *) value_data, &n); |
b17b7644 | 515 | value_data[n] = 0; |
37770e00 | 516 | if (!verbose) |
7077c48e | 517 | printf ("%ls\n", value_name); |
37770e00 CF |
518 | else |
519 | { | |
7077c48e | 520 | printf ("%ls (%s) = ", value_name, types[t]); |
37770e00 CF |
521 | switch (t) |
522 | { | |
70158caf | 523 | case REG_NONE: |
37770e00 CF |
524 | case REG_BINARY: |
525 | for (j = 0; j < 8 && j < n; j++) | |
526 | printf ("%02x ", value_data[j]); | |
527 | printf ("\n"); | |
528 | break; | |
529 | case REG_DWORD: | |
61522196 CV |
530 | printf ("0x%08x (%u)\n", *(unsigned int *) value_data, |
531 | *(unsigned int *) value_data); | |
37770e00 CF |
532 | break; |
533 | case REG_DWORD_BIG_ENDIAN: | |
534 | v = ((value_data[0] << 24) | |
535 | | (value_data[1] << 16) | |
70158caf CV |
536 | | (value_data[2] << 8) |
537 | | (value_data[3])); | |
37770e00 CF |
538 | printf ("0x%08x (%d)\n", v, v); |
539 | break; | |
70158caf CV |
540 | case REG_QWORD: |
541 | printf ("0x%016llx (%llu)\n", | |
542 | *(unsigned long long *) value_data, | |
543 | *(unsigned long long *) value_data); | |
544 | break; | |
37770e00 CF |
545 | case REG_EXPAND_SZ: |
546 | case REG_SZ: | |
7077c48e CV |
547 | case REG_LINK: |
548 | printf ("\"%ls\"\n", (wchar_t *) value_data); | |
37770e00 CF |
549 | break; |
550 | case REG_MULTI_SZ: | |
7077c48e | 551 | vd = (wchar_t *) value_data; |
37770e00 CF |
552 | while (vd && *vd) |
553 | { | |
7077c48e CV |
554 | printf ("\"%ls\"", vd); |
555 | vd = vd + wcslen (vd) + 1; | |
37770e00 CF |
556 | if (*vd) |
557 | printf (", "); | |
558 | } | |
559 | printf ("\n"); | |
560 | break; | |
561 | default: | |
70158caf CV |
562 | printf ("?\n"); |
563 | break; | |
37770e00 CF |
564 | } |
565 | } | |
566 | } | |
1fd5e000 CF |
567 | return 0; |
568 | } | |
569 | ||
570 | int | |
b82a7a5e | 571 | cmd_add () |
1fd5e000 | 572 | { |
b82a7a5e | 573 | find_key (2, KEY_ALL_ACCESS); |
1fd5e000 CF |
574 | HKEY newkey; |
575 | DWORD newtype; | |
7077c48e CV |
576 | int rv = RegCreateKeyExW (key, value, 0, NULL, REG_OPTION_NON_VOLATILE, |
577 | KEY_ALL_ACCESS | wow64, 0, &newkey, &newtype); | |
1fd5e000 | 578 | if (rv != ERROR_SUCCESS) |
b82a7a5e | 579 | Fail (rv); |
1fd5e000 CF |
580 | |
581 | if (verbose) | |
582 | { | |
583 | if (newtype == REG_OPENED_EXISTING_KEY) | |
7077c48e | 584 | printf ("Key %ls already exists\n", value); |
1fd5e000 | 585 | else |
7077c48e | 586 | printf ("Key %ls created\n", value); |
1fd5e000 CF |
587 | } |
588 | return 0; | |
589 | } | |
590 | ||
591 | int | |
b82a7a5e | 592 | cmd_remove () |
1fd5e000 | 593 | { |
40c60b89 CV |
594 | DWORD rv; |
595 | ||
b82a7a5e | 596 | find_key (2, KEY_ALL_ACCESS); |
40c60b89 | 597 | if (wow64) |
f36dd402 | 598 | rv = RegDeleteKeyExW (key, value, wow64, 0); |
40c60b89 | 599 | else |
7077c48e | 600 | rv = RegDeleteKeyW (key, value); |
1fd5e000 | 601 | if (rv != ERROR_SUCCESS) |
b82a7a5e | 602 | Fail (rv); |
1fd5e000 | 603 | if (verbose) |
7077c48e | 604 | printf ("subkey %ls deleted\n", value); |
1fd5e000 CF |
605 | return 0; |
606 | } | |
607 | ||
608 | int | |
b82a7a5e | 609 | cmd_check () |
1fd5e000 | 610 | { |
b82a7a5e | 611 | find_key (1, KEY_READ); |
1fd5e000 | 612 | if (verbose) |
b82a7a5e | 613 | printf ("key %s exists\n", argv[0]); |
1fd5e000 CF |
614 | return 0; |
615 | } | |
616 | ||
617 | int | |
b82a7a5e | 618 | cmd_set () |
1fd5e000 | 619 | { |
7077c48e | 620 | int i, n, max_n; |
1fd5e000 | 621 | DWORD v, rv; |
70158caf | 622 | unsigned long long llval; |
17c8ac39 | 623 | char *a = argv[1], *data = 0; |
b82a7a5e | 624 | find_key (2, KEY_ALL_ACCESS); |
1fd5e000 | 625 | |
70158caf CV |
626 | if (!a) |
627 | usage (); | |
628 | if (value_type == REG_AUTO) | |
1fd5e000 CF |
629 | { |
630 | char *e; | |
70158caf | 631 | llval = strtoull (a, &e, 0); |
1fd5e000 | 632 | if (a[0] == '%') |
70158caf | 633 | value_type = REG_EXPAND_SZ; |
1fd5e000 | 634 | else if (a[0] && !*e) |
70158caf | 635 | value_type = llval > 0xffffffffULL ? REG_QWORD : REG_DWORD; |
1fd5e000 | 636 | else if (argv[2]) |
70158caf | 637 | value_type = REG_MULTI_SZ; |
1fd5e000 | 638 | else |
70158caf | 639 | value_type = REG_SZ; |
1fd5e000 CF |
640 | } |
641 | ||
70158caf | 642 | switch (value_type) |
1fd5e000 | 643 | { |
70158caf CV |
644 | case REG_NONE: |
645 | case REG_BINARY: | |
17c8ac39 | 646 | for (n = 0; argv[n+1]; n++) |
1b23b30b | 647 | ; |
17c8ac39 CV |
648 | if (n == 1 && strcmp (argv[1], "-") == 0) |
649 | { /* read from stdin */ | |
650 | i = n = 0; | |
651 | for (;;) | |
652 | { | |
653 | if (i <= n) | |
654 | { | |
655 | i = n + BUFSIZ; | |
656 | data = (char *) realloc (data, i); | |
657 | } | |
658 | int r = fread (data+n, 1, i-n, stdin); | |
659 | if (r <= 0) | |
660 | break; | |
661 | n += r; | |
662 | } | |
663 | } | |
664 | else if (n > 0) | |
665 | { /* parse hex from argv */ | |
666 | data = (char *) malloc (n); | |
667 | for (i = 0; i < n; i++) | |
668 | { | |
669 | char *e; | |
670 | errno = 0; | |
671 | v = strtoul (argv[i+1], &e, 16); | |
672 | if (errno || v > 0xff || *e) | |
673 | { | |
674 | fprintf (stderr, "Invalid hex constant `%s'\n", argv[i+1]); | |
675 | exit (1); | |
676 | } | |
677 | data[i] = (char) v; | |
678 | } | |
679 | } | |
7077c48e | 680 | rv = RegSetValueExW (key, value, 0, value_type, (const BYTE *) data, n); |
17c8ac39 | 681 | break; |
70158caf | 682 | case REG_DWORD: |
b82a7a5e | 683 | v = strtoul (a, 0, 0); |
7077c48e | 684 | rv = RegSetValueExW (key, value, 0, REG_DWORD, (const BYTE *) &v, |
b82a7a5e | 685 | sizeof (v)); |
1fd5e000 | 686 | break; |
70158caf CV |
687 | case REG_DWORD_BIG_ENDIAN: |
688 | v = strtoul (a, 0, 0); | |
689 | v = (((v & 0xff) << 24) | |
690 | | ((v & 0xff00) << 8) | |
691 | | ((v & 0xff0000) >> 8) | |
692 | | ((v & 0xff000000) >> 24)); | |
7077c48e | 693 | rv = RegSetValueExW (key, value, 0, REG_DWORD_BIG_ENDIAN, |
70158caf CV |
694 | (const BYTE *) &v, sizeof (v)); |
695 | break; | |
696 | case REG_QWORD: | |
697 | llval = strtoul (a, 0, 0); | |
7077c48e | 698 | rv = RegSetValueExW (key, value, 0, REG_QWORD, (const BYTE *) &llval, |
70158caf CV |
699 | sizeof (llval)); |
700 | break; | |
701 | case REG_SZ: | |
70158caf | 702 | case REG_EXPAND_SZ: |
51564c78 CV |
703 | { |
704 | n = mbstowcs (NULL, a, 0); | |
705 | wchar_t w[n + 1]; | |
706 | mbstowcs (w, a, n + 1); | |
707 | rv = RegSetValueExW (key, value, 0, value_type, | |
708 | (const BYTE *) w, (n + 1) * sizeof (wchar_t)); | |
709 | } | |
1fd5e000 | 710 | break; |
70158caf | 711 | case REG_MULTI_SZ: |
7077c48e CV |
712 | for (i = 1, max_n = 1; argv[i]; i++) |
713 | max_n += mbstowcs (NULL, argv[i], 0) + 1; | |
714 | data = (char *) malloc (max_n * sizeof (wchar_t)); | |
b82a7a5e | 715 | for (i = 1, n = 0; argv[i]; i++) |
7077c48e CV |
716 | n += mbstowcs ((wchar_t *) data + n, argv[i], max_n - n) + 1; |
717 | ((wchar_t *)data)[n] = L'\0'; | |
718 | rv = RegSetValueExW (key, value, 0, REG_MULTI_SZ, (const BYTE *) data, | |
41870989 | 719 | (n + 1) * sizeof (wchar_t)); |
1fd5e000 | 720 | break; |
70158caf | 721 | case REG_AUTO: |
b82a7a5e | 722 | rv = ERROR_SUCCESS; |
1fd5e000 | 723 | break; |
7adad121 CF |
724 | default: |
725 | rv = ERROR_INVALID_CATEGORY; | |
726 | break; | |
1fd5e000 | 727 | } |
1b23b30b | 728 | |
17c8ac39 CV |
729 | if (data) |
730 | free(data); | |
1fd5e000 CF |
731 | |
732 | if (rv != ERROR_SUCCESS) | |
b82a7a5e | 733 | Fail (rv); |
1fd5e000 CF |
734 | |
735 | return 0; | |
736 | } | |
737 | ||
738 | int | |
b82a7a5e | 739 | cmd_unset () |
1fd5e000 | 740 | { |
b82a7a5e | 741 | find_key (2, KEY_ALL_ACCESS); |
7077c48e | 742 | DWORD rv = RegDeleteValueW (key, value); |
1fd5e000 | 743 | if (rv != ERROR_SUCCESS) |
b82a7a5e | 744 | Fail (rv); |
1fd5e000 | 745 | if (verbose) |
7077c48e | 746 | printf ("value %ls deleted\n", value); |
1fd5e000 CF |
747 | return 0; |
748 | } | |
749 | ||
750 | int | |
b82a7a5e | 751 | cmd_get () |
1fd5e000 | 752 | { |
b82a7a5e | 753 | find_key (2, KEY_READ); |
1fd5e000 | 754 | DWORD vtype, dsize, rv; |
7077c48e CV |
755 | PBYTE data; |
756 | wchar_t *vd; | |
757 | ||
758 | rv = RegQueryValueExW (key, value, 0, &vtype, 0, &dsize); | |
1fd5e000 | 759 | if (rv != ERROR_SUCCESS) |
b82a7a5e | 760 | Fail (rv); |
7077c48e CV |
761 | data = (PBYTE) malloc (dsize + 1); |
762 | rv = RegQueryValueExW (key, value, 0, &vtype, data, &dsize); | |
1fd5e000 | 763 | if (rv != ERROR_SUCCESS) |
b82a7a5e | 764 | Fail (rv); |
70158caf | 765 | if (value_type == REG_BINARY) |
1fd5e000 | 766 | { |
70158caf CV |
767 | for (unsigned i = 0; i < dsize; i++) |
768 | printf ("%02x%c", (unsigned char)data[i], | |
769 | (i < dsize-1 ? ' ' : '\n')); | |
1fd5e000 | 770 | } |
70158caf CV |
771 | else if (value_type == REG_NONE) |
772 | fwrite (data, dsize, 1, stdout); | |
773 | else | |
774 | switch (vtype) | |
775 | { | |
776 | case REG_NONE: | |
777 | case REG_BINARY: | |
70158caf CV |
778 | fwrite (data, dsize, 1, stdout); |
779 | break; | |
780 | case REG_DWORD: | |
61522196 | 781 | printf (hex ? "0x%08x\n" : "%u\n", *(unsigned int *) data); |
70158caf CV |
782 | break; |
783 | case REG_DWORD_BIG_ENDIAN: | |
784 | rv = ((data[0] << 24) | |
785 | | (data[1] << 16) | |
786 | | (data[2] << 8) | |
787 | | (data[3])); | |
61522196 | 788 | printf (hex ? "0x%08x\n" : "%u\n", (unsigned int) rv); |
70158caf CV |
789 | break; |
790 | case REG_QWORD: | |
791 | printf (hex ? "0x%016llx\n" : "%llu\n", *(unsigned long long *) data); | |
792 | break; | |
793 | case REG_SZ: | |
7077c48e CV |
794 | case REG_LINK: |
795 | printf ("%ls\n", (wchar_t *) data); | |
70158caf CV |
796 | break; |
797 | case REG_EXPAND_SZ: | |
798 | if (value_type == REG_EXPAND_SZ) // hack | |
799 | { | |
7077c48e | 800 | wchar_t *buf; |
70158caf | 801 | DWORD bufsize; |
7077c48e CV |
802 | bufsize = ExpandEnvironmentStringsW ((wchar_t *) data, 0, 0); |
803 | buf = (wchar_t *) malloc (bufsize + 1); | |
804 | ExpandEnvironmentStringsW ((wchar_t *) data, buf, bufsize + 1); | |
70158caf | 805 | free (data); |
7077c48e | 806 | data = (PBYTE) buf; |
70158caf | 807 | } |
7077c48e | 808 | printf ("%ls\n", (wchar_t *) data); |
70158caf CV |
809 | break; |
810 | case REG_MULTI_SZ: | |
7077c48e | 811 | vd = (wchar_t *) data; |
70158caf CV |
812 | while (vd && *vd) |
813 | { | |
7077c48e CV |
814 | printf ("%ls\n", vd); |
815 | vd = vd + wcslen (vd) + 1; | |
70158caf CV |
816 | } |
817 | break; | |
818 | } | |
1fd5e000 CF |
819 | return 0; |
820 | } | |
821 | ||
17c8ac39 CV |
822 | int |
823 | cmd_load () | |
824 | { | |
825 | if (!argv[1]) | |
e7fca6f8 | 826 | usage (); |
17c8ac39 CV |
827 | find_key (1, 0); |
828 | return 0; | |
829 | } | |
830 | ||
831 | int | |
832 | cmd_unload () | |
833 | { | |
834 | if (argv[1]) | |
e7fca6f8 | 835 | usage (); |
17c8ac39 CV |
836 | find_key (1, 0); |
837 | return 0; | |
838 | } | |
839 | ||
17c8ac39 CV |
840 | int |
841 | cmd_save () | |
842 | { | |
843 | if (!argv[1]) | |
e7fca6f8 | 844 | usage (); |
17c8ac39 CV |
845 | /* REG_OPTION_BACKUP_RESTORE is necessary to save /HKLM/SECURITY */ |
846 | find_key (1, KEY_QUERY_VALUE, REG_OPTION_BACKUP_RESTORE); | |
7077c48e CV |
847 | ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], NULL, 0); |
848 | wchar_t win32_path[len]; | |
849 | cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], win32_path, len); | |
850 | DWORD rv = RegSaveKeyW (key, win32_path, NULL); | |
17c8ac39 CV |
851 | if (rv != ERROR_SUCCESS) |
852 | Fail (rv); | |
853 | if (verbose) | |
7077c48e | 854 | printf ("key saved to %ls\n", win32_path); |
17c8ac39 CV |
855 | return 0; |
856 | } | |
857 | ||
a6791b3b CV |
858 | int |
859 | cmd_restore () | |
860 | { | |
861 | if (!argv[1]) | |
e7fca6f8 | 862 | usage (); |
a6791b3b CV |
863 | /* REG_OPTION_BACKUP_RESTORE is necessary to restore /HKLM/SECURITY */ |
864 | find_key (1, KEY_ALL_ACCESS, REG_OPTION_BACKUP_RESTORE); | |
865 | ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], NULL, 0); | |
866 | wchar_t win32_path[len]; | |
867 | cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], win32_path, len); | |
868 | DWORD rv = RegRestoreKeyW (key, win32_path, restore_flags); | |
869 | if (rv != ERROR_SUCCESS) | |
870 | Fail (rv); | |
871 | if (verbose) | |
872 | printf ("key saved to %ls\n", win32_path); | |
873 | return 0; | |
874 | } | |
875 | ||
a35d9f1a | 876 | static struct |
b82a7a5e | 877 | { |
1fd5e000 | 878 | const char *name; |
b82a7a5e CF |
879 | int (*func) (); |
880 | } commands[] = | |
881 | { | |
882 | {"list", cmd_list}, | |
883 | {"add", cmd_add}, | |
884 | {"remove", cmd_remove}, | |
885 | {"check", cmd_check}, | |
886 | {"set", cmd_set}, | |
887 | {"unset", cmd_unset}, | |
888 | {"get", cmd_get}, | |
17c8ac39 CV |
889 | {"load", cmd_load}, |
890 | {"unload", cmd_unload}, | |
891 | {"save", cmd_save}, | |
a6791b3b | 892 | {"restore", cmd_restore}, |
b82a7a5e | 893 | {0, 0} |
1fd5e000 CF |
894 | }; |
895 | ||
896 | int | |
b82a7a5e | 897 | main (int argc, char **_argv) |
1fd5e000 | 898 | { |
9bd02410 CF |
899 | int g; |
900 | ||
7077c48e | 901 | setlocale (LC_ALL, ""); |
92b499ac CV |
902 | |
903 | prog_name = program_invocation_short_name; | |
9bd02410 CF |
904 | |
905 | while ((g = getopt_long (argc, _argv, opts, longopts, NULL)) != EOF) | |
906 | switch (g) | |
b82a7a5e | 907 | { |
17c8ac39 | 908 | case 'b': |
70158caf CV |
909 | value_type = REG_BINARY; |
910 | break; | |
911 | case 'd': | |
912 | value_type = REG_DWORD; | |
913 | break; | |
914 | case 'D': | |
915 | value_type = REG_DWORD_BIG_ENDIAN; | |
17c8ac39 | 916 | break; |
9bd02410 | 917 | case 'e': |
70158caf | 918 | value_type = REG_EXPAND_SZ; |
37770e00 | 919 | break; |
a6791b3b CV |
920 | case 'f': |
921 | restore_flags = REG_FORCE_RESTORE; | |
922 | break; | |
37770e00 CF |
923 | case 'k': |
924 | listwhat |= LIST_KEYS; | |
925 | break; | |
9bd02410 CF |
926 | case 'h': |
927 | usage (stdout); | |
928 | case 'i': | |
70158caf | 929 | value_type = REG_DWORD; |
9bd02410 | 930 | break; |
37770e00 CF |
931 | case 'l': |
932 | listwhat |= LIST_VALS; | |
933 | break; | |
9bd02410 | 934 | case 'm': |
70158caf CV |
935 | value_type = REG_MULTI_SZ; |
936 | break; | |
937 | case 'n': | |
938 | value_type = REG_NONE; | |
9bd02410 CF |
939 | break; |
940 | case 'p': | |
941 | postfix++; | |
942 | break; | |
943 | case 'q': | |
944 | quiet++; | |
b82a7a5e | 945 | break; |
70158caf CV |
946 | case 'Q': |
947 | value_type = REG_QWORD; | |
948 | break; | |
b82a7a5e | 949 | case 's': |
70158caf | 950 | value_type = REG_SZ; |
b82a7a5e | 951 | break; |
9bd02410 CF |
952 | case 'v': |
953 | verbose++; | |
b82a7a5e | 954 | break; |
9bd02410 CF |
955 | case 'V': |
956 | print_version (); | |
957 | exit (0); | |
40c60b89 CV |
958 | case 'w': |
959 | wow64 = KEY_WOW64_64KEY; | |
960 | break; | |
3c256e38 CV |
961 | case 'W': |
962 | wow64 = KEY_WOW64_32KEY; | |
963 | break; | |
70158caf CV |
964 | case 'x': |
965 | hex++; | |
966 | break; | |
403985a4 | 967 | case 'K': |
9dc6005a | 968 | key_sep = *optarg; |
403985a4 | 969 | break; |
9bd02410 | 970 | default : |
92b499ac CV |
971 | fprintf (stderr, "Try `%s --help' for more information.\n", |
972 | prog_name); | |
973 | return 1; | |
b82a7a5e | 974 | } |
9bd02410 CF |
975 | |
976 | if ((_argv[optind] == NULL) || (_argv[optind+1] == NULL)) | |
b82a7a5e | 977 | usage (); |
1fd5e000 | 978 | |
b82a7a5e | 979 | argv = _argv + optind; |
1fd5e000 | 980 | int i; |
b82a7a5e CF |
981 | for (i = 0; commands[i].name; i++) |
982 | if (strcmp (commands[i].name, argv[0]) == 0) | |
1fd5e000 CF |
983 | { |
984 | argv++; | |
b82a7a5e | 985 | return commands[i].func (); |
1fd5e000 | 986 | } |
b82a7a5e | 987 | usage (); |
1fd5e000 | 988 | } |