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