Merge pull request #1345 from kaldap/registry
New 'listWindowsRegistry' OS API method for enumerating content of single registry subkey
This commit is contained in:
commit
3f0cf0b5c0
@ -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;
|
||||
}
|
||||
|
||||
|
252
src/host/os_listWindowsRegistry.c
Normal file
252
src/host/os_listWindowsRegistry.c
Normal 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
|
@ -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 },
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
--
|
||||
|
Loading…
Reference in New Issue
Block a user