Merged 1001:b0c6f2f4f361 through 1003:a28d30d275e2 from default branch. stable-2.0
authorRyan C. Gordon <icculus@icculus.org>
Wed, 08 Jul 2009 17:59:29 -0400
branchstable-2.0
changeset 1004 cd500847dbc9
parent 1000 66f622c889bc
child 1007 d6729def6251
Merged 1001:b0c6f2f4f361 through 1003:a28d30d275e2 from default branch. Fixes /proc/*/exe behaviour on Linux.
platform/unix.c
--- a/platform/unix.c	Sun Jun 21 17:42:20 2009 -0400
+++ b/platform/unix.c	Wed Jul 08 17:59:29 2009 -0400
@@ -182,12 +182,42 @@
 } /* findBinaryInPath */
 
 
+static char *readSymLink(const char *path)
+{
+    ssize_t len = 64;
+    ssize_t rc = -1;
+    char *retval = NULL;
+
+    while (1)
+    {
+         char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
+         if (ptr == NULL)
+             break;   /* out of memory. */
+         retval = ptr;
+
+         rc = readlink(path, retval, len);
+         if (rc == -1)
+             break;  /* not a symlink, i/o error, etc. */
+
+         else if (rc < len)
+         {
+             retval[rc] = '\0';  /* readlink doesn't null-terminate. */
+             return retval;  /* we're good to go. */
+         } /* else if */
+
+         len *= 2;  /* grow buffer, try again. */
+    } /* while */
+
+    if (retval != NULL)
+        allocator.Free(retval);
+    return NULL;
+} /* readSymLink */
+
+
 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
 {
-    const char *PROC_SELF_EXE = "/proc/self/exe";
     char *retval = NULL;
     char *envr = NULL;
-    struct stat stbuf;
 
     /* fast path: default behaviour can handle this. */
     if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) )
@@ -198,20 +228,22 @@
      *  /proc filesystem, you can get the full path to the current process from
      *  the /proc/self/exe symlink.
      */
-    if ((lstat(PROC_SELF_EXE, &stbuf) != -1) && (S_ISLNK(stbuf.st_mode)))
+    retval = readSymLink("/proc/self/exe");
+    if (retval == NULL)
     {
-        const size_t len = stbuf.st_size;
-        char *buf = (char *) allocator.Malloc(len+1);
-        if (buf != NULL)  /* if NULL, maybe you'll get lucky later. */
-        {
-            if (readlink(PROC_SELF_EXE, buf, len) != len)
-                allocator.Free(buf);
-            else
-            {
-                buf[len] = '\0';  /* readlink doesn't null-terminate. */
-                retval = buf;  /* we're good to go. */
-            } /* else */
-        } /* if */
+        /* older kernels don't have /proc/self ... try PID version... */
+        const unsigned long long pid = (unsigned long long) getpid();
+        char path[64];
+        const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
+        if ( (rc > 0) && (rc < sizeof(path)) )
+            retval = readSymLink(path);
+    } /* if */
+
+    if (retval != NULL)  /* chop off filename. */
+    {
+        char *ptr = strrchr(retval, '/');
+        if (ptr != NULL)
+            *ptr = '\0';
     } /* if */
 
     if ((retval == NULL) && (argv0 != NULL))
@@ -223,6 +255,14 @@
         allocator.Free(envr);
     } /* if */
 
+    if (retval != NULL)
+    {
+        /* try to shrink buffer... */
+        char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
+        if (ptr != NULL)
+            retval = ptr;  /* oh well if it failed. */
+    } /* if */
+
     return(retval);
 } /* __PHYSFS_platformCalcBaseDir */