diff --git a/BasiliskII/src/Unix/prefs_unix.cpp b/BasiliskII/src/Unix/prefs_unix.cpp index 4f2aa7ee..75ac72d2 100644 --- a/BasiliskII/src/Unix/prefs_unix.cpp +++ b/BasiliskII/src/Unix/prefs_unix.cpp @@ -19,16 +19,12 @@ */ #include "sysdeps.h" - -#include -#include - +#include #include using std::string; #include "prefs.h" - // Platform-specific preferences items prefs_desc platform_prefs_items[] = { {"fbdevicefile", TYPE_STRING, false, "path of frame buffer device specification file"}, @@ -41,72 +37,200 @@ prefs_desc platform_prefs_items[] = { {NULL, TYPE_END, false, NULL} // End of list }; +// Standard file names and paths +static const char PREFS_FILE_NAME[] = "/.basilisk_ii_prefs"; +static const char XDG_PREFS_FILE_NAME[] = "/prefs"; +static const char XPRAM_FILE_NAME[] = "/.basilisk_ii_xpram"; +static const char XDG_XPRAM_FILE_NAME[] = "/xpram"; +static const char XDG_CONFIG_SUBDIR[] = "/BasiliskII"; // Prefs file name and path -const char PREFS_FILE_NAME[] = ".basilisk_ii_prefs"; string UserPrefsPath; -static string prefs_path; +static string home_dir; +static string xdg_config_dir; +static string prefs_name; +extern string xpram_name; - -/* - * Load preferences from settings file - */ - -void LoadPrefs(const char *vmdir) +static string get_xdg_config_dir(void) { - if (vmdir) { - prefs_path = string(vmdir) + '/' + string("prefs"); - FILE *prefs = fopen(prefs_path.c_str(), "r"); - if (!prefs) { - printf("No file at %s found.\n", prefs_path.c_str()); - exit(1); - } - LoadPrefsFromStream(prefs); - fclose(prefs); + char *env; + if (env = getenv("XDG_CONFIG_HOME")) + return string(env) + XDG_CONFIG_SUBDIR; + if (env = getenv("HOME")) + return string(env) + "/.config" + XDG_CONFIG_SUBDIR; + return ""; +} + +static string get_home_dir(void) +{ + char *env; + if(env = getenv("HOME")) + return string(env); + return "."; // last resort, use the current directory +} + +static string get_dir(string *path) +{ + int pos = path->find_last_of('/'); + if (pos == 0) + return ""; // file is in root folder + if (pos == std::string::npos) + return "."; // file is in current folder + return path->substr(0, pos); +} + +static void exit_if_dir(const string& path) +{ + struct stat info; + if (stat(path.c_str(), &info) != 0){ return; } - - // Construct prefs path - if (UserPrefsPath.empty()) { - char *home = getenv("HOME"); - if (home) - prefs_path = string(home) + '/'; - prefs_path += PREFS_FILE_NAME; - } else - prefs_path = UserPrefsPath; - - // Read preferences from settings file - FILE *f = fopen(prefs_path.c_str(), "r"); - if (f != NULL) { - - // Prefs file found, load settings - LoadPrefsFromStream(f); - fclose(f); - - } else { -#ifdef __linux__ - PrefsAddString("cdrom", "/dev/cdrom"); -#endif - // No prefs file, save defaults - SavePrefs(); + if ((info.st_mode & S_IFDIR) != 0) + { + fprintf(stderr, "ERROR: Cannot open %s (Is a directory)\n", prefs_name.c_str()); + exit(1); } } +static bool load_prefs_file(const string& path, bool exit_on_failure) +{ + exit_if_dir(path); + FILE *prefs = fopen(path.c_str(), "r"); + if (prefs != NULL) + { + LoadPrefsFromStream(prefs); + fclose(prefs); + printf("Using prefs file at %s\n", prefs_name.c_str()); + return true; + } + else if (exit_on_failure) + { + fprintf(stderr, "ERROR: Could not load prefs file from %s (%s)\n", + path.c_str(), strerror(errno)); + exit(1); + } + return false; +} + +/* + * Look for prefs file in the following locations (in order of priority): + * 1. From vmdir/.basilisk_ii_prefs if a vmdir has been specified + * 2. From path specified with --config command line + * 3. From $HOME/.basilisk_ii_prefs if it exists + * 4. From $XDG_CONFIG_HOME/BasiliskII/prefs if it exists + * 5. Create a new prefs file at $XDG_CONFIG_HOME/BasiliskII/prefs + * If $XDG_CONFIG_HOME doesn't exist, $HOME/.config is used instead, + * in accordance with XDG Base Directory Specification: + * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ + +void LoadPrefs(const char* vmdir) +{ + home_dir = get_home_dir(); + xdg_config_dir = get_xdg_config_dir(); + + // vmdir was specified on the command line + if (vmdir) + { + prefs_name = string(vmdir) + XDG_PREFS_FILE_NAME; + xpram_name = string(vmdir) + XDG_XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, true)) + return; + } + + // --config was specified + if (!UserPrefsPath.empty()) + { + prefs_name = UserPrefsPath; + xpram_name = get_dir(&prefs_name) + XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, true)) + return; + } + + // Load .basilisk_ii_prefs from $HOME if it exists + if (!home_dir.empty()) + { + prefs_name = home_dir + PREFS_FILE_NAME; + xpram_name = home_dir + XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, false)) + return; + } + + // If no other prefs file exists, try the $XDG_CONFIG_HOME directory + if (!xdg_config_dir.empty()) + { + prefs_name = xdg_config_dir + XDG_PREFS_FILE_NAME; + xpram_name = xdg_config_dir + XDG_XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, false)) + return; + } + + // No prefs file, save defaults in $XDG_CONFIG_HOME directory +#ifdef __linux__ + PrefsAddString("cdrom", "/dev/cdrom"); +#endif + printf("No prefs file found, creating new one at %s\n", prefs_name.c_str()); + SavePrefs(); +} + +static bool is_dir(const string& path) +{ + struct stat info; + if (stat(path.c_str(), &info) != 0){ + return false; + } + return (info.st_mode & S_IFDIR) != 0; +} + +static bool create_directories(const string& path, mode_t mode) +{ + if (mkdir(path.c_str(), mode) == 0) + return true; + + switch (errno) + { + case ENOENT: + { + int pos = path.find_last_of('/'); + if (pos == std::string::npos) + return false; + if (!create_directories(path.substr(0,pos),mode)) + return false; + } + return 0 == mkdir(path.c_str(),mode); + + case EEXIST: + return is_dir(path); + default: + return false; + } +} /* * Save preferences to settings file */ + void SavePrefs(void) { FILE *f; - if ((f = fopen(prefs_path.c_str(), "w")) != NULL) { + string prefs_dir = get_dir(&prefs_name); + if (!prefs_dir.empty() && !is_dir(prefs_dir)) + { + create_directories(prefs_dir, 0700); + } + if ((f = fopen(prefs_name.c_str(), "w")) != NULL) + { SavePrefsToStream(f); fclose(f); } + else + { + fprintf(stderr, "WARNING: Unable to save %s (%s)\n", + prefs_name.c_str(), strerror(errno)); + } } - /* * Add defaults of platform-specific prefs items * You may also override the defaults set in PrefsInit() @@ -119,14 +243,20 @@ void AddPlatformPrefsDefaults(void) PrefsReplaceInt32("mousewheelmode", 1); PrefsReplaceInt32("mousewheellines", 3); #ifdef __linux__ - if (access("/dev/sound/dsp", F_OK) == 0) { + if (access("/dev/sound/dsp", F_OK) == 0) + { PrefsReplaceString("dsp", "/dev/sound/dsp"); - } else { + } + else + { PrefsReplaceString("dsp", "/dev/dsp"); } - if (access("/dev/sound/mixer", F_OK) == 0) { + if (access("/dev/sound/mixer", F_OK) == 0) + { PrefsReplaceString("mixer", "/dev/sound/mixer"); - } else { + } + else + { PrefsReplaceString("mixer", "/dev/mixer"); } #else diff --git a/BasiliskII/src/Unix/xpram_unix.cpp b/BasiliskII/src/Unix/xpram_unix.cpp index 3037b02d..dcf9c0bf 100644 --- a/BasiliskII/src/Unix/xpram_unix.cpp +++ b/BasiliskII/src/Unix/xpram_unix.cpp @@ -20,81 +20,61 @@ #include "sysdeps.h" -#include +#include +using std::string; #include "xpram.h" - -// XPRAM file name and path -#if POWERPC_ROM -const char XPRAM_FILE_NAME[] = ".sheepshaver_nvram"; -#else -const char XPRAM_FILE_NAME[] = ".basilisk_ii_xpram"; -#endif -static char xpram_path[1024]; - +// XPRAM file name, set by LoadPrefs() in prefs_unix.cpp +string xpram_name; /* * Load XPRAM from settings file */ -void LoadXPRAM(const char *vmdir) +void LoadXPRAM(const char* vmdir) { - if (vmdir) { -#if POWERPC_ROM - snprintf(xpram_path, sizeof(xpram_path), "%s/nvram", vmdir); -#else - snprintf(xpram_path, sizeof(xpram_path), "%s/xpram", vmdir); -#endif - } else { - // Construct XPRAM path - xpram_path[0] = 0; - char *home = getenv("HOME"); - if (home != NULL && strlen(home) < 1000) { - strncpy(xpram_path, home, 1000); - strcat(xpram_path, "/"); - } - strcat(xpram_path, XPRAM_FILE_NAME); - } - - // Load XPRAM from settings file + assert(!xpram_name.empty()); int fd; - if ((fd = open(xpram_path, O_RDONLY)) >= 0) { + if ((fd = open(xpram_name.c_str(), O_RDONLY)) >= 0) + { read(fd, XPRAM, XPRAM_SIZE); close(fd); } + else + { + fprintf(stderr, "WARNING: Unable to load %s (%s)\n", + xpram_name.c_str(), strerror(errno)); + } } - /* * Save XPRAM to settings file */ void SaveXPRAM(void) { + assert(!xpram_name.empty()); int fd; - if ((fd = open(xpram_path, O_WRONLY | O_CREAT, 0666)) >= 0) { + if ((fd = open(xpram_name.c_str(), O_WRONLY | O_CREAT, 0666)) >= 0) + { write(fd, XPRAM, XPRAM_SIZE); close(fd); } + else + { + fprintf(stderr, "WARNING: Unable to save %s (%s)\n", + xpram_name.c_str(), strerror(errno)); + } } - /* * Delete PRAM file */ void ZapPRAM(void) { - // Construct PRAM path - xpram_path[0] = 0; - char *home = getenv("HOME"); - if (home != NULL && strlen(home) < 1000) { - strncpy(xpram_path, home, 1000); - strcat(xpram_path, "/"); - } - strcat(xpram_path, XPRAM_FILE_NAME); - // Delete file - unlink(xpram_path); + assert(!xpram_name.empty()); + unlink(xpram_name.c_str()); } diff --git a/SheepShaver/src/Unix/prefs_unix.cpp b/SheepShaver/src/Unix/prefs_unix.cpp index 7ac92cbd..f8784b09 100644 --- a/SheepShaver/src/Unix/prefs_unix.cpp +++ b/SheepShaver/src/Unix/prefs_unix.cpp @@ -1,5 +1,5 @@ /* - * prefs_unix.cpp - Preferences handling, Unix specific things + * prefs_unix.cpp - Preferences handling, Unix specific stuff * * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig * @@ -19,15 +19,12 @@ */ #include "sysdeps.h" - -#include -#include -#include +#include #include +using std::string; #include "prefs.h" - // Platform-specific preferences items prefs_desc platform_prefs_items[] = { {"ether", TYPE_STRING, false, "device name of Mac ethernet adapter"}, @@ -45,60 +42,174 @@ prefs_desc platform_prefs_items[] = { {NULL, TYPE_END, false, NULL} // End of list }; +// Standard file names and paths +static const char PREFS_FILE_NAME[] = "/.sheepshaver_prefs"; +static const char XDG_PREFS_FILE_NAME[] = "/prefs"; +static const char XPRAM_FILE_NAME[] = "/.sheepshaver_nvram"; +static const char XDG_XPRAM_FILE_NAME[] = "/nvram"; +static const char XDG_CONFIG_SUBDIR[] = "/SheepShaver"; // Prefs file name and path -const char PREFS_FILE_NAME[] = ".sheepshaver_prefs"; -static char prefs_path[1024]; +string UserPrefsPath; +static string home_dir; +static string xdg_config_dir; +static string prefs_name; +extern string xpram_name; -std::string UserPrefsPath; - -/* - * Load preferences from settings file - */ - -void LoadPrefs(const char *vmdir) +static string get_xdg_config_dir(void) { - if (vmdir) { - snprintf(prefs_path, sizeof(prefs_path), "%s/prefs", vmdir); - FILE *prefs = fopen(prefs_path, "r"); - if (!prefs) { - printf("No file at %s found.\n", prefs_path); - exit(1); - } - LoadPrefsFromStream(prefs); - fclose(prefs); + char *env; + if (env = getenv("XDG_CONFIG_HOME")) + return string(env) + XDG_CONFIG_SUBDIR; + if (env = getenv("HOME")) + return string(env) + "/.config" + XDG_CONFIG_SUBDIR; + return ""; +} + +static string get_home_dir(void) +{ + char *env; + if(env = getenv("HOME")) + return string(env); + return "."; // last resort, use the current directory +} + +static string get_dir(string *path) +{ + int pos = path->find_last_of('/'); + if (pos == 0) + return ""; // file is in root folder + if (pos == std::string::npos) + return "."; // file is in current folder + return path->substr(0, pos); +} + +static void exit_if_dir(const string& path) +{ + struct stat info; + if (stat(path.c_str(), &info) != 0){ return; } - - if (!UserPrefsPath.empty()) strncpy(prefs_path, UserPrefsPath.c_str(), 1000); - else { - // Construct prefs path - prefs_path[0] = 0; - char *home = getenv("HOME"); - if (home != NULL && strlen(home) < 1000) { - strncpy(prefs_path, home, 1000); - strcat(prefs_path, "/"); - } - strcat(prefs_path, PREFS_FILE_NAME); - } - - // Read preferences from settings file - FILE *f = fopen(prefs_path, "r"); - if (f != NULL) { - - // Prefs file found, load settings - LoadPrefsFromStream(f); - fclose(f); - - } else { -#ifdef __linux__ - PrefsAddString("cdrom", "/dev/cdrom"); -#endif - // No prefs file, save defaults - SavePrefs(); + if ((info.st_mode & S_IFDIR) != 0) + { + fprintf(stderr, "ERROR: Cannot open %s (Is a directory)\n", prefs_name.c_str()); + exit(1); } } +static bool load_prefs_file(const string& path, bool exit_on_failure) +{ + exit_if_dir(path); + FILE *prefs = fopen(path.c_str(), "r"); + if (prefs != NULL) + { + LoadPrefsFromStream(prefs); + fclose(prefs); + printf("Using prefs file at %s\n", prefs_name.c_str()); + return true; + } + else if (exit_on_failure) + { + fprintf(stderr, "ERROR: Could not load prefs file from %s (%s)\n", + path.c_str(), strerror(errno)); + exit(1); + } + return false; +} + +/* + * Look for prefs file in the following locations (in order of priority): + * 1. From vmdir/.sheepshaver_prefs if a vmdir has been specified + * 2. From path specified with --config command line + * 3. From $HOME/.sheepshaver_prefs if it exists + * 4. From $XDG_CONFIG_HOME/SheepShaver/prefs if it exists + * 5. Create a new prefs file at $XDG_CONFIG_HOME/SheepShaver/prefs + * If $XDG_CONFIG_HOME doesn't exist, $HOME/.config is used instead, + * in accordance with XDG Base Directory Specification: + * https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ + +void LoadPrefs(const char* vmdir) +{ + home_dir = get_home_dir(); + xdg_config_dir = get_xdg_config_dir(); + + // vmdir was specified on the command line + if (vmdir) + { + prefs_name = string(vmdir) + XDG_PREFS_FILE_NAME; + xpram_name = string(vmdir) + XDG_XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, true)) + return; + } + + // --config was specified + if (!UserPrefsPath.empty()) + { + prefs_name = UserPrefsPath; + xpram_name = get_dir(&prefs_name) + XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, true)) + return; + } + + // Load .basilisk_ii_prefs from $HOME if it exists + if (!home_dir.empty()) + { + prefs_name = home_dir + PREFS_FILE_NAME; + xpram_name = home_dir + XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, false)) + return; + } + + // If no other prefs file exists, try the $XDG_CONFIG_HOME directory + if (!xdg_config_dir.empty()) + { + prefs_name = xdg_config_dir + XDG_PREFS_FILE_NAME; + xpram_name = xdg_config_dir + XDG_XPRAM_FILE_NAME; + if (load_prefs_file(prefs_name, false)) + return; + } + + // No prefs file, save defaults in $XDG_CONFIG_HOME directory +#ifdef __linux__ + PrefsAddString("cdrom", "/dev/cdrom"); +#endif + printf("No prefs file found, creating new one at %s\n", prefs_name.c_str()); + SavePrefs(); +} + +static bool is_dir(const string& path) +{ + struct stat info; + if (stat(path.c_str(), &info) != 0){ + return false; + } + return (info.st_mode & S_IFDIR) != 0; +} + +static bool create_directories(const string& path, mode_t mode) +{ + if (mkdir(path.c_str(), mode) == 0) + return true; + + switch (errno) + { + case ENOENT: + { + int pos = path.find_last_of('/'); + if (pos == std::string::npos) + return false; + if (!create_directories(path.substr(0,pos),mode)) + return false; + } + return 0 == mkdir(path.c_str(),mode); + + case EEXIST: + return is_dir(path); + default: + return false; + } +} /* * Save preferences to settings file @@ -107,13 +218,23 @@ void LoadPrefs(const char *vmdir) void SavePrefs(void) { FILE *f; - if ((f = fopen(prefs_path, "w")) != NULL) { + string prefs_dir = get_dir(&prefs_name); + if (!prefs_dir.empty() && !is_dir(prefs_dir)) + { + create_directories(prefs_dir, 0700); + } + if ((f = fopen(prefs_name.c_str(), "w")) != NULL) + { SavePrefsToStream(f); fclose(f); } + else + { + fprintf(stderr, "WARNING: Unable to save %s (%s)\n", + prefs_name.c_str(), strerror(errno)); + } } - /* * Add defaults of platform-specific prefs items * You may also override the defaults set in PrefsInit() @@ -126,14 +247,20 @@ void AddPlatformPrefsDefaults(void) PrefsReplaceInt32("mousewheelmode", 1); PrefsReplaceInt32("mousewheellines", 3); #ifdef __linux__ - if (access("/dev/sound/dsp", F_OK) == 0) { + if (access("/dev/sound/dsp", F_OK) == 0) + { PrefsReplaceString("dsp", "/dev/sound/dsp"); - } else { + } + else + { PrefsReplaceString("dsp", "/dev/dsp"); } - if (access("/dev/sound/mixer", F_OK) == 0) { + if (access("/dev/sound/mixer", F_OK) == 0) + { PrefsReplaceString("mixer", "/dev/sound/mixer"); - } else { + } + else + { PrefsReplaceString("mixer", "/dev/mixer"); } #else