Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Unix support routines for PhysicsFS.
  3.  *
  4.  * Please see the file LICENSE.txt in the source's root directory.
  5.  *
  6.  *  This file written by Ryan C. Gordon.
  7.  */
  8.  
  9. #define __PHYSICSFS_INTERNAL__
  10. #include "physfs_platforms.h"
  11.  
  12. #ifdef PHYSFS_PLATFORM_UNIX
  13.  
  14. #include <ctype.h>
  15. #include <unistd.h>
  16. #include <stdlib.h>
  17. #include <sys/types.h>
  18. #include <pwd.h>
  19. #include <sys/stat.h>
  20. #include <sys/param.h>
  21. #include <dirent.h>
  22. #include <time.h>
  23. #include <errno.h>
  24. #include <limits.h>
  25.  
  26. #if PHYSFS_NO_CDROM_SUPPORT
  27. #elif PHYSFS_PLATFORM_LINUX
  28. #  define PHYSFS_HAVE_MNTENT_H 1
  29. #elif defined __CYGWIN__
  30. #  define PHYSFS_HAVE_MNTENT_H 1
  31. #elif PHYSFS_PLATFORM_SOLARIS
  32. #  define PHYSFS_HAVE_SYS_MNTTAB_H 1
  33. #elif PHYSFS_PLATFORM_BSD
  34. #  define PHYSFS_HAVE_SYS_UCRED_H 1
  35. #else
  36. #  warning No CD-ROM support included. Either define your platform here,
  37. #  warning  or define PHYSFS_NO_CDROM_SUPPORT=1 to confirm this is intentional.
  38. #endif
  39.  
  40. #ifdef PHYSFS_HAVE_SYS_UCRED_H
  41. #  ifdef PHYSFS_HAVE_MNTENT_H
  42. #    undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
  43. #  endif
  44. #  include <sys/mount.h>
  45. #  include <sys/ucred.h>
  46. #endif
  47.  
  48. #ifdef PHYSFS_HAVE_MNTENT_H
  49. #include <mntent.h>
  50. #endif
  51.  
  52. #ifdef PHYSFS_HAVE_SYS_MNTTAB_H
  53. #include <sys/mnttab.h>
  54. #endif
  55.  
  56. #ifdef PHYSFS_PLATFORM_FREEBSD
  57. #include <sys/sysctl.h>
  58. #endif
  59.  
  60.  
  61. #include "physfs_internal.h"
  62.  
  63. int __PHYSFS_platformInit(void)
  64. {
  65.     return 1;  /* always succeed. */
  66. } /* __PHYSFS_platformInit */
  67.  
  68.  
  69. void __PHYSFS_platformDeinit(void)
  70. {
  71.     /* no-op */
  72. } /* __PHYSFS_platformDeinit */
  73.  
  74.  
  75. void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  76. {
  77. #if (defined PHYSFS_NO_CDROM_SUPPORT)
  78.     /* no-op. */
  79.  
  80. #elif (defined PHYSFS_HAVE_SYS_UCRED_H)
  81.     int i;
  82.     struct statfs *mntbufp = NULL;
  83.     int mounts = getmntinfo(&mntbufp, MNT_NOWAIT);
  84.  
  85.     for (i = 0; i < mounts; i++)
  86.     {
  87.         int add_it = 0;
  88.  
  89.         if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
  90.             add_it = 1;
  91.         else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
  92.             add_it = 1;
  93.  
  94.         /* add other mount types here */
  95.  
  96.         if (add_it)
  97.             cb(data, mntbufp[i].f_mntonname);
  98.     } /* for */
  99.  
  100. #elif (defined PHYSFS_HAVE_MNTENT_H)
  101.     FILE *mounts = NULL;
  102.     struct mntent *ent = NULL;
  103.  
  104.     mounts = setmntent("/etc/mtab", "r");
  105.     BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
  106.  
  107.     while ( (ent = getmntent(mounts)) != NULL )
  108.     {
  109.         int add_it = 0;
  110.         if (strcmp(ent->mnt_type, "iso9660") == 0)
  111.             add_it = 1;
  112.         else if (strcmp(ent->mnt_type, "udf") == 0)
  113.             add_it = 1;
  114.  
  115.         /* !!! FIXME: these might pick up floppy drives, right? */
  116.         else if (strcmp(ent->mnt_type, "auto") == 0)
  117.             add_it = 1;
  118.         else if (strcmp(ent->mnt_type, "supermount") == 0)
  119.             add_it = 1;
  120.  
  121.         /* add other mount types here */
  122.  
  123.         if (add_it)
  124.             cb(data, ent->mnt_dir);
  125.     } /* while */
  126.  
  127.     endmntent(mounts);
  128.  
  129. #elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
  130.     FILE *mounts = fopen(MNTTAB, "r");
  131.     struct mnttab ent;
  132.  
  133.     BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
  134.     while (getmntent(mounts, &ent) == 0)
  135.     {
  136.         int add_it = 0;
  137.         if (strcmp(ent.mnt_fstype, "hsfs") == 0)
  138.             add_it = 1;
  139.  
  140.         /* add other mount types here */
  141.  
  142.         if (add_it)
  143.             cb(data, ent.mnt_mountp);
  144.     } /* while */
  145.  
  146.     fclose(mounts);
  147. #endif
  148. } /* __PHYSFS_platformDetectAvailableCDs */
  149.  
  150.  
  151. /*
  152.  * See where program (bin) resides in the $PATH specified by (envr).
  153.  *  returns a copy of the first element in envr that contains it, or NULL
  154.  *  if it doesn't exist or there were other problems. PHYSFS_SetError() is
  155.  *  called if we have a problem.
  156.  *
  157.  * (envr) will be scribbled over, and you are expected to allocator.Free() the
  158.  *  return value when you're done with it.
  159.  */
  160. static char *findBinaryInPath(const char *bin, char *envr)
  161. {
  162.     size_t alloc_size = 0;
  163.     char *exe = NULL;
  164.     char *start = envr;
  165.     char *ptr;
  166.  
  167.     assert(bin != NULL);
  168.     assert(envr != NULL);
  169.  
  170.     do
  171.     {
  172.         size_t size;
  173.         size_t binlen;
  174.  
  175.         ptr = strchr(start, ':');  /* find next $PATH separator. */
  176.         if (ptr)
  177.             *ptr = '\0';
  178.  
  179.         binlen = strlen(bin);
  180.         size = strlen(start) + binlen + 2;
  181.         if (size >= alloc_size)
  182.         {
  183.             char *x = (char *) allocator.Realloc(exe, size);
  184.             if (!x)
  185.             {
  186.                 if (exe != NULL)
  187.                     allocator.Free(exe);
  188.                 BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  189.             } /* if */
  190.  
  191.             alloc_size = size;
  192.             exe = x;
  193.         } /* if */
  194.  
  195.         /* build full binary path... */
  196.         strcpy(exe, start);
  197.         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
  198.             strcat(exe, "/");
  199.         strcat(exe, bin);
  200.  
  201.         if (access(exe, X_OK) == 0)  /* Exists as executable? We're done. */
  202.         {
  203.             exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */
  204.             return exe;
  205.         } /* if */
  206.  
  207.         start = ptr + 1;  /* start points to beginning of next element. */
  208.     } while (ptr != NULL);
  209.  
  210.     if (exe != NULL)
  211.         allocator.Free(exe);
  212.  
  213.     return NULL;  /* doesn't exist in path. */
  214. } /* findBinaryInPath */
  215.  
  216.  
  217. static char *readSymLink(const char *path)
  218. {
  219.     ssize_t len = 64;
  220.     ssize_t rc = -1;
  221.     char *retval = NULL;
  222.  
  223.     while (1)
  224.     {
  225.          char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
  226.          if (ptr == NULL)
  227.              break;   /* out of memory. */
  228.          retval = ptr;
  229.  
  230.          rc = readlink(path, retval, len);
  231.          if (rc == -1)
  232.              break;  /* not a symlink, i/o error, etc. */
  233.  
  234.          else if (rc < len)
  235.          {
  236.              retval[rc] = '\0';  /* readlink doesn't null-terminate. */
  237.              return retval;  /* we're good to go. */
  238.          } /* else if */
  239.  
  240.          len *= 2;  /* grow buffer, try again. */
  241.     } /* while */
  242.  
  243.     if (retval != NULL)
  244.         allocator.Free(retval);
  245.     return NULL;
  246. } /* readSymLink */
  247.  
  248.  
  249. char *__PHYSFS_platformCalcBaseDir(const char *argv0)
  250. {
  251.     char *retval = NULL;
  252.     const char *envr = NULL;
  253.  
  254.     /* Try to avoid using argv0 unless forced to. Try system-specific stuff. */
  255.  
  256.     #if defined(PHYSFS_PLATFORM_FREEBSD)
  257.     {
  258.         char fullpath[PATH_MAX];
  259.         size_t buflen = sizeof (fullpath);
  260.         int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
  261.         if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1)
  262.             retval = __PHYSFS_strdup(fullpath);
  263.     }
  264.     #elif defined(PHYSFS_PLATFORM_SOLARIS)
  265.     {
  266.         const char *path = getexecname();
  267.         if ((path != NULL) && (path[0] == '/'))  /* must be absolute path... */
  268.             retval = __PHYSFS_strdup(path);
  269.     }
  270.     #endif
  271.  
  272.     /* If there's a Linux-like /proc filesystem, you can get the full path to
  273.      *  the current process from a symlink in there.
  274.      */
  275.  
  276.     if (!retval && (access("/proc", F_OK) == 0))
  277.     {
  278.         retval = readSymLink("/proc/self/exe");
  279.         if (!retval) retval = readSymLink("/proc/curproc/file");
  280.         if (!retval) retval = readSymLink("/proc/curproc/exe");
  281.         if (retval == NULL)
  282.         {
  283.             /* older kernels don't have /proc/self ... try PID version... */
  284.             const unsigned long long pid = (unsigned long long) getpid();
  285.             char path[64];
  286.             const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
  287.             if ( (rc > 0) && (rc < sizeof(path)) )
  288.                 retval = readSymLink(path);
  289.         } /* if */
  290.     } /* if */
  291.  
  292.     if (retval != NULL)  /* chop off filename. */
  293.     {
  294.         char *ptr = strrchr(retval, '/');
  295.         if (ptr != NULL)
  296.             *(ptr+1) = '\0';
  297.         else  /* shouldn't happen, but just in case... */
  298.         {
  299.             allocator.Free(retval);
  300.             retval = NULL;
  301.         } /* else */
  302.     } /* if */
  303.  
  304.     /* No /proc/self/exe, etc, but we have an argv[0] we can parse? */
  305.     if ((retval == NULL) && (argv0 != NULL))
  306.     {
  307.         /* fast path: default behaviour can handle this. */
  308.         if (strchr(argv0, '/') != NULL)
  309.             return NULL;  /* higher level parses out real path from argv0. */
  310.  
  311.         /* If there's no dirsep on argv0, then look through $PATH for it. */
  312.         envr = getenv("PATH");
  313.         if (envr != NULL)
  314.         {
  315.             char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1);
  316.             BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  317.             strcpy(path, envr);
  318.             retval = findBinaryInPath(argv0, path);
  319.             __PHYSFS_smallFree(path);
  320.         } /* if */
  321.     } /* if */
  322.  
  323.     if (retval != NULL)
  324.     {
  325.         /* try to shrink buffer... */
  326.         char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
  327.         if (ptr != NULL)
  328.             retval = ptr;  /* oh well if it failed. */
  329.     } /* if */
  330.  
  331.     return retval;
  332. } /* __PHYSFS_platformCalcBaseDir */
  333.  
  334.  
  335. char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
  336. {
  337.     /*
  338.      * We use XDG's base directory spec, even if you're not on Linux.
  339.      *  This isn't strictly correct, but the results are relatively sane
  340.      *  in any case.
  341.      *
  342.      * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
  343.      */
  344.     const char *envr = getenv("XDG_DATA_HOME");
  345.     const char *append = "/";
  346.     char *retval = NULL;
  347.     size_t len = 0;
  348.  
  349.     if (!envr)
  350.     {
  351.         /* You end up with "$HOME/.local/share/Game Name 2" */
  352.         envr = __PHYSFS_getUserDir();
  353.         BAIL_IF_ERRPASS(!envr, NULL);  /* oh well. */
  354.         append = ".local/share/";
  355.     } /* if */
  356.  
  357.     len = strlen(envr) + strlen(append) + strlen(app) + 2;
  358.     retval = (char *) allocator.Malloc(len);
  359.     BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  360.     snprintf(retval, len, "%s%s%s/", envr, append, app);
  361.     return retval;
  362. } /* __PHYSFS_platformCalcPrefDir */
  363.  
  364. #endif /* PHYSFS_PLATFORM_UNIX */
  365.  
  366. /* end of physfs_platform_unix.c ... */
  367.  
  368.