No specific user configuration
[platform/upstream/bash.git] / lib / intl / relocatable.c
1 /* relocatable.c - Provide relocatable packages. */
2
3 /* Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc.
4    Written by Bruno Haible <bruno@clisp.org>, 2003.
5
6    This file is part of GNU Bash.
7
8    Bash is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12
13    Bash is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /* Tell glibc's <stdio.h> to provide a prototype for getline().
23    This must come before <config.h> because <config.h> may include
24    <features.h>, and once <features.h> has been included, it's too late.  */
25 #ifndef _GNU_SOURCE
26 # define _GNU_SOURCE    1
27 #endif
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 /* Specification.  */
34 #include "relocatable.h"
35
36 #if ENABLE_RELOCATABLE
37
38 #include <stddef.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 #ifdef NO_XMALLOC
44 # define xmalloc malloc
45 #else
46 # include "xmalloc.h"
47 #endif
48
49 #if DEPENDS_ON_LIBCHARSET
50 # include <libcharset.h>
51 #endif
52 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
53 # include <iconv.h>
54 #endif
55 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
56 # include <libintl.h>
57 #endif
58
59 /* Faked cheap 'bool'.  */
60 #undef bool
61 #undef false
62 #undef true
63 #define bool int
64 #define false 0
65 #define true 1
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 __EMX__ || defined __DJGPP__
72   /* Win32, 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 FILESYSTEM_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 FILESYSTEM_PREFIX_LEN(P) 0
85 #endif
86
87 /* Original installation prefix.  */
88 static char *orig_prefix;
89 static size_t orig_prefix_len;
90 /* Current installation prefix.  */
91 static char *curr_prefix;
92 static size_t curr_prefix_len;
93 /* These prefixes do not end in a slash.  Anything that will be concatenated
94    to them must start with a slash.  */
95
96 /* Sets the original and the current installation prefix of this module.
97    Relocation simply replaces a pathname starting with the original prefix
98    by the corresponding pathname with the current prefix instead.  Both
99    prefixes should be directory names without trailing slash (i.e. use ""
100    instead of "/").  */
101 static void
102 set_this_relocation_prefix (const char *orig_prefix_arg,
103                             const char *curr_prefix_arg)
104 {
105   if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
106       /* Optimization: if orig_prefix and curr_prefix are equal, the
107          relocation is a nop.  */
108       && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
109     {
110       /* Duplicate the argument strings.  */
111       char *memory;
112
113       orig_prefix_len = strlen (orig_prefix_arg);
114       curr_prefix_len = strlen (curr_prefix_arg);
115       memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
116 #ifdef NO_XMALLOC
117       if (memory != NULL)
118 #endif
119         {
120           memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
121           orig_prefix = memory;
122           memory += orig_prefix_len + 1;
123           memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
124           curr_prefix = memory;
125           return;
126         }
127     }
128   orig_prefix = NULL;
129   curr_prefix = NULL;
130   /* Don't worry about wasted memory here - this function is usually only
131      called once.  */
132 }
133
134 /* Sets the original and the current installation prefix of the package.
135    Relocation simply replaces a pathname starting with the original prefix
136    by the corresponding pathname with the current prefix instead.  Both
137    prefixes should be directory names without trailing slash (i.e. use ""
138    instead of "/").  */
139 void
140 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
141 {
142   set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
143
144   /* Now notify all dependent libraries.  */
145 #if DEPENDS_ON_LIBCHARSET
146   libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
147 #endif
148 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
149   libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
150 #endif
151 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
152   libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
153 #endif
154 }
155
156 /* Convenience function:
157    Computes the current installation prefix, based on the original
158    installation prefix, the original installation directory of a particular
159    file, and the current pathname of this file.  Returns NULL upon failure.  */
160 #ifdef IN_LIBRARY
161 #define compute_curr_prefix local_compute_curr_prefix
162 static
163 #endif
164 const char *
165 compute_curr_prefix (const char *orig_installprefix,
166                      const char *orig_installdir,
167                      const char *curr_pathname)
168 {
169   const char *curr_installdir;
170   const char *rel_installdir;
171
172   if (curr_pathname == NULL)
173     return NULL;
174
175   /* Determine the relative installation directory, relative to the prefix.
176      This is simply the difference between orig_installprefix and
177      orig_installdir.  */
178   if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
179       != 0)
180     /* Shouldn't happen - nothing should be installed outside $(prefix).  */
181     return NULL;
182   rel_installdir = orig_installdir + strlen (orig_installprefix);
183
184   /* Determine the current installation directory.  */
185   {
186     const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
187     const char *p = curr_pathname + strlen (curr_pathname);
188     char *q;
189
190     while (p > p_base)
191       {
192         p--;
193         if (ISSLASH (*p))
194           break;
195       }
196
197     q = (char *) xmalloc (p - curr_pathname + 1);
198 #ifdef NO_XMALLOC
199     if (q == NULL)
200       return NULL;
201 #endif
202     memcpy (q, curr_pathname, p - curr_pathname);
203     q[p - curr_pathname] = '\0';
204     curr_installdir = q;
205   }
206
207   /* Compute the current installation prefix by removing the trailing
208      rel_installdir from it.  */
209   {
210     const char *rp = rel_installdir + strlen (rel_installdir);
211     const char *cp = curr_installdir + strlen (curr_installdir);
212     const char *cp_base =
213       curr_installdir + FILESYSTEM_PREFIX_LEN (curr_installdir);
214
215     while (rp > rel_installdir && cp > cp_base)
216       {
217         bool same = false;
218         const char *rpi = rp;
219         const char *cpi = cp;
220
221         while (rpi > rel_installdir && cpi > cp_base)
222           {
223             rpi--;
224             cpi--;
225             if (ISSLASH (*rpi) || ISSLASH (*cpi))
226               {
227                 if (ISSLASH (*rpi) && ISSLASH (*cpi))
228                   same = true;
229                 break;
230               }
231 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
232             /* Win32, OS/2, DOS - case insignificant filesystem */
233             if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
234                 != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
235               break;
236 #else
237             if (*rpi != *cpi)
238               break;
239 #endif
240           }
241         if (!same)
242           break;
243         /* The last pathname component was the same.  opi and cpi now point
244            to the slash before it.  */
245         rp = rpi;
246         cp = cpi;
247       }
248
249     if (rp > rel_installdir)
250       /* Unexpected: The curr_installdir does not end with rel_installdir.  */
251       return NULL;
252
253     {
254       size_t curr_prefix_len = cp - curr_installdir;
255       char *curr_prefix;
256
257       curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
258 #ifdef NO_XMALLOC
259       if (curr_prefix == NULL)
260         return NULL;
261 #endif
262       memcpy (curr_prefix, curr_installdir, curr_prefix_len);
263       curr_prefix[curr_prefix_len] = '\0';
264
265       return curr_prefix;
266     }
267   }
268 }
269
270 #if defined PIC && defined INSTALLDIR
271
272 /* Full pathname of shared library, or NULL.  */
273 static char *shared_library_fullname;
274
275 #if defined _WIN32 || defined __WIN32__
276
277 /* Determine the full pathname of the shared library when it is loaded.  */
278
279 BOOL WINAPI
280 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
281 {
282   (void) reserved;
283
284   if (event == DLL_PROCESS_ATTACH)
285     {
286       /* The DLL is being loaded into an application's address range.  */
287       static char location[MAX_PATH];
288
289       if (!GetModuleFileName (module_handle, location, sizeof (location)))
290         /* Shouldn't happen.  */
291         return FALSE;
292
293       if (!IS_PATH_WITH_DIR (location))
294         /* Shouldn't happen.  */
295         return FALSE;
296
297       shared_library_fullname = strdup (location);
298     }
299
300   return TRUE;
301 }
302
303 #else /* Unix */
304
305 static void
306 find_shared_library_fullname ()
307 {
308 #ifdef __linux__
309   FILE *fp;
310
311   /* Open the current process' maps file.  It describes one VMA per line.  */
312   fp = fopen ("/proc/self/maps", "r");
313   if (fp)
314     {
315       unsigned long address = (unsigned long) &find_shared_library_fullname;
316       for (;;)
317         {
318           unsigned long start, end;
319           int c;
320
321           if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
322             break;
323           if (address >= start && address <= end - 1)
324             {
325               /* Found it.  Now see if this line contains a filename.  */
326               while (c = getc (fp), c != EOF && c != '\n' && c != '/')
327                 continue;
328               if (c == '/')
329                 {
330                   size_t size;
331                   int len;
332
333                   ungetc (c, fp);
334                   shared_library_fullname = NULL; size = 0;
335                   len = getline (&shared_library_fullname, &size, fp);
336                   if (len >= 0)
337                     {
338                       /* Success: filled shared_library_fullname.  */
339                       if (len > 0 && shared_library_fullname[len - 1] == '\n')
340                         shared_library_fullname[len - 1] = '\0';
341                     }
342                 }
343               break;
344             }
345           while (c = getc (fp), c != EOF && c != '\n')
346             continue;
347         }
348       fclose (fp);
349     }
350 #endif
351 }
352
353 #endif /* WIN32 / Unix */
354
355 /* Return the full pathname of the current shared library.
356    Return NULL if unknown.
357    Guaranteed to work only on Linux and Woe32.  */
358 static char *
359 get_shared_library_fullname ()
360 {
361 #if !(defined _WIN32 || defined __WIN32__)
362   static bool tried_find_shared_library_fullname;
363   if (!tried_find_shared_library_fullname)
364     {
365       find_shared_library_fullname ();
366       tried_find_shared_library_fullname = true;
367     }
368 #endif
369   return shared_library_fullname;
370 }
371
372 #endif /* PIC */
373
374 /* Returns the pathname, relocated according to the current installation
375    directory.  */
376 const char *
377 relocate (const char *pathname)
378 {
379 #if defined PIC && defined INSTALLDIR
380   static int initialized;
381
382   /* Initialization code for a shared library.  */
383   if (!initialized)
384     {
385       /* At this point, orig_prefix and curr_prefix likely have already been
386          set through the main program's set_program_name_and_installdir
387          function.  This is sufficient in the case that the library has
388          initially been installed in the same orig_prefix.  But we can do
389          better, to also cover the cases that 1. it has been installed
390          in a different prefix before being moved to orig_prefix and (later)
391          to curr_prefix, 2. unlike the program, it has not moved away from
392          orig_prefix.  */
393       const char *orig_installprefix = INSTALLPREFIX;
394       const char *orig_installdir = INSTALLDIR;
395       const char *curr_prefix_better;
396
397       curr_prefix_better =
398         compute_curr_prefix (orig_installprefix, orig_installdir,
399                              get_shared_library_fullname ());
400       if (curr_prefix_better == NULL)
401         curr_prefix_better = curr_prefix;
402
403       set_relocation_prefix (orig_installprefix, curr_prefix_better);
404
405       initialized = 1;
406     }
407 #endif
408
409   /* Note: It is not necessary to perform case insensitive comparison here,
410      even for DOS-like filesystems, because the pathname argument was
411      typically created from the same Makefile variable as orig_prefix came
412      from.  */
413   if (orig_prefix != NULL && curr_prefix != NULL
414       && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
415     {
416       if (pathname[orig_prefix_len] == '\0')
417         /* pathname equals orig_prefix.  */
418         return curr_prefix;
419       if (ISSLASH (pathname[orig_prefix_len]))
420         {
421           /* pathname starts with orig_prefix.  */
422           const char *pathname_tail = &pathname[orig_prefix_len];
423           char *result =
424             (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
425
426 #ifdef NO_XMALLOC
427           if (result != NULL)
428 #endif
429             {
430               memcpy (result, curr_prefix, curr_prefix_len);
431               strcpy (result + curr_prefix_len, pathname_tail);
432               return result;
433             }
434         }
435     }
436   /* Nothing to relocate.  */
437   return pathname;
438 }
439
440 #endif