]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake3/q3map2/path_init.c
q3map2: use ~/Library/Application Support on Mac
[xonotic/netradiant.git] / tools / quake3 / q3map2 / path_init.c
index 2591c5bf45b54ba9e7467bd43a42e9f9f139ae28..96b33fcbae6d39cfd9decb777018d76d94f5845a 100644 (file)
 /* path support */
 #define MAX_BASE_PATHS  10
 #define MAX_GAME_PATHS  10
+#define MAX_PAK_PATHS  200
 
+qboolean                               customHomePath = qfalse;
 char                    *homePath;
+
+#if GDEF_OS_MACOS
+char                                   *macLibraryApplicationSupportPath;
+#endif
+
+#if (GDEF_OS_POSIX && !GDEF_OS_MACOS)
+char                    *xdgDataHomePath;
+#endif // (GDEF_OS_POSIX && !GDEF_OS_MACOS)
+
 char installPath[ MAX_OS_PATH ];
 
 int numBasePaths;
 char                    *basePaths[ MAX_BASE_PATHS ];
 int numGamePaths;
 char                    *gamePaths[ MAX_GAME_PATHS ];
+int numPakPaths;
+char                    *pakPaths[ MAX_PAK_PATHS ];
 char                    *homeBasePath = NULL;
 
 
