Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / progreloc.c
1 /* Provide relocatable programs.
2    Copyright (C) 2003-2010 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 "relocatable.h"
47
48 #ifdef NO_XMALLOC
49 # include "areadlink.h"
50 # define xreadlink areadlink
51 #else
52 # include "xreadlink.h"
53 #endif
54
55 #ifdef NO_XMALLOC
56 # define xmalloc malloc
57 # define xstrdup strdup
58 #else
59 # include "xalloc.h"
60 #endif
61
62 /* Declare canonicalize_file_name.
63    The <stdlib.h> included above may be the system's one, not the gnulib
64    one.  */
65 extern char * canonicalize_file_name (const char *name);
66
67 /* Pathname support.
68    ISSLASH(C)           tests whether C is a directory separator character.
69    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
70  */
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')) \
76      && (P)[1] == ':')
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)
80 #else
81   /* Unix */
82 # define ISSLASH(C) ((C) == '/')
83 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
84 # define FILE_SYSTEM_PREFIX_LEN(P) 0
85 #endif
86
87 /* The results of open() in this file are not used with fchdir,
88    therefore save some unnecessary work in fchdir.c.  */
89 #undef open
90 #undef close
91
92 #undef set_program_name
93
94
95 #if ENABLE_RELOCATABLE
96
97 #ifdef __linux__
98 /* File descriptor of the executable.
99    (Only used to verify that we find the correct executable.)  */
100 static int executable_fd = -1;
101 #endif
102
103 /* Tests whether a given pathname may belong to the executable.  */
104 static bool
105 maybe_executable (const char *filename)
106 {
107   /* Woe32 lacks the access() function, but Cygwin doesn't.  */
108 #if !(defined WIN32_NATIVE && !defined __CYGWIN__)
109   if (access (filename, X_OK) < 0)
110     return false;
111
112 #ifdef __linux__
113   if (executable_fd >= 0)
114     {
115       /* If we already have an executable_fd, check that filename points to
116          the same inode.  */
117       struct stat statexe;
118       struct stat statfile;
119
120       if (fstat (executable_fd, &statexe) >= 0)
121         {
122           if (stat (filename, &statfile) < 0)
123             return false;
124           if (!(statfile.st_dev
125                 && statfile.st_dev == statexe.st_dev
126                 && statfile.st_ino == statexe.st_ino))
127             return false;
128         }
129     }
130 #endif
131 #endif
132
133   return true;
134 }
135
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.  */
140 static char *
141 find_executable (const char *argv0)
142 {
143 #if defined WIN32_NATIVE || defined __CYGWIN__
144   char location[MAX_PATH];
145   int length = GetModuleFileName (NULL, location, sizeof (location));
146   if (length < 0)
147     return NULL;
148   if (!IS_PATH_WITH_DIR (location))
149     /* Shouldn't happen.  */
150     return NULL;
151   {
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.  */
168       return NULL;
169     /* Call canonicalize_file_name, because Cygwin supports symbolic links.  */
170     return canonicalize_file_name (location_as_posix_path);
171 #else
172     return xstrdup (location);
173 #endif
174   }
175 #else /* Unix && !Cygwin */
176 #ifdef __linux__
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.  */
181   {
182     char *link;
183
184     link = xreadlink ("/proc/self/exe");
185     if (link != NULL && link[0] != '[')
186       return link;
187     if (executable_fd < 0)
188       executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
189
190     {
191       char buf[6+10+5];
192       sprintf (buf, "/proc/%d/exe", getpid ());
193       link = xreadlink (buf);
194       if (link != NULL && link[0] != '[')
195         return link;
196       if (executable_fd < 0)
197         executable_fd = open (buf, O_RDONLY, 0);
198     }
199   }
200 #endif
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.  */
205   char location[4096];
206   unsigned int length = sizeof (location);
207   if (_NSGetExecutablePath (location, &length) == 0
208       && location[0] == '/')
209     return canonicalize_file_name (location);
210 #endif
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.  */
214   {
215     bool has_slash = false;
216     {
217       const char *p;
218       for (p = argv0; *p; p++)
219         if (*p == '/')
220           {
221             has_slash = true;
222             break;
223           }
224     }
225     if (!has_slash)
226       {
227         /* exec searches paths without slashes in the directory list given
228            by $PATH.  */
229         const char *path = getenv ("PATH");
230
231         if (path != NULL)
232           {
233             const char *p;
234             const char *p_next;
235
236             for (p = path; *p; p = p_next)
237               {
238                 const char *q;
239                 size_t p_len;
240                 char *concat_name;
241
242                 for (q = p; *q; q++)
243                   if (*q == ':')
244                     break;
245                 p_len = q - p;
246                 p_next = (*q == '\0' ? q : q + 1);
247
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);
251 #ifdef NO_XMALLOC
252                 if (concat_name == NULL)
253                   return NULL;
254 #endif
255                 if (p_len == 0)
256                   /* An empty PATH element designates the current directory.  */
257                   strcpy (concat_name, argv0);
258                 else
259                   {
260                     memcpy (concat_name, p, p_len);
261                     concat_name[p_len] = '/';
262                     strcpy (concat_name + p_len + 1, argv0);
263                   }
264                 if (maybe_executable (concat_name))
265                   return canonicalize_file_name (concat_name);
266                 free (concat_name);
267               }
268           }
269         /* Not found in the PATH, assume the current directory.  */
270       }
271     /* exec treats paths containing slashes as relative to the current
272        directory.  */
273     if (maybe_executable (argv0))
274       return canonicalize_file_name (argv0);
275   }
276   /* No way to find the executable.  */
277   return NULL;
278 #endif
279 }
280
281 /* Full pathname of executable, or NULL.  */
282 static char *executable_fullname;
283
284 static void
285 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
286                   const char *argv0)
287 {
288   char *curr_prefix;
289
290   /* Determine the full pathname of the current executable.  */
291   executable_fullname = find_executable (argv0);
292
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)
297     {
298       /* Now pass this prefix to all copies of the relocate.c source file.  */
299       set_relocation_prefix (orig_installprefix, curr_prefix);
300
301       free (curr_prefix);
302     }
303 }
304
305 /* Set program_name, based on argv[0], and original installation prefix and
306    directory, for relocatability.  */
307 void
308 set_program_name_and_installdir (const char *argv0,
309                                  const char *orig_installprefix,
310                                  const char *orig_installdir)
311 {
312   const char *argv0_stripped = argv0;
313
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.  */
317   {
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)
322         {
323           if (sizeof (EXEEXT) > sizeof (""))
324             {
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++)
333                 {
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))
338                     goto done_stripping;
339                 }
340             }
341           /* Remove ".bin" before EXEEXT or its equivalent.  */
342           {
343             char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
344 #ifdef NO_XMALLOC
345             if (shorter != NULL)
346 #endif
347               {
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,
352                           exeext_len);
353                 shorter[argv0_len - 4] = '\0';
354                 argv0_stripped = shorter;
355               }
356           }
357          done_stripping: ;
358       }
359   }
360
361   set_program_name (argv0_stripped);
362
363   prepare_relocate (orig_installprefix, orig_installdir, argv0);
364 }
365
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.  */
368 char *
369 get_full_program_name (void)
370 {
371   return executable_fullname;
372 }
373
374 #endif