stdclass: Add support for separate config/data dirs and system wide dirs

This adds support for separate config and data dirs.

On Linux, these will be compliant XDG Basedir Specification, i.e.
XDG_CONFIG_HOME and XDG_CONFIG_DIRS (or XDG_DATA_HOME and XDG_DATA_DIRS
respectively). On all other platforms, there currently just set to the
homedir path (so no previous behaviour has been changed).

If reicast wants to read and write a data file, it just calls
get_data_path("/samplefile.txt"). If it does not need to write to
that file, it just uses get_data_path("/samplefile.txt", false). That
way, we can also use system-wide dirs (like /usr/share/reicast on
linux), that the user usually doesn't have write access to.

The same applies for config file, where you use get_config_path(args)
respectively.
This commit is contained in:
Jan Holthuis 2015-08-22 21:49:12 +02:00
parent c24a5faf5b
commit b6d0cddcaa
19 changed files with 231 additions and 40 deletions

View File

@ -78,7 +78,7 @@ void cfgSaveStr(const wchar * Section, const wchar * Key, const wchar * String)
bool cfgOpen()
{
cfgPath=GetPath("/emu.cfg");
cfgPath=get_config_path("/emu.cfg");
FILE* cfgfile = fopen(cfgPath.c_str(),"r");
if(cfgfile != NULL) {

View File

@ -277,7 +277,7 @@ struct maple_sega_vmu: maple_base
memset(lcd_data,0,sizeof(lcd_data));
wchar tempy[512];
sprintf(tempy,"/vmu_save_%s.bin",logical_port);
string apath=GetPath(tempy);
string apath=get_data_path(tempy);
file=fopen(apath.c_str(),"rb+");
if (!file)

View File

@ -580,7 +580,7 @@ error:
{
#if HOST_OS == OS_DARWIN
string path = GetPath("/dcnzorz_mem");
string path = get_data_path("/dcnzorz_mem");
fd = open(path.c_str(),O_CREAT|O_RDWR|O_TRUNC,S_IRWXU|S_IRWXG|S_IRWXO);
unlink(path.c_str());
verify(ftruncate(fd,RAM_SIZE + VRAM_SIZE +ARAM_SIZE)==0);

View File

@ -607,7 +607,7 @@ void print_blocks()
if (print_stats)
{
f=fopen(GetPath("/blkmap.lst").c_str(),"w");
f=fopen(get_data_path("/blkmap.lst").c_str(),"w");
print_stats=0;
printf("Writing blocks to %p\n",f);

View File

@ -54,7 +54,7 @@ void emit_WriteCodeCache()
{
wchar path[512];
sprintf(path,"/code_cache_%8p.bin",CodeCache);
string pt2=GetPath(path);
string pt2=get_data_path(path);
printf("Writing code cache to %s\n",pt2.c_str());
FILE*f=fopen(pt2.c_str(),"wb");
if (f)

View File

@ -223,7 +223,7 @@ void mem_Term()
sh4_area0_Term();
//write back Flash/SRAM
SaveRomFiles(GetPath("/data/"));
SaveRomFiles(get_data_path("/data/"));
//mem_b.Term(); // handled by vmem

View File

@ -239,7 +239,7 @@ void dc_run();
}
#endif
string get_config_dir()
string find_user_config_dir()
{
#ifdef USES_HOMEDIR
struct stat info;
@ -259,14 +259,13 @@ string get_config_dir()
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
*/
home = (string)getenv("HOME") + "/.config/reicast";
}
if(getenv("XDG_CONFIG_HOME") != NULL)
{
// If XDG_CONFIG_HOME is set explicitly, we'll use that instead of $HOME/.config
home = (string)getenv("XDG_CONFIG_HOME") + "/reicast";
}
if(!home.empty())
{
if((stat(home.c_str(), &info) != 0) || !(info.st_mode & S_IFDIR))
@ -277,11 +276,105 @@ string get_config_dir()
return home;
}
#endif
// Unable to detect config dir, use the current folder
return ".";
}
string find_user_data_dir()
{
#ifdef USES_HOMEDIR
struct stat info;
string data = "";
if(getenv("HOME") != NULL)
{
// Support for the legacy config dir at "$HOME/.reicast"
string legacy_data = (string)getenv("HOME") + "/.reicast";
if((stat(legacy_data.c_str(), &info) == 0) && (info.st_mode & S_IFDIR))
{
// "$HOME/.reicast" already exists, let's use it!
return legacy_data;
}
/* If $XDG_DATA_HOME is not set, we're supposed to use "$HOME/.local/share" instead.
* Consult the XDG Base Directory Specification for details:
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
*/
data = (string)getenv("HOME") + "/.local/share/reicast";
}
if(getenv("XDG_DATA_HOME") != NULL)
{
// If XDG_DATA_HOME is set explicitly, we'll use that instead of $HOME/.config
data = (string)getenv("XDG_DATA_HOME") + "/reicast";
}
if(!data.empty())
{
if((stat(data.c_str(), &info) != 0) || !(info.st_mode & S_IFDIR))
{
// If the directory doesn't exist yet, create it!
mkdir(data.c_str(), 0755);
}
return data;
}
#endif
// Unable to detect config dir, use the current folder
return ".";
}
std::vector<string> find_system_config_dirs()
{
std::vector<string> dirs;
if (getenv("XDG_DATA_DIRS") != NULL)
{
string s = (string)getenv("XDG_CONFIG_DIRS");
string::size_type pos = 0;
string::size_type n = s.find(":", pos);
while(n != std::string::npos)
{
dirs.push_back(s.substr(pos, n-pos) + "/reicast");
pos = n + 1;
n = s.find(":", pos);
}
// Separator not found
dirs.push_back(s.substr(pos) + "/reicast");
}
else
{
dirs.push_back("/etc/xdg/reicast");
}
return dirs;
}
std::vector<string> find_system_data_dirs()
{
std::vector<string> dirs;
if (getenv("XDG_DATA_DIRS") != NULL)
{
string s = (string)getenv("XDG_DATA_DIRS");
string::size_type pos = 0;
string::size_type n = s.find(":", pos);
while(n != std::string::npos)
{
dirs.push_back(s.substr(pos, n-pos) + "/reicast");
pos = n + 1;
n = s.find(":", pos);
}
// Separator not found
dirs.push_back(s.substr(pos) + "/reicast");
}
else
{
dirs.push_back("/usr/local/share/reicast");
dirs.push_back("/usr/share/reicast");
}
return dirs;
}
int main(int argc, wchar* argv[])
{
#ifdef TARGET_PANDORA
@ -289,9 +382,22 @@ int main(int argc, wchar* argv[])
signal(SIGKILL, clean_exit);
#endif
/* Set home dir */
SetHomeDir(get_config_dir());
printf("Home dir is: %s\n", GetPath("/").c_str());
/* Set directories */
set_user_config_dir(find_user_config_dir());
set_user_data_dir(find_user_data_dir());
std::vector<string> dirs;
dirs = find_system_config_dirs();
for(unsigned int i = 0; i < dirs.size(); i++)
{
add_system_data_dir(dirs[i]);
}
dirs = find_system_data_dirs();
for(unsigned int i = 0; i < dirs.size(); i++)
{
add_system_data_dir(dirs[i]);
}
printf("Config dir is: %s\n", get_config_path("/").c_str());
printf("Data dir is: %s\n", get_data_path("/").c_str());
common_linux_setup();

View File

@ -207,7 +207,7 @@ void* prof(void *ptr)
sprintf(line, "/%d.reprof", tick_count);
string logfile=GetPath(line);
string logfile=get_data_path(line);
printf("Profiler thread logging to -> %s\n", logfile.c_str());

View File

@ -48,7 +48,8 @@ void* emuthread(void* ) {
char *Args[3];
Args[0] = "dc";
SetHomeDir("/http");
set_user_config_dir("/http");
set_user_data_dir("/http");
dc_init(1,Args);
dc_run();

View File

@ -165,9 +165,9 @@ int dc_init(int argc,wchar* argv[])
#define DATA_PATH "/"
#endif
if (settings.bios.UseReios || !LoadRomFiles(GetPath(DATA_PATH)))
if (settings.bios.UseReios || !LoadRomFiles(get_data_path(DATA_PATH, false)))
{
if (!LoadHle(GetPath(DATA_PATH)))
if (!LoadHle(get_data_path(DATA_PATH, false)))
return -3;
else
printf("Did not load bios, using reios\n");
@ -224,7 +224,7 @@ void dc_term()
#ifndef _ANDROID
SaveSettings();
#endif
SaveRomFiles(GetPath("/data/"));
SaveRomFiles(get_data_path("/data/"));
}
void LoadSettings()

View File

@ -930,9 +930,9 @@ bool gl_create_resources()
#endif
int w, h;
osd_tex=loadPNG(GetPath("/data/buttons.png"),w,h);
osd_tex=loadPNG(get_data_path("/data/buttons.png", false),w,h);
#ifdef TARGET_PANDORA
osd_font=loadPNG(GetPath("/font2.png"),w,h);
osd_font=loadPNG(get_data_path("/font2.png", false),w,h);
#endif
return true;

View File

@ -435,15 +435,19 @@ int main(int argc, wchar* argv[])
{
home += "/.reicast";
mkdir(home.c_str(), 0755); // create the directory if missing
SetHomeDir(home);
set_user_config_dir(home);
set_user_data_dir(home);
}
else
SetHomeDir(".");
set_user_config_dir(".");
set_user_data_dir(".");
#else
SetHomeDir(".");
set_user_config_dir(".");
set_user_data_dir(".");
#endif
printf("Home dir is: %s\n",GetPath("/").c_str());
printf("Config dir is: %s\n", get_config_path("/").c_str());
printf("Data dir is: %s\n", get_data_path("/").c_str());
common_linux_setup();

View File

@ -1,22 +1,88 @@
#include <string.h>
#include <vector>
#include <sys/stat.h>
#include "types.h"
#include "hw/mem/_vmem.h"
#include "types.h"
string home_dir;
string user_config_dir;
string user_data_dir;
std::vector<string> system_config_dirs;
std::vector<string> system_data_dirs;
void SetHomeDir(const string& home)
bool file_exists(const string& filename)
{
home_dir=home;
struct stat info;
return (stat (filename.c_str(), &info) == 0);
}
//subpath format: /data/fsca-table.bit
string GetPath(const string& subpath)
void set_user_config_dir(const string& dir)
{
return (home_dir+subpath);
user_config_dir = dir;
}
void set_user_data_dir(const string& dir)
{
user_data_dir = dir;
}
void add_system_config_dir(const string& dir)
{
system_config_dirs.push_back(dir);
}
void add_system_data_dir(const string& dir)
{
system_data_dirs.push_back(dir);
}
string get_config_path(const string& filename, bool user_writable)
{
if(user_writable)
{
/* Only stuff in the user_config_dir is supposed to be writable,
* so we always return that.
*/
return (user_config_dir + filename);
}
string filepath;
for (unsigned int i = 0; i < system_config_dirs.size(); i++) {
filepath = system_config_dirs[i] + filename;
if (file_exists(filepath))
{
return filepath;
}
}
// Not found, so we return the user variant
return (user_config_dir + filename);
}
string get_data_path(const string& filename, bool user_writable)
{
if(user_writable)
{
/* Only stuff in the user_data_dir is supposed to be writable,
* so we always return that.
*/
return (user_data_dir + filename);
}
string filepath;
for (unsigned int i = 0; i < system_data_dirs.size(); i++) {
filepath = system_data_dirs[i] + filename;
if (file_exists(filepath))
{
return filepath;
}
}
// Not found, so we return the user variant
return (user_data_dir + filename);
}

View File

@ -251,9 +251,14 @@ public :
};
//Set the path !
void SetHomeDir(const string& home);
void set_user_config_dir(const string& dir);
void set_user_data_dir(const string& dir);
void add_system_config_dir(const string& dir);
void add_system_data_dir(const string& dir);
//subpath format: /data/fsca-table.bit
string GetPath(const string& subpath);
string get_config_path(const string& filename, bool user_writable = true);
string get_data_path(const string& filename, bool user_writable = true);
class VArray2

View File

@ -79,7 +79,7 @@ enum demo_protocols {
DEMO_PROTOCOL_COUNT
};
#define WEBUI_PATH GetPath("/webui")
#define WEBUI_PATH get_data_path("/webui")
/*
* We take a strict whitelist approach to stop ../ attacks

View File

@ -157,7 +157,8 @@ void SetupPath()
GetModuleFileName(0,fname,512);
string fn=string(fname);
fn=fn.substr(0,fn.find_last_of('\\'));
SetHomeDir(fn);
set_user_config_dir(fn);
set_user_data_dir(fn);
}
int msgboxf(const wchar* text,unsigned int type,...)

View File

@ -253,8 +253,10 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_config(JNIEnv *env,jo
{
// Set home directory based on User config
const char* D = dirName? env->GetStringUTFChars(dirName,0):0;
SetHomeDir(D);
printf("Home dir is: '%s'\n",GetPath("/").c_str());
set_user_config_dir(D);
set_user_data_dir(D);
printf("Config dir is: %s\n", get_config_path("/").c_str());
printf("Data dir is: %s\n", get_data_path("/").c_str());
env->ReleaseStringUTFChars(dirName,D);
}
JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_init(JNIEnv *env,jobject obj,jstring fileName)

View File

@ -66,11 +66,13 @@ extern "C" int reicast_main(int argc, wchar* argv[])
//ndcid=atoi(argv[1]);
string homedir = [ [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0] path] UTF8String];
SetHomeDir(homedir);
set_user_config_dir(homedir);
set_user_data_dir(homedir);
freopen( (homedir + "/log.txt").c_str(), "wb", stdout);
printf("Home dir is: %s\n",GetPath("/").c_str());
printf("Config dir is: %s\n", get_config_path("/").c_str());
printf("Data dir is: %s\n", get_data_path("/").c_str());
common_linux_setup();

View File

@ -95,10 +95,14 @@ void* emuthread(void*) {
{
home += "/.reicast";
mkdir(home.c_str(), 0755); // create the directory if missing
SetHomeDir(home);
set_user_config_dir(home);
set_user_data_dir(home);
}
else
SetHomeDir(".");
{
set_user_config_dir(".");
set_user_data_dir(".");
}
char* argv[] = { "reicast" };
dc_init(1,argv);