1 /* Provide relocatable programs.
2 Copyright (C) 2003-2009 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 3 of the License, or
8 (at your option) any later version.
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, see <http://www.gnu.org/licenses/>. */
32 /* Get declaration of _NSGetExecutablePath on MacOS X 10.2 or newer. */
33 #if HAVE_MACH_O_DYLD_H
34 # include <mach-o/dyld.h>
37 #if defined _WIN32 || defined __WIN32__
41 #if defined WIN32_NATIVE || defined __CYGWIN__
42 # define WIN32_LEAN_AND_MEAN
46 #include "relocatable.h"
49 # include "areadlink.h"
50 # define xreadlink areadlink
52 # include "xreadlink.h"
56 # define xmalloc malloc
57 # define xstrdup strdup
63 ISSLASH(C) tests whether C is a directory separator character.
64 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
66 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
67 /* Win32, Cygwin, OS/2, DOS */
68 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
69 # define HAS_DEVICE(P) \
70 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
72 # define IS_PATH_WITH_DIR(P) \
73 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
74 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
77 # define ISSLASH(C) ((C) == '/')
78 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
79 # define FILE_SYSTEM_PREFIX_LEN(P) 0
82 /* The results of open() in this file are not used with fchdir,
83 therefore save some unnecessary work in fchdir.c. */
87 #undef set_program_name
90 #if ENABLE_RELOCATABLE
93 /* File descriptor of the executable.
94 (Only used to verify that we find the correct executable.) */
95 static int executable_fd = -1;
98 /* Tests whether a given pathname may belong to the executable. */
100 maybe_executable (const char *filename)
102 /* Woe32 lacks the access() function, but Cygwin doesn't. */
103 #if !(defined WIN32_NATIVE && !defined __CYGWIN__)
104 if (access (filename, X_OK) < 0)
108 if (executable_fd >= 0)
110 /* If we already have an executable_fd, check that filename points to
113 struct stat statfile;
115 if (fstat (executable_fd, &statexe) >= 0)
117 if (stat (filename, &statfile) < 0)
119 if (!(statfile.st_dev
120 && statfile.st_dev == statexe.st_dev
121 && statfile.st_ino == statexe.st_ino))
131 /* Determine the full pathname of the current executable, freshly allocated.
132 Return NULL if unknown.
133 Guaranteed to work on Linux and Woe32. Likely to work on the other
134 Unixes (maybe except BeOS), under most conditions. */
136 find_executable (const char *argv0)
138 #if defined WIN32_NATIVE || defined __CYGWIN__
139 char location[MAX_PATH];
140 int length = GetModuleFileName (NULL, location, sizeof (location));
143 if (!IS_PATH_WITH_DIR (location))
144 /* Shouldn't happen. */
147 #if defined __CYGWIN__
148 /* cygwin-1.5.13 (2005-03-01) or newer would also allow a Linux-like
149 implementation: readlink of "/proc/self/exe". But using the
150 result of the Win32 system call is simpler and is consistent with the
151 code in relocatable.c. */
152 /* On Cygwin, we need to convert paths coming from Win32 system calls
153 to the Unix-like slashified notation. */
154 static char location_as_posix_path[2 * MAX_PATH];
155 /* There's no error return defined for cygwin_conv_to_posix_path.
156 See cygwin-api/func-cygwin-conv-to-posix-path.html.
157 Does it overflow the buffer of expected size MAX_PATH or does it
158 truncate the path? I don't know. Let's catch both. */
159 cygwin_conv_to_posix_path (location, location_as_posix_path);
160 location_as_posix_path[MAX_PATH - 1] = '\0';
161 if (strlen (location_as_posix_path) >= MAX_PATH - 1)
162 /* A sign of buffer overflow or path truncation. */
164 /* Call canonicalize_file_name, because Cygwin supports symbolic links. */
165 return canonicalize_file_name (location_as_posix_path);
167 return xstrdup (location);
170 #else /* Unix && !Cygwin */
172 /* The executable is accessible as /proc/<pid>/exe. In newer Linux
173 versions, also as /proc/self/exe. Linux >= 2.1 provides a symlink
174 to the true pathname; older Linux versions give only device and ino,
175 enclosed in brackets, which we cannot use here. */
179 link = xreadlink ("/proc/self/exe");
180 if (link != NULL && link[0] != '[')
182 if (executable_fd < 0)
183 executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
187 sprintf (buf, "/proc/%d/exe", getpid ());
188 link = xreadlink (buf);
189 if (link != NULL && link[0] != '[')
191 if (executable_fd < 0)
192 executable_fd = open (buf, O_RDONLY, 0);
196 #if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
197 /* On MacOS X 10.2 or newer, the function
198 int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
199 can be used to retrieve the executable's full path. */
201 unsigned int length = sizeof (location);
202 if (_NSGetExecutablePath (location, &length) == 0
203 && location[0] == '/')
204 return canonicalize_file_name (location);
206 /* Guess the executable's full path. We assume the executable has been
207 called via execlp() or execvp() with properly set up argv[0]. The
208 login(1) convention to add a '-' prefix to argv[0] is not supported. */
210 bool has_slash = false;
213 for (p = argv0; *p; p++)
222 /* exec searches paths without slashes in the directory list given
224 const char *path = getenv ("PATH");
231 for (p = path; *p; p = p_next)
241 p_next = (*q == '\0' ? q : q + 1);
243 /* We have a path item at p, of length p_len.
244 Now concatenate the path item and argv0. */
245 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
247 if (concat_name == NULL)
251 /* An empty PATH element designates the current directory. */
252 strcpy (concat_name, argv0);
255 memcpy (concat_name, p, p_len);
256 concat_name[p_len] = '/';
257 strcpy (concat_name + p_len + 1, argv0);
259 if (maybe_executable (concat_name))
260 return canonicalize_file_name (concat_name);
264 /* Not found in the PATH, assume the current directory. */
266 /* exec treats paths containing slashes as relative to the current
268 if (maybe_executable (argv0))
269 return canonicalize_file_name (argv0);
271 /* No way to find the executable. */
276 /* Full pathname of executable, or NULL. */
277 static char *executable_fullname;
280 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
285 /* Determine the full pathname of the current executable. */
286 executable_fullname = find_executable (argv0);
288 /* Determine the current installation prefix from it. */
289 curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
290 executable_fullname);
291 if (curr_prefix != NULL)
293 /* Now pass this prefix to all copies of the relocate.c source file. */
294 set_relocation_prefix (orig_installprefix, curr_prefix);
300 /* Set program_name, based on argv[0], and original installation prefix and
301 directory, for relocatability. */
303 set_program_name_and_installdir (const char *argv0,
304 const char *orig_installprefix,
305 const char *orig_installdir)
307 const char *argv0_stripped = argv0;
309 /* Relocatable programs are renamed to .bin by install-reloc. Or, more
310 generally, their suffix is changed from $exeext to .bin$exeext.
311 Remove the ".bin" here. */
313 size_t argv0_len = strlen (argv0);
314 const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
315 if (argv0_len > 4 + exeext_len)
316 if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
318 if (sizeof (EXEEXT) > sizeof (""))
320 /* Compare using an inlined copy of c_strncasecmp(), because
321 the filenames may have undergone a case conversion since
322 they were packaged. In other words, EXEEXT may be ".exe"
323 on one system and ".EXE" on another. */
324 static const char exeext[] = EXEEXT;
325 const char *s1 = argv0 + argv0_len - exeext_len;
326 const char *s2 = exeext;
327 for (; *s1 != '\0'; s1++, s2++)
329 unsigned char c1 = *s1;
330 unsigned char c2 = *s2;
331 if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
332 != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
336 /* Remove ".bin" before EXEEXT or its equivalent. */
338 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
343 memcpy (shorter, argv0, argv0_len - exeext_len - 4);
344 if (sizeof (EXEEXT) > sizeof (""))
345 memcpy (shorter + argv0_len - exeext_len - 4,
346 argv0 + argv0_len - exeext_len - 4,
348 shorter[argv0_len - 4] = '\0';
349 argv0_stripped = shorter;
356 set_program_name (argv0_stripped);
358 prepare_relocate (orig_installprefix, orig_installdir, argv0);
361 /* Return the full pathname of the current executable, based on the earlier
362 call to set_program_name_and_installdir. Return NULL if unknown. */
364 get_full_program_name (void)
366 return executable_fullname;