1 /* Provide relocatable programs.
2 Copyright (C) 2003-2010 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
62 /* Declare canonicalize_file_name.
63 The <stdlib.h> included above may be the system's one, not the gnulib
65 extern char * canonicalize_file_name (const char *name);
68 ISSLASH(C) tests whether C is a directory separator character.
69 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
71 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
72 /* Win32, Cygwin, OS/2, DOS */
73 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
74 # define HAS_DEVICE(P) \
75 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
77 # define IS_PATH_WITH_DIR(P) \
78 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
79 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
82 # define ISSLASH(C) ((C) == '/')
83 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
84 # define FILE_SYSTEM_PREFIX_LEN(P) 0
87 /* The results of open() in this file are not used with fchdir,
88 therefore save some unnecessary work in fchdir.c. */
92 #undef set_program_name
95 #if ENABLE_RELOCATABLE
98 /* File descriptor of the executable.
99 (Only used to verify that we find the correct executable.) */
100 static int executable_fd = -1;
103 /* Tests whether a given pathname may belong to the executable. */
105 maybe_executable (const char *filename)
107 /* Woe32 lacks the access() function, but Cygwin doesn't. */
108 #if !(defined WIN32_NATIVE && !defined __CYGWIN__)
109 if (access (filename, X_OK) < 0)
113 if (executable_fd >= 0)
115 /* If we already have an executable_fd, check that filename points to
118 struct stat statfile;
120 if (fstat (executable_fd, &statexe) >= 0)
122 if (stat (filename, &statfile) < 0)
124 if (!(statfile.st_dev
125 && statfile.st_dev == statexe.st_dev
126 && statfile.st_ino == statexe.st_ino))
136 /* Determine the full pathname of the current executable, freshly allocated.
137 Return NULL if unknown.
138 Guaranteed to work on Linux and Woe32. Likely to work on the other
139 Unixes (maybe except BeOS), under most conditions. */
141 find_executable (const char *argv0)
143 #if defined WIN32_NATIVE || defined __CYGWIN__
144 char location[MAX_PATH];
145 int length = GetModuleFileName (NULL, location, sizeof (location));
148 if (!IS_PATH_WITH_DIR (location))
149 /* Shouldn't happen. */
152 #if defined __CYGWIN__
153 /* cygwin-1.5.13 (2005-03-01) or newer would also allow a Linux-like
154 implementation: readlink of "/proc/self/exe". But using the
155 result of the Win32 system call is simpler and is consistent with the
156 code in relocatable.c. */
157 /* On Cygwin, we need to convert paths coming from Win32 system calls
158 to the Unix-like slashified notation. */
159 static char location_as_posix_path[2 * MAX_PATH];
160 /* There's no error return defined for cygwin_conv_to_posix_path.
161 See cygwin-api/func-cygwin-conv-to-posix-path.html.
162 Does it overflow the buffer of expected size MAX_PATH or does it
163 truncate the path? I don't know. Let's catch both. */
164 cygwin_conv_to_posix_path (location, location_as_posix_path);
165 location_as_posix_path[MAX_PATH - 1] = '\0';
166 if (strlen (location_as_posix_path) >= MAX_PATH - 1)
167 /* A sign of buffer overflow or path truncation. */
169 /* Call canonicalize_file_name, because Cygwin supports symbolic links. */
170 return canonicalize_file_name (location_as_posix_path);
172 return xstrdup (location);
175 #else /* Unix && !Cygwin */
177 /* The executable is accessible as /proc/<pid>/exe. In newer Linux
178 versions, also as /proc/self/exe. Linux >= 2.1 provides a symlink
179 to the true pathname; older Linux versions give only device and ino,
180 enclosed in brackets, which we cannot use here. */
184 link = xreadlink ("/proc/self/exe");
185 if (link != NULL && link[0] != '[')
187 if (executable_fd < 0)
188 executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
192 sprintf (buf, "/proc/%d/exe", getpid ());
193 link = xreadlink (buf);
194 if (link != NULL && link[0] != '[')
196 if (executable_fd < 0)
197 executable_fd = open (buf, O_RDONLY, 0);
201 #if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
202 /* On MacOS X 10.2 or newer, the function
203 int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
204 can be used to retrieve the executable's full path. */
206 unsigned int length = sizeof (location);
207 if (_NSGetExecutablePath (location, &length) == 0
208 && location[0] == '/')
209 return canonicalize_file_name (location);
211 /* Guess the executable's full path. We assume the executable has been
212 called via execlp() or execvp() with properly set up argv[0]. The
213 login(1) convention to add a '-' prefix to argv[0] is not supported. */
215 bool has_slash = false;
218 for (p = argv0; *p; p++)
227 /* exec searches paths without slashes in the directory list given
229 const char *path = getenv ("PATH");
236 for (p = path; *p; p = p_next)
246 p_next = (*q == '\0' ? q : q + 1);
248 /* We have a path item at p, of length p_len.
249 Now concatenate the path item and argv0. */
250 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
252 if (concat_name == NULL)
256 /* An empty PATH element designates the current directory. */
257 strcpy (concat_name, argv0);
260 memcpy (concat_name, p, p_len);
261 concat_name[p_len] = '/';
262 strcpy (concat_name + p_len + 1, argv0);
264 if (maybe_executable (concat_name))
265 return canonicalize_file_name (concat_name);
269 /* Not found in the PATH, assume the current directory. */
271 /* exec treats paths containing slashes as relative to the current
273 if (maybe_executable (argv0))
274 return canonicalize_file_name (argv0);
276 /* No way to find the executable. */
281 /* Full pathname of executable, or NULL. */
282 static char *executable_fullname;
285 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
290 /* Determine the full pathname of the current executable. */
291 executable_fullname = find_executable (argv0);
293 /* Determine the current installation prefix from it. */
294 curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
295 executable_fullname);
296 if (curr_prefix != NULL)
298 /* Now pass this prefix to all copies of the relocate.c source file. */
299 set_relocation_prefix (orig_installprefix, curr_prefix);
305 /* Set program_name, based on argv[0], and original installation prefix and
306 directory, for relocatability. */
308 set_program_name_and_installdir (const char *argv0,
309 const char *orig_installprefix,
310 const char *orig_installdir)
312 const char *argv0_stripped = argv0;
314 /* Relocatable programs are renamed to .bin by install-reloc. Or, more
315 generally, their suffix is changed from $exeext to .bin$exeext.
316 Remove the ".bin" here. */
318 size_t argv0_len = strlen (argv0);
319 const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
320 if (argv0_len > 4 + exeext_len)
321 if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
323 if (sizeof (EXEEXT) > sizeof (""))
325 /* Compare using an inlined copy of c_strncasecmp(), because
326 the filenames may have undergone a case conversion since
327 they were packaged. In other words, EXEEXT may be ".exe"
328 on one system and ".EXE" on another. */
329 static const char exeext[] = EXEEXT;
330 const char *s1 = argv0 + argv0_len - exeext_len;
331 const char *s2 = exeext;
332 for (; *s1 != '\0'; s1++, s2++)
334 unsigned char c1 = *s1;
335 unsigned char c2 = *s2;
336 if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
337 != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
341 /* Remove ".bin" before EXEEXT or its equivalent. */
343 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
348 memcpy (shorter, argv0, argv0_len - exeext_len - 4);
349 if (sizeof (EXEEXT) > sizeof (""))
350 memcpy (shorter + argv0_len - exeext_len - 4,
351 argv0 + argv0_len - exeext_len - 4,
353 shorter[argv0_len - 4] = '\0';
354 argv0_stripped = shorter;
361 set_program_name (argv0_stripped);
363 prepare_relocate (orig_installprefix, orig_installdir, argv0);
366 /* Return the full pathname of the current executable, based on the earlier
367 call to set_program_name_and_installdir. Return NULL if unknown. */
369 get_full_program_name (void)
371 return executable_fullname;