Back to index

tetex-bin  3.0
relocatable.c
Go to the documentation of this file.
00001 /* Provide relocatable packages.
00002    Copyright (C) 2003 Free Software Foundation, Inc.
00003    Written by Bruno Haible <bruno@clisp.org>, 2003.
00004 
00005    This program is free software; you can redistribute it and/or modify it
00006    under the terms of the GNU Library General Public License as published
00007    by the Free Software Foundation; either version 2, or (at your option)
00008    any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public
00016    License along with this program; if not, write to the Free Software
00017    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
00018    USA.  */
00019 
00020 
00021 /* Tell glibc's <stdio.h> to provide a prototype for getline().
00022    This must come before <config.h> because <config.h> may include
00023    <features.h>, and once <features.h> has been included, it's too late.  */
00024 #ifndef _GNU_SOURCE
00025 # define _GNU_SOURCE 1
00026 #endif
00027 
00028 #ifdef HAVE_CONFIG_H
00029 # include "config.h"
00030 #endif
00031 
00032 /* Specification.  */
00033 #include "relocatable.h"
00034 
00035 #if ENABLE_RELOCATABLE
00036 
00037 #include <stddef.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 
00042 #ifdef NO_XMALLOC
00043 # define xmalloc malloc
00044 #else
00045 # include "xalloc.h"
00046 #endif
00047 
00048 #if defined _WIN32 || defined __WIN32__
00049 # define WIN32_LEAN_AND_MEAN
00050 # include <windows.h>
00051 #endif
00052 
00053 #if DEPENDS_ON_LIBCHARSET
00054 # include <libcharset.h>
00055 #endif
00056 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
00057 # include <iconv.h>
00058 #endif
00059 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
00060 # include <libintl.h>
00061 #endif
00062 
00063 /* Faked cheap 'bool'.  */
00064 #undef bool
00065 #undef false
00066 #undef true
00067 #define bool int
00068 #define false 0
00069 #define true 1
00070 
00071 /* Pathname support.
00072    ISSLASH(C)           tests whether C is a directory separator character.
00073    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
00074  */
00075 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
00076   /* Win32, OS/2, DOS */
00077 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
00078 # define HAS_DEVICE(P) \
00079     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
00080      && (P)[1] == ':')
00081 # define IS_PATH_WITH_DIR(P) \
00082     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
00083 # define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
00084 #else
00085   /* Unix */
00086 # define ISSLASH(C) ((C) == '/')
00087 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
00088 # define FILESYSTEM_PREFIX_LEN(P) 0
00089 #endif
00090 
00091 /* Original installation prefix.  */
00092 static char *orig_prefix;
00093 static size_t orig_prefix_len;
00094 /* Current installation prefix.  */
00095 static char *curr_prefix;
00096 static size_t curr_prefix_len;
00097 /* These prefixes do not end in a slash.  Anything that will be concatenated
00098    to them must start with a slash.  */
00099 
00100 /* Sets the original and the current installation prefix of this module.
00101    Relocation simply replaces a pathname starting with the original prefix
00102    by the corresponding pathname with the current prefix instead.  Both
00103    prefixes should be directory names without trailing slash (i.e. use ""
00104    instead of "/").  */
00105 static void
00106 set_this_relocation_prefix (const char *orig_prefix_arg,
00107                          const char *curr_prefix_arg)
00108 {
00109   if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
00110       /* Optimization: if orig_prefix and curr_prefix are equal, the
00111         relocation is a nop.  */
00112       && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
00113     {
00114       /* Duplicate the argument strings.  */
00115       char *memory;
00116 
00117       orig_prefix_len = strlen (orig_prefix_arg);
00118       curr_prefix_len = strlen (curr_prefix_arg);
00119       memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
00120 #ifdef NO_XMALLOC
00121       if (memory != NULL)
00122 #endif
00123        {
00124          memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
00125          orig_prefix = memory;
00126          memory += orig_prefix_len + 1;
00127          memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
00128          curr_prefix = memory;
00129          return;
00130        }
00131     }
00132   orig_prefix = NULL;
00133   curr_prefix = NULL;
00134   /* Don't worry about wasted memory here - this function is usually only
00135      called once.  */
00136 }
00137 
00138 /* Sets the original and the current installation prefix of the package.
00139    Relocation simply replaces a pathname starting with the original prefix
00140    by the corresponding pathname with the current prefix instead.  Both
00141    prefixes should be directory names without trailing slash (i.e. use ""
00142    instead of "/").  */
00143 void
00144 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
00145 {
00146   set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00147 
00148   /* Now notify all dependent libraries.  */
00149 #if DEPENDS_ON_LIBCHARSET
00150   libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00151 #endif
00152 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
00153   libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00154 #endif
00155 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
00156   libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00157 #endif
00158 }
00159 
00160 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
00161 
00162 /* Convenience function:
00163    Computes the current installation prefix, based on the original
00164    installation prefix, the original installation directory of a particular
00165    file, and the current pathname of this file.  Returns NULL upon failure.  */
00166 #ifdef IN_LIBRARY
00167 #define compute_curr_prefix local_compute_curr_prefix
00168 static
00169 #endif
00170 const char *
00171 compute_curr_prefix (const char *orig_installprefix,
00172                    const char *orig_installdir,
00173                    const char *curr_pathname)
00174 {
00175   const char *curr_installdir;
00176   const char *rel_installdir;
00177 
00178   if (curr_pathname == NULL)
00179     return NULL;
00180 
00181   /* Determine the relative installation directory, relative to the prefix.
00182      This is simply the difference between orig_installprefix and
00183      orig_installdir.  */
00184   if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
00185       != 0)
00186     /* Shouldn't happen - nothing should be installed outside $(prefix).  */
00187     return NULL;
00188   rel_installdir = orig_installdir + strlen (orig_installprefix);
00189 
00190   /* Determine the current installation directory.  */
00191   {
00192     const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
00193     const char *p = curr_pathname + strlen (curr_pathname);
00194     char *q;
00195 
00196     while (p > p_base)
00197       {
00198        p--;
00199        if (ISSLASH (*p))
00200          break;
00201       }
00202 
00203     q = (char *) xmalloc (p - curr_pathname + 1);
00204 #ifdef NO_XMALLOC
00205     if (q == NULL)
00206       return NULL;
00207 #endif
00208     memcpy (q, curr_pathname, p - curr_pathname);
00209     q[p - curr_pathname] = '\0';
00210     curr_installdir = q;
00211   }
00212 
00213   /* Compute the current installation prefix by removing the trailing
00214      rel_installdir from it.  */
00215   {
00216     const char *rp = rel_installdir + strlen (rel_installdir);
00217     const char *cp = curr_installdir + strlen (curr_installdir);
00218     const char *cp_base =
00219       curr_installdir + FILESYSTEM_PREFIX_LEN (curr_installdir);
00220 
00221     while (rp > rel_installdir && cp > cp_base)
00222       {
00223        bool same = false;
00224        const char *rpi = rp;
00225        const char *cpi = cp;
00226 
00227        while (rpi > rel_installdir && cpi > cp_base)
00228          {
00229            rpi--;
00230            cpi--;
00231            if (ISSLASH (*rpi) || ISSLASH (*cpi))
00232              {
00233               if (ISSLASH (*rpi) && ISSLASH (*cpi))
00234                 same = true;
00235               break;
00236              }
00237 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
00238            /* Win32, OS/2, DOS - case insignificant filesystem */
00239            if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
00240               != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
00241              break;
00242 #else
00243            if (*rpi != *cpi)
00244              break;
00245 #endif
00246          }
00247        if (!same)
00248          break;
00249        /* The last pathname component was the same.  opi and cpi now point
00250           to the slash before it.  */
00251        rp = rpi;
00252        cp = cpi;
00253       }
00254 
00255     if (rp > rel_installdir)
00256       /* Unexpected: The curr_installdir does not end with rel_installdir.  */
00257       return NULL;
00258 
00259     {
00260       size_t curr_prefix_len = cp - curr_installdir;
00261       char *curr_prefix;
00262 
00263       curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
00264 #ifdef NO_XMALLOC
00265       if (curr_prefix == NULL)
00266        return NULL;
00267 #endif
00268       memcpy (curr_prefix, curr_installdir, curr_prefix_len);
00269       curr_prefix[curr_prefix_len] = '\0';
00270 
00271       return curr_prefix;
00272     }
00273   }
00274 }
00275 
00276 #endif /* !IN_LIBRARY || PIC */
00277 
00278 #if defined PIC && defined INSTALLDIR
00279 
00280 /* Full pathname of shared library, or NULL.  */
00281 static char *shared_library_fullname;
00282 
00283 #if defined _WIN32 || defined __WIN32__
00284 
00285 /* Determine the full pathname of the shared library when it is loaded.  */
00286 
00287 BOOL WINAPI
00288 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
00289 {
00290   (void) reserved;
00291 
00292   if (event == DLL_PROCESS_ATTACH)
00293     {
00294       /* The DLL is being loaded into an application's address range.  */
00295       static char location[MAX_PATH];
00296 
00297       if (!GetModuleFileName (module_handle, location, sizeof (location)))
00298        /* Shouldn't happen.  */
00299        return FALSE;
00300 
00301       if (!IS_PATH_WITH_DIR (location))
00302        /* Shouldn't happen.  */
00303        return FALSE;
00304 
00305       shared_library_fullname = strdup (location);
00306     }
00307 
00308   return TRUE;
00309 }
00310 
00311 #else /* Unix */
00312 
00313 static void
00314 find_shared_library_fullname ()
00315 {
00316 #if defined __linux__ && __GLIBC__ >= 2
00317   /* Linux has /proc/self/maps. glibc 2 has the getline() function.  */
00318   FILE *fp;
00319 
00320   /* Open the current process' maps file.  It describes one VMA per line.  */
00321   fp = fopen ("/proc/self/maps", "r");
00322   if (fp)
00323     {
00324       unsigned long address = (unsigned long) &find_shared_library_fullname;
00325       for (;;)
00326        {
00327          unsigned long start, end;
00328          int c;
00329 
00330          if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
00331            break;
00332          if (address >= start && address <= end - 1)
00333            {
00334              /* Found it.  Now see if this line contains a filename.  */
00335              while (c = getc (fp), c != EOF && c != '\n' && c != '/')
00336               continue;
00337              if (c == '/')
00338               {
00339                 size_t size;
00340                 int len;
00341 
00342                 ungetc (c, fp);
00343                 shared_library_fullname = NULL; size = 0;
00344                 len = getline (&shared_library_fullname, &size, fp);
00345                 if (len >= 0)
00346                   {
00347                     /* Success: filled shared_library_fullname.  */
00348                     if (len > 0 && shared_library_fullname[len - 1] == '\n')
00349                      shared_library_fullname[len - 1] = '\0';
00350                   }
00351               }
00352              break;
00353            }
00354          while (c = getc (fp), c != EOF && c != '\n')
00355            continue;
00356        }
00357       fclose (fp);
00358     }
00359 #endif
00360 }
00361 
00362 #endif /* WIN32 / Unix */
00363 
00364 /* Return the full pathname of the current shared library.
00365    Return NULL if unknown.
00366    Guaranteed to work only on Linux and Woe32.  */
00367 static char *
00368 get_shared_library_fullname ()
00369 {
00370 #if !(defined _WIN32 || defined __WIN32__)
00371   static bool tried_find_shared_library_fullname;
00372   if (!tried_find_shared_library_fullname)
00373     {
00374       find_shared_library_fullname ();
00375       tried_find_shared_library_fullname = true;
00376     }
00377 #endif
00378   return shared_library_fullname;
00379 }
00380 
00381 #endif /* PIC */
00382 
00383 /* Returns the pathname, relocated according to the current installation
00384    directory.  */
00385 const char *
00386 relocate (const char *pathname)
00387 {
00388 #if defined PIC && defined INSTALLDIR
00389   static int initialized;
00390 
00391   /* Initialization code for a shared library.  */
00392   if (!initialized)
00393     {
00394       /* At this point, orig_prefix and curr_prefix likely have already been
00395         set through the main program's set_program_name_and_installdir
00396         function.  This is sufficient in the case that the library has
00397         initially been installed in the same orig_prefix.  But we can do
00398         better, to also cover the cases that 1. it has been installed
00399         in a different prefix before being moved to orig_prefix and (later)
00400         to curr_prefix, 2. unlike the program, it has not moved away from
00401         orig_prefix.  */
00402       const char *orig_installprefix = INSTALLPREFIX;
00403       const char *orig_installdir = INSTALLDIR;
00404       const char *curr_prefix_better;
00405 
00406       curr_prefix_better =
00407        compute_curr_prefix (orig_installprefix, orig_installdir,
00408                           get_shared_library_fullname ());
00409       if (curr_prefix_better == NULL)
00410        curr_prefix_better = curr_prefix;
00411 
00412       set_relocation_prefix (orig_installprefix, curr_prefix_better);
00413 
00414       initialized = 1;
00415     }
00416 #endif
00417 
00418   /* Note: It is not necessary to perform case insensitive comparison here,
00419      even for DOS-like filesystems, because the pathname argument was
00420      typically created from the same Makefile variable as orig_prefix came
00421      from.  */
00422   if (orig_prefix != NULL && curr_prefix != NULL
00423       && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
00424     {
00425       if (pathname[orig_prefix_len] == '\0')
00426        /* pathname equals orig_prefix.  */
00427        return curr_prefix;
00428       if (ISSLASH (pathname[orig_prefix_len]))
00429        {
00430          /* pathname starts with orig_prefix.  */
00431          const char *pathname_tail = &pathname[orig_prefix_len];
00432          char *result =
00433            (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
00434 
00435 #ifdef NO_XMALLOC
00436          if (result != NULL)
00437 #endif
00438            {
00439              memcpy (result, curr_prefix, curr_prefix_len);
00440              strcpy (result + curr_prefix_len, pathname_tail);
00441              return result;
00442            }
00443        }
00444     }
00445   /* Nothing to relocate.  */
00446   return pathname;
00447 }
00448 
00449 #endif