Patch-ID: bash40-030
[bash.git] / lib / intl / relocatable.c
blob34b2b183bc19a4a4b919ce197fe6508694673b70
1 /* relocatable.c - Provide relocatable packages. */
3 /* Copyright (C) 2003, 2005-2009 Free Software Foundation, Inc.
4 Written by Bruno Haible <bruno@clisp.org>, 2003.
6 This file is part of GNU Bash.
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.
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.
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/>.
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
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 /* Specification. */
34 #include "relocatable.h"
36 #if ENABLE_RELOCATABLE
38 #include <stddef.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
43 #ifdef NO_XMALLOC
44 # define xmalloc malloc
45 #else
46 # include "xmalloc.h"
47 #endif
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
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
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.
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
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. */
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)
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)
110 /* Duplicate the argument strings. */
111 char *memory;
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
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;
128 orig_prefix = NULL;
129 curr_prefix = NULL;
130 /* Don't worry about wasted memory here - this function is usually only
131 called once. */
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)
142 set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
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
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)
169 const char *curr_installdir;
170 const char *rel_installdir;
172 if (curr_pathname == NULL)
173 return NULL;
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);
184 /* Determine the current installation directory. */
186 const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
187 const char *p = curr_pathname + strlen (curr_pathname);
188 char *q;
190 while (p > p_base)
192 p--;
193 if (ISSLASH (*p))
194 break;
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;
207 /* Compute the current installation prefix by removing the trailing
208 rel_installdir from it. */
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);
215 while (rp > rel_installdir && cp > cp_base)
217 bool same = false;
218 const char *rpi = rp;
219 const char *cpi = cp;
221 while (rpi > rel_installdir && cpi > cp_base)
223 rpi--;
224 cpi--;
225 if (ISSLASH (*rpi) || ISSLASH (*cpi))
227 if (ISSLASH (*rpi) && ISSLASH (*cpi))
228 same = true;
229 break;
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
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;
249 if (rp > rel_installdir)
250 /* Unexpected: The curr_installdir does not end with rel_installdir. */
251 return NULL;
254 size_t curr_prefix_len = cp - curr_installdir;
255 char *curr_prefix;
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';
265 return curr_prefix;
270 #if defined PIC && defined INSTALLDIR
272 /* Full pathname of shared library, or NULL. */
273 static char *shared_library_fullname;
275 #if defined _WIN32 || defined __WIN32__
277 /* Determine the full pathname of the shared library when it is loaded. */
279 BOOL WINAPI
280 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
282 (void) reserved;
284 if (event == DLL_PROCESS_ATTACH)
286 /* The DLL is being loaded into an application's address range. */
287 static char location[MAX_PATH];
289 if (!GetModuleFileName (module_handle, location, sizeof (location)))
290 /* Shouldn't happen. */
291 return FALSE;
293 if (!IS_PATH_WITH_DIR (location))
294 /* Shouldn't happen. */
295 return FALSE;
297 shared_library_fullname = strdup (location);
300 return TRUE;
303 #else /* Unix */
305 static void
306 find_shared_library_fullname ()
308 #ifdef __linux__
309 FILE *fp;
311 /* Open the current process' maps file. It describes one VMA per line. */
312 fp = fopen ("/proc/self/maps", "r");
313 if (fp)
315 unsigned long address = (unsigned long) &find_shared_library_fullname;
316 for (;;)
318 unsigned long start, end;
319 int c;
321 if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
322 break;
323 if (address >= start && address <= end - 1)
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 == '/')
330 size_t size;
331 int len;
333 ungetc (c, fp);
334 shared_library_fullname = NULL; size = 0;
335 len = getline (&shared_library_fullname, &size, fp);
336 if (len >= 0)
338 /* Success: filled shared_library_fullname. */
339 if (len > 0 && shared_library_fullname[len - 1] == '\n')
340 shared_library_fullname[len - 1] = '\0';
343 break;
345 while (c = getc (fp), c != EOF && c != '\n')
346 continue;
348 fclose (fp);
350 #endif
353 #endif /* WIN32 / Unix */
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 ()
361 #if !(defined _WIN32 || defined __WIN32__)
362 static bool tried_find_shared_library_fullname;
363 if (!tried_find_shared_library_fullname)
365 find_shared_library_fullname ();
366 tried_find_shared_library_fullname = true;
368 #endif
369 return shared_library_fullname;
372 #endif /* PIC */
374 /* Returns the pathname, relocated according to the current installation
375 directory. */
376 const char *
377 relocate (const char *pathname)
379 #if defined PIC && defined INSTALLDIR
380 static int initialized;
382 /* Initialization code for a shared library. */
383 if (!initialized)
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;
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;
403 set_relocation_prefix (orig_installprefix, curr_prefix_better);
405 initialized = 1;
407 #endif
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)
416 if (pathname[orig_prefix_len] == '\0')
417 /* pathname equals orig_prefix. */
418 return curr_prefix;
419 if (ISSLASH (pathname[orig_prefix_len]))
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);
426 #ifdef NO_XMALLOC
427 if (result != NULL)
428 #endif
430 memcpy (result, curr_prefix, curr_prefix_len);
431 strcpy (result + curr_prefix_len, pathname_tail);
432 return result;
436 /* Nothing to relocate. */
437 return pathname;
440 #endif