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