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