/* Provide relocatable programs.
- Copyright (C) 2003-2004 Free Software Foundation, Inc.
+ Copyright (C) 2003-2006 Free Software Foundation, Inc.
Written by Bruno Haible <bruno@clisp.org>, 2003.
This program is free software; you can redistribute it and/or modify
#endif
#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
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;
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
executable_fd = open (buf, O_RDONLY, 0);
}
}
+#endif
+#if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
+ /* On MacOS X 10.2 or newer, the function
+ int _NSGetExecutablePath (char *buf, unsigned long *bufsize);
+ can be used to retrieve the executable's full path. */
+ char location[4096];
+ unsigned long 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
{
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: ;
}
}