XDG home directory support
This commit is contained in:
1 parent
da4f9bd13d
commit
0ebf1df742
18 files changed
+527
-143
No files matched your search
+3
-3
@@ -181,10 +181,10 @@ Sys_PIDFileName
|
||||
*/
|
||||
static char *Sys_PIDFileName( const char *gamedir )
|
||||
{
|
||||
const char *homePath = Cvar_VariableString( "fs_homepath" );
|
||||
const char *homeStatePath = Cvar_VariableString( "fs_homestatepath" );
|
||||
|
||||
if( *homePath != '\0' )
|
||||
return va( "%s/%s/%s", homePath, gamedir, PID_FILENAME );
|
||||
if( *homeStatePath != '\0' )
|
||||
return va( "%s/%s/%s", homeStatePath, gamedir, PID_FILENAME );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+347
-49
@@ -112,73 +112,371 @@ static int Sys_Exec( void )
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_DefaultHomePath
|
||||
==================
|
||||
*/
|
||||
char *Sys_DefaultHomePath(void)
|
||||
static char *Sys_DefaultHomePath(void)
|
||||
{
|
||||
static char homePath[ MAX_OSPATH ] = { 0 };
|
||||
char *p;
|
||||
|
||||
if( !*homePath && com_homepath != NULL )
|
||||
if( !*homePath )
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
if( ( p = getenv( "HOME" ) ) != NULL )
|
||||
{
|
||||
Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP);
|
||||
Com_sprintf( homePath, sizeof(homePath), "%s%c%s",
|
||||
p, PATH_SEP, "Library/Application Support/" );
|
||||
|
||||
Q_strcat(homePath, sizeof(homePath),
|
||||
"Library/Application Support/");
|
||||
|
||||
if(com_homepath->string[0])
|
||||
if( com_homepath && com_homepath->string[0] )
|
||||
Q_strcat(homePath, sizeof(homePath), com_homepath->string);
|
||||
else
|
||||
Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_MACOSX);
|
||||
Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME);
|
||||
}
|
||||
#else
|
||||
if( ( p = getenv( "FLATPAK_ID" ) ) != NULL && *p != '\0' )
|
||||
{
|
||||
if( ( p = getenv( "XDG_DATA_HOME" ) ) != NULL && *p != '\0' )
|
||||
{
|
||||
Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP);
|
||||
}
|
||||
else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' )
|
||||
{
|
||||
Com_sprintf(homePath, sizeof(homePath), "%s%c.local%cshare%c", p, PATH_SEP, PATH_SEP, PATH_SEP);
|
||||
}
|
||||
|
||||
if( *homePath )
|
||||
{
|
||||
char *dir;
|
||||
|
||||
if(com_homepath->string[0])
|
||||
dir = com_homepath->string;
|
||||
else
|
||||
dir = HOMEPATH_NAME_UNIX;
|
||||
|
||||
if(dir[0] == '.' && dir[1] != '\0')
|
||||
dir++;
|
||||
|
||||
Q_strcat(homePath, sizeof(homePath), dir);
|
||||
}
|
||||
}
|
||||
else if( ( p = getenv( "HOME" ) ) != NULL )
|
||||
{
|
||||
Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP);
|
||||
|
||||
if(com_homepath->string[0])
|
||||
Q_strcat(homePath, sizeof(homePath), com_homepath->string);
|
||||
else
|
||||
Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_UNIX);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return homePath;
|
||||
}
|
||||
|
||||
char *Sys_DefaultHomeConfigPath(void) { return Sys_DefaultHomePath(); }
|
||||
char *Sys_DefaultHomeDataPath(void) { return Sys_DefaultHomePath(); }
|
||||
char *Sys_DefaultHomeStatePath(void) { return Sys_DefaultHomePath(); }
|
||||
|
||||
#else // __APPLE__
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_HomeConfigPath
|
||||
==================
|
||||
*/
|
||||
char *Sys_HomeConfigPath(void)
|
||||
{
|
||||
static char homeConfigPath[ MAX_OSPATH ] = { 0 };
|
||||
char *p;
|
||||
|
||||
if( !*homeConfigPath )
|
||||
{
|
||||
if( ( p = getenv( "XDG_CONFIG_HOME" ) ) != NULL && *p != '\0' )
|
||||
Com_sprintf(homeConfigPath, sizeof(homeConfigPath), "%s%c", p, PATH_SEP);
|
||||
else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' )
|
||||
Com_sprintf(homeConfigPath, sizeof(homeConfigPath), "%s%c.config%c", p, PATH_SEP, PATH_SEP);
|
||||
|
||||
if( *homeConfigPath )
|
||||
{
|
||||
if( com_homepath && com_homepath->string[0] )
|
||||
Q_strcat(homeConfigPath, sizeof(homeConfigPath), com_homepath->string);
|
||||
else
|
||||
Q_strcat(homeConfigPath, sizeof(homeConfigPath), HOMEPATH_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
return homeConfigPath;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_HomeDataPath
|
||||
==================
|
||||
*/
|
||||
char *Sys_HomeDataPath(void)
|
||||
{
|
||||
static char homeDataPath[ MAX_OSPATH ] = { 0 };
|
||||
char *p;
|
||||
|
||||
if( !*homeDataPath )
|
||||
{
|
||||
if( ( p = getenv( "XDG_DATA_HOME" ) ) != NULL && *p != '\0' )
|
||||
Com_sprintf(homeDataPath, sizeof(homeDataPath), "%s%c", p, PATH_SEP);
|
||||
else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' )
|
||||
Com_sprintf(homeDataPath, sizeof(homeDataPath), "%s%c.local%cshare%c", p, PATH_SEP, PATH_SEP, PATH_SEP);
|
||||
|
||||
if( *homeDataPath )
|
||||
{
|
||||
if( com_homepath && com_homepath->string[0] )
|
||||
Q_strcat(homeDataPath, sizeof(homeDataPath), com_homepath->string);
|
||||
else
|
||||
Q_strcat(homeDataPath, sizeof(homeDataPath), HOMEPATH_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
return homeDataPath;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_HomeStatePath
|
||||
==================
|
||||
*/
|
||||
char *Sys_HomeStatePath(void)
|
||||
{
|
||||
static char homeStatePath[ MAX_OSPATH ] = { 0 };
|
||||
char *p;
|
||||
|
||||
if( !*homeStatePath )
|
||||
{
|
||||
if( ( p = getenv( "XDG_STATE_HOME" ) ) != NULL && *p != '\0' )
|
||||
Com_sprintf(homeStatePath, sizeof(homeStatePath), "%s%c", p, PATH_SEP);
|
||||
else if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' )
|
||||
Com_sprintf(homeStatePath, sizeof(homeStatePath), "%s%c.local%cstate%c", p, PATH_SEP, PATH_SEP, PATH_SEP);
|
||||
|
||||
if( *homeStatePath )
|
||||
{
|
||||
if( com_homepath && com_homepath->string[0] )
|
||||
Q_strcat(homeStatePath, sizeof(homeStatePath), com_homepath->string);
|
||||
else
|
||||
Q_strcat(homeStatePath, sizeof(homeStatePath), HOMEPATH_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
return homeStatePath;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_LegacyHomePath
|
||||
==================
|
||||
*/
|
||||
static char *Sys_LegacyHomePath(void)
|
||||
{
|
||||
static char homePath[ MAX_OSPATH ] = { 0 };
|
||||
char *p;
|
||||
|
||||
if( ( p = getenv( "FLATPAK_ID" ) ) != NULL && *p != '\0' )
|
||||
{
|
||||
// Flatpaks always use XDG
|
||||
return "";
|
||||
}
|
||||
|
||||
if( !*homePath )
|
||||
{
|
||||
if( ( p = getenv( "HOME" ) ) != NULL && *p != '\0' )
|
||||
{
|
||||
Com_sprintf(homePath, sizeof(homePath), "%s%c%s",
|
||||
p, PATH_SEP, HOMEPATH_NAME_UNIX_LEGACY);
|
||||
}
|
||||
}
|
||||
|
||||
return homePath;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_MigrateToXDG
|
||||
==================
|
||||
*/
|
||||
qboolean Sys_MigrateToXDG(void)
|
||||
{
|
||||
const char *scriptTemplate =
|
||||
"#!/bin/sh\n"
|
||||
|
||||
"set -eu\n"
|
||||
|
||||
"legacy_home=\"%s\"\n"
|
||||
"xdg_config_home=\"%s\"\n"
|
||||
"xdg_data_home=\"%s\"\n"
|
||||
"xdg_state_home=\"%s\"\n"
|
||||
|
||||
"xdg_config_pattern=\"*.cfg\"\n"
|
||||
"xdg_data_pattern=\"demos/*.dm_* *.log *.pk3 *.txt \\\n"
|
||||
" screenshots/*.jpg screenshots/*.tga videos/*.avi\"\n"
|
||||
"xdg_state_pattern=\"*.dat q3history q3key\"\n"
|
||||
|
||||
"glob_copy() {\n"
|
||||
" game_dir=${1:+$1/}\n"
|
||||
" dst=\"$2\"\n"
|
||||
" shift 2\n"
|
||||
" for pattern in \"$@\"; do\n"
|
||||
" subdir=$(dirname \"$pattern\")\n"
|
||||
" [ \"$subdir\" = \".\" ] && subdir=\"\"\n"
|
||||
" find \"$legacy_home/$game_dir\" \\\n"
|
||||
" -path \"$legacy_home/$game_dir$pattern\" -type f \\\n"
|
||||
" -exec mkdir -p \"$dst/$game_dir$subdir\" \\; \\\n"
|
||||
" -exec cp -av {} \"$dst/$game_dir$subdir\" \\;\n"
|
||||
" done\n"
|
||||
"}\n"
|
||||
|
||||
"unmatched_copy() {\n"
|
||||
" game_dir=${1:+$1/}\n"
|
||||
" shift\n"
|
||||
" find_args=\"\"\n"
|
||||
" for pattern in \"$@\"; do\n"
|
||||
" find_args=\"$find_args \\\n"
|
||||
" -not -path \\\"$legacy_home/$game_dir$pattern\\\"\"\n"
|
||||
" done\n"
|
||||
" eval \"find '$legacy_home/$game_dir' -type f $find_args\" | \\\n"
|
||||
" while IFS= read -r file; do\n"
|
||||
" dst=\"$xdg_data_home${file#$legacy_home}\"\n"
|
||||
" dst_dir=$(dirname \"$dst\")\n"
|
||||
" mkdir -p \"$dst_dir\"\n"
|
||||
" cp -av \"$file\" \"$dst\"\n"
|
||||
" done\n"
|
||||
"}\n"
|
||||
|
||||
"echo \"Starting XDG migration...\"\n"
|
||||
|
||||
"glob_copy \"\" \"$xdg_state_home\" \"qkey\"\n"
|
||||
|
||||
"for game_dir in \"$legacy_home\"/*; do\n"
|
||||
" [ -d \"$game_dir\" ] || continue\n"
|
||||
" game=$(basename \"$game_dir\")\n"
|
||||
" glob_copy \"$game\" \"$xdg_config_home\" $xdg_config_pattern\n"
|
||||
" glob_copy \"$game\" \"$xdg_data_home\" $xdg_data_pattern\n"
|
||||
" glob_copy \"$game\" \"$xdg_state_home\" $xdg_state_pattern\n"
|
||||
" unmatched_copy \"$game\" \\\n"
|
||||
" $xdg_config_pattern \\\n"
|
||||
" $xdg_data_pattern \\\n"
|
||||
" $xdg_state_pattern\n"
|
||||
"done\n"
|
||||
|
||||
"echo \"XDG migration complete!\"\n";
|
||||
|
||||
char scriptBuffer[2048];
|
||||
int len = Com_sprintf( scriptBuffer, sizeof( scriptBuffer ), scriptTemplate,
|
||||
Sys_LegacyHomePath( ), Sys_HomeConfigPath( ),
|
||||
Sys_HomeDataPath( ), Sys_HomeStatePath( ) );
|
||||
|
||||
if( len < 0 || len >= (int)sizeof( scriptBuffer ) )
|
||||
{
|
||||
Com_Printf( "XDG migration error: substitution failed.\n" );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
char scriptPath[] = "/tmp/xdgmigrationXXXXXX";
|
||||
int fd = mkstemp( scriptPath );
|
||||
if( fd == -1 )
|
||||
{
|
||||
Com_Printf( "XDG migration error: script creation failed.\n" );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if( write( fd, scriptBuffer, len ) != len )
|
||||
{
|
||||
close( fd );
|
||||
unlink( scriptPath );
|
||||
Com_Printf( "XDG migration error: script write failed.\n" );
|
||||
return qfalse;
|
||||
}
|
||||
close( fd );
|
||||
|
||||
if( chmod( scriptPath, 0700 ) == -1 )
|
||||
{
|
||||
unlink( scriptPath );
|
||||
Com_Printf( "XDG migration error: script chmod failed.\n" );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
Sys_ClearExecBuffer( );
|
||||
Sys_AppendToExecBuffer( scriptPath );
|
||||
int result = Sys_Exec( );
|
||||
unlink( scriptPath );
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_ShouldUseLegacyHomePath
|
||||
==================
|
||||
*/
|
||||
static qboolean Sys_ShouldUseLegacyHomePath(void)
|
||||
{
|
||||
if( access( Sys_HomeConfigPath( ), F_OK ) == 0 )
|
||||
{
|
||||
// If the XDG config directory exists, prefer XDG layout, regardless
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if( ( com_homepath && com_homepath->string[0] ) ||
|
||||
Cvar_VariableString( "fs_homepath" )[0] )
|
||||
{
|
||||
// If a custom homepath has been explicity set then
|
||||
// that strongly implies that migration isn't desired
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
const char *legacyHomePath = Sys_LegacyHomePath();
|
||||
|
||||
if( !*legacyHomePath || access( legacyHomePath, F_OK ) != 0 )
|
||||
{
|
||||
// The legacy home path doesn't exist
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
char migrationRefusedPath[ MAX_OSPATH ];
|
||||
Com_sprintf( migrationRefusedPath, sizeof( migrationRefusedPath ),
|
||||
"%s/.xdgMigrationRefused", legacyHomePath );
|
||||
|
||||
// If the user hasn't already refused, ask if they want to migrate
|
||||
if( access( migrationRefusedPath, F_OK ) != 0 )
|
||||
{
|
||||
dialogResult_t result = Sys_Dialog( DT_YES_NO, va(
|
||||
"Modern games and applications store files in "
|
||||
"directories according to the Free Desktop standard. "
|
||||
"Here's what that would look like for %s:\n\n"
|
||||
"Configuration files:\n %s\n\n"
|
||||
"Data files; pk3s, screenshots, logs, demos, etc.:\n %s\n\n"
|
||||
"Internal runtime files:\n %s\n\n"
|
||||
"At the moment all of these files are found here:\n %s\n\n"
|
||||
"Do you want to copy your files to these new directories?",
|
||||
PRODUCT_NAME,
|
||||
Sys_HomeConfigPath( ), Sys_HomeDataPath( ), Sys_HomeStatePath( ),
|
||||
legacyHomePath ),
|
||||
"Home Directory Files Upgrade" );
|
||||
|
||||
if( result == DR_YES )
|
||||
return !Sys_MigrateToXDG( );
|
||||
|
||||
// Guard against asking again in future
|
||||
fclose( fopen( migrationRefusedPath, "w" ) );
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_DefaultHomeConfigPath
|
||||
==================
|
||||
*/
|
||||
char *Sys_DefaultHomeConfigPath(void)
|
||||
{
|
||||
if( Sys_ShouldUseLegacyHomePath( ) )
|
||||
return Sys_LegacyHomePath( );
|
||||
|
||||
return Sys_HomeConfigPath( );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_DefaultHomeDataPath
|
||||
==================
|
||||
*/
|
||||
char *Sys_DefaultHomeDataPath(void)
|
||||
{
|
||||
if( Sys_ShouldUseLegacyHomePath( ) )
|
||||
return Sys_LegacyHomePath( );
|
||||
|
||||
return Sys_HomeDataPath( );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
Sys_DefaultHomeStatePath
|
||||
==================
|
||||
*/
|
||||
char *Sys_DefaultHomeStatePath(void)
|
||||
{
|
||||
if( Sys_ShouldUseLegacyHomePath( ) )
|
||||
return Sys_LegacyHomePath( );
|
||||
|
||||
return Sys_HomeStatePath( );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
================
|
||||
Sys_SteamPath
|
||||
@@ -668,10 +966,10 @@ void Sys_ErrorDialog( const char *error )
|
||||
char buffer[ 1024 ];
|
||||
unsigned int size;
|
||||
int f = -1;
|
||||
const char *homepath = Cvar_VariableString( "fs_homepath" );
|
||||
const char *homedatapath = Cvar_VariableString( "fs_homedatapath" );
|
||||
const char *gamedir = Cvar_VariableString( "fs_game" );
|
||||
const char *fileName = "crashlog.txt";
|
||||
char *ospath = FS_BuildOSPath( homepath, gamedir, fileName );
|
||||
char *ospath = FS_BuildOSPath( homedatapath, gamedir, fileName );
|
||||
|
||||
Sys_Print( va( "%s\n", error ) );
|
||||
|
||||
@@ -680,7 +978,7 @@ void Sys_ErrorDialog( const char *error )
|
||||
#endif
|
||||
|
||||
// Make sure the write path for the crashlog exists...
|
||||
if( FS_CreatePath( ospath ) )
|
||||
if( FS_CreatePath( homedatapath ) )
|
||||
{
|
||||
Com_Printf("ERROR: couldn't create path '%s' for crash log.\n", ospath);
|
||||
return;
|
||||
|
||||
@@ -88,7 +88,7 @@ void Sys_SetFloatEnv(void)
|
||||
Sys_DefaultHomePath
|
||||
================
|
||||
*/
|
||||
char *Sys_DefaultHomePath( void )
|
||||
static char *Sys_DefaultHomePath( void )
|
||||
{
|
||||
static char homePath[ MAX_OSPATH ] = { 0 };
|
||||
|
||||
@@ -108,12 +108,16 @@ char *Sys_DefaultHomePath( void )
|
||||
if(com_homepath->string[0])
|
||||
Q_strcat(homePath, sizeof(homePath), com_homepath->string);
|
||||
else
|
||||
Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_WIN);
|
||||
Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME);
|
||||
}
|
||||
|
||||
return homePath;
|
||||
}
|
||||
|
||||
char *Sys_DefaultHomeConfigPath(void) { return Sys_DefaultHomePath(); }
|
||||
char *Sys_DefaultHomeDataPath(void) { return Sys_DefaultHomePath(); }
|
||||
char *Sys_DefaultHomeStatePath(void) { return Sys_DefaultHomePath(); }
|
||||
|
||||
/*
|
||||
================
|
||||
Sys_SteamPath
|
||||
|
||||
Reference in new issue
Block a user