Back to index

courier  0.68.2
relocatable.c
Go to the documentation of this file.
00001 /* Provide relocatable packages.
00002    Copyright (C) 2003-2006, 2008-2009 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
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 #include <config.h>
00029 
00030 /* Specification.  */
00031 #include "relocatable.h"
00032 
00033 #if ENABLE_RELOCATABLE
00034 
00035 #include <stddef.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 
00040 #ifdef NO_XMALLOC
00041 # define xmalloc malloc
00042 #else
00043 # include "xalloc.h"
00044 #endif
00045 
00046 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
00047 # define WIN32_LEAN_AND_MEAN
00048 # include <windows.h>
00049 #endif
00050 
00051 #if DEPENDS_ON_LIBCHARSET
00052 # include <libcharset.h>
00053 #endif
00054 #if DEPENDS_ON_LIBICONV && HAVE_ICONV
00055 # include <iconv.h>
00056 #endif
00057 #if DEPENDS_ON_LIBINTL && ENABLE_NLS
00058 # include <libintl.h>
00059 #endif
00060 
00061 /* Faked cheap 'bool'.  */
00062 #undef bool
00063 #undef false
00064 #undef true
00065 #define bool int
00066 #define false 0
00067 #define true 1
00068 
00069 /* Pathname support.
00070    ISSLASH(C)           tests whether C is a directory separator character.
00071    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
00072  */
00073 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
00074   /* Win32, Cygwin, OS/2, DOS */
00075 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
00076 # define HAS_DEVICE(P) \
00077     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
00078      && (P)[1] == ':')
00079 # define IS_PATH_WITH_DIR(P) \
00080     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
00081 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
00082 #else
00083   /* Unix */
00084 # define ISSLASH(C) ((C) == '/')
00085 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
00086 # define FILE_SYSTEM_PREFIX_LEN(P) 0
00087 #endif
00088 
00089 /* Original installation prefix.  */
00090 static char *orig_prefix;
00091 static size_t orig_prefix_len;
00092 /* Current installation prefix.  */
00093 static char *curr_prefix;
00094 static size_t curr_prefix_len;
00095 /* These prefixes do not end in a slash.  Anything that will be concatenated
00096    to them must start with a slash.  */
00097 
00098 /* Sets the original and the current installation prefix of this module.
00099    Relocation simply replaces a pathname starting with the original prefix
00100    by the corresponding pathname with the current prefix instead.  Both
00101    prefixes should be directory names without trailing slash (i.e. use ""
00102    instead of "/").  */
00103 static void
00104 set_this_relocation_prefix (const char *orig_prefix_arg,
00105                             const char *curr_prefix_arg)
00106 {
00107   if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
00108       /* Optimization: if orig_prefix and curr_prefix are equal, the
00109          relocation is a nop.  */
00110       && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
00111     {
00112       /* Duplicate the argument strings.  */
00113       char *memory;
00114 
00115       orig_prefix_len = strlen (orig_prefix_arg);
00116       curr_prefix_len = strlen (curr_prefix_arg);
00117       memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
00118 #ifdef NO_XMALLOC
00119       if (memory != NULL)
00120 #endif
00121         {
00122           memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
00123           orig_prefix = memory;
00124           memory += orig_prefix_len + 1;
00125           memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
00126           curr_prefix = memory;
00127           return;
00128         }
00129     }
00130   orig_prefix = NULL;
00131   curr_prefix = NULL;
00132   /* Don't worry about wasted memory here - this function is usually only
00133      called once.  */
00134 }
00135 
00136 /* Sets the original and the current installation prefix of the package.
00137    Relocation simply replaces a pathname starting with the original prefix
00138    by the corresponding pathname with the current prefix instead.  Both
00139    prefixes should be directory names without trailing slash (i.e. use ""
00140    instead of "/").  */
00141 void
00142 set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
00143 {
00144   set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00145 
00146   /* Now notify all dependent libraries.  */
00147 #if DEPENDS_ON_LIBCHARSET
00148   libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00149 #endif
00150 #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
00151   libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00152 #endif
00153 #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
00154   libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
00155 #endif
00156 }
00157 
00158 #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR)
00159 
00160 /* Convenience function:
00161    Computes the current installation prefix, based on the original
00162    installation prefix, the original installation directory of a particular
00163    file, and the current pathname of this file.
00164    Returns it, freshly allocated.  Returns NULL upon failure.  */
00165 #ifdef IN_LIBRARY
00166 #define compute_curr_prefix local_compute_curr_prefix
00167 static
00168 #endif
00169 char *
00170 compute_curr_prefix (const char *orig_installprefix,
00171                      const char *orig_installdir,
00172                      const char *curr_pathname)
00173 {
00174   char *curr_installdir;
00175   const char *rel_installdir;
00176 
00177   if (curr_pathname == NULL)
00178     return NULL;
00179 
00180   /* Determine the relative installation directory, relative to the prefix.
00181      This is simply the difference between orig_installprefix and
00182      orig_installdir.  */
00183   if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
00184       != 0)
00185     /* Shouldn't happen - nothing should be installed outside $(prefix).  */
00186     return NULL;
00187   rel_installdir = orig_installdir + strlen (orig_installprefix);
00188 
00189   /* Determine the current installation directory.  */
00190   {
00191     const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname);
00192     const char *p = curr_pathname + strlen (curr_pathname);
00193     char *q;
00194 
00195     while (p > p_base)
00196       {
00197         p--;
00198         if (ISSLASH (*p))
00199           break;
00200       }
00201 
00202     q = (char *) xmalloc (p - curr_pathname + 1);
00203 #ifdef NO_XMALLOC
00204     if (q == NULL)
00205       return NULL;
00206 #endif
00207     memcpy (q, curr_pathname, p - curr_pathname);
00208     q[p - curr_pathname] = '\0';
00209     curr_installdir = q;
00210   }
00211 
00212   /* Compute the current installation prefix by removing the trailing
00213      rel_installdir from it.  */
00214   {
00215     const char *rp = rel_installdir + strlen (rel_installdir);
00216     const char *cp = curr_installdir + strlen (curr_installdir);
00217     const char *cp_base =
00218       curr_installdir + FILE_SYSTEM_PREFIX_LEN (curr_installdir);
00219 
00220     while (rp > rel_installdir && cp > cp_base)
00221       {
00222         bool same = false;
00223         const char *rpi = rp;
00224         const char *cpi = cp;
00225 
00226         while (rpi > rel_installdir && cpi > cp_base)
00227           {
00228             rpi--;
00229             cpi--;
00230             if (ISSLASH (*rpi) || ISSLASH (*cpi))
00231               {
00232                 if (ISSLASH (*rpi) && ISSLASH (*cpi))
00233                   same = true;
00234                 break;
00235               }
00236             /* Do case-insensitive comparison if the file system is always or
00237                often case-insensitive.  It's better to accept the comparison
00238                if the difference is only in case, rather than to fail.  */
00239 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
00240             /* Win32, Cygwin, OS/2, DOS - case insignificant file system */
00241             if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
00242                 != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
00243               break;
00244 #else
00245             if (*rpi != *cpi)
00246               break;
00247 #endif
00248           }
00249         if (!same)
00250           break;
00251         /* The last pathname component was the same.  opi and cpi now point
00252            to the slash before it.  */
00253         rp = rpi;
00254         cp = cpi;
00255       }
00256 
00257     if (rp > rel_installdir)
00258       {
00259         /* Unexpected: The curr_installdir does not end with rel_installdir.  */
00260         free (curr_installdir);
00261         return NULL;
00262       }
00263 
00264     {
00265       size_t curr_prefix_len = cp - curr_installdir;
00266       char *curr_prefix;
00267 
00268       curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
00269 #ifdef NO_XMALLOC
00270       if (curr_prefix == NULL)
00271         {
00272           free (curr_installdir);
00273           return NULL;
00274         }
00275 #endif
00276       memcpy (curr_prefix, curr_installdir, curr_prefix_len);
00277       curr_prefix[curr_prefix_len] = '\0';
00278 
00279       free (curr_installdir);
00280 
00281       return curr_prefix;
00282     }
00283   }
00284 }
00285 
00286 #endif /* !IN_LIBRARY || PIC */
00287 
00288 #if defined PIC && defined INSTALLDIR
00289 
00290 /* Full pathname of shared library, or NULL.  */
00291 static char *shared_library_fullname;
00292 
00293 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
00294 
00295 /* Determine the full pathname of the shared library when it is loaded.  */
00296 
00297 BOOL WINAPI
00298 DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
00299 {
00300   (void) reserved;
00301 
00302   if (event == DLL_PROCESS_ATTACH)
00303     {
00304       /* The DLL is being loaded into an application's address range.  */
00305       static char location[MAX_PATH];
00306 
00307       if (!GetModuleFileName (module_handle, location, sizeof (location)))
00308         /* Shouldn't happen.  */
00309         return FALSE;
00310 
00311       if (!IS_PATH_WITH_DIR (location))
00312         /* Shouldn't happen.  */
00313         return FALSE;
00314 
00315       {
00316 #if defined __CYGWIN__
00317         /* On Cygwin, we need to convert paths coming from Win32 system calls
00318            to the Unix-like slashified notation.  */
00319         static char location_as_posix_path[2 * MAX_PATH];
00320         /* There's no error return defined for cygwin_conv_to_posix_path.
00321            See cygwin-api/func-cygwin-conv-to-posix-path.html.
00322            Does it overflow the buffer of expected size MAX_PATH or does it
00323            truncate the path?  I don't know.  Let's catch both.  */
00324         cygwin_conv_to_posix_path (location, location_as_posix_path);
00325         location_as_posix_path[MAX_PATH - 1] = '\0';
00326         if (strlen (location_as_posix_path) >= MAX_PATH - 1)
00327           /* A sign of buffer overflow or path truncation.  */
00328           return FALSE;
00329         shared_library_fullname = strdup (location_as_posix_path);
00330 #else
00331         shared_library_fullname = strdup (location);
00332 #endif
00333       }
00334     }
00335 
00336   return TRUE;
00337 }
00338 
00339 #else /* Unix except Cygwin */
00340 
00341 static void
00342 find_shared_library_fullname ()
00343 {
00344 #if defined __linux__ && __GLIBC__ >= 2
00345   /* Linux has /proc/self/maps. glibc 2 has the getline() function.  */
00346   FILE *fp;
00347 
00348   /* Open the current process' maps file.  It describes one VMA per line.  */
00349   fp = fopen ("/proc/self/maps", "r");
00350   if (fp)
00351     {
00352       unsigned long address = (unsigned long) &find_shared_library_fullname;
00353       for (;;)
00354         {
00355           unsigned long start, end;
00356           int c;
00357 
00358           if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
00359             break;
00360           if (address >= start && address <= end - 1)
00361             {
00362               /* Found it.  Now see if this line contains a filename.  */
00363               while (c = getc (fp), c != EOF && c != '\n' && c != '/')
00364                 continue;
00365               if (c == '/')
00366                 {
00367                   size_t size;
00368                   int len;
00369 
00370                   ungetc (c, fp);
00371                   shared_library_fullname = NULL; size = 0;
00372                   len = getline (&shared_library_fullname, &size, fp);
00373                   if (len >= 0)
00374                     {
00375                       /* Success: filled shared_library_fullname.  */
00376                       if (len > 0 && shared_library_fullname[len - 1] == '\n')
00377                         shared_library_fullname[len - 1] = '\0';
00378                     }
00379                 }
00380               break;
00381             }
00382           while (c = getc (fp), c != EOF && c != '\n')
00383             continue;
00384         }
00385       fclose (fp);
00386     }
00387 #endif
00388 }
00389 
00390 #endif /* (WIN32 or Cygwin) / (Unix except Cygwin) */
00391 
00392 /* Return the full pathname of the current shared library.
00393    Return NULL if unknown.
00394    Guaranteed to work only on Linux, Cygwin and Woe32.  */
00395 static char *
00396 get_shared_library_fullname ()
00397 {
00398 #if !(defined _WIN32 || defined __WIN32__ || defined __CYGWIN__)
00399   static bool tried_find_shared_library_fullname;
00400   if (!tried_find_shared_library_fullname)
00401     {
00402       find_shared_library_fullname ();
00403       tried_find_shared_library_fullname = true;
00404     }
00405 #endif
00406   return shared_library_fullname;
00407 }
00408 
00409 #endif /* PIC */
00410 
00411 /* Returns the pathname, relocated according to the current installation
00412    directory.
00413    The returned string is either PATHNAME unmodified or a freshly allocated
00414    string that you can free with free() after casting it to 'char *'.  */
00415 const char *
00416 relocate (const char *pathname)
00417 {
00418 #if defined PIC && defined INSTALLDIR
00419   static int initialized;
00420 
00421   /* Initialization code for a shared library.  */
00422   if (!initialized)
00423     {
00424       /* At this point, orig_prefix and curr_prefix likely have already been
00425          set through the main program's set_program_name_and_installdir
00426          function.  This is sufficient in the case that the library has
00427          initially been installed in the same orig_prefix.  But we can do
00428          better, to also cover the cases that 1. it has been installed
00429          in a different prefix before being moved to orig_prefix and (later)
00430          to curr_prefix, 2. unlike the program, it has not moved away from
00431          orig_prefix.  */
00432       const char *orig_installprefix = INSTALLPREFIX;
00433       const char *orig_installdir = INSTALLDIR;
00434       char *curr_prefix_better;
00435 
00436       curr_prefix_better =
00437         compute_curr_prefix (orig_installprefix, orig_installdir,
00438                              get_shared_library_fullname ());
00439 
00440       set_relocation_prefix (orig_installprefix,
00441                              curr_prefix_better != NULL
00442                              ? curr_prefix_better
00443                              : curr_prefix);
00444 
00445       if (curr_prefix_better != NULL)
00446         free (curr_prefix_better);
00447 
00448       initialized = 1;
00449     }
00450 #endif
00451 
00452   /* Note: It is not necessary to perform case insensitive comparison here,
00453      even for DOS-like file systems, because the pathname argument was
00454      typically created from the same Makefile variable as orig_prefix came
00455      from.  */
00456   if (orig_prefix != NULL && curr_prefix != NULL
00457       && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
00458     {
00459       if (pathname[orig_prefix_len] == '\0')
00460         {
00461           /* pathname equals orig_prefix.  */
00462           char *result = (char *) xmalloc (strlen (curr_prefix) + 1);
00463 
00464 #ifdef NO_XMALLOC
00465           if (result != NULL)
00466 #endif
00467             {
00468               strcpy (result, curr_prefix);
00469               return result;
00470             }
00471         }
00472       else if (ISSLASH (pathname[orig_prefix_len]))
00473         {
00474           /* pathname starts with orig_prefix.  */
00475           const char *pathname_tail = &pathname[orig_prefix_len];
00476           char *result =
00477             (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
00478 
00479 #ifdef NO_XMALLOC
00480           if (result != NULL)
00481 #endif
00482             {
00483               memcpy (result, curr_prefix, curr_prefix_len);
00484               strcpy (result + curr_prefix_len, pathname_tail);
00485               return result;
00486             }
00487         }
00488     }
00489   /* Nothing to relocate.  */
00490   return pathname;
00491 }
00492 
00493 #endif