/* regtool.cc Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat Inc. This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include #include #include #include #include #include #include #define WINVER 0x0502 #include #include #define DEFAULT_KEY_SEPARATOR '\\' #define REG_AUTO -1 int value_type = REG_AUTO; char key_sep = DEFAULT_KEY_SEPARATOR; #define LIST_KEYS 0x01 #define LIST_VALS 0x02 #define LIST_ALL (LIST_KEYS | LIST_VALS) static const char version[] = "$Revision$"; static char *prog_name; static struct option longopts[] = { {"binary", no_argument, NULL, 'b' }, {"dword", no_argument, NULL, 'd' }, {"dword-be", no_argument, NULL, 'D' }, {"expand-string", no_argument, NULL, 'e' }, {"help", no_argument, NULL, 'h' }, {"integer", no_argument, NULL, 'i' }, {"keys", no_argument, NULL, 'k'}, {"list", no_argument, NULL, 'l'}, {"multi-string", no_argument, NULL, 'm'}, {"none", no_argument, NULL, 'n' }, {"postfix", no_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"qword", no_argument, NULL, 'Q' }, {"string", no_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"wow64", no_argument, NULL, 'w'}, {"wow32", no_argument, NULL, 'W'}, {"hex", no_argument, NULL, 'x'}, {"key-separator", required_argument, NULL, 'K'}, {NULL, 0, NULL, 0} }; static char opts[] = "bdDehiklmnpqQsvVwWxK:"; const char *types[] = { "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", "REG_DWORD_BIG_ENDIAN", "REG_LINK", "REG_MULTI_SZ", "REG_RESOURCE_LIST", "REG_FULL_RESOURCE_DESCRIPTOR", "REG_RESOURCE_REQUIREMENTS_LIST", "REG_QWORD", }; int listwhat = 0; int postfix = 0; int verbose = 0; int quiet = 0; int hex = 0; DWORD wow64 = 0; char **argv; HKEY key; wchar_t *value; static void usage (FILE *where = stderr) { fprintf (where, "" "Usage: %s [OPTION] ACTION KEY [data...]\n" "View or edit the Win32 registry\n" "\n", prog_name); if (where == stdout) { fprintf (where, "" "Actions:\n" " add KEY\\SUBKEY add new SUBKEY\n" " check KEY exit 0 if KEY exists, 1 if not\n" " get KEY\\VALUE prints VALUE to stdout\n" " list KEY list SUBKEYs and VALUEs\n" " remove KEY remove KEY\n" " set KEY\\VALUE [data ...] set VALUE\n" " unset KEY\\VALUE removes VALUE from KEY\n" " load KEY\\SUBKEY PATH load hive from PATH into new SUBKEY\n" " unload KEY\\SUBKEY unload hive and remove SUBKEY\n" " save KEY\\SUBKEY PATH save SUBKEY into new hive PATH\n" "\n"); fprintf (where, "" "Options for 'list' Action:\n" " -k, --keys print only KEYs\n" " -l, --list print only VALUEs\n" " -p, --postfix like ls -p, appends '\\' postfix to KEY names\n" "\n" "Options for 'get' Action:\n" " -b, --binary print data as printable hex bytes\n" " -n, --none print data as stream of bytes as stored in registry\n" " -x, --hex print numerical data as hex numbers\n" "\n" "Options for 'set' Action:\n" " -b, --binary set type to REG_BINARY (hex args or '-')\n" " -d, --dword set type to REG_DWORD\n" " -D, --dword-be set type to REG_DWORD_BIG_ENDIAN\n" " -e, --expand-string set type to REG_EXPAND_SZ\n" " -i, --integer set type to REG_DWORD\n" " -m, --multi-string set type to REG_MULTI_SZ\n" " -n, --none set type to REG_NONE\n" " -Q, --qword set type to REG_QWORD\n" " -s, --string set type to REG_SZ\n" "\n" "Options for 'set' and 'unset' Actions:\n" " -K, --key-separator[=] set key-value separator to instead of '\\'\n" "\n" "Other Options:\n" " -h, --help output usage information and exit\n" " -q, --quiet no error output, just nonzero return if KEY/VALUE missing\n" " -v, --verbose verbose output, including VALUE contents when applicable\n" " -w, --wow64 access 64 bit registry view (ignored on 32 bit Windows)\n" " -W, --wow32 access 32 bit registry view (ignored on 32 bit Windows)\n" " -V, --version output version information and exit\n" "\n"); fprintf (where, "" "KEY is in the format [host]\\prefix\\KEY\\KEY\\VALUE, where host is optional\n" "remote host in either \\\\hostname or hostname: format and prefix is any of:\n" " root HKCR HKEY_CLASSES_ROOT (local only)\n" " config HKCC HKEY_CURRENT_CONFIG (local only)\n" " user HKCU HKEY_CURRENT_USER (local only)\n" " machine HKLM HKEY_LOCAL_MACHINE\n" " users HKU HKEY_USERS\n" "\n" "If the keyname starts with a forward slash ('/'), the forward slash is used\n" "as separator and the backslash can be used as escape character.\n"); fprintf (where, "" "Example:\n" "%s list '/machine/SOFTWARE/Classes/MIME/Database/Content Type/audio\\/wav'\n", prog_name); } if (where == stderr) fprintf (where, "ACTION is one of add, check, get, list, remove, set, unset, load, unload, save\n" "\n" "Try '%s --help' for more information.\n", prog_name); exit (where == stderr ? 1 : 0); } static void print_version () { const char *v = strchr (version, ':'); int len; if (!v) { v = "?"; len = 1; } else { v += 2; len = strchr (v, ' ') - v; } printf ("\ %s (cygwin) %.*s\n\ Registry Tool\n\ Copyright 2000-2009 Red Hat, Inc.\n\ Compiled on %s\n\ ", prog_name, len, v, __DATE__); } void Fail (DWORD rv) { char *buf; if (!quiet) { FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, rv, 0, (CHAR *) & buf, 0, 0); fprintf (stderr, "Error (%ld): %s\n", rv, buf); LocalFree (buf); } exit (1); } static struct { const char *string; HKEY key; } wkprefixes[] = { {"root", HKEY_CLASSES_ROOT}, {"HKCR", HKEY_CLASSES_ROOT}, {"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT}, {"config", HKEY_CURRENT_CONFIG}, {"HKCC", HKEY_CURRENT_CONFIG}, {"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG}, {"user", HKEY_CURRENT_USER}, {"HKCU", HKEY_CURRENT_USER}, {"HKEY_CURRENT_USER", HKEY_CURRENT_USER}, {"machine", HKEY_LOCAL_MACHINE}, {"HKLM", HKEY_LOCAL_MACHINE}, {"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE}, {"users", HKEY_USERS}, {"HKU", HKEY_USERS}, {"HKEY_USERS", HKEY_USERS}, {0, 0} }; void translate (char *key) { #define isodigit(c) (strchr("01234567", c)) #define tooct(c) ((c)-'0') #define tohex(c) (strchr(_hs,tolower(c))-_hs) static char _hs[] = "0123456789abcdef"; char *d = key; char *s = key; char c; while (*s) { if (*s == '\\') switch (*++s) { case 'a': *d++ = '\007'; break; case 'b': *d++ = '\b'; break; case 'e': *d++ = '\033'; break; case 'f': *d++ = '\f'; break; case 'n': *d++ = '\n'; break; case 'r': *d++ = '\r'; break; case 't': *d++ = '\t'; break; case 'v': *d++ = '\v'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = tooct (*s); if (isodigit (s[1])) { c = (c << 3) | tooct (*++s); if (isodigit (s[1])) c = (c << 3) | tooct (*++s); } *d++ = c; break; case 'x': if (!isxdigit (s[1])) c = '0'; else { c = tohex (*++s); if (isxdigit (s[1])) c = (c << 4) | tohex (*++s); } *d++ = c; break; default: /* before non-special char: just add the char */ *d++ = *s; break; } else if (*s == '/') *d++ = '\\'; else *d++ = *s; ++s; } *d = '\0'; } void find_key (int howmanyparts, REGSAM access, int option = 0) { HKEY base; int rv; char *n = argv[0], *e, *h, c; char* host = NULL; int i; size_t len; if (*n == '/') translate (n); if (*n != '\\') { /* expect host:/key/value format */ host = (char*) malloc (strlen (n) + 1); host[0] = host [1] = '\\'; for (e = n, h = host + 2; *e && *e != ':'; e++, h++) *h = *e; *h = 0; n = e + 1; if (*n == '/') translate (n); } else if (n[0] == '\\' && n[1] == '\\') { /* expect //host/key/value format */ host = (char*) malloc (strlen (n) + 1); host[0] = host[1] = '\\'; for (e = n + 2, h = host + 2; *e && *e != '\\'; e++, h++) *h = *e; *h = 0; n = e; } while (*n != '\\') n++; *n++ = 0; for (e = n; *e && *e != '\\'; e++); c = *e; *e = 0; for (i = 0; wkprefixes[i].string; i++) if (strcmp (wkprefixes[i].string, n) == 0) break; if (!wkprefixes[i].string) { fprintf (stderr, "Unknown key prefix. Valid prefixes are:\n"); for (i = 0; wkprefixes[i].string; i++) fprintf (stderr, "\t%s\n", wkprefixes[i].string); exit (1); } n = e; *e = c; while (*n && *n == '\\') n++; e = n + strlen (n); if (howmanyparts > 1) { while (n < e && *e != key_sep) e--; if (*e != key_sep) { key = wkprefixes[i].key; if (value) free (value); len = mbstowcs (NULL, n, 0) + 1; value = (wchar_t *) malloc (len); mbstowcs (value, n, len); return; } else { *e = 0; if (value) free (value); len = mbstowcs (NULL, e + 1, 0) + 1; value = (wchar_t *) malloc (len); mbstowcs (value, e + 1, len); } } if (host) { rv = RegConnectRegistry (host, wkprefixes[i].key, &base); if (rv != ERROR_SUCCESS) Fail (rv); free (host); } else base = wkprefixes[i].key; if (n[0] == 0) key = base; else { len = mbstowcs (NULL, n, 0) + 1; wchar_t name[len]; mbstowcs (name, n, len); if (access) { rv = RegOpenKeyExW (base, name, 0, access | wow64, &key); if (option && (rv == ERROR_SUCCESS || rv == ERROR_ACCESS_DENIED)) { /* reopen with desired option due to missing option support in RegOpenKeyE */ /* FIXME: may create the key in rare cases (e.g. access denied in parent) */ HKEY key2; if (RegCreateKeyExW (base, name, 0, NULL, option, access | wow64, NULL, &key2, NULL) == ERROR_SUCCESS) { if (rv == ERROR_SUCCESS) RegCloseKey (key); key = key2; rv = ERROR_SUCCESS; } } if (rv != ERROR_SUCCESS) Fail (rv); } else if (argv[1]) { ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], NULL, 0); wchar_t win32_path[len]; cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], win32_path, len); rv = RegLoadKeyW (base, name, win32_path); if (rv != ERROR_SUCCESS) Fail (rv); if (verbose) printf ("key %ls loaded from file %ls\n", name, win32_path); } else { rv = RegUnLoadKeyW (base, name); if (rv != ERROR_SUCCESS) Fail (rv); if (verbose) printf ("key %ls unloaded\n", name); } } } int cmd_list () { DWORD num_subkeys, maxsubkeylen, num_values, maxvalnamelen, maxvaluelen; DWORD maxclasslen; wchar_t *subkey_name, *value_name, *class_name, *vd; unsigned char *value_data; DWORD i, j, m, n, t; int v; find_key (1, KEY_READ); RegQueryInfoKeyW (key, 0, 0, 0, &num_subkeys, &maxsubkeylen, &maxclasslen, &num_values, &maxvalnamelen, &maxvaluelen, 0, 0); subkey_name = (wchar_t *) malloc ((maxsubkeylen + 1) * sizeof (wchar_t)); class_name = (wchar_t *) malloc ((maxclasslen + 1) * sizeof (wchar_t)); value_name = (wchar_t *) malloc ((maxvalnamelen + 1) * sizeof (wchar_t)); value_data = (unsigned char *) malloc (maxvaluelen + 1); if (!listwhat) listwhat = LIST_ALL; if (listwhat & LIST_KEYS) for (i = 0; i < num_subkeys; i++) { m = (maxsubkeylen + 1) * sizeof (wchar_t); n = (maxclasslen + 1) * sizeof (wchar_t); RegEnumKeyExW (key, i, subkey_name, &m, 0, class_name, &n, 0); printf ("%ls", subkey_name); if (postfix || verbose) fputc (key_sep, stdout); if (verbose) printf (" (%ls)", class_name); puts (""); } if (listwhat & LIST_VALS) for (i = 0; i < num_values; i++) { m = (maxvalnamelen + 1) * sizeof (wchar_t); n = maxvaluelen + 1; RegEnumValueW (key, i, value_name, &m, 0, &t, (BYTE *) value_data, &n); value_data[n] = 0; if (!verbose) printf ("%ls\n", value_name); else { printf ("%ls (%s) = ", value_name, types[t]); switch (t) { case REG_NONE: case REG_BINARY: for (j = 0; j < 8 && j < n; j++) printf ("%02x ", value_data[j]); printf ("\n"); break; case REG_DWORD: printf ("0x%08lx (%lu)\n", *(DWORD *) value_data, *(DWORD *) value_data); break; case REG_DWORD_BIG_ENDIAN: v = ((value_data[0] << 24) | (value_data[1] << 16) | (value_data[2] << 8) | (value_data[3])); printf ("0x%08x (%d)\n", v, v); break; case REG_QWORD: printf ("0x%016llx (%llu)\n", *(unsigned long long *) value_data, *(unsigned long long *) value_data); break; case REG_EXPAND_SZ: case REG_SZ: case REG_LINK: printf ("\"%ls\"\n", (wchar_t *) value_data); break; case REG_MULTI_SZ: vd = (wchar_t *) value_data; while (vd && *vd) { printf ("\"%ls\"", vd); vd = vd + wcslen (vd) + 1; if (*vd) printf (", "); } printf ("\n"); break; default: printf ("?\n"); break; } } } return 0; } int cmd_add () { find_key (2, KEY_ALL_ACCESS); HKEY newkey; DWORD newtype; int rv = RegCreateKeyExW (key, value, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | wow64, 0, &newkey, &newtype); if (rv != ERROR_SUCCESS) Fail (rv); if (verbose) { if (newtype == REG_OPENED_EXISTING_KEY) printf ("Key %ls already exists\n", value); else printf ("Key %ls created\n", value); } return 0; } extern "C" { WINADVAPI LONG WINAPI (*regDeleteKeyEx)(HKEY, LPCWSTR, REGSAM, DWORD); } int cmd_remove () { DWORD rv; find_key (2, KEY_ALL_ACCESS); if (wow64) { HMODULE mod = LoadLibrary ("advapi32.dll"); if (mod) regDeleteKeyEx = (WINADVAPI LONG WINAPI (*)(HKEY, LPCWSTR, REGSAM, DWORD)) GetProcAddress (mod, "RegDeleteKeyExW"); } if (regDeleteKeyEx) rv = (*regDeleteKeyEx) (key, value, wow64, 0); else rv = RegDeleteKeyW (key, value); if (rv != ERROR_SUCCESS) Fail (rv); if (verbose) printf ("subkey %ls deleted\n", value); return 0; } int cmd_check () { find_key (1, KEY_READ); if (verbose) printf ("key %s exists\n", argv[0]); return 0; } int cmd_set () { int i, n, max_n; DWORD v, rv; unsigned long long llval; char *a = argv[1], *data = 0; find_key (2, KEY_ALL_ACCESS); if (!a) usage (); if (value_type == REG_AUTO) { char *e; llval = strtoull (a, &e, 0); if (a[0] == '%') value_type = REG_EXPAND_SZ; else if (a[0] && !*e) value_type = llval > 0xffffffffULL ? REG_QWORD : REG_DWORD; else if (argv[2]) value_type = REG_MULTI_SZ; else value_type = REG_SZ; } switch (value_type) { case REG_NONE: case REG_BINARY: for (n = 0; argv[n+1]; n++) ; if (n == 1 && strcmp (argv[1], "-") == 0) { /* read from stdin */ i = n = 0; for (;;) { if (i <= n) { i = n + BUFSIZ; data = (char *) realloc (data, i); } int r = fread (data+n, 1, i-n, stdin); if (r <= 0) break; n += r; } } else if (n > 0) { /* parse hex from argv */ data = (char *) malloc (n); for (i = 0; i < n; i++) { char *e; errno = 0; v = strtoul (argv[i+1], &e, 16); if (errno || v > 0xff || *e) { fprintf (stderr, "Invalid hex constant `%s'\n", argv[i+1]); exit (1); } data[i] = (char) v; } } rv = RegSetValueExW (key, value, 0, value_type, (const BYTE *) data, n); break; case REG_DWORD: v = strtoul (a, 0, 0); rv = RegSetValueExW (key, value, 0, REG_DWORD, (const BYTE *) &v, sizeof (v)); break; case REG_DWORD_BIG_ENDIAN: v = strtoul (a, 0, 0); v = (((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24)); rv = RegSetValueExW (key, value, 0, REG_DWORD_BIG_ENDIAN, (const BYTE *) &v, sizeof (v)); break; case REG_QWORD: llval = strtoul (a, 0, 0); rv = RegSetValueExW (key, value, 0, REG_QWORD, (const BYTE *) &llval, sizeof (llval)); break; case REG_SZ: case REG_EXPAND_SZ: n = mbstowcs (NULL, a, 0); wchar_t w[n + 1]; mbstowcs (w, a, n + 1); rv = RegSetValueExW (key, value, 0, value_type, (const BYTE *) w, (n + 1) * sizeof (wchar_t)); break; case REG_MULTI_SZ: for (i = 1, max_n = 1; argv[i]; i++) max_n += mbstowcs (NULL, argv[i], 0) + 1; data = (char *) malloc (max_n * sizeof (wchar_t)); for (i = 1, n = 0; argv[i]; i++) n += mbstowcs ((wchar_t *) data + n, argv[i], max_n - n) + 1; ((wchar_t *)data)[n] = L'\0'; rv = RegSetValueExW (key, value, 0, REG_MULTI_SZ, (const BYTE *) data, (max_n + 1) * sizeof (wchar_t)); break; case REG_AUTO: rv = ERROR_SUCCESS; break; default: rv = ERROR_INVALID_CATEGORY; break; } if (data) free(data); if (rv != ERROR_SUCCESS) Fail (rv); return 0; } int cmd_unset () { find_key (2, KEY_ALL_ACCESS); DWORD rv = RegDeleteValueW (key, value); if (rv != ERROR_SUCCESS) Fail (rv); if (verbose) printf ("value %ls deleted\n", value); return 0; } int cmd_get () { find_key (2, KEY_READ); DWORD vtype, dsize, rv; PBYTE data; wchar_t *vd; rv = RegQueryValueExW (key, value, 0, &vtype, 0, &dsize); if (rv != ERROR_SUCCESS) Fail (rv); data = (PBYTE) malloc (dsize + 1); rv = RegQueryValueExW (key, value, 0, &vtype, data, &dsize); if (rv != ERROR_SUCCESS) Fail (rv); if (value_type == REG_BINARY) { for (unsigned i = 0; i < dsize; i++) printf ("%02x%c", (unsigned char)data[i], (i < dsize-1 ? ' ' : '\n')); } else if (value_type == REG_NONE) fwrite (data, dsize, 1, stdout); else switch (vtype) { case REG_NONE: case REG_BINARY: fwrite (data, dsize, 1, stdout); break; case REG_DWORD: printf (hex ? "0x%08lx\n" : "%lu\n", *(DWORD *) data); break; case REG_DWORD_BIG_ENDIAN: rv = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3])); printf (hex ? "0x%08lx\n" : "%lu\n", rv); break; case REG_QWORD: printf (hex ? "0x%016llx\n" : "%llu\n", *(unsigned long long *) data); break; case REG_SZ: case REG_LINK: printf ("%ls\n", (wchar_t *) data); break; case REG_EXPAND_SZ: if (value_type == REG_EXPAND_SZ) // hack { wchar_t *buf; DWORD bufsize; bufsize = ExpandEnvironmentStringsW ((wchar_t *) data, 0, 0); buf = (wchar_t *) malloc (bufsize + 1); ExpandEnvironmentStringsW ((wchar_t *) data, buf, bufsize + 1); free (data); data = (PBYTE) buf; } printf ("%ls\n", (wchar_t *) data); break; case REG_MULTI_SZ: vd = (wchar_t *) data; while (vd && *vd) { printf ("%ls\n", vd); vd = vd + wcslen (vd) + 1; } break; } return 0; } int cmd_load () { if (!argv[1]) { usage (); return 1; } find_key (1, 0); return 0; } int cmd_unload () { if (argv[1]) { usage (); return 1; } find_key (1, 0); return 0; } DWORD set_privilege (const char *name) { TOKEN_PRIVILEGES tp; if (!LookupPrivilegeValue (NULL, name, &tp.Privileges[0].Luid)) return GetLastError (); tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; HANDLE t; /* OpenProcessToken does not work here, because main thread has its own impersonation token */ if (!OpenThreadToken (GetCurrentThread (), TOKEN_ADJUST_PRIVILEGES, FALSE, &t)) return GetLastError (); AdjustTokenPrivileges (t, FALSE, &tp, 0, NULL, NULL); DWORD rv = GetLastError (); CloseHandle (t); return rv; } int cmd_save () { if (!argv[1]) { usage (); return 1; } /* try to set SeBackupPrivilege, let RegSaveKey report the error */ set_privilege (SE_BACKUP_NAME); /* REG_OPTION_BACKUP_RESTORE is necessary to save /HKLM/SECURITY */ find_key (1, KEY_QUERY_VALUE, REG_OPTION_BACKUP_RESTORE); ssize_t len = cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], NULL, 0); wchar_t win32_path[len]; cygwin_conv_path (CCP_POSIX_TO_WIN_W, argv[1], win32_path, len); DWORD rv = RegSaveKeyW (key, win32_path, NULL); if (rv != ERROR_SUCCESS) Fail (rv); if (verbose) printf ("key saved to %ls\n", win32_path); return 0; } static struct { const char *name; int (*func) (); } commands[] = { {"list", cmd_list}, {"add", cmd_add}, {"remove", cmd_remove}, {"check", cmd_check}, {"set", cmd_set}, {"unset", cmd_unset}, {"get", cmd_get}, {"load", cmd_load}, {"unload", cmd_unload}, {"save", cmd_save}, {0, 0} }; int main (int argc, char **_argv) { int g; setlocale (LC_ALL, ""); prog_name = strrchr (_argv[0], '/'); if (prog_name == NULL) prog_name = strrchr (_argv[0], '\\'); if (prog_name == NULL) prog_name = _argv[0]; else prog_name++; while ((g = getopt_long (argc, _argv, opts, longopts, NULL)) != EOF) switch (g) { case 'b': value_type = REG_BINARY; break; case 'd': value_type = REG_DWORD; break; case 'D': value_type = REG_DWORD_BIG_ENDIAN; break; case 'e': value_type = REG_EXPAND_SZ; break; case 'k': listwhat |= LIST_KEYS; break; case 'h': usage (stdout); case 'i': value_type = REG_DWORD; break; case 'l': listwhat |= LIST_VALS; break; case 'm': value_type = REG_MULTI_SZ; break; case 'n': value_type = REG_NONE; break; case 'p': postfix++; break; case 'q': quiet++; break; case 'Q': value_type = REG_QWORD; break; case 's': value_type = REG_SZ; break; case 'v': verbose++; break; case 'V': print_version (); exit (0); case 'w': wow64 = KEY_WOW64_64KEY; break; case 'W': wow64 = KEY_WOW64_32KEY; break; case 'x': hex++; break; case 'K': key_sep = *optarg; break; default : usage (); } if ((_argv[optind] == NULL) || (_argv[optind+1] == NULL)) usage (); argv = _argv + optind; int i; for (i = 0; commands[i].name; i++) if (strcmp (commands[i].name, argv[0]) == 0) { argv++; return commands[i].func (); } usage (); return 0; }