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