1 /* Provide relocatable programs.
2 Copyright (C) 2003-2006 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
35 /* Get declaration of _NSGetExecutablePath on MacOS X 10.2 or newer. */
36 #if HAVE_MACH_O_DYLD_H
37 # include <mach-o/dyld.h>
40 #if defined _WIN32 || defined __WIN32__
44 #if defined WIN32_NATIVE || defined __CYGWIN__
45 # define WIN32_LEAN_AND_MEAN
49 #include "xreadlink.h"
50 #include "canonicalize.h"
51 #include "relocatable.h"
54 # define xmalloc malloc
55 # define xstrdup strdup
61 ISSLASH(C) tests whether C is a directory separator character.
62 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
64 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
65 /* Win32, Cygwin, OS/2, DOS */
66 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
67 # define HAS_DEVICE(P) \
68 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
70 # define IS_PATH_WITH_DIR(P) \
71 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
72 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
75 # define ISSLASH(C) ((C) == '/')
76 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
77 # define FILE_SYSTEM_PREFIX_LEN(P) 0
80 /* The results of open() in this file are not used with fchdir,
81 therefore save some unnecessary work in fchdir.c. */
85 #undef set_program_name
88 #if ENABLE_RELOCATABLE
91 /* File descriptor of the executable.
92 (Only used to verify that we find the correct executable.) */
93 static int executable_fd = -1;
96 /* Tests whether a given pathname may belong to the executable. */
98 maybe_executable (const char *filename)
100 /* Woe32 lacks the access() function, but Cygwin doesn't. */
101 #if !(defined WIN32_NATIVE && !defined __CYGWIN__)
102 if (access (filename, X_OK) < 0)
106 if (executable_fd >= 0)
108 /* If we already have an executable_fd, check that filename points to
111 struct stat statfile;
113 if (fstat (executable_fd, &statexe) >= 0)
115 if (stat (filename, &statfile) < 0)
117 if (!(statfile.st_dev
118 && statfile.st_dev == statexe.st_dev
119 && statfile.st_ino == statexe.st_ino))
129 /* Determine the full pathname of the current executable, freshly allocated.
130 Return NULL if unknown.
131 Guaranteed to work on Linux and Woe32. Likely to work on the other
132 Unixes (maybe except BeOS), under most conditions. */
134 find_executable (const char *argv0)
136 #if defined WIN32_NATIVE || defined __CYGWIN__
137 char location[MAX_PATH];
138 int length = GetModuleFileName (NULL, location, sizeof (location));
141 if (!IS_PATH_WITH_DIR (location))
142 /* Shouldn't happen. */
145 #if defined __CYGWIN__
146 /* cygwin-1.5.13 (2005-03-01) or newer would also allow a Linux-like
147 implementation: readlink of "/proc/self/exe". But using the
148 result of the Win32 system call is simpler and is consistent with the
149 code in relocatable.c. */
150 /* On Cygwin, we need to convert paths coming from Win32 system calls
151 to the Unix-like slashified notation. */
152 static char location_as_posix_path[2 * MAX_PATH];
153 /* There's no error return defined for cygwin_conv_to_posix_path.
154 See cygwin-api/func-cygwin-conv-to-posix-path.html.
155 Does it overflow the buffer of expected size MAX_PATH or does it
156 truncate the path? I don't know. Let's catch both. */
157 cygwin_conv_to_posix_path (location, location_as_posix_path);
158 location_as_posix_path[MAX_PATH - 1] = '\0';
159 if (strlen (location_as_posix_path) >= MAX_PATH - 1)
160 /* A sign of buffer overflow or path truncation. */
162 /* Call canonicalize_file_name, because Cygwin supports symbolic links. */
163 return canonicalize_file_name (location_as_posix_path);
165 return xstrdup (location);
168 #else /* Unix && !Cygwin */
170 /* The executable is accessible as /proc/<pid>/exe. In newer Linux
171 versions, also as /proc/self/exe. Linux >= 2.1 provides a symlink
172 to the true pathname; older Linux versions give only device and ino,
173 enclosed in brackets, which we cannot use here. */
177 link = xreadlink ("/proc/self/exe");
178 if (link != NULL && link[0] != '[')
180 if (executable_fd < 0)
181 executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
185 sprintf (buf, "/proc/%d/exe", getpid ());
186 link = xreadlink (buf);
187 if (link != NULL && link[0] != '[')
189 if (executable_fd < 0)
190 executable_fd = open (buf, O_RDONLY, 0);
194 #if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
195 /* On MacOS X 10.2 or newer, the function
196 int _NSGetExecutablePath (char *buf, unsigned long *bufsize);
197 can be used to retrieve the executable's full path. */
199 unsigned long length = sizeof (location);
200 if (_NSGetExecutablePath (location, &length) == 0
201 && location[0] == '/')
202 return canonicalize_file_name (location);
204 /* Guess the executable's full path. We assume the executable has been
205 called via execlp() or execvp() with properly set up argv[0]. The
206 login(1) convention to add a '-' prefix to argv[0] is not supported. */
208 bool has_slash = false;
211 for (p = argv0; *p; p++)
220 /* exec searches paths without slashes in the directory list given
222 const char *path = getenv ("PATH");
229 for (p = path; *p; p = p_next)
239 p_next = (*q == '\0' ? q : q + 1);
241 /* We have a path item at p, of length p_len.
242 Now concatenate the path item and argv0. */
243 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
245 if (concat_name == NULL)
249 /* An empty PATH element designates the current directory. */
250 strcpy (concat_name, argv0);
253 memcpy (concat_name, p, p_len);
254 concat_name[p_len] = '/';
255 strcpy (concat_name + p_len + 1, argv0);
257 if (maybe_executable (concat_name))
258 return canonicalize_file_name (concat_name);
262 /* Not found in the PATH, assume the current directory. */
264 /* exec treats paths containing slashes as relative to the current
266 if (maybe_executable (argv0))
267 return canonicalize_file_name (argv0);
269 /* No way to find the executable. */
274 /* Full pathname of executable, or NULL. */
275 static char *executable_fullname;
278 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
281 const char *curr_prefix;
283 /* Determine the full pathname of the current executable. */
284 executable_fullname = find_executable (argv0);
286 /* Determine the current installation prefix from it. */
287 curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
288 executable_fullname);
289 if (curr_prefix != NULL)
290 /* Now pass this prefix to all copies of the relocate.c source file. */
291 set_relocation_prefix (orig_installprefix, curr_prefix);
294 /* Set program_name, based on argv[0], and original installation prefix and
295 directory, for relocatability. */
297 set_program_name_and_installdir (const char *argv0,
298 const char *orig_installprefix,
299 const char *orig_installdir)
301 const char *argv0_stripped = argv0;
303 /* Relocatable programs are renamed to .bin by install-reloc. Or, more
304 generally, their suffix is changed from $exeext to .bin$exeext.
305 Remove the ".bin" here. */
307 size_t argv0_len = strlen (argv0);
308 const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
309 if (argv0_len > 4 + exeext_len)
310 if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
312 if (sizeof (EXEEXT) > sizeof (""))
314 /* Compare using an inlined copy of c_strncasecmp(), because
315 the filenames may have undergone a case conversion since
316 they were packaged. In other words, EXEEXT may be ".exe"
317 on one system and ".EXE" on another. */
318 static const char exeext[] = EXEEXT;
319 const char *s1 = argv0 + argv0_len - exeext_len;
320 const char *s2 = exeext;
321 for (; *s1 != '\0'; s1++, s2++)
323 unsigned char c1 = *s1;
324 unsigned char c2 = *s2;
325 if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
326 != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
330 /* Remove ".bin" before EXEEXT or its equivalent. */
332 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
337 memcpy (shorter, argv0, argv0_len - exeext_len - 4);
338 if (sizeof (EXEEXT) > sizeof (""))
339 memcpy (shorter + argv0_len - exeext_len - 4,
340 argv0 + argv0_len - exeext_len - 4,
342 shorter[argv0_len - 4] = '\0';
343 argv0_stripped = shorter;
350 set_program_name (argv0_stripped);
352 prepare_relocate (orig_installprefix, orig_installdir, argv0);
355 /* Return the full pathname of the current executable, based on the earlier
356 call to set_program_name_and_installdir. Return NULL if unknown. */
358 get_full_program_name (void)
360 return executable_fullname;