-mautoupdate
[gnulib.git] / lib / progreloc.c
index 0f87c1c..a79ef7a 100644 (file)
@@ -1,26 +1,22 @@
 /* Provide relocatable programs.
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003-2009 Free Software Foundation, Inc.
    Written by Bruno Haible <bruno@clisp.org>, 2003.
 
-   This program is free software; you can redistribute it and/or modify it
-   under the terms of the GNU Library General Public License as published
-   by the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-   USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include <config.h>
 
 /* Specification.  */
 #include "progname.h"
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <unistd.h>
 #include <sys/stat.h>
 
+/* Get declaration of _NSGetExecutablePath on MacOS X 10.2 or newer.  */
+#if HAVE_MACH_O_DYLD_H
+# include <mach-o/dyld.h>
+#endif
+
 #if defined _WIN32 || defined __WIN32__
-# undef WIN32   /* avoid warning on mingw32 */
-# define WIN32
+# define WIN32_NATIVE
 #endif
 
-#ifdef WIN32
+#if defined WIN32_NATIVE || defined __CYGWIN__
 # define WIN32_LEAN_AND_MEAN
 # include <windows.h>
 #endif
 
-#include "xreadlink.h"
 #include "canonicalize.h"
 #include "relocatable.h"
 
 #ifdef NO_XMALLOC
+# include "areadlink.h"
+# define xreadlink areadlink
+#else
+# include "xreadlink.h"
+#endif
+
+#ifdef NO_XMALLOC
 # define xmalloc malloc
+# define xstrdup strdup
 #else
-# include "xmalloc.h"
+# include "xalloc.h"
 #endif
 
 /* Pathname support.
      && (P)[1] == ':')
 # define IS_PATH_WITH_DIR(P) \
     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
-# define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
+# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
 #else
   /* Unix */
 # define ISSLASH(C) ((C) == '/')
 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
-# define FILESYSTEM_PREFIX_LEN(P) 0
+# define FILE_SYSTEM_PREFIX_LEN(P) 0
 #endif
 
+/* The results of open() in this file are not used with fchdir,
+   therefore save some unnecessary work in fchdir.c.  */
+#undef open
+#undef close
+
 #undef set_program_name
 
 
@@ -90,7 +100,8 @@ static int executable_fd = -1;
 static bool
 maybe_executable (const char *filename)
 {
-#if !defined WIN32
+  /* Woe32 lacks the access() function, but Cygwin doesn't.  */
+#if !(defined WIN32_NATIVE && !defined __CYGWIN__)
   if (access (filename, X_OK) < 0)
     return false;
 
@@ -125,16 +136,39 @@ maybe_executable (const char *filename)
 static char *
 find_executable (const char *argv0)
 {
-#ifdef WIN32
-  char buf[1024];
-  int length = GetModuleFileName (NULL, buf, sizeof (buf));
+#if defined WIN32_NATIVE || defined __CYGWIN__
+  char location[MAX_PATH];
+  int length = GetModuleFileName (NULL, location, sizeof (location));
   if (length < 0)
     return NULL;
-  if (!IS_PATH_WITH_DIR (buf))
+  if (!IS_PATH_WITH_DIR (location))
     /* Shouldn't happen.  */
     return NULL;
-  return xstrdup (buf);
-#else /* Unix */
+  {
+#if defined __CYGWIN__
+    /* cygwin-1.5.13 (2005-03-01) or newer would also allow a Linux-like
+       implementation: readlink of "/proc/self/exe".  But using the
+       result of the Win32 system call is simpler and is consistent with the
+       code in relocatable.c.  */
+    /* On Cygwin, we need to convert paths coming from Win32 system calls
+       to the Unix-like slashified notation.  */
+    static char location_as_posix_path[2 * MAX_PATH];
+    /* There's no error return defined for cygwin_conv_to_posix_path.
+       See cygwin-api/func-cygwin-conv-to-posix-path.html.
+       Does it overflow the buffer of expected size MAX_PATH or does it
+       truncate the path?  I don't know.  Let's catch both.  */
+    cygwin_conv_to_posix_path (location, location_as_posix_path);
+    location_as_posix_path[MAX_PATH - 1] = '\0';
+    if (strlen (location_as_posix_path) >= MAX_PATH - 1)
+      /* A sign of buffer overflow or path truncation.  */
+      return NULL;
+    /* Call canonicalize_file_name, because Cygwin supports symbolic links.  */
+    return canonicalize_file_name (location_as_posix_path);
+#else
+    return xstrdup (location);
+#endif
+  }
+#else /* Unix && !Cygwin */
 #ifdef __linux__
   /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
      versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
@@ -160,6 +194,16 @@ find_executable (const char *argv0)
     }
   }
 #endif
