]>
Commit | Line | Data |
---|---|---|
1 | /* regtool.cc | |
2 | ||
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> | |
11 | #include <errno.h> | |
12 | #include <ctype.h> | |
13 | #include <wchar.h> | |
14 | #include <getopt.h> | |
15 | #include <locale.h> | |
16 | #include <windows.h> | |
17 | #include <sys/cygwin.h> | |
18 | #include <cygwin/version.h> | |
19 | ||
20 | #define DEFAULT_KEY_SEPARATOR '\\' | |
21 | ||
22 | #define REG_AUTO -1 | |
23 | ||
24 | int value_type = REG_AUTO; | |
25 | ||
26 | char key_sep = DEFAULT_KEY_SEPARATOR; | |
27 | ||
28 | #define LIST_KEYS 0x01 | |
29 | #define LIST_VALS 0x02 | |
30 | #define LIST_ALL (LIST_KEYS | LIST_VALS) | |
31 | ||
32 | static char *prog_name; | |
33 | ||
34 | static struct option longopts[] = | |
35 | { | |
36 | {"binary", no_argument, NULL, 'b' }, | |
37 | {"dword", no_argument, NULL, 'd' }, | |
38 | {"dword-be", no_argument, NULL, 'D' }, | |
39 | {"expand-string", no_argument, NULL, 'e' }, | |
40 | {"force", no_argument, NULL, 'f' }, | |
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'}, | |
46 | {"none", no_argument, NULL, 'n' }, | |
47 | {"postfix", no_argument, NULL, 'p'}, | |
48 | {"quiet", no_argument, NULL, 'q'}, | |
49 | {"qword", no_argument, NULL, 'Q' }, | |
50 | {"string", no_argument, NULL, 's'}, | |
51 | {"verbose", no_argument, NULL, 'v'}, | |
52 | {"version", no_argument, NULL, 'V'}, | |
53 | {"wow64", no_argument, NULL, 'w'}, | |
54 | {"wow32", no_argument, NULL, 'W'}, | |
55 | {"hex", no_argument, NULL, 'x'}, | |
56 | {"key-separator", required_argument, NULL, 'K'}, | |
57 | {NULL, 0, NULL, 0} | |
58 | }; | |
59 | ||
60 | static char opts[] = "bdDefhiklmnpqQsvVwWxK:"; | |
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 | }; | |
77 | ||
78 | int listwhat = 0; | |
79 | int postfix = 0; | |
80 | int verbose = 0; | |
81 | int quiet = 0; | |
82 | int hex = 0; | |
83 | DWORD wow64 = 0; | |
84 | DWORD restore_flags = 0; | |
85 | char **argv; | |
86 | ||
87 | HKEY key; | |
88 | wchar_t *value; | |
89 | ||
90 | static void __attribute__ ((__noreturn__)) | |
91 | usage (FILE *where = stderr) | |
92 | { | |
93 | fprintf (where, "" | |
94 | "Usage: %s [OPTION] ACTION KEY [data...]\n" | |
95 | "\n" | |
96 | "View or edit the Win32 registry\n" | |
97 | "\n", prog_name); | |
98 | if (where == stdout) | |
99 | { | |
100 | fprintf (where, "" | |
101 | "Actions:\n" | |
102 | "\n" | |
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" | |
112 | " save KEY\\SUBKEY PATH save SUBKEY into new file PATH\n" | |
113 | " restore KEY\\SUBKEY PATH restore SUBKEY from file PATH\n" | |
114 | "\n"); | |
115 | fprintf (where, "" | |
116 | "Options for 'list' action:\n" | |
117 | "\n" | |
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" | |
122 | "Options for 'get' action:\n" | |
123 | "\n" | |
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" | |
128 | "Options for 'set' action:\n" | |
129 | "\n" | |
130 | " -b, --binary set type to REG_BINARY (hex args or '-')\n" | |
131 | " -d, --dword set type to REG_DWORD\n" | |
132 | " -D, --dword-be set type to REG_DWORD_BIG_ENDIAN\n" | |
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" | |
141 | "\n" | |
142 | " -K<c>, --key-separator[=]<c> set key-value separator to <c> instead of '\\'\n" | |
143 | "\n" | |
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" | |
149 | "Other Options:\n" | |
150 | "\n" | |
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" | |
154 | " -w, --wow64 access 64 bit registry view\n" | |
155 | " -W, --wow32 access 32 bit registry view\n" | |
156 | " -V, --version output version information and exit\n" | |
157 | "\n"); | |
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" | |
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"); | |
171 | fprintf (where, "" | |
172 | "Example:\n" | |
173 | "%s list '/HKLM/SOFTWARE/Classes/MIME/Database/Content Type/audio\\/wav'\n\n", prog_name); | |
174 | } | |
175 | if (where == stderr) | |
176 | fprintf (where, | |
177 | "ACTION is one of add, check, get, list, remove, set, unset, load, unload, save\n" | |
178 | "\n" | |
179 | "Try `%s --help' for more information.\n", prog_name); | |
180 | exit (where == stderr ? 1 : 0); | |
181 | } | |
182 | ||
183 | static void | |
184 | print_version () | |
185 | { | |
186 | printf ("regtool (cygwin) %d.%d.%d\n" | |
187 | "Registry tool\n" | |
188 | "Copyright (C) 2000 - %s Cygwin Authors\n" | |
189 | "This is free software; see the source for copying conditions. There is NO\n" | |
190 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", | |
191 | CYGWIN_VERSION_DLL_MAJOR / 1000, | |
192 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
193 | CYGWIN_VERSION_DLL_MINOR, | |
194 | strrchr (__DATE__, ' ') + 1); | |
195 | } | |
196 | ||
197 | void | |
198 | Fail (unsigned int rv) | |
199 | { | |
200 | wchar_t *buf; | |
201 | ||
202 | if (!quiet) | |
203 | { | |
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); | |
208 | LocalFree (buf); | |
209 | } | |
210 | exit (1); | |
211 | } | |
212 | ||
213 | static struct | |
214 | { | |
215 | const char *string; | |
216 | HKEY key; | |
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} | |
235 | }; | |
236 | ||
237 | void | |
238 | translate (char *key) | |
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 == '\\') | |
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])) | |
297 | c = '0'; | |
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 | } | |
310 | else if (*s == '/') | |
311 | *d++ = '\\'; | |
312 | else | |
313 | *d++ = *s; | |
314 | ++s; | |
315 | } | |
316 | *d = '\0'; | |
317 | } | |
318 | ||
319 | void | |
320 | find_key (int howmanyparts, REGSAM access, int option = 0) | |
321 | { | |
322 | HKEY base; | |
323 | int rv; | |
324 | char *n = argv[0], *e, *h, c; | |
325 | char* host = NULL; | |
326 | int i; | |
327 | size_t len; | |
328 | ||
329 | if (*n == '/') | |
330 | translate (n); | |
331 | if (*n != '\\') | |
332 | { | |
333 | /* expect host:/key/value format */ | |
334 | host = (char*) malloc (strlen (n) + 1); | |
335 | host[0] = host [1] = '\\'; | |
336 | for (e = n, h = host + 2; *e && *e != ':'; e++, h++) | |
337 | *h = *e; | |
338 | *h = 0; | |
339 | n = e + 1; | |
340 | if (*n == '/') | |
341 | translate (n); | |
342 | } | |
343 | else if (n[0] == '\\' && n[1] == '\\') | |
344 | { | |
345 | /* expect //host/key/value format */ | |
346 | host = (char*) malloc (strlen (n) + 1); | |
347 | host[0] = host[1] = '\\'; | |
348 | for (e = n + 2, h = host + 2; *e && *e != '\\'; e++, h++) | |
349 | *h = *e; | |
350 | *h = 0; | |
351 | n = e; | |
352 | } | |
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 | } | |
362 | while (*n != '\\') | |
363 | n++; | |
364 | *n++ = 0; | |
365 | for (e = n; *e && *e != '\\'; e++); | |
366 | c = *e; | |
367 | *e = 0; | |
368 | for (i = 0; wkprefixes[i].string; i++) | |
369 | if (strcmp (wkprefixes[i].string, n) == 0) | |
370 | break; | |
371 | if (!wkprefixes[i].string) | |
372 | { | |
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); | |
377 | } | |
378 | ||
379 | n = e; | |
380 | *e = c; | |
381 | while (*n && *n == '\\') | |
382 | n++; | |
383 | e = n + strlen (n); | |
384 | if (howmanyparts > 1) | |
385 | { | |
386 | while (n < e && *e != key_sep) | |
387 | e--; | |
388 | if (*e != key_sep) | |
389 | { | |
390 | key = wkprefixes[i].key; | |
391 | if (value) | |
392 | free (value); | |
393 | len = mbstowcs (NULL, n, 0) + 1; | |
394 | value = (wchar_t *) malloc (len * sizeof (wchar_t)); | |
395 | mbstowcs (value, n, len); | |
396 | return; | |
397 | } | |
398 | else | |
399 | { | |
400 | *e = 0; | |
401 | if (value) | |
402 | free (value); | |
403 | len = mbstowcs (NULL, e + 1, 0) + 1; | |
404 | value = (wchar_t *) malloc (len * sizeof (wchar_t)); | |
405 | mbstowcs (value, e + 1, len); | |
406 | } | |
407 | } | |
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 | ||
418 | if (n[0] == 0) | |
419 | key = base; | |
420 | else | |
421 | { | |
422 | len = mbstowcs (NULL, n, 0) + 1; | |
423 | wchar_t name[len]; | |
424 | mbstowcs (name, n, len); | |
425 | if (access) | |
426 | { | |
427 | rv = RegOpenKeyExW (base, name, 0, access | wow64, &key); | |
428 | if (option && (rv == ERROR_SUCCESS || rv == ERROR_ACCESS_DENIED)) | |
429 | { | |
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) */ | |
434 | HKEY key2; | |
435 | if (RegCreateKeyExW (base, name, 0, NULL, option, access | wow64, | |
436 | NULL, &key2, NULL) | |
437 | == ERROR_SUCCESS) | |
438 | { | |
439 | if (rv == ERROR_SUCCESS) | |
440 | RegCloseKey (key); | |
441 | key = key2; | |
442 | rv = ERROR_SUCCESS; | |
443 | } | |
444 | } | |
445 | if (rv != ERROR_SUCCESS) | |
446 | Fail (rv); | |
447 | } | |
448 | else if (argv[1]) | |
449 | { | |
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); | |
454 | if (rv != ERROR_SUCCESS) | |
455 | Fail (rv); | |
456 | if (verbose) | |
457 | printf ("key %ls loaded from file %ls\n", name, win32_path); | |
458 | } | |
459 | else | |
460 | { | |
461 | rv = RegUnLoadKeyW (base, name); | |
462 | if (rv != ERROR_SUCCESS) | |
463 | Fail (rv); | |
464 | if (verbose) | |
465 | printf ("key %ls unloaded\n", name); | |
466 | } | |
467 | } | |
468 | } | |
469 | ||
470 | ||
471 | int | |
472 | cmd_list () | |
473 | { | |
474 | DWORD num_subkeys, maxsubkeylen, num_values, maxvalnamelen, maxvaluelen; | |
475 | DWORD maxclasslen; | |
476 | wchar_t *subkey_name, *value_name, *class_name, *vd; | |
477 | unsigned char *value_data; | |
478 | DWORD i, j, m, n, t; | |
479 | int v; | |
480 | ||
481 | find_key (1, KEY_READ); | |
482 | RegQueryInfoKeyW (key, 0, 0, 0, &num_subkeys, &maxsubkeylen, &maxclasslen, | |
483 | &num_values, &maxvalnamelen, &maxvaluelen, 0, 0); | |
484 | ||
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)); | |
488 | value_data = (unsigned char *) malloc (maxvaluelen + 1); | |
489 | ||
490 | if (!listwhat) | |
491 | listwhat = LIST_ALL; | |
492 | ||
493 | if (listwhat & LIST_KEYS) | |
494 | for (i = 0; i < num_subkeys; i++) | |
495 | { | |
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); | |
500 | if (postfix || verbose) | |
501 | fputc (key_sep, stdout); | |
502 | ||
503 | if (verbose) | |
504 | printf (" (%ls)", class_name); | |
505 | ||
506 | puts (""); | |
507 | } | |
508 | ||
509 | if (listwhat & LIST_VALS) | |
510 | for (i = 0; i < num_values; i++) | |
511 | { | |
512 | m = (maxvalnamelen + 1) * sizeof (wchar_t); | |
513 | n = maxvaluelen + 1; | |
514 | RegEnumValueW (key, i, value_name, &m, 0, &t, (BYTE *) value_data, &n); | |
515 | value_data[n] = 0; | |
516 | if (!verbose) | |
517 | printf ("%ls\n", value_name); | |
518 | else | |
519 | { | |
520 | printf ("%ls (%s) = ", value_name, types[t]); | |
521 | switch (t) | |
522 | { | |
523 | case REG_NONE: | |
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: | |
530 | printf ("0x%08x (%u)\n", *(unsigned int *) value_data, | |
531 | *(unsigned int *) value_data); | |
532 | break; | |
533 | case REG_DWORD_BIG_ENDIAN: | |
534 | v = ((value_data[0] << 24) | |
535 | | (value_data[1] << 16) | |
536 | | (value_data[2] << 8) | |
537 | | (value_data[3])); | |
538 | printf ("0x%08x (%d)\n", v, v); | |
539 | break; | |
540 | case REG_QWORD: | |
541 | printf ("0x%016llx (%llu)\n", | |
542 | *(unsigned long long *) value_data, | |
543 | *(unsigned long long *) value_data); | |
544 | break; | |
545 | case REG_EXPAND_SZ: | |
546 | case REG_SZ: | |
547 | case REG_LINK: | |
548 | printf ("\"%ls\"\n", (wchar_t *) value_data); | |
549 | break; | |
550 | case REG_MULTI_SZ: | |
551 | vd = (wchar_t *) value_data; | |
552 | while (vd && *vd) | |
553 | { | |
554 | printf ("\"%ls\"", vd); | |
555 | vd = vd + wcslen (vd) + 1; | |
556 | if (*vd) | |
557 | printf (", "); | |
558 | } | |
559 | printf ("\n"); | |
560 | break; | |
561 | default: | |
562 | printf ("?\n"); | |
563 | break; | |
564 | } | |
565 | } | |
566 | } | |
567 | return 0; | |
568 | } | |
569 | ||
570 | int | |
571 | cmd_add () | |
572 | { | |
573 | find_key (2, KEY_ALL_ACCESS); | |
574 | HKEY newkey; | |
575 | DWORD newtype; | |
576 | int rv = RegCreateKeyExW (key, value, 0, NULL, REG_OPTION_NON_VOLATILE, | |
577 | KEY_ALL_ACCESS | wow64, 0, &newkey, &newtype); | |
578 | if (rv != ERROR_SUCCESS) | |
579 | Fail (rv); | |
580 | ||
581 | if (verbose) | |
582 | { | |
583 | if (newtype == REG_OPENED_EXISTING_KEY) | |
584 | printf ("Key %ls already exists\n", value); | |
585 | else | |
586 | printf ("Key %ls created\n", value); | |
587 | } | |
588 | return 0; | |
589 | } | |
590 | ||
591 | int | |
592 | cmd_remove () | |
593 | { | |
594 | DWORD rv; | |
595 | ||
596 | find_key (2, KEY_ALL_ACCESS); | |
597 | if (wow64) | |
598 | rv = RegDeleteKeyExW (key, value, wow64, 0); | |
599 | else | |
600 | rv = RegDeleteKeyW (key, value); | |
601 | if (rv != ERROR_SUCCESS) | |
602 | Fail (rv); | |
603 | if (verbose) | |
604 | printf ("subkey %ls deleted\n", value); | |
605 | return 0; | |
606 | } | |
607 | ||
608 | int | |
609 | cmd_check () | |
610 | { | |
611 | find_key (1, KEY_READ); | |
612 | if (verbose) | |
613 | printf ("key %s exists\n", argv[0]); | |
614 | return 0; | |
615 | } | |
616 | ||
617 | int | |
618 | cmd_set () | |
619 | { | |
620 | int i, n, max_n; | |
621 | DWORD v, rv; | |
622 | unsigned long long llval; | |
623 | char *a = argv[1], *data = 0; | |
624 | find_key (2, KEY_ALL_ACCESS); | |
625 | ||
626 | if (!a) | |
627 | usage (); | |
628 | if (value_type == REG_AUTO) | |
629 | { | |
630 | char *e; | |
631 | llval = strtoull (a, &e, 0); | |
632 | if (a[0] == '%') | |
633 | value_type = REG_EXPAND_SZ; | |
634 | else if (a[0] && !*e) | |
635 | value_type = llval > 0xffffffffULL ? REG_QWORD : REG_DWORD; | |
636 | else if (argv[2]) | |
637 | value_type = REG_MULTI_SZ; | |
638 | else | |
639 | value_type = REG_SZ; | |
640 | } | |
641 | ||
642 | switch (value_type) | |
643 | { | |
644 | case REG_NONE: | |
645 | case REG_BINARY: | |
646 | for (n = 0; argv[n+1]; n++) | |
647 | ; | |
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 | } | |
680 | rv = RegSetValueExW (key, value, 0, value_type, (const BYTE *) data, n); | |
681 | break; | |
682 | case REG_DWORD: | |
683 | v = strtoul (a, 0, 0); | |
684 | rv = RegSetValueExW (key, value, 0, REG_DWORD, (const BYTE *) &v, | |
685 | sizeof (v)); | |
686 | break; | |
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)); | |
693 | rv = RegSetValueExW (key, value, 0, REG_DWORD_BIG_ENDIAN, | |
694 | (const BYTE *) &v, sizeof (v)); | |
695 | break; | |
696 | case REG_QWORD: | |
697 | llval = strtoul (a, 0, 0); | |
698 | rv = RegSetValueExW (key, value, 0, REG_QWORD, (const BYTE *) &llval, | |
699 | sizeof (llval)); | |
700 | break; | |
701 | case REG_SZ: | |
702 | case REG_EXPAND_SZ: | |
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 | } | |
710 | break; | |
711 | case REG_MULTI_SZ: | |
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)); | |
715 | for (i = 1, n = 0; argv[i]; i++) | |
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, | |
719 | (n + 1) * sizeof (wchar_t)); | |
720 | break; | |
721 | case REG_AUTO: | |
722 | rv = ERROR_SUCCESS; | |
723 | break; | |
724 | default: | |
725 | rv = ERROR_INVALID_CATEGORY; | |
726 | break; | |
727 | } | |
728 | ||
729 | if (data) | |
730 | free(data); | |
731 | ||
732 | if (rv != ERROR_SUCCESS) | |
733 | Fail (rv); | |
734 | ||
735 | return 0; | |
736 | } | |
737 | ||
738 | int | |
739 | cmd_unset () | |
740 | { | |
741 | find_key (2, KEY_ALL_ACCESS); | |
742 | DWORD rv = RegDeleteValueW (key, value); | |
743 | if (rv != ERROR_SUCCESS) | |
744 | Fail (rv); | |
745 | if (verbose) | |
746 | printf ("value %ls deleted\n", value); | |
747 | return 0; | |
748 | } | |
749 | ||
750 | int | |
751 | cmd_get () | |
752 | { | |
753 | find_key (2, KEY_READ); | |
754 | DWORD vtype, dsize, rv; | |
755 | PBYTE data; | |
756 | wchar_t *vd; | |
757 | ||
758 | rv = RegQueryValueExW (key, value, 0, &vtype, 0, &dsize); | |
759 | if (rv != ERROR_SUCCESS) | |
760 | Fail (rv); | |
761 | data = (PBYTE) malloc (dsize + 1); | |
762 | rv = RegQueryValueExW (key, value, 0, &vtype, data, &dsize); | |
763 | if (rv != ERROR_SUCCESS) | |
764 | Fail (rv); | |
765 | if (value_type == REG_BINARY) | |
766 | { | |
767 | for (unsigned i = 0; i < dsize; i++) | |
768 | printf ("%02x%c", (unsigned char)data[i], | |
769 | (i < dsize-1 ? ' ' : '\n')); | |
770 | } | |
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: | |
778 | fwrite (data, dsize, 1, stdout); | |
779 | break; | |
780 | case REG_DWORD: | |
781 | printf (hex ? "0x%08x\n" : "%u\n", *(unsigned int *) data); | |
782 | break; | |
783 | case REG_DWORD_BIG_ENDIAN: | |
784 | rv = ((data[0] << 24) | |
785 | | (data[1] << 16) | |
786 | | (data[2] << 8) | |
787 | | (data[3])); | |
788 | printf (hex ? "0x%08x\n" : "%u\n", (unsigned int) rv); | |
789 | break; | |
790 | case REG_QWORD: | |
791 | printf (hex ? "0x%016llx\n" : "%llu\n", *(unsigned long long *) data); | |
792 | break; | |
793 | case REG_SZ: | |
794 | case REG_LINK: | |
795 | printf ("%ls\n", (wchar_t *) data); | |
796 | break; | |
797 | case REG_EXPAND_SZ: | |
798 | if (value_type == REG_EXPAND_SZ) // hack | |
799 | { | |
800 | wchar_t *buf; | |
801 | DWORD bufsize; | |
802 | bufsize = ExpandEnvironmentStringsW ((wchar_t *) data, 0, 0); | |
803 | buf = (wchar_t *) malloc (bufsize + 1); | |
804 | ExpandEnvironmentStringsW ((wchar_t *) data, buf, bufsize + 1); | |
805 | free (data); | |
806 | data = (PBYTE) buf; | |
807 | } | |
808 | printf ("%ls\n", (wchar_t *) data); | |
809 | break; | |
810 | case REG_MULTI_SZ: | |
811 | vd = (wchar_t *) data; | |
812 | while (vd && *vd) | |
813 | { | |
814 | printf ("%ls\n", vd); | |
815 | vd = vd + wcslen (vd) + 1; | |
816 | } | |
817 | break; | |
818 | } | |
819 | return 0; | |
820 | } | |
821 | ||
822 | int | |
823 | cmd_load () | |
824 | { | |
825 | if (!argv[1]) | |
826 | usage (); | |
827 | find_key (1, 0); | |
828 | return 0; | |
829 | } | |
830 | ||
831 | int | |
832 | cmd_unload () | |
833 | { | |
834 | if (argv[1]) | |
835 | usage (); | |
836 | find_key (1, 0); | |
837 | return 0; | |
838 | } | |
839 | ||
840 | int | |
841 | cmd_save () | |
842 | { | |
843 | if (!argv[1]) | |
844 | usage (); | |
845 | /* REG_OPTION_BACKUP_RESTORE is necessary to save /HKLM/SECURITY */ | |
846 | find_key (1, KEY_QUERY_VALUE, REG_OPTION_BACKUP_RESTORE); | |
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); | |
851 | if (rv != ERROR_SUCCESS) | |
852 | Fail (rv); | |
853 | if (verbose) | |
854 | printf ("key saved to %ls\n", win32_path); | |
855 | return 0; | |
856 | } | |
857 | ||
858 | int | |
859 | cmd_restore () | |
860 | { | |
861 | if (!argv[1]) | |
862 | usage (); | |
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 | ||
876 | static struct | |
877 | { | |
878 | const char *name; | |
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}, | |
889 | {"load", cmd_load}, | |
890 | {"unload", cmd_unload}, | |
891 | {"save", cmd_save}, | |
892 | {"restore", cmd_restore}, | |
893 | {0, 0} | |
894 | }; | |
895 | ||
896 | int | |
897 | main (int argc, char **_argv) | |
898 | { | |
899 | int g; | |
900 | ||
901 | setlocale (LC_ALL, ""); | |
902 | ||
903 | prog_name = program_invocation_short_name; | |
904 | ||
905 | while ((g = getopt_long (argc, _argv, opts, longopts, NULL)) != EOF) | |
906 | switch (g) | |
907 | { | |
908 | case 'b': | |
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; | |
916 | break; | |
917 | case 'e': | |
918 | value_type = REG_EXPAND_SZ; | |
919 | break; | |
920 | case 'f': | |
921 | restore_flags = REG_FORCE_RESTORE; | |
922 | break; | |
923 | case 'k': | |
924 | listwhat |= LIST_KEYS; | |
925 | break; | |
926 | case 'h': | |
927 | usage (stdout); | |
928 | case 'i': | |
929 | value_type = REG_DWORD; | |
930 | break; | |
931 | case 'l': | |
932 | listwhat |= LIST_VALS; | |
933 | break; | |
934 | case 'm': | |
935 | value_type = REG_MULTI_SZ; | |
936 | break; | |
937 | case 'n': | |
938 | value_type = REG_NONE; | |
939 | break; | |
940 | case 'p': | |
941 | postfix++; | |
942 | break; | |
943 | case 'q': | |
944 | quiet++; | |
945 | break; | |
946 | case 'Q': | |
947 | value_type = REG_QWORD; | |
948 | break; | |
949 | case 's': | |
950 | value_type = REG_SZ; | |
951 | break; | |
952 | case 'v': | |
953 | verbose++; | |
954 | break; | |
955 | case 'V': | |
956 | print_version (); | |
957 | exit (0); | |
958 | case 'w': | |
959 | wow64 = KEY_WOW64_64KEY; | |
960 | break; | |
961 | case 'W': | |
962 | wow64 = KEY_WOW64_32KEY; | |
963 | break; | |
964 | case 'x': | |
965 | hex++; | |
966 | break; | |
967 | case 'K': | |
968 | key_sep = *optarg; | |
969 | break; | |
970 | default : | |
971 | fprintf (stderr, "Try `%s --help' for more information.\n", | |
972 | prog_name); | |
973 | return 1; | |
974 | } | |
975 | ||
976 | if ((_argv[optind] == NULL) || (_argv[optind+1] == NULL)) | |
977 | usage (); | |
978 | ||
979 | argv = _argv + optind; | |
980 | int i; | |
981 | for (i = 0; commands[i].name; i++) | |
982 | if (strcmp (commands[i].name, argv[0]) == 0) | |
983 | { | |
984 | argv++; | |
985 | return commands[i].func (); | |
986 | } | |
987 | usage (); | |
988 | } |