@@ -63,35 +76,26 @@ char                    *homeBasePath = NULL;
  */
 
 char *LokiGetHomeDir( void ){
-       #ifndef Q_UNIX
+       #if GDEF_OS_WINDOWS
        return NULL;
-       #else
+       #else // !GDEF_OS_WINDOWS
+       static char     buf[ 4096 ];
+       struct passwd   pw, *pwp;
        char            *home;
-       uid_t id;
-       struct passwd   *pwd;
-       static char homeBuf[MAX_OS_PATH];
-
 
        /* get the home environment variable */
        home = getenv( "HOME" );
-       if ( home == NULL ) {
-               /* do some more digging */
-               id = getuid();
-               setpwent();
-               while ( ( pwd = getpwent() ) != NULL )
-               {
-                       if ( pwd->pw_uid == id ) {
-                               home = pwd->pw_dir;
-                               break;
-                       }
+
+       /* look up home dir in password database */
+       if( home == NULL )
+       {
+               if ( getpwuid_r( getuid(), &pw, buf, sizeof( buf ), &pwp ) == 0 ) {
+                       return pw.pw_dir;
                }
-               endpwent();
        }
 
-       snprintf( homeBuf, sizeof( homeBuf ), "%s/.", home );
-
        /* return it */
-       return homeBuf;
+       return home;
        #endif
 }
 
@@ -103,9 +107,9 @@ char *LokiGetHomeDir( void ){
  */
 
 void LokiInitPaths( char *argv0 ){
-       char        *home;
+       char *home;
 
-       if ( !homePath ) {
+       if ( homePath == NULL ) {
                /* get home dir */
                home = LokiGetHomeDir();
                if ( home == NULL ) {
@@ -115,36 +119,66 @@ void LokiInitPaths( char *argv0 ){
                /* set home path */
                homePath = home;
        }
-       else{
+       else {
                home = homePath;
        }
 
-       #ifndef Q_UNIX
+       #if GDEF_OS_MACOS
+               char *subPath = "/Library/Application Support";
+               macLibraryApplicationSupportPath = safe_malloc( sizeof( char ) * ( strlen( home ) + strlen( subPath ) ) );
+               sprintf( macLibraryApplicationSupportPath, "%s%s", home, subPath );
+       #endif // GDEF_OS_MACOS
+
+       #if (GDEF_OS_POSIX && !GDEF_OS_MACOS)
+       xdgDataHomePath = getenv( "XDG_DATA_HOME" );
+
+       if ( xdgDataHomePath == NULL ) {
+               char *subPath = "/.local/share";
+               xdgDataHomePath = safe_malloc( sizeof( char ) * ( strlen( home ) + strlen( subPath ) ) );
+               sprintf( xdgDataHomePath, "%s%s", home, subPath );
+       }
+       #endif // (GDEF_OS_POSIX && !GDEF_OS_MACOS)
+
+       #if GDEF_OS_WINDOWS
        /* this is kinda crap, but hey */
        strcpy( installPath, "../" );
-       #else
+       #else // !GDEF_OS_WINDOWS
+
        char temp[ MAX_OS_PATH ];
-       char last0[ 2 ];
-       char        *path;
-       char        *last;
+       char *path;
+       char *last;
        qboolean found;
 
 
+       path = getenv( "PATH" );
+
        /* do some path divining */
-       strcpy( temp, argv0 );
-       if ( strrchr( argv0, '/' ) ) {
+       Q_strncpyz( temp, argv0, sizeof( temp ) );
+       if ( strrchr( temp, '/' ) ) {
                argv0 = strrchr( argv0, '/' ) + 1;
        }
-       else
-       {
-               /* get path environment variable */
-               path = getenv( "PATH" );
+       else if ( path != NULL ) {
+
+               /*
+                  This code has a special behavior when q3map2 is a symbolic link.
+
+                  For each dir in ${PATH} (example: "/usr/bin", "/usr/local/bin" if ${PATH} == "/usr/bin:/usr/local/bin"),
+                  it looks for "${dir}/q3map2" (file exists and is executable),
+                  then it uses "dirname(realpath("${dir}/q3map2"))/../" as installPath.
+
+                  So, if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
+                  it will find the installPath "/usr/share/radiant/",
+                  so q3map2 will look for "/opt/radiant/baseq3" to find paks.
+
+                  More precisely, it looks for "${dir}/${argv[0]}",
+                  so if "/usr/bin/q3map2" is a symbolic link to "/opt/radiant/tools/q3map2",
+                  and if "/opt/radiant/tools/q3ma2" is a symbolic link to "/opt/radiant/tools/q3map2.x86_64",
+                  it will use "dirname("/opt/radiant/tools/q3map2.x86_64")/../" as path,
+                  so it will use "/opt/radiant/" as installPath, which will be expanded later to "/opt/radiant/baseq3" to find paks.
+               */
 
-               /* minor setup */
-               last = last0;
-               last[ 0 ] = path[ 0 ];
-               last[ 1 ] = '\0';
                found = qfalse;
+               last = path;
 
                /* go through each : segment of path */
                while ( last[ 0 ] != '\0' && found == qfalse )
@@ -160,21 +194,23 @@ void LokiInitPaths( char *argv0 ){
 
                        /* found home dir candidate */
                        if ( *path == '~' ) {
-                               strcpy( temp, home );
+                               Q_strncpyz( temp, home, sizeof( temp ) );
                                path++;
                        }
 
                        /* concatenate */
                        if ( last > ( path + 1 ) ) {
-                               strncat( temp, path, ( last - path ) );
-                               strcat( temp, "/" );
+                               // +1 hack: Q_strncat calls Q_strncpyz that expects a len including '\0'
+                               // so that extraneous char will be rewritten by '\0', so it's ok.
+                               // Also, in this case this extraneous char is always ':' or '\0', so it's ok.
+                               Q_strncat( temp, sizeof( temp ), path, ( last - path + 1) );
+                               Q_strcat( temp, sizeof( temp ), "/" );
                        }
-                       strcat( temp, "./" );
-                       strcat( temp, argv0 );
+                       Q_strcat( temp, sizeof( temp ), argv0 );
 
                        /* verify the path */
                        if ( access( temp, X_OK ) == 0 ) {
-                               found++;
+                               found = qtrue;
                        }
                        path = last + 1;
                }
@@ -182,11 +218,14 @@ void LokiInitPaths( char *argv0 ){
 
        /* flake */
        if ( realpath( temp, installPath ) ) {
-               /* q3map is in "tools/" */
+               /*
+                  if "q3map2" is "/opt/radiant/tools/q3map2",
+                  installPath is "/opt/radiant"
+               */
+               *( strrchr( installPath, '/' ) ) = '\0';
                *( strrchr( installPath, '/' ) ) = '\0';
-               *( strrchr( installPath, '/' ) + 1 ) = '\0';
        }
-       #endif
+       #endif // !GDEF_OS_WINDOWS
 }
 
 
@@ -273,15 +312,14 @@ void AddBasePath( char *path ){
 
 /*
    AddHomeBasePath() - ydnar
-   adds a base path to the beginning of the list, prefixed by ~/
+   adds a base path to the beginning of the list
  */
 
 void AddHomeBasePath( char *path ){
        int i;
        char temp[ MAX_OS_PATH ];
-       int homePathLen;
 
-       if ( !homePath ) {
+       if ( homePath == NULL ) {
                return;
        }
 
@@ -290,28 +328,57 @@ void AddHomeBasePath( char *path ){
                return;
        }
 
-       /* strip leading dot, if homePath does not end in /. */
-       homePathLen = strlen( homePath );
-       if ( !strcmp( path, "." ) ) {
+       if ( strcmp( path, "." ) == 0 ) {
                /* -fs_homebase . means that -fs_home is to be used as is */
                strcpy( temp, homePath );
        }
-       else if ( homePathLen >= 2 && !strcmp( homePath + homePathLen - 2, "/." ) ) {
-               /* remove trailing /. of homePath */
-               homePathLen -= 2;
-
-               /* concatenate home dir and path */
-               sprintf( temp, "%.*s/%s", homePathLen, homePath, path );
-       }
-       else
-       {
-               /* remove leading . of path */
-               if ( path[0] == '.' ) {
-                       ++path;
+       else {
+               char *tempHomePath;
+               tempHomePath = homePath;
+
+               /* homePath is . on Windows if not user supplied */
+
+               #if GDEF_OS_MACOS
+               /*
+                  use ${HOME}/Library/Application as ${HOME}
+                  if home path is not user supplied
+                  and strip the leading dot from prefix in any case
+                 
+                  basically it produces
+                  ${HOME}/Library/Application/unvanquished
+                  /user/supplied/home/path/unvanquished
+               */
+               tempHomePath = macLibraryApplicationSupportPath;
+               path = path + 1;
+               #endif // GDEF_OS_MACOS
+
+               #if (GDEF_OS_POSIX && !GDEF_OS_MACOS)
+               /*
+                  on Linux, check if game uses ${XDG_DATA_HOME}/prefix instead of ${HOME}/.prefix
+                  if yes and home path is not user supplied
+                  use XDG_DATA_HOME instead of HOME
+                  and strip the leading dot
+                 
+                  basically it produces
+                  ${XDG_DATA_HOME}/unvanquished
+                  /user/supplied/home/path/unvanquished
+                  
+                  or
+                  ${HOME}/.q3a
+                  /user/supplied/home/path/.q3a
+                */
+
+               sprintf( temp, "%s/%s", xdgDataHomePath, ( path + 1 ) );
+               if ( access( temp, X_OK ) == 0 ) {
+                       if ( customHomePath == qfalse ) {
+                               tempHomePath = xdgDataHomePath;
+                       }
+                       path = path + 1;
                }
+               #endif // (GDEF_OS_POSIX && !GDEF_OS_MACOS)
 
                /* concatenate home dir and path */
-               sprintf( temp, "%s/%s", homePath, path );
+               sprintf( temp, "%s/%s", tempHomePath, path );
        }
 
        /* make a hole */
@@ -360,6 +427,24 @@ void AddGamePath( char *path ){
 }
 
 
+/*
+   AddPakPath()
+   adds a pak path to the list
+ */
+
+void AddPakPath( char *path ){
+       /* dummy check */
+       if ( path == NULL || path[ 0 ] == '\0' || numPakPaths >= MAX_PAK_PATHS ) {
+               return;
+       }
+
+       /* add it to the list */
+       pakPaths[ numPakPaths ] = safe_malloc( strlen( path ) + 1 );
+       strcpy( pakPaths[ numPakPaths ], path );
+       CleanPath( pakPaths[ numPakPaths ] );
+       numPakPaths++;
+}
+
 
 
 /*
@@ -372,6 +457,9 @@ void InitPaths( int *argc, char **argv ){
        int i, j, k, len, len2;
        char temp[ MAX_OS_PATH ];
 
+       int noBasePath = 0;
+       int noHomePath = 0;
+       int noMagicPath = 0;
 
        /* note it */
        Sys_FPrintf( SYS_VRB, "--- InitPaths ---\n" );
@@ -419,6 +507,20 @@ void InitPaths( int *argc, char **argv ){
                        argv[ i ] = NULL;
                }
 
+               /* -fs_nobasepath */
+               else if ( strcmp( argv[ i ], "-fs_nobasepath" ) == 0 ) {
+                       noBasePath = 1;
+                       // we don't want any basepath, neither guessed ones
+                       noMagicPath = 1;
+                       argv[ i ] = NULL;
+               }               
+
+               /* -fs_nomagicpath */
+               else if ( strcmp( argv[ i ], "-fs_nomagicpath") == 0) {
+                       noMagicPath = 1;
+                       argv[ i ] = NULL;
+               }
+
                /* -fs_basepath */
                else if ( strcmp( argv[ i ], "-fs_basepath" ) == 0 ) {
                        if ( ++i >= *argc ) {
@@ -445,10 +547,17 @@ void InitPaths( int *argc, char **argv ){
                                Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
                        }
                        argv[ i - 1 ] = NULL;
+                       customHomePath = qtrue;
                        homePath = argv[i];
                        argv[ i ] = NULL;
                }
 
+               /* -fs_nohomepath */
+               else if ( strcmp( argv[ i ], "-fs_nohomepath" ) == 0 ) {
+                       noHomePath = 1;
+                       argv[ i ] = NULL;
+               }               
+
                /* -fs_homebase */
                else if ( strcmp( argv[ i ], "-fs_homebase" ) == 0 ) {
                        if ( ++i >= *argc ) {
@@ -469,6 +578,16 @@ void InitPaths( int *argc, char **argv ){
                        homeBasePath = ".";
                        argv[ i ] = NULL;
                }
+
+               /* -fs_pakpath */
+               else if ( strcmp( argv[ i ], "-fs_pakpath" ) == 0 ) {
+                       if ( ++i >= *argc ) {
+                               Error( "Out of arguments: No path specified after %s.", argv[ i - 1 ] );
+                       }
+                       argv[ i - 1 ] = NULL;
+                       AddPakPath( argv[ i ] );
+                       argv[ i ] = NULL;
+               }
        }
 
        /* remove processed arguments */
@@ -485,8 +604,8 @@ void InitPaths( int *argc, char **argv ){
        /* add standard game path */
        AddGamePath( game->gamePath );
 
-       /* if there is no base path set, figure it out */
-       if ( numBasePaths == 0 ) {
+       /* if there is no base path set, figure it out unless fs_nomagicpath is set */
+       if ( numBasePaths == 0 && noBasePath == 0 && noMagicPath == 0 ) {
                /* this is another crappy replacement for SetQdirFromPath() */
                len2 = strlen( game->magic );
                for ( i = 0; i < *argc && numBasePaths == 0; i++ )
@@ -524,12 +643,18 @@ void InitPaths( int *argc, char **argv ){
                }
        }
 
-       /* this only affects unix */
-       if ( homeBasePath ) {
-               AddHomeBasePath( homeBasePath );
+       if ( noBasePath == 1 ) {
+               numBasePaths = 0;
        }
-       else{
-               AddHomeBasePath( game->homeBasePath );
+
+       if ( noHomePath == 0 ) {
+               /* this only affects unix */
+               if ( homeBasePath ) {
+                       AddHomeBasePath( homeBasePath );
+               }
+               else{
+                       AddHomeBasePath( game->homeBasePath );
+               }
        }
 
        /* initialize vfs paths */
@@ -552,6 +677,18 @@ void InitPaths( int *argc, char **argv ){
                }
        }
 
+       /* initialize vfs paths */
+       if ( numPakPaths > MAX_PAK_PATHS ) {
+               numPakPaths = MAX_PAK_PATHS;
+       }
+
+       /* walk the list of pak paths */
+       for ( i = 0; i < numPakPaths; i++ )
+       {
+               /* initialize this pak path */
+               vfsInitDirectory( pakPaths[ i ] );
+       }
+
        /* done */
        Sys_Printf( "\n" );
 }