Home | History | Annotate | Download | only in intl
      1 /* Provide relocatable packages.
      2    Copyright (C) 2003 Free Software Foundation, Inc.
      3    Written by Bruno Haible <bruno (at) 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 __EMX__ || defined __DJGPP__
     76   /* Win32, 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 FILESYSTEM_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 FILESYSTEM_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 + FILESYSTEM_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 + FILESYSTEM_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 __EMX__ || defined __DJGPP__
    238 	    /* Win32, 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
    450