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. */
33 /* Get declaration of _NSGetExecutablePath on MacOS X 10.2 or newer. */
34 #if HAVE_MACH_O_DYLD_H
35 # include <mach-o/dyld.h>
38 #if defined _WIN32 || defined __WIN32__
42 #if defined WIN32_NATIVE || defined __CYGWIN__
43 # define WIN32_LEAN_AND_MEAN
47 #include "xreadlink.h"
48 #include "canonicalize.h"
49 #include "relocatable.h"
52 # define xmalloc malloc
53 # define xstrdup strdup
59 ISSLASH(C) tests whether C is a directory separator character.
60 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
62 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
63 /* Win32, Cygwin, OS/2, DOS */
64 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
65 # define HAS_DEVICE(P) \
66 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
68 # define IS_PATH_WITH_DIR(P) \
69 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
70 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
73 # define ISSLASH(C) ((C) == '/')
74 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
75 # define FILE_SYSTEM_PREFIX_LEN(P) 0
78 /* The results of open() in this file are not used with fchdir,
79 therefore save some unnecessary work in fchdir.c. */
83 #undef set_program_name
86 #if ENABLE_RELOCATABLE
89 /* File descriptor of the executable.
90 (Only used to verify that we find the correct executable.) */
91 static int executable_fd = -1;
94 /* Tests whether a given pathname may belong to the executable. */
96 maybe_executable (const char *filename)
98 /* Woe32 lacks the access() function, but Cygwin doesn't. */
99 #if !(defined WIN32_NATIVE && !defined __CYGWIN__)
100 if (access (filename, X_OK) < 0)
104 if (executable_fd >= 0)
106 /* If we already have an executable_fd, check that filename points to
109 struct stat statfile;
111 if (fstat (executable_fd, &statexe) >= 0)
113 if (stat (filename, &statfile) < 0)
115 if (!(statfile.st_dev
116 && statfile.st_dev == statexe.st_dev
117 && statfile.st_ino == statexe.st_ino))
127 /* Determine the full pathname of the current executable, freshly allocated.
128 Return NULL if unknown.
129 Guaranteed to work on Linux and Woe32. Likely to work on the other
130 Unixes (maybe except BeOS), under most conditions. */
132 find_executable (const char *argv0)
134 #if defined WIN32_NATIVE || defined __CYGWIN__
135 char location[MAX_PATH];
136 int length = GetModuleFileName (NULL, location, sizeof (location));
139 if (!IS_PATH_WITH_DIR (location))
140 /* Shouldn't happen. */
143 #if defined __CYGWIN__
144 /* cygwin-1.5.13 (2005-03-01) or newer would also allow a Linux-like
145 implementation: readlink of "/proc/self/exe". But using the
146 result of the Win32 system call is simpler and is consistent with the
147 code in relocatable.c. */
148 /* On Cygwin, we need to convert paths coming from Win32 system calls
149 to the Unix-like slashified notation. */
150 static char location_as_posix_path[2 * MAX_PATH];
151 /* There's no error return defined for cygwin_conv_to_posix_path.
152 See cygwin-api/func-cygwin-conv-to-posix-path.html.
153 Does it overflow the buffer of expected size MAX_PATH or does it
154 truncate the path? I don't know. Let's catch both. */
155 cygwin_conv_to_posix_path (location, location_as_posix_path);
156 location_as_posix_path[MAX_PATH - 1] = '\0';
157 if (strlen (location_as_posix_path) >= MAX_PATH - 1)
158 /* A sign of buffer overflow or path truncation. */
160 /* Call canonicalize_file_name, because Cygwin supports symbolic links. */
161 return canonicalize_file_name (location_as_posix_path);
163 return xstrdup (location);
166 #else /* Unix && !Cygwin */
168 /* The executable is accessible as /proc/<pid>/exe. In newer Linux
169 versions, also as /proc/self/exe. Linux >= 2.1 provides a symlink
170 to the true pathname; older Linux versions give only device and ino,
171 enclosed in brackets, which we cannot use here. */
175 link = xreadlink ("/proc/self/exe");
176 if (link != NULL && link[0] != '[')
178 if (executable_fd < 0)
179 executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
183 sprintf (buf, "/proc/%d/exe", getpid ());
184 link = xreadlink (buf);
185 if (link != NULL && link[0] != '[')
187 if (executable_fd < 0)
188 executable_fd = open (buf, O_RDONLY, 0);
192 #if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
193 /* On MacOS X 10.2 or newer, the function
194 int _NSGetExecutablePath (char *buf, unsigned long *bufsize);
195 can be used to retrieve the executable's full path. */
197 unsigned long length = sizeof (location);
198 if (_NSGetExecutablePath (location, &length) == 0
199 && location[0] == '/')
200 return canonicalize_file_name (location);
202 /* Guess the executable's full path. We assume the executable has been
203 called via execlp() or execvp() with properly set up argv[0]. The
204 login(1) convention to add a '-' prefix to argv[0] is not supported. */
206 bool has_slash = false;
209 for (p = argv0; *p; p++)
218 /* exec searches paths without slashes in the directory list given
220 const char *path = getenv ("PATH");
227 for (p = path; *p; p = p_next)
237 p_next = (*q == '\0' ? q : q + 1);
239 /* We have a path item at p, of length p_len.
240 Now concatenate the path item and argv0. */
241 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
243 if (concat_name == NULL)
247 /* An empty PATH element designates the current directory. */
248 strcpy (concat_name, argv0);
251 memcpy (concat_name, p, p_len);
252 concat_name[p_len] = '/';
253 strcpy (concat_name + p_len + 1, argv0);
255 if (maybe_executable (concat_name))
256 return canonicalize_file_name (concat_name);
260 /* Not found in the PATH, assume the current directory. */
262 /* exec treats paths containing slashes as relative to the current
264 if (maybe_executable (argv0))
265 return canonicalize_file_name (argv0);
267 /* No way to find the executable. */
272 /* Full pathname of executable, or NULL. */
273 static char *executable_fullname;
276 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
279 const char *curr_prefix;
281 /* Determine the full pathname of the current executable. */
282 executable_fullname = find_executable (argv0);
284 /* Determine the current installation prefix from it. */
285 curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
286 executable_fullname);
287 if (curr_prefix != NULL)
288 /* Now pass this prefix to all copies of the relocate.c source file. */
289 set_relocation_prefix (orig_installprefix, curr_prefix);
292 /* Set program_name, based on argv[0], and original installation prefix and
293 directory, for relocatability. */
295 set_program_name_and_installdir (const char *argv0,
296 const char *orig_installprefix,
297 const char *orig_installdir)
299 const char *argv0_stripped = argv0;
301 /* Relocatable programs are renamed to .bin by install-reloc. Or, more
302 generally, their suffix is changed from $exeext to .bin$exeext.
303 Remove the ".bin" here. */
305 size_t argv0_len = strlen (argv0);
306 const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
307 if (argv0_len > 4 + exeext_len)
308 if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
310 if (sizeof (EXEEXT) > sizeof (""))
312 /* Compare using an inlined copy of c_strncasecmp(), because
313 the filenames may have undergone a case conversion since
314 they were packaged. In other words, EXEEXT may be ".exe"
315 on one system and ".EXE" on another. */
316 static const char exeext[] = EXEEXT;
317 const char *s1 = argv0 + argv0_len - exeext_len;
318 const char *s2 = exeext;
319 for (; *s1 != '\0'; s1++, s2++)
321 unsigned char c1 = *s1;
322 unsigned char c2 = *s2;
323 if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
324 != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
328 /* Remove ".bin" before EXEEXT or its equivalent. */
330 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
335 memcpy (shorter, argv0, argv0_len - exeext_len - 4);
336 if (sizeof (EXEEXT) > sizeof (""))
337 memcpy (shorter + argv0_len - exeext_len - 4,
338 argv0 + argv0_len - exeext_len - 4,
340 shorter[argv0_len - 4] = '\0';
341 argv0_stripped = shorter;
348 set_program_name (argv0_stripped);
350 prepare_relocate (orig_installprefix, orig_installdir, argv0);
353 /* Return the full pathname of the current executable, based on the earlier
354 call to set_program_name_and_installdir. Return NULL if unknown. */
356 get_full_program_name (void)
358 return executable_fullname;