mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
[common/client] platform: make filesystem access portable
This commit is contained in:
60
client/platform/Linux/config.c
Normal file
60
client/platform/Linux/config.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config_platform.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/paths.h"
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static bool visitJoinedPath(const char * directory, const char * filename,
|
||||
ConfigFileVisitor visitor, void * opaque)
|
||||
{
|
||||
char * path;
|
||||
if (alloc_sprintf(&path, "%s/%s", directory, filename) < 0)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool result = visitor(path, opaque);
|
||||
free(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool config_platformVisitDefaultFiles(
|
||||
ConfigFileVisitor visitor, void * opaque)
|
||||
{
|
||||
if (!visitor("/etc/looking-glass-client.ini", opaque))
|
||||
return false;
|
||||
|
||||
const struct passwd * password = getpwuid(getuid());
|
||||
if (!password)
|
||||
DEBUG_WARN("getpwuid failed, unable to load user configuration");
|
||||
else if (!visitJoinedPath(password->pw_dir, ".looking-glass-client.ini",
|
||||
visitor, opaque))
|
||||
return false;
|
||||
|
||||
return visitJoinedPath(lgConfigDir(), "client.ini", visitor, opaque);
|
||||
}
|
||||
42
client/platform/Windows/config.c
Normal file
42
client/platform/Windows/config.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config_platform.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/paths.h"
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
bool config_platformVisitDefaultFiles(
|
||||
ConfigFileVisitor visitor, void * opaque)
|
||||
{
|
||||
char * path;
|
||||
if (alloc_sprintf(&path, "%s\\client.ini", lgConfigDir()) < 0)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool result = visitor(path, opaque);
|
||||
free(path);
|
||||
return result;
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "app.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/option.h"
|
||||
#include "common/paths.h"
|
||||
#include "common/stringlist.h"
|
||||
@@ -35,7 +36,6 @@
|
||||
#include "basic.vert.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
@@ -46,7 +46,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define GLSL_MAX_TEXTURES 16
|
||||
#define GLSL_EXPR_STACK 128
|
||||
@@ -1133,27 +1132,24 @@ static bool scanDirectory(const char * root, const char * relative,
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
DIR * directory = opendir(path);
|
||||
if (!directory)
|
||||
StringList entries = lgListDirectory(path);
|
||||
if (!entries)
|
||||
{
|
||||
DEBUG_ERROR("Unable to open GLSL shader directory '%s': %s",
|
||||
path, strerror(errno));
|
||||
DEBUG_ERROR("Unable to open GLSL shader directory '%s'", path);
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
struct dirent * entry;
|
||||
while ((entry = readdir(directory)))
|
||||
for (unsigned int i = 0; i < stringlist_count(entries); ++i)
|
||||
{
|
||||
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
|
||||
continue;
|
||||
const char * entry = stringlist_at(entries, i);
|
||||
|
||||
char * childRelative;
|
||||
if (relative && *relative)
|
||||
alloc_sprintf(&childRelative, "%s/%s", relative, entry->d_name);
|
||||
alloc_sprintf(&childRelative, "%s/%s", relative, entry);
|
||||
else
|
||||
childRelative = lg_strdup(entry->d_name);
|
||||
childRelative = lg_strdup(entry);
|
||||
if (!childRelative)
|
||||
{
|
||||
result = false;
|
||||
@@ -1169,23 +1165,18 @@ static bool scanDirectory(const char * root, const char * relative,
|
||||
break;
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
if (lstat(childPath, &statbuf) == 0)
|
||||
const LGFileType type = lgPathTypeNoFollow(childPath);
|
||||
if (type == LG_FILE_TYPE_DIRECTORY)
|
||||
{
|
||||
if (S_ISDIR(statbuf.st_mode))
|
||||
{
|
||||
result = scanDirectory(root, childRelative, files);
|
||||
free(childRelative);
|
||||
}
|
||||
else if (S_ISREG(statbuf.st_mode) && hasGLSLExtension(entry->d_name))
|
||||
{
|
||||
const unsigned int count = stringlist_count(files);
|
||||
stringlist_push(files, childRelative);
|
||||
result = stringlist_count(files) == count + 1;
|
||||
if (!result)
|
||||
free(childRelative);
|
||||
}
|
||||
else
|
||||
result = scanDirectory(root, childRelative, files);
|
||||
free(childRelative);
|
||||
}
|
||||
else if (type == LG_FILE_TYPE_REGULAR && hasGLSLExtension(entry))
|
||||
{
|
||||
const unsigned int count = stringlist_count(files);
|
||||
stringlist_push(files, childRelative);
|
||||
result = stringlist_count(files) == count + 1;
|
||||
if (!result)
|
||||
free(childRelative);
|
||||
}
|
||||
else
|
||||
@@ -1196,7 +1187,7 @@ static bool scanDirectory(const char * root, const char * relative,
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(directory);
|
||||
stringlist_free(&entries);
|
||||
free(path);
|
||||
return result;
|
||||
}
|
||||
@@ -1219,13 +1210,13 @@ static bool glslCreate(EGL_FilterAddFn add, void * opaque)
|
||||
return false;
|
||||
DEBUG_INFO("Scanning runtime GLSL filters in: %s", g_shaderPath);
|
||||
|
||||
struct stat statbuf;
|
||||
if (stat(g_shaderPath, &statbuf) < 0 && (!configured || !*configured))
|
||||
if (lgPathType(g_shaderPath) == LG_FILE_TYPE_MISSING &&
|
||||
(!configured || !*configured))
|
||||
{
|
||||
if (mkdir(g_shaderPath, S_IRWXU) < 0 && errno != EEXIST)
|
||||
if (!lgCreateDirectory(g_shaderPath))
|
||||
{
|
||||
DEBUG_ERROR("Unable to create GLSL shader directory '%s': %s",
|
||||
g_shaderPath, strerror(errno));
|
||||
DEBUG_ERROR("Unable to create GLSL shader directory '%s'",
|
||||
g_shaderPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,13 @@
|
||||
#include "app.h"
|
||||
#include "cimgui.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/array.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/option.h"
|
||||
#include "common/paths.h"
|
||||
#include "common/stringlist.h"
|
||||
@@ -113,8 +111,6 @@ static void loadPreset(struct EGL_PostProcess * this, const char * name);
|
||||
|
||||
static void loadPresetList(struct EGL_PostProcess * this)
|
||||
{
|
||||
DIR * dir = NULL;
|
||||
|
||||
alloc_sprintf(&this->presetDir, "%s/presets", lgConfigDir());
|
||||
if (!this->presetDir)
|
||||
{
|
||||
@@ -122,14 +118,14 @@ static void loadPresetList(struct EGL_PostProcess * this)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mkdir(this->presetDir, S_IRWXU) < 0 && errno != EEXIST)
|
||||
if (!lgCreateDirectory(this->presetDir))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create presets directory: %s", this->presetDir);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dir = opendir(this->presetDir);
|
||||
if (!dir)
|
||||
StringList entries = lgListDirectory(this->presetDir);
|
||||
if (!entries)
|
||||
{
|
||||
DEBUG_ERROR("Failed to open presets directory: %s", this->presetDir);
|
||||
goto fail;
|
||||
@@ -139,33 +135,45 @@ static void loadPresetList(struct EGL_PostProcess * this)
|
||||
if (!this->presets)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory for preset list");
|
||||
goto fail;
|
||||
goto fail_entries;
|
||||
}
|
||||
|
||||
struct dirent * entry;
|
||||
const char * preset = option_get_string("egl", "preset");
|
||||
this->activePreset = -1;
|
||||
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
for (unsigned int i = 0; i < stringlist_count(entries); ++i)
|
||||
{
|
||||
if (entry->d_type != DT_REG)
|
||||
const char * entry = stringlist_at(entries, i);
|
||||
char * path;
|
||||
alloc_sprintf(&path, "%s/%s", this->presetDir, entry);
|
||||
if (!path)
|
||||
goto fail_entries;
|
||||
|
||||
const LGFileType type = lgPathTypeNoFollow(path);
|
||||
free(path);
|
||||
if (type != LG_FILE_TYPE_REGULAR)
|
||||
continue;
|
||||
|
||||
DEBUG_INFO("Found preset: %s", entry->d_name);
|
||||
char * name = strdup(entry->d_name);
|
||||
DEBUG_INFO("Found preset: %s", entry);
|
||||
char * name = strdup(entry);
|
||||
if (!name)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
goto fail;
|
||||
goto fail_entries;
|
||||
}
|
||||
const unsigned int count = stringlist_count(this->presets);
|
||||
stringlist_push(this->presets, name);
|
||||
if (stringlist_count(this->presets) != count + 1)
|
||||
{
|
||||
free(name);
|
||||
goto fail_entries;
|
||||
}
|
||||
app_registerImGuiText(name);
|
||||
|
||||
if (preset && strcmp(preset, name) == 0)
|
||||
this->activePreset = stringlist_count(this->presets) - 1;
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
stringlist_free(&entries);
|
||||
|
||||
if (preset)
|
||||
{
|
||||
@@ -177,11 +185,12 @@ static void loadPresetList(struct EGL_PostProcess * this)
|
||||
|
||||
return;
|
||||
|
||||
fail_entries:
|
||||
stringlist_free(&entries);
|
||||
|
||||
fail:
|
||||
free(this->presetDir);
|
||||
this->presetDir = NULL;
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
if (this->presets)
|
||||
stringlist_free(&this->presets);
|
||||
}
|
||||
@@ -222,7 +231,7 @@ static bool savePreset(struct EGL_PostProcess * this, const char * name)
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE * file = fopen(path, "w");
|
||||
FILE * file = lgOpenFile(path, "w");
|
||||
if (!file)
|
||||
{
|
||||
const char * strError = strerror(errno);
|
||||
@@ -362,7 +371,7 @@ static void deletePreset(struct EGL_PostProcess * this)
|
||||
return;
|
||||
}
|
||||
|
||||
unlink(path);
|
||||
lgDeleteFile(path);
|
||||
free(path);
|
||||
stringlist_remove(this->presets, this->activePreset);
|
||||
if (this->activePreset >= stringlist_count(this->presets))
|
||||
|
||||
@@ -20,19 +20,17 @@
|
||||
|
||||
#include "main.h"
|
||||
#include "config.h"
|
||||
#include "config_platform.h"
|
||||
#include "kb.h"
|
||||
|
||||
#include "common/option.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/paths.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/option.h"
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// forwards
|
||||
static bool optRendererParse (struct Option * opt, const char * str);
|
||||
@@ -565,62 +563,21 @@ void config_init(void)
|
||||
option_register(options);
|
||||
}
|
||||
|
||||
static bool loadDefaultConfig(const char * path, void * opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
if (lgPathType(path) != LG_FILE_TYPE_REGULAR)
|
||||
return true;
|
||||
|
||||
DEBUG_INFO("Loading config from: %s", path);
|
||||
return option_load(path);
|
||||
}
|
||||
|
||||
bool config_load(int argc, char * argv[])
|
||||
{
|
||||
// load any global options first
|
||||
struct stat st;
|
||||
if (stat("/etc/looking-glass-client.ini", &st) >= 0 && S_ISREG(st.st_mode))
|
||||
{
|
||||
DEBUG_INFO("Loading config from: /etc/looking-glass-client.ini");
|
||||
if (!option_load("/etc/looking-glass-client.ini"))
|
||||
return false;
|
||||
}
|
||||
|
||||
// load config from user's home directory
|
||||
struct passwd * pw = getpwuid(getuid());
|
||||
if (!pw)
|
||||
DEBUG_WARN("getpwuid failed, unable to load user configuration");
|
||||
else
|
||||
{
|
||||
char * localFile;
|
||||
alloc_sprintf(&localFile, "%s/.looking-glass-client.ini", pw->pw_dir);
|
||||
if (!localFile)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat(localFile, &st) >= 0 && S_ISREG(st.st_mode))
|
||||
{
|
||||
DEBUG_INFO("Loading config from: %s", localFile);
|
||||
if (!option_load(localFile))
|
||||
{
|
||||
free(localFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
free(localFile);
|
||||
}
|
||||
|
||||
// load config from XDG_CONFIG_HOME
|
||||
char * xdgFile;
|
||||
alloc_sprintf(&xdgFile, "%s/client.ini", lgConfigDir());
|
||||
if (!xdgFile)
|
||||
{
|
||||
DEBUG_ERROR("out of memory");
|
||||
if (!config_platformVisitDefaultFiles(loadDefaultConfig, NULL))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xdgFile && stat(xdgFile, &st) >= 0 && S_ISREG(st.st_mode))
|
||||
{
|
||||
DEBUG_INFO("Loading config from: %s", xdgFile);
|
||||
if (!option_load(xdgFile))
|
||||
{
|
||||
free(xdgFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
free(xdgFile);
|
||||
|
||||
// parse the command line arguments
|
||||
if (!option_parse(argc, argv))
|
||||
@@ -630,7 +587,7 @@ bool config_load(int argc, char * argv[])
|
||||
const char * configFile = option_get_string("app", "configFile");
|
||||
if (configFile)
|
||||
{
|
||||
if (stat(configFile, &st) < 0 || !S_ISREG(st.st_mode))
|
||||
if (lgPathType(configFile) != LG_FILE_TYPE_REGULAR)
|
||||
{
|
||||
DEBUG_ERROR("app:configFile set to invalid file: %s", configFile);
|
||||
return false;
|
||||
|
||||
31
client/src/config_platform.h
Normal file
31
client/src/config_platform.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_CLIENT_CONFIG_PLATFORM_
|
||||
#define _H_LG_CLIENT_CONFIG_PLATFORM_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef bool (*ConfigFileVisitor)(const char * path, void * opaque);
|
||||
|
||||
bool config_platformVisitDefaultFiles(
|
||||
ConfigFileVisitor visitor, void * opaque);
|
||||
|
||||
#endif
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "common/thread.h"
|
||||
#include "common/locking.h"
|
||||
#include "common/event.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/time.h"
|
||||
#include "common/version.h"
|
||||
#include "common/paths.h"
|
||||
@@ -716,8 +717,8 @@ static void preSwapCallback(void * udata)
|
||||
.dataSize = capture.dataSize,
|
||||
};
|
||||
|
||||
FILE * file = fopen(l_testCapture.path, "wb");
|
||||
bool written = false;
|
||||
FILE * file = lgOpenFile(l_testCapture.path, "wb");
|
||||
bool written = false;
|
||||
if (file)
|
||||
{
|
||||
written =
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "font.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -279,7 +280,7 @@ static ImFont * uiFontAddSize(ImFontAtlas * atlas, float size)
|
||||
|
||||
bool util_fileGetContents(const char * filename, char ** buffer, size_t * length)
|
||||
{
|
||||
FILE * fh = fopen(filename, "r");
|
||||
FILE * fh = lgOpenFile(filename, "r");
|
||||
if (!fh)
|
||||
{
|
||||
DEBUG_ERROR("Failed to open the file: %s", filename);
|
||||
|
||||
@@ -7,6 +7,9 @@ target_link_libraries(common-platform-tests
|
||||
${EXE_FLAGS}
|
||||
lg_common
|
||||
)
|
||||
target_compile_definitions(common-platform-tests PRIVATE
|
||||
LG_TEST_TMP_DIR="${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
add_test(NAME common-platform-tests
|
||||
COMMAND common-platform-tests
|
||||
)
|
||||
@@ -14,6 +17,26 @@ set_tests_properties(common-platform-tests PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
|
||||
if(LG_CLIENT_PLATFORM_LINUX)
|
||||
add_executable(config-platform-tests
|
||||
config_platform_test.c
|
||||
../platform/Linux/config.c
|
||||
)
|
||||
target_include_directories(config-platform-tests PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
|
||||
)
|
||||
target_link_libraries(config-platform-tests
|
||||
${EXE_FLAGS}
|
||||
lg_common
|
||||
)
|
||||
add_test(NAME config-platform-tests
|
||||
COMMAND config-platform-tests
|
||||
)
|
||||
set_tests_properties(config-platform-tests PROPERTIES
|
||||
TIMEOUT 10
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LG_CLIENT_PLATFORM_WINDOWS)
|
||||
add_executable(common-windows-tests
|
||||
common_windows_test.c
|
||||
@@ -822,6 +845,7 @@ add_executable(config-message-tests
|
||||
../src/message.c
|
||||
../src/core.c
|
||||
../src/config.c
|
||||
../platform/${LG_CLIENT_PLATFORM}/config.c
|
||||
)
|
||||
target_compile_definitions(config-message-tests PRIVATE
|
||||
CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
|
||||
|
||||
@@ -21,13 +21,17 @@
|
||||
#include "test.h"
|
||||
|
||||
#include "common/event.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/random.h"
|
||||
#include "common/semaphore.h"
|
||||
#include "common/stringutils.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/time.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TEST_WAIT_NS UINT64_C(1000000000)
|
||||
@@ -161,6 +165,42 @@ static void testRandom(void)
|
||||
CHECK(memcmp(first, second, sizeof(first)) != 0);
|
||||
}
|
||||
|
||||
static void testFilesystem(void)
|
||||
{
|
||||
char * directory;
|
||||
char * file;
|
||||
CHECK(alloc_sprintf(&directory, "%s/filesystem-test", LG_TEST_TMP_DIR) > 0);
|
||||
CHECK(alloc_sprintf(&file, "%s/entry.txt", directory) > 0);
|
||||
|
||||
lgDeleteFile(file);
|
||||
lgDeleteDirectory(directory);
|
||||
CHECK(lgPathType(directory) == LG_FILE_TYPE_MISSING);
|
||||
CHECK(lgCreateDirectory(directory));
|
||||
CHECK(lgCreateDirectory(directory));
|
||||
CHECK(lgPathType(directory) == LG_FILE_TYPE_DIRECTORY);
|
||||
|
||||
FILE * stream = lgOpenFile(file, "wb");
|
||||
CHECK(stream);
|
||||
CHECK(fputs("Looking Glass", stream) >= 0);
|
||||
CHECK(fclose(stream) == 0);
|
||||
CHECK(lgPathType(file) == LG_FILE_TYPE_REGULAR);
|
||||
|
||||
StringList entries = lgListDirectory(directory);
|
||||
bool found = false;
|
||||
CHECK(entries);
|
||||
for (unsigned int i = 0; i < stringlist_count(entries); ++i)
|
||||
if (!strcmp(stringlist_at(entries, i), "entry.txt"))
|
||||
found = true;
|
||||
CHECK(found);
|
||||
stringlist_free(&entries);
|
||||
|
||||
CHECK(lgDeleteFile(file));
|
||||
CHECK(lgDeleteDirectory(directory));
|
||||
CHECK(lgPathType(directory) == LG_FILE_TYPE_MISSING);
|
||||
free(file);
|
||||
free(directory);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
lgTimeInit();
|
||||
@@ -169,5 +209,6 @@ int main(void)
|
||||
testTimer();
|
||||
testSemaphore();
|
||||
testRandom();
|
||||
testFilesystem();
|
||||
return 0;
|
||||
}
|
||||
|
||||
92
client/tests/config_platform_test.c
Normal file
92
client/tests/config_platform_test.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config_platform.h"
|
||||
#include "test.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_PATHS 3U
|
||||
|
||||
struct PathList
|
||||
{
|
||||
char * paths[MAX_PATHS];
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
const char * lgConfigDir(void)
|
||||
{
|
||||
return "/test/config";
|
||||
}
|
||||
|
||||
static bool collectPath(const char * path, void * opaque)
|
||||
{
|
||||
struct PathList * paths = opaque;
|
||||
CHECK(paths->count < MAX_PATHS);
|
||||
paths->paths[paths->count] = strdup(path);
|
||||
CHECK(paths->paths[paths->count]);
|
||||
++paths->count;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool stopAtFirstPath(const char * path, void * opaque)
|
||||
{
|
||||
(void)path;
|
||||
unsigned int * count = opaque;
|
||||
++*count;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void testCandidates(void)
|
||||
{
|
||||
struct PathList paths = { 0 };
|
||||
CHECK(config_platformVisitDefaultFiles(collectPath, &paths));
|
||||
CHECK(paths.count == MAX_PATHS);
|
||||
CHECK(strcmp(paths.paths[0], "/etc/looking-glass-client.ini") == 0);
|
||||
|
||||
const struct passwd * password = getpwuid(getuid());
|
||||
CHECK(password);
|
||||
|
||||
const size_t homeLength = strlen(password->pw_dir);
|
||||
const char suffix[] = "/.looking-glass-client.ini";
|
||||
char * expected = malloc(homeLength + sizeof(suffix));
|
||||
CHECK(expected);
|
||||
memcpy(expected, password->pw_dir, homeLength);
|
||||
memcpy(expected + homeLength, suffix, sizeof(suffix));
|
||||
CHECK(strcmp(paths.paths[1], expected) == 0);
|
||||
CHECK(strcmp(paths.paths[2], "/test/config/client.ini") == 0);
|
||||
|
||||
free(expected);
|
||||
for (unsigned int i = 0; i < paths.count; ++i)
|
||||
free(paths.paths[i]);
|
||||
|
||||
unsigned int count = 0;
|
||||
CHECK(!config_platformVisitDefaultFiles(stopAtFirstPath, &count));
|
||||
CHECK(count == 1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testCandidates();
|
||||
return 0;
|
||||
}
|
||||
50
common/include/common/filesystem.h
Normal file
50
common/include/common/filesystem.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_COMMON_FILESYSTEM_
|
||||
#define _H_LG_COMMON_FILESYSTEM_
|
||||
|
||||
#include "common/stringlist.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum LGFileType
|
||||
{
|
||||
LG_FILE_TYPE_ERROR,
|
||||
LG_FILE_TYPE_MISSING,
|
||||
LG_FILE_TYPE_REGULAR,
|
||||
LG_FILE_TYPE_DIRECTORY,
|
||||
LG_FILE_TYPE_OTHER,
|
||||
}
|
||||
LGFileType;
|
||||
|
||||
LGFileType lgPathType (const char * path);
|
||||
LGFileType lgPathTypeNoFollow(const char * path);
|
||||
bool lgCreateDirectory (const char * path);
|
||||
bool lgDeleteFile (const char * path);
|
||||
bool lgDeleteDirectory (const char * path);
|
||||
FILE * lgOpenFile (const char * path, const char * mode);
|
||||
|
||||
/* Returns an owned list of entry names, or NULL when the directory cannot be
|
||||
* opened or read. The special entries "." and ".." are omitted. */
|
||||
StringList lgListDirectory (const char * path);
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "common/option.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/filesystem.h"
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
@@ -506,7 +507,7 @@ static bool process_option_line(const char * module, const char * name,
|
||||
|
||||
bool option_load(const char * filename)
|
||||
{
|
||||
FILE * fp = fopen(filename, "r");
|
||||
FILE * fp = lgOpenFile(filename, "r");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ add_library(lg_common_platform_code STATIC
|
||||
time.c
|
||||
paths.c
|
||||
open.c
|
||||
filesystem.c
|
||||
cpuinfo.c
|
||||
proctitle.c
|
||||
)
|
||||
|
||||
131
common/src/platform/linux/filesystem.c
Normal file
131
common/src/platform/linux/filesystem.c
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "common/filesystem.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static LGFileType pathType(const char * path, bool follow)
|
||||
{
|
||||
struct stat value;
|
||||
if ((follow ? stat(path, &value) : lstat(path, &value)) < 0)
|
||||
return errno == ENOENT || errno == ENOTDIR ?
|
||||
LG_FILE_TYPE_MISSING : LG_FILE_TYPE_ERROR;
|
||||
|
||||
if (S_ISREG(value.st_mode))
|
||||
return LG_FILE_TYPE_REGULAR;
|
||||
if (S_ISDIR(value.st_mode))
|
||||
return LG_FILE_TYPE_DIRECTORY;
|
||||
return LG_FILE_TYPE_OTHER;
|
||||
}
|
||||
|
||||
LGFileType lgPathType(const char * path)
|
||||
{
|
||||
return pathType(path, true);
|
||||
}
|
||||
|
||||
LGFileType lgPathTypeNoFollow(const char * path)
|
||||
{
|
||||
return pathType(path, false);
|
||||
}
|
||||
|
||||
bool lgCreateDirectory(const char * path)
|
||||
{
|
||||
if (mkdir(path, S_IRWXU) == 0)
|
||||
return true;
|
||||
|
||||
return errno == EEXIST && lgPathType(path) == LG_FILE_TYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
bool lgDeleteFile(const char * path)
|
||||
{
|
||||
return unlink(path) == 0;
|
||||
}
|
||||
|
||||
bool lgDeleteDirectory(const char * path)
|
||||
{
|
||||
return rmdir(path) == 0;
|
||||
}
|
||||
|
||||
FILE * lgOpenFile(const char * path, const char * mode)
|
||||
{
|
||||
return fopen(path, mode);
|
||||
}
|
||||
|
||||
StringList lgListDirectory(const char * path)
|
||||
{
|
||||
DIR * directory = opendir(path);
|
||||
if (!directory)
|
||||
return NULL;
|
||||
|
||||
StringList result = stringlist_new(true);
|
||||
if (!result)
|
||||
{
|
||||
closedir(directory);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int error = 0;
|
||||
for (;;)
|
||||
{
|
||||
errno = 0;
|
||||
struct dirent * entry = readdir(directory);
|
||||
if (!entry)
|
||||
{
|
||||
error = errno;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
|
||||
continue;
|
||||
|
||||
char * name = lg_strdup(entry->d_name);
|
||||
if (!name)
|
||||
goto fail;
|
||||
|
||||
const unsigned int count = stringlist_count(result);
|
||||
stringlist_push(result, name);
|
||||
if (stringlist_count(result) != count + 1)
|
||||
{
|
||||
free(name);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(directory);
|
||||
if (error)
|
||||
goto fail_closed;
|
||||
return result;
|
||||
|
||||
fail:
|
||||
closedir(directory);
|
||||
|
||||
fail_closed:
|
||||
stringlist_free(&result);
|
||||
return NULL;
|
||||
}
|
||||
@@ -43,6 +43,7 @@ add_library(lg_common_platform_code STATIC
|
||||
win32.c
|
||||
paths.c
|
||||
open.c
|
||||
filesystem.c
|
||||
cpuinfo.c
|
||||
display.c
|
||||
proctitle.c
|
||||
|
||||
229
common/src/platform/windows/filesystem.c
Normal file
229
common/src/platform/windows/filesystem.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "common/filesystem.h"
|
||||
|
||||
#include "common/win32.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
static wchar_t * widePath(const char * path)
|
||||
{
|
||||
return lgUtf8ToWide(path);
|
||||
}
|
||||
|
||||
static LGFileType attributesType(DWORD attributes)
|
||||
{
|
||||
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return LG_FILE_TYPE_DIRECTORY;
|
||||
if (!(attributes & FILE_ATTRIBUTE_DEVICE))
|
||||
return LG_FILE_TYPE_REGULAR;
|
||||
return LG_FILE_TYPE_OTHER;
|
||||
}
|
||||
|
||||
static LGFileType pathError(DWORD error)
|
||||
{
|
||||
return error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND ?
|
||||
LG_FILE_TYPE_MISSING : LG_FILE_TYPE_ERROR;
|
||||
}
|
||||
|
||||
static LGFileType pathType(const char * path, bool follow)
|
||||
{
|
||||
wchar_t * wide = widePath(path);
|
||||
if (!wide)
|
||||
return LG_FILE_TYPE_ERROR;
|
||||
|
||||
if (follow)
|
||||
{
|
||||
HANDLE file = CreateFileW(wide, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
const DWORD openError = file == INVALID_HANDLE_VALUE ?
|
||||
GetLastError() : ERROR_SUCCESS;
|
||||
free(wide);
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return pathError(openError);
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
const bool result = GetFileInformationByHandle(file, &info);
|
||||
const DWORD error = result ? ERROR_SUCCESS : GetLastError();
|
||||
CloseHandle(file);
|
||||
if (!result)
|
||||
return pathError(error);
|
||||
return attributesType(info.dwFileAttributes);
|
||||
}
|
||||
|
||||
const DWORD attributes = GetFileAttributesW(wide);
|
||||
const DWORD error = attributes == INVALID_FILE_ATTRIBUTES ?
|
||||
GetLastError() : ERROR_SUCCESS;
|
||||
free(wide);
|
||||
if (attributes == INVALID_FILE_ATTRIBUTES)
|
||||
return pathError(error);
|
||||
if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
return LG_FILE_TYPE_OTHER;
|
||||
return attributesType(attributes);
|
||||
}
|
||||
|
||||
LGFileType lgPathType(const char * path)
|
||||
{
|
||||
return pathType(path, true);
|
||||
}
|
||||
|
||||
LGFileType lgPathTypeNoFollow(const char * path)
|
||||
{
|
||||
return pathType(path, false);
|
||||
}
|
||||
|
||||
bool lgCreateDirectory(const char * path)
|
||||
{
|
||||
wchar_t * wide = widePath(path);
|
||||
if (!wide)
|
||||
return false;
|
||||
|
||||
bool result = CreateDirectoryW(wide, NULL);
|
||||
if (!result && GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
const DWORD attributes = GetFileAttributesW(wide);
|
||||
result = attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
free(wide);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool lgDeleteFile(const char * path)
|
||||
{
|
||||
wchar_t * wide = widePath(path);
|
||||
if (!wide)
|
||||
return false;
|
||||
|
||||
const bool result = DeleteFileW(wide);
|
||||
free(wide);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool lgDeleteDirectory(const char * path)
|
||||
{
|
||||
wchar_t * wide = widePath(path);
|
||||
if (!wide)
|
||||
return false;
|
||||
|
||||
const bool result = RemoveDirectoryW(wide);
|
||||
free(wide);
|
||||
return result;
|
||||
}
|
||||
|
||||
FILE * lgOpenFile(const char * path, const char * mode)
|
||||
{
|
||||
wchar_t * widePathValue = lgUtf8ToWide(path);
|
||||
wchar_t * wideMode = lgUtf8ToWide(mode);
|
||||
if (!widePathValue || !wideMode)
|
||||
{
|
||||
free(widePathValue);
|
||||
free(wideMode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE * result = _wfopen(widePathValue, wideMode);
|
||||
free(widePathValue);
|
||||
free(wideMode);
|
||||
return result;
|
||||
}
|
||||
|
||||
StringList lgListDirectory(const char * path)
|
||||
{
|
||||
wchar_t * wide = widePath(path);
|
||||
if (!wide)
|
||||
return NULL;
|
||||
|
||||
const size_t length = wcslen(wide);
|
||||
if (length > SIZE_MAX / sizeof(*wide) - 3)
|
||||
{
|
||||
free(wide);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t * pattern = realloc(wide, (length + 3) * sizeof(*pattern));
|
||||
if (!pattern)
|
||||
{
|
||||
free(wide);
|
||||
return NULL;
|
||||
}
|
||||
size_t offset = length;
|
||||
if (length && pattern[length - 1] != L'\\' &&
|
||||
pattern[length - 1] != L'/')
|
||||
pattern[offset++] = L'\\';
|
||||
pattern[offset++] = L'*';
|
||||
pattern[offset] = L'\0';
|
||||
|
||||
StringList result = stringlist_new(true);
|
||||
if (!result)
|
||||
{
|
||||
free(pattern);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WIN32_FIND_DATAW entry;
|
||||
HANDLE find = FindFirstFileW(pattern, &entry);
|
||||
free(pattern);
|
||||
if (find == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
const DWORD error = GetLastError();
|
||||
if (error == ERROR_FILE_NOT_FOUND)
|
||||
return result;
|
||||
stringlist_free(&result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (!wcscmp(entry.cFileName, L".") || !wcscmp(entry.cFileName, L".."))
|
||||
continue;
|
||||
|
||||
char * name = lgWideToUtf8(entry.cFileName);
|
||||
if (!name)
|
||||
goto fail;
|
||||
|
||||
const unsigned int count = stringlist_count(result);
|
||||
stringlist_push(result, name);
|
||||
if (stringlist_count(result) != count + 1)
|
||||
{
|
||||
free(name);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
while (FindNextFileW(find, &entry));
|
||||
|
||||
const DWORD error = GetLastError();
|
||||
FindClose(find);
|
||||
if (error != ERROR_NO_MORE_FILES)
|
||||
goto fail_closed;
|
||||
return result;
|
||||
|
||||
fail:
|
||||
FindClose(find);
|
||||
|
||||
fail_closed:
|
||||
stringlist_free(&result);
|
||||
return NULL;
|
||||
}
|
||||
Reference in New Issue
Block a user