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