diff options
Diffstat (limited to 'windows/utils/registry.c')
-rw-r--r-- | windows/utils/registry.c | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/windows/utils/registry.c b/windows/utils/registry.c new file mode 100644 index 00000000..1f50e67a --- /dev/null +++ b/windows/utils/registry.c @@ -0,0 +1,184 @@ +/* + * Implement convenience wrappers on the awkward low-level functions + * for accessing the Windows registry. + */ + +#include "putty.h" + +HKEY open_regkey_fn(bool create, HKEY hk, const char *path, ...) +{ + HKEY toret = NULL; + bool hk_needs_close = false; + va_list ap; + va_start(ap, path); + + for (; path; path = va_arg(ap, const char *)) { + HKEY hk_sub = NULL; + + LONG status; + if (create) + status = RegCreateKeyEx( + hk, path, 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_READ | KEY_WRITE, NULL, &hk_sub, NULL); + else + status = RegOpenKeyEx( + hk, path, 0, KEY_READ | KEY_WRITE, &hk_sub); + + if (status != ERROR_SUCCESS) + goto out; + + if (hk_needs_close) + RegCloseKey(hk); + hk = hk_sub; + hk_needs_close = true; + } + + toret = hk; + hk = NULL; + hk_needs_close = false; + + out: + va_end(ap); + if (hk_needs_close) + RegCloseKey(hk); + return toret; +} + +void close_regkey(HKEY key) +{ + RegCloseKey(key); +} + +void del_regkey(HKEY key, const char *name) +{ + RegDeleteKey(key, name); +} + +char *enum_regkey(HKEY key, int index) +{ + size_t regbuf_size = MAX_PATH + 1; + char *regbuf = snewn(regbuf_size, char); + + while (1) { + LONG status = RegEnumKey(key, index, regbuf, regbuf_size); + if (status == ERROR_SUCCESS) + return regbuf; + if (status != ERROR_MORE_DATA) { + sfree(regbuf); + return NULL; + } + sgrowarray(regbuf, regbuf_size, regbuf_size); + } +} + +bool get_reg_dword(HKEY key, const char *name, DWORD *out) +{ + DWORD type, size; + size = sizeof(*out); + + if (RegQueryValueEx(key, name, 0, &type, + (BYTE *)out, &size) != ERROR_SUCCESS || + size != sizeof(*out) || type != REG_DWORD) + return false; + else + return true; +} + +bool put_reg_dword(HKEY key, const char *name, DWORD value) +{ + return RegSetValueEx(key, name, 0, REG_DWORD, (CONST BYTE *) &value, + sizeof(value)) == ERROR_SUCCESS; +} + +char *get_reg_sz(HKEY key, const char *name) +{ + DWORD type, size; + + if (RegQueryValueEx(key, name, 0, &type, NULL, + &size) != ERROR_SUCCESS || type != REG_SZ) + return NULL; /* not a string */ + + size_t allocsize = size+1; /* allow for an extra NUL if needed */ + char *toret = snewn(allocsize, char); + if (RegQueryValueEx(key, name, 0, &type, (BYTE *)toret, + &size) != ERROR_SUCCESS || type != REG_SZ) { + sfree(toret); + return NULL; + } + assert(size < allocsize); + toret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx + * didn't supply one */ + + return toret; +} + +bool put_reg_sz(HKEY key, const char *name, const char *str) +{ + /* You have to store the trailing NUL as well */ + return RegSetValueEx(key, name, 0, REG_SZ, (CONST BYTE *)str, + 1 + strlen(str)) == ERROR_SUCCESS; +} + +/* + * REG_MULTI_SZ items are stored as a concatenation of NUL-terminated + * strings, terminated in turn with an empty string, i.e. a second + * consecutive NUL. + * + * We represent these in their storage format, as a strbuf - but + * *without* the second consecutive NUL. + * + * So you can build up a new MULTI_SZ value in a strbuf by calling + * put_asciz once per output string and then put_reg_multi_sz; and you + * can consume one by initialising a BinarySource to the result of + * get_reg_multi_sz, and then calling get_asciz on it and assuming + * that !get_err(src) means you have a real output string. + * + * Also, calling strbuf_to_str on one of these will give you back a + * bare 'char *' with the same double-NUL termination, to pass back to + * a caller. + */ +strbuf *get_reg_multi_sz(HKEY key, const char *name) +{ + DWORD type, size; + + if (RegQueryValueEx(key, name, 0, &type, NULL, + &size) != ERROR_SUCCESS || type != REG_MULTI_SZ) + return NULL; /* not a string */ + + strbuf *toret = strbuf_new(); + void *ptr = strbuf_append(toret, (size_t)size + 2); + if (RegQueryValueEx(key, name, 0, &type, (BYTE *)ptr, + &size) != ERROR_SUCCESS || type != REG_MULTI_SZ) { + strbuf_free(toret); + return NULL; + } + strbuf_shrink_to(toret, size); + /* Ensure we end with exactly one \0 */ + while (strbuf_chomp(toret, '\0')); + put_byte(toret, '\0'); + return toret; +} + +bool put_reg_multi_sz(HKEY key, const char *name, strbuf *str) +{ + /* + * Of course, to write our string list into the registry, we _do_ + * have to include both trailing NULs. But this is easy, because a + * strbuf is also designed to hold a single string and make it + * conveniently accessible in NUL-terminated form, so it stores a + * NUL in its buffer just beyond its formal length. So we just + * include that extra byte in the data we write. + */ + return RegSetValueEx(key, name, 0, REG_MULTI_SZ, (CONST BYTE *)str->s, + str->len + 1) == ERROR_SUCCESS; +} + +char *get_reg_sz_simple(HKEY key, const char *name, const char *leaf) +{ + HKEY subkey = open_regkey(false, key, name); + if (!subkey) + return NULL; + char *toret = get_reg_sz(subkey, leaf); + RegCloseKey(subkey); + return toret; +} |