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