Merge pull request #1345 from kaldap/registry

New 'listWindowsRegistry' OS API method for enumerating content of single registry subkey
This commit is contained in:
@starkos 2019-10-14 16:01:14 -04:00 committed by GitHub
commit 3f0cf0b5c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 328 additions and 11 deletions

View File

@ -15,7 +15,7 @@ typedef struct RegKeyInfo
char * value;
} RegKeyInfo;
static HKEY get_key(const char **path)
HKEY getRegistryKey(const char **path)
{
if (_strnicmp(*path, "HKCU:", 5) == 0) {
*path += 5;
@ -42,7 +42,7 @@ static HKEY get_key(const char **path)
return NULL;
}
static HKEY get_subkey(HKEY key, const char **path)
static HKEY getSubkey(HKEY key, const char **path)
{
HKEY subkey;
size_t length;
@ -84,7 +84,7 @@ static HKEY get_subkey(HKEY key, const char **path)
return subkey;
}
static char * get_value(HKEY key, const char * name)
static char * getValue(HKEY key, const char * name)
{
DWORD length;
char * value;
@ -111,14 +111,14 @@ static char * get_value(HKEY key, const char * name)
return value;
}
static void fetch_keyinfo(struct RegKeyInfo *info, const char *path)
static void fetchKeyInfo(struct RegKeyInfo *info, const char *path)
{
info->key = get_key(&path);
info->subkey = get_subkey(info->key, &path);
info->value = get_value(info->subkey, path);
info->key = getRegistryKey(&path);
info->subkey = getSubkey(info->key, &path);
info->value = getValue(info->subkey, path);
}
static void release_keyinfo(struct RegKeyInfo *info)
static void releaseKeyInfo(struct RegKeyInfo *info)
{
free(info->value);
RegCloseKey(info->subkey);
@ -127,9 +127,9 @@ static void release_keyinfo(struct RegKeyInfo *info)
int os_getWindowsRegistry(lua_State *L)
{
RegKeyInfo info;
fetch_keyinfo(&info, luaL_checkstring(L, 1));
fetchKeyInfo(&info, luaL_checkstring(L, 1));
lua_pushstring(L, info.value);
release_keyinfo(&info);
releaseKeyInfo(&info);
return 1;
}

View File

@ -0,0 +1,252 @@
/**
* \file os_reg.c
* \brief Returns true if the given file exists on the file system.
* \author Copyright (c) 2002-2016 Jason Perkins and the Premake project
*/
#include "premake.h"
#if PLATFORM_WINDOWS
typedef struct RegNodeInfo
{
const char * name;
const char * value;
DWORD valueSize;
DWORD type;
} RegNodeInfo;
typedef void (*ListCallback)(const RegNodeInfo * info, void * user);
extern HKEY getRegistryKey(const char** path);
static const char* getTypeString(DWORD type)
{
switch (type)
{
case REG_NONE: return "REG_NONE";
case REG_SZ: return "REG_SZ";
case REG_EXPAND_SZ: return "REG_EXPAND_SZ";
case REG_BINARY: return "REG_BINARY";
case REG_DWORD: return "REG_DWORD";
case REG_DWORD_BIG_ENDIAN: return "REG_DWORD_BIG_ENDIAN";
case REG_LINK: return "REG_LINK";
case REG_MULTI_SZ: return "REG_MULTI_SZ";
case REG_RESOURCE_LIST: return "REG_RESOURCE_LIST";
case REG_FULL_RESOURCE_DESCRIPTOR: return "REG_FULL_RESOURCE_DESCRIPTOR";
case REG_RESOURCE_REQUIREMENTS_LIST: return "REG_RESOURCE_REQUIREMENTS_LIST";
case REG_QWORD: return "REG_QWORD";
default: return NULL;
}
}
static HKEY openKey(const char *path)
{
HKEY key, subkey;
// check string
if (path == NULL)
return NULL;
// get HKEY
key = getRegistryKey(&path);
if (key == NULL)
return NULL;
// skip the initial path separator
if (path[0] == '\\')
path++;
// open the key for reading
if (RegOpenKeyExA(key, path, 0, KEY_READ, &subkey) != ERROR_SUCCESS)
subkey = NULL;
return subkey;
}
static int listNodes(HKEY key, ListCallback callback, void * user)
{
RegNodeInfo node;
DWORD maxSubkeyLength;
DWORD maxValueLength;
DWORD maxNameLength;
DWORD numSubkeys;
DWORD numValues;
DWORD length;
DWORD index;
char* name;
char* value;
int ok;
if (key == NULL || callback == NULL)
return 0;
// Initialize node structure
node.value = NULL;
node.valueSize = 0;
node.type = REG_NONE;
// Fetch info about key content
if (RegQueryInfoKeyA(key, NULL, NULL, NULL, &numSubkeys, &maxSubkeyLength, NULL, &numValues, &maxNameLength, &maxValueLength, NULL, NULL) != ERROR_SUCCESS)
return 0;
// Allocate name and value buffers
if (maxSubkeyLength > maxNameLength)
maxNameLength = maxSubkeyLength;
maxNameLength++;
maxValueLength++;
name = (char*)malloc((size_t)maxNameLength);
value = (char*)malloc((size_t)maxValueLength + 1);
// Iterate over subkeys
ok = 1;
node.name = name;
for (index = 0; index < numSubkeys; index++) {
length = maxNameLength;
if (RegEnumKeyExA(key, index, name, &length, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
ok = 0;
break;
}
callback(&node, user);
}
// Iterate over values
if (ok) {
node.value = value;
for (index = 0; index < numValues; index++) {
length = maxNameLength;
node.valueSize = maxValueLength;
if (RegEnumValueA(key, index, name, &length, NULL, &node.type, (LPBYTE)value, &node.valueSize) != ERROR_SUCCESS) {
ok = 0;
break;
}
// Ensure proper termination of strings (two terminators for the REG_MULTI_SZ)
value[node.valueSize] = '\0';
value[node.valueSize + 1] = '\0';
callback(&node, user);
}
}
// Free buffers
free(name);
free(value);
return ok;
}
static void listCallback(const RegNodeInfo* info, void* user)
{
lua_State* L = (lua_State*)user;
const char* typeString;
// Insert key into the result table (keys are represented as empty tables)
if (info->value == NULL) {
lua_createtable(L, 0, 0);
lua_setfield(L, -2, info->name);
return;
}
// Values are represented as tables containing "type" and "value" records
typeString = getTypeString(info->type);
lua_createtable(L, 0, 2);
lua_pushstring(L, typeString ? typeString : "Unknown");
lua_setfield(L, -2, "type");
switch (info->type)
{
// Binary encoded values -> size defined string
case REG_NONE:
case REG_BINARY:
case REG_RESOURCE_LIST:
case REG_FULL_RESOURCE_DESCRIPTOR:
case REG_RESOURCE_REQUIREMENTS_LIST: {
lua_pushlstring(L, info->value, info->valueSize);
break;
}
// String encoded values -> zero terminated string
case REG_SZ:
case REG_EXPAND_SZ:
case REG_LINK: {
lua_pushstring(L, info->value);
break;
}
// Numbers
case REG_DWORD: {
lua_pushinteger(L, *(DWORD32*)info->value);
break;
}
case REG_DWORD_BIG_ENDIAN: {
lua_pushinteger(L, (info->value[3] << 0) | (info->value[2] << 8) | (info->value[1] << 16) | (info->value[0] << 24));
break;
}
case REG_QWORD: {
lua_pushinteger(L, *(DWORD64*)info->value);
break;
}
// Multiple strings
case REG_MULTI_SZ: {
DWORD i, j, k;
lua_newtable(L);
for (i = j = 0, k = 1; i < info->valueSize; i++)
{
if (info->value[i] != 0)
continue;
if (i == j)
break;
lua_pushlstring(L, &info->value[j], i - j);
lua_rawseti(L, -2, k);
j = i + 1;
k++;
}
break;
}
// Unknown field -> nil
default: {
lua_pushnil(L);
break;
}
}
lua_setfield(L, -2, "value");
// Complete the value subtable
lua_setfield(L, -2, info->name);
}
int os_listWindowsRegistry(lua_State* L)
{
HKEY key = openKey(luaL_checkstring(L, 1));
if (key == NULL) {
lua_pushnil(L);
return 1;
}
lua_newtable(L);
if (!listNodes(key, listCallback, (void *)L)) {
// Discard table in case of fault and push nil instead
lua_pop(L, 1);
lua_pushnil(L);
}
RegCloseKey(key);
return 1;
}
#else
int os_listWindowsRegistry(lua_State* L)
{
lua_pushnil(L);
return 1;
}
#endif

View File

@ -68,6 +68,7 @@ static const luaL_Reg os_functions[] = {
{ "getcwd", os_getcwd },
{ "getpass", os_getpass },
{ "getWindowsRegistry", os_getWindowsRegistry },
{ "listWindowsRegistry", os_listWindowsRegistry },
{ "getversion", os_getversion },
{ "host", os_host },
{ "isfile", os_isfile },

View File

@ -123,6 +123,7 @@ int os_copyfile(lua_State* L);
int os_getcwd(lua_State* L);
int os_getpass(lua_State* L);
int os_getWindowsRegistry(lua_State* L);
int os_listWindowsRegistry(lua_State* L);
int os_getversion(lua_State* L);
int os_host(lua_State* L);
int os_is64bit(lua_State* L);

View File

@ -257,7 +257,8 @@
function suite.getreg_noSeparators()
if os.ishost("windows") then
os.getWindowsRegistry("HKCU:ShouldNotExistAtAll")
local x = os.getWindowsRegistry("HKCU:ShouldNotExistAtAll")
test.isequal(nil, x)
end
end
@ -283,6 +284,68 @@
end
--
-- os.listWindowsRegistry windows tests
--
function suite.listreg_nonExistentKey()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKCU:Should\\Not\\Exist\\At\\All")
test.isequal(nil, x)
end
end
function suite.listreg_nonExistentKeyTrailingBackslash()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKCU:Should\\Not\\Exist\\At\\All\\")
test.isequal(nil, x)
end
end
function suite.listreg_noSeparators()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKCU:ShouldNotExistAtAll")
test.isequal(nil, x)
end
end
function suite.listreg_noSeparatorExistingPath()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKCU:Environment")
test.istrue(x ~= nil and x["TEMP"] ~= nil)
end
end
function suite.listreg_optSeparators()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKCU:\\Environment\\")
test.istrue(x ~= nil and x["TEMP"] ~= nil)
end
end
function suite.listreg_keyDefaultValueAndStringValueFormat()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKLM:SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Minimal\\AppInfo")
test.isequal(x[""]["value"], "Service")
test.isequal(x[""]["type"], "REG_SZ")
end
end
function suite.listreg_numericValueFormat()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKCU:Console")
test.isequal(type(x["FullScreen"]["value"]), "number")
test.isequal(x["FullScreen"]["type"], "REG_DWORD")
end
end
function suite.listreg_subkeyFormat()
if os.ishost("windows") then
local x = os.listWindowsRegistry("HKLM:")
test.isequal(type(x["SOFTWARE"]), "table")
test.isequal(next(x["SOFTWARE"]), nil)
end
end
--
-- os.getversion tests.
--