+#if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
+  /* On MacOS X 10.2 or newer, the function
+       int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
+     can be used to retrieve the executable's full path.  */
+  char location[4096];
+  unsigned int length = sizeof (location);
+  if (_NSGetExecutablePath (location, &length) == 0
+      && location[0] == '/')
+    return canonicalize_file_name (location);
+#endif
   /* Guess the executable's full path.  We assume the executable has been
      called via execlp() or execvp() with properly set up argv[0].  The
      login(1) convention to add a '-' prefix to argv[0] is not supported.  */
@@ -237,7 +281,7 @@ static void
 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
                  const char *argv0)
 {
-  const char *curr_prefix;
+  char *curr_prefix;
 
   /* Determine the full pathname of the current executable.  */
   executable_fullname = find_executable (argv0);
@@ -246,8 +290,12 @@ prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
   curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
                                     executable_fullname);
   if (curr_prefix != NULL)
-    /* Now pass this prefix to all copies of the relocate.c source file.  */
-    set_relocation_prefix (orig_installprefix, curr_prefix);
+    {
+      /* Now pass this prefix to all copies of the relocate.c source file.  */
+      set_relocation_prefix (orig_installprefix, curr_prefix);
+
+      free (curr_prefix);
+    }
 }
 
 /* Set program_name, based on argv[0], and original installation prefix and
@@ -259,21 +307,50 @@ set_program_name_and_installdir (const char *argv0,
 {
   const char *argv0_stripped = argv0;
 
-  /* Relocatable programs are renamed to .bin by install-reloc.  Remove
-     this suffix here.  */
+  /* Relocatable programs are renamed to .bin by install-reloc.  Or, more
+     generally, their suffix is changed from $exeext to .bin$exeext.
+     Remove the ".bin" here.  */
   {
     size_t argv0_len = strlen (argv0);
-    if (argv0_len > 4 && memcmp (argv0 + argv0_len - 4, ".bin", 4) == 0)
-      {
-       char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
+    const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
+    if (argv0_len > 4 + exeext_len)
+      if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
+       {
+         if (sizeof (EXEEXT) > sizeof (""))
+           {
+             /* Compare using an inlined copy of c_strncasecmp(), because
+                the filenames may have undergone a case conversion since
+                they were packaged.  In other words, EXEEXT may be ".exe"
+                on one system and ".EXE" on another.  */
+             static const char exeext[] = EXEEXT;
+             const char *s1 = argv0 + argv0_len - exeext_len;
+             const char *s2 = exeext;
+             for (; *s1 != '\0'; s1++, s2++)
+               {
+                 unsigned char c1 = *s1;
+                 unsigned char c2 = *s2;
+                 if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
+                     != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
+                   goto done_stripping;
+               }
+           }
+         /* Remove ".bin" before EXEEXT or its equivalent.  */
+         {
+           char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
 #ifdef NO_XMALLOC
-       if (shorter != NULL)
+           if (shorter != NULL)
 #endif
-         {
-           memcpy (shorter, argv0, argv0_len - 4);
-           shorter[argv0_len - 4] = '\0';
-           argv0_stripped = shorter;
+             {
+               memcpy (shorter, argv0, argv0_len - exeext_len - 4);
+               if (sizeof (EXEEXT) > sizeof (""))
+                 memcpy (shorter + argv0_len - exeext_len - 4,
+                         argv0 + argv0_len - exeext_len - 4,
+                         exeext_len);
+               shorter[argv0_len - 4] = '\0';
+               argv0_stripped = shorter;
+             }
          }
+        done_stripping: ;
       }
   }