• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-runtime/gnulib-lib/
1/* Provide relocatable programs.
2   Copyright (C) 2003-2006 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2003.
4
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.
9
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.
14
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/>.  */
17
18
19#include <config.h>
20
21/* Specification.  */
22#include "progname.h"
23
24#include <stdbool.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <sys/stat.h>
31
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>
35#endif
36
37#if defined _WIN32 || defined __WIN32__
38# define WIN32_NATIVE
39#endif
40
41#if defined WIN32_NATIVE || defined __CYGWIN__
42# define WIN32_LEAN_AND_MEAN
43# include <windows.h>
44#endif
45
46#include "xreadlink.h"
47#include "canonicalize.h"
48#include "relocatable.h"
49
50#ifdef NO_XMALLOC
51# define xmalloc malloc
52# define xstrdup strdup
53#else
54# include "xalloc.h"
55#endif
56
57/* Pathname support.
58   ISSLASH(C)           tests whether C is a directory separator character.
59   IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
60 */
61#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
62  /* Win32, Cygwin, OS/2, DOS */
63# define ISSLASH(C) ((C) == '/' || (C) == '\\')
64# define HAS_DEVICE(P) \
65    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
66     && (P)[1] == ':')
67# define IS_PATH_WITH_DIR(P) \
68    (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
69# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
70#else
71  /* Unix */
72# define ISSLASH(C) ((C) == '/')
73# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
74# define FILE_SYSTEM_PREFIX_LEN(P) 0
75#endif
76
77/* The results of open() in this file are not used with fchdir,
78   therefore save some unnecessary work in fchdir.c.  */
79#undef open
80#undef close
81
82#undef set_program_name
83
84
85#if ENABLE_RELOCATABLE
86
87#ifdef __linux__
88/* File descriptor of the executable.
89   (Only used to verify that we find the correct executable.)  */
90static int executable_fd = -1;
91#endif
92
93/* Tests whether a given pathname may belong to the executable.  */
94static bool
95maybe_executable (const char *filename)
96{
97  /* Woe32 lacks the access() function, but Cygwin doesn't.  */
98#if !(defined WIN32_NATIVE && !defined __CYGWIN__)
99  if (access (filename, X_OK) < 0)
100    return false;
101
102#ifdef __linux__
103  if (executable_fd >= 0)
104    {
105      /* If we already have an executable_fd, check that filename points to
106	 the same inode.  */
107      struct stat statexe;
108      struct stat statfile;
109
110      if (fstat (executable_fd, &statexe) >= 0)
111	{
112	  if (stat (filename, &statfile) < 0)
113	    return false;
114	  if (!(statfile.st_dev
115		&& statfile.st_dev == statexe.st_dev
116		&& statfile.st_ino == statexe.st_ino))
117	    return false;
118	}
119    }
120#endif
121#endif
122
123  return true;
124}
125
126/* Determine the full pathname of the current executable, freshly allocated.
127   Return NULL if unknown.
128   Guaranteed to work on Linux and Woe32.  Likely to work on the other
129   Unixes (maybe except BeOS), under most conditions.  */
130static char *
131find_executable (const char *argv0)
132{
133#if defined WIN32_NATIVE || defined __CYGWIN__
134  char location[MAX_PATH];
135  int length = GetModuleFileName (NULL, location, sizeof (location));
136  if (length < 0)
137    return NULL;
138  if (!IS_PATH_WITH_DIR (location))
139    /* Shouldn't happen.  */
140    return NULL;
141  {
142#if defined __CYGWIN__
143    /* cygwin-1.5.13 (2005-03-01) or newer would also allow a Linux-like
144       implementation: readlink of "/proc/self/exe".  But using the
145       result of the Win32 system call is simpler and is consistent with the
146       code in relocatable.c.  */
147    /* On Cygwin, we need to convert paths coming from Win32 system calls
148       to the Unix-like slashified notation.  */
149    static char location_as_posix_path[2 * MAX_PATH];
150    /* There's no error return defined for cygwin_conv_to_posix_path.
151       See cygwin-api/func-cygwin-conv-to-posix-path.html.
152       Does it overflow the buffer of expected size MAX_PATH or does it
153       truncate the path?  I don't know.  Let's catch both.  */
154    cygwin_conv_to_posix_path (location, location_as_posix_path);
155    location_as_posix_path[MAX_PATH - 1] = '\0';
156    if (strlen (location_as_posix_path) >= MAX_PATH - 1)
157      /* A sign of buffer overflow or path truncation.  */
158      return NULL;
159    /* Call canonicalize_file_name, because Cygwin supports symbolic links.  */
160    return canonicalize_file_name (location_as_posix_path);
161#else
162    return xstrdup (location);
163#endif
164  }
165#else /* Unix && !Cygwin */
166#ifdef __linux__
167  /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
168     versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
169     to the true pathname; older Linux versions give only device and ino,
170     enclosed in brackets, which we cannot use here.  */
171  {
172    char *link;
173
174    link = xreadlink ("/proc/self/exe");
175    if (link != NULL && link[0] != '[')
176      return link;
177    if (executable_fd < 0)
178      executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
179
180    {
181      char buf[6+10+5];
182      sprintf (buf, "/proc/%d/exe", getpid ());
183      link = xreadlink (buf);
184      if (link != NULL && link[0] != '[')
185	return link;
186      if (executable_fd < 0)
187	executable_fd = open (buf, O_RDONLY, 0);
188    }
189  }
190#endif
191#if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
192  /* On MacOS X 10.2 or newer, the function
193       int _NSGetExecutablePath (char *buf, unsigned long *bufsize);
194     can be used to retrieve the executable's full path.  */
195  char location[4096];
196  unsigned long length = sizeof (location);
197  if (_NSGetExecutablePath (location, &length) == 0
198      && location[0] == '/')
199    return canonicalize_file_name (location);
200#endif
201  /* Guess the executable's full path.  We assume the executable has been
202     called via execlp() or execvp() with properly set up argv[0].  The
203     login(1) convention to add a '-' prefix to argv[0] is not supported.  */
204  {
205    bool has_slash = false;
206    {
207      const char *p;
208      for (p = argv0; *p; p++)
209	if (*p == '/')
210	  {
211	    has_slash = true;
212	    break;
213	  }
214    }
215    if (!has_slash)
216      {
217	/* exec searches paths without slashes in the directory list given
218	   by $PATH.  */
219	const char *path = getenv ("PATH");
220
221	if (path != NULL)
222	  {
223	    const char *p;
224	    const char *p_next;
225
226	    for (p = path; *p; p = p_next)
227	      {
228		const char *q;
229		size_t p_len;
230		char *concat_name;
231
232		for (q = p; *q; q++)
233		  if (*q == ':')
234		    break;
235		p_len = q - p;
236		p_next = (*q == '\0' ? q : q + 1);
237
238		/* We have a path item at p, of length p_len.
239		   Now concatenate the path item and argv0.  */
240		concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
241#ifdef NO_XMALLOC
242		if (concat_name == NULL)
243		  return NULL;
244#endif
245		if (p_len == 0)
246		  /* An empty PATH element designates the current directory.  */
247		  strcpy (concat_name, argv0);
248		else
249		  {
250		    memcpy (concat_name, p, p_len);
251		    concat_name[p_len] = '/';
252		    strcpy (concat_name + p_len + 1, argv0);
253		  }
254		if (maybe_executable (concat_name))
255		  return canonicalize_file_name (concat_name);
256		free (concat_name);
257	      }
258	  }
259	/* Not found in the PATH, assume the current directory.  */
260      }
261    /* exec treats paths containing slashes as relative to the current
262       directory.  */
263    if (maybe_executable (argv0))
264      return canonicalize_file_name (argv0);
265  }
266  /* No way to find the executable.  */
267  return NULL;
268#endif
269}
270
271/* Full pathname of executable, or NULL.  */
272static char *executable_fullname;
273
274static void
275prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
276		  const char *argv0)
277{
278  const char *curr_prefix;
279
280  /* Determine the full pathname of the current executable.  */
281  executable_fullname = find_executable (argv0);
282
283  /* Determine the current installation prefix from it.  */
284  curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
285				     executable_fullname);
286  if (curr_prefix != NULL)
287    /* Now pass this prefix to all copies of the relocate.c source file.  */
288    set_relocation_prefix (orig_installprefix, curr_prefix);
289}
290
291/* Set program_name, based on argv[0], and original installation prefix and
292   directory, for relocatability.  */
293void
294set_program_name_and_installdir (const char *argv0,
295				 const char *orig_installprefix,
296				 const char *orig_installdir)
297{
298  const char *argv0_stripped = argv0;
299
300  /* Relocatable programs are renamed to .bin by install-reloc.  Or, more
301     generally, their suffix is changed from $exeext to .bin$exeext.
302     Remove the ".bin" here.  */
303  {
304    size_t argv0_len = strlen (argv0);
305    const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
306    if (argv0_len > 4 + exeext_len)
307      if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
308	{
309	  if (sizeof (EXEEXT) > sizeof (""))
310	    {
311	      /* Compare using an inlined copy of c_strncasecmp(), because
312		 the filenames may have undergone a case conversion since
313		 they were packaged.  In other words, EXEEXT may be ".exe"
314		 on one system and ".EXE" on another.  */
315	      static const char exeext[] = EXEEXT;
316	      const char *s1 = argv0 + argv0_len - exeext_len;
317	      const char *s2 = exeext;
318	      for (; *s1 != '\0'; s1++, s2++)
319		{
320		  unsigned char c1 = *s1;
321		  unsigned char c2 = *s2;
322		  if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
323		      != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
324		    goto done_stripping;
325		}
326	    }
327	  /* Remove ".bin" before EXEEXT or its equivalent.  */
328	  {
329	    char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
330#ifdef NO_XMALLOC
331	    if (shorter != NULL)
332#endif
333	      {
334		memcpy (shorter, argv0, argv0_len - exeext_len - 4);
335		if (sizeof (EXEEXT) > sizeof (""))
336		  memcpy (shorter + argv0_len - exeext_len - 4,
337			  argv0 + argv0_len - exeext_len - 4,
338			  exeext_len);
339		shorter[argv0_len - 4] = '\0';
340		argv0_stripped = shorter;
341	      }
342	  }
343	 done_stripping: ;
344      }
345  }
346
347  set_program_name (argv0_stripped);
348
349  prepare_relocate (orig_installprefix, orig_installdir, argv0);
350}
351
352/* Return the full pathname of the current executable, based on the earlier
353   call to set_program_name_and_installdir.  Return NULL if unknown.  */
354char *
355get_full_program_name (void)
356{
357  return executable_fullname;
358}
359
360#endif
361