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