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