(root)/
gettext-0.22.4/
gettext-runtime/
intl/
gnulib-lib/
relocatable.c
       1  /* Provide relocatable packages.
       2     Copyright (C) 2003-2006, 2008-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2003.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file 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
      13     GNU Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  
      19  /* Tell glibc's <stdio.h> to provide a prototype for getline().
      20     This must come before <config.h> because <config.h> may include
      21     <features.h>, and once <features.h> has been included, it's too late.  */
      22  #ifndef _GNU_SOURCE
      23  # define _GNU_SOURCE 1
      24  #endif
      25  
      26  #define _GL_USE_STDLIB_ALLOC 1
      27  #include <config.h>
      28  
      29  /* Specification.  */
      30  #include "relocatable.h"
      31  
      32  #if ENABLE_RELOCATABLE
      33  
      34  #include <stddef.h>
      35  #include <stdio.h>
      36  #include <stdlib.h>
      37  #include <string.h>
      38  
      39  #ifdef NO_XMALLOC
      40  # define xmalloc malloc
      41  #else
      42  # include "xalloc.h"
      43  #endif
      44  
      45  #if defined _WIN32 && !defined __CYGWIN__
      46  # define WIN32_LEAN_AND_MEAN
      47  # include <windows.h>
      48  #endif
      49  
      50  #ifdef __EMX__
      51  # define INCL_DOS
      52  # include <os2.h>
      53  
      54  # define strcmp  stricmp
      55  # define strncmp strnicmp
      56  #endif
      57  
      58  #if DEPENDS_ON_LIBCHARSET
      59  # include <libcharset.h>
      60  #endif
      61  #if DEPENDS_ON_LIBICONV && HAVE_ICONV
      62  # include <iconv.h>
      63  #endif
      64  #if DEPENDS_ON_LIBINTL && ENABLE_NLS
      65  # include <libintl.h>
      66  #endif
      67  
      68  #if defined _WIN32 && !defined __CYGWIN__
      69  /* Don't assume that UNICODE is not defined.  */
      70  # undef GetModuleFileName
      71  # define GetModuleFileName GetModuleFileNameA
      72  #endif
      73  
      74  /* Faked cheap 'bool'.  */
      75  #undef bool
      76  #undef false
      77  #undef true
      78  #define bool int
      79  #define false 0
      80  #define true 1
      81  
      82  /* Pathname support.
      83     ISSLASH(C)                tests whether C is a directory separator character.
      84     IS_FILE_NAME_WITH_DIR(P)  tests whether P contains a directory specification.
      85   */
      86  #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
      87    /* Native Windows, OS/2, DOS */
      88  # define ISSLASH(C) ((C) == '/' || (C) == '\\')
      89  # define HAS_DEVICE(P) \
      90      ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
      91       && (P)[1] == ':')
      92  # define IS_FILE_NAME_WITH_DIR(P) \
      93      (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
      94  # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
      95  #else
      96    /* Unix */
      97  # define ISSLASH(C) ((C) == '/')
      98  # define IS_FILE_NAME_WITH_DIR(P) (strchr (P, '/') != NULL)
      99  # define FILE_SYSTEM_PREFIX_LEN(P) 0
     100  #endif
     101  
     102  /* Whether to enable the more costly support for relocatable libraries.
     103     It allows libraries to be have been installed with a different original
     104     prefix than the program.  But it is quite costly, especially on Cygwin
     105     platforms, see below.  Therefore we enable it by default only on native
     106     Windows platforms.  */
     107  #ifndef ENABLE_COSTLY_RELOCATABLE
     108  # if defined _WIN32 && !defined __CYGWIN__
     109  #  define ENABLE_COSTLY_RELOCATABLE 1
     110  # else
     111  #  define ENABLE_COSTLY_RELOCATABLE 0
     112  # endif
     113  #endif
     114  
     115  /* Original installation prefix.  */
     116  static char *orig_prefix;
     117  static size_t orig_prefix_len;
     118  /* Current installation prefix.  */
     119  static char *curr_prefix;
     120  static size_t curr_prefix_len;
     121  /* These prefixes do not end in a slash.  Anything that will be concatenated
     122     to them must start with a slash.  */
     123  
     124  /* Sets the original and the current installation prefix of this module.
     125     Relocation simply replaces a pathname starting with the original prefix
     126     by the corresponding pathname with the current prefix instead.  Both
     127     prefixes should be directory names without trailing slash (i.e. use ""
     128     instead of "/").  */
     129  static void
     130  set_this_relocation_prefix (const char *orig_prefix_arg,
     131                              const char *curr_prefix_arg)
     132  {
     133    if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
     134        /* Optimization: if orig_prefix and curr_prefix are equal, the
     135           relocation is a nop.  */
     136        && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
     137      {
     138        /* Duplicate the argument strings.  */
     139        char *memory;
     140  
     141        orig_prefix_len = strlen (orig_prefix_arg);
     142        curr_prefix_len = strlen (curr_prefix_arg);
     143        memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
     144  #ifdef NO_XMALLOC
     145        if (memory != NULL)
     146  #endif
     147          {
     148            memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
     149            orig_prefix = memory;
     150            memory += orig_prefix_len + 1;
     151            memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
     152            curr_prefix = memory;
     153            return;
     154          }
     155      }
     156    orig_prefix = NULL;
     157    curr_prefix = NULL;
     158    /* Don't worry about wasted memory here - this function is usually only
     159       called once.  */
     160  }
     161  
     162  /* Sets the original and the current installation prefix of the package.
     163     Relocation simply replaces a pathname starting with the original prefix
     164     by the corresponding pathname with the current prefix instead.  Both
     165     prefixes should be directory names without trailing slash (i.e. use ""
     166     instead of "/").  */
     167  void
     168  set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
     169  {
     170    set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
     171  
     172    /* Now notify all dependent libraries.  */
     173  #if DEPENDS_ON_LIBCHARSET
     174    libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
     175  #endif
     176  #if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
     177    libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
     178  #endif
     179  #if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
     180    libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
     181  #endif
     182  }
     183  
     184  #if !defined IN_LIBRARY || (defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE)
     185  
     186  /* Convenience function:
     187     Computes the current installation prefix, based on the original
     188     installation prefix, the original installation directory of a particular
     189     file, and the current pathname of this file.
     190     Returns it, freshly allocated.  Returns NULL upon failure.  */
     191  #ifdef IN_LIBRARY
     192  #define compute_curr_prefix local_compute_curr_prefix
     193  static
     194  #endif
     195  char *
     196  compute_curr_prefix (const char *orig_installprefix,
     197                       const char *orig_installdir,
     198                       const char *curr_pathname)
     199  {
     200    char *curr_installdir;
     201    const char *rel_installdir;
     202  
     203    if (curr_pathname == NULL)
     204      return NULL;
     205  
     206    /* Determine the relative installation directory, relative to the prefix.
     207       This is simply the difference between orig_installprefix and
     208       orig_installdir.  */
     209    if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
     210        != 0)
     211      /* Shouldn't happen - nothing should be installed outside $(prefix).  */
     212      return NULL;
     213    rel_installdir = orig_installdir + strlen (orig_installprefix);
     214  
     215    /* Determine the current installation directory.  */
     216    {
     217      const char *p_base = curr_pathname + FILE_SYSTEM_PREFIX_LEN (curr_pathname);
     218      const char *p = curr_pathname + strlen (curr_pathname);
     219      char *q;
     220  
     221      while (p > p_base)
     222        {
     223          p--;
     224          if (ISSLASH (*p))
     225            break;
     226        }
     227  
     228      q = (char *) xmalloc (p - curr_pathname + 1);
     229  #ifdef NO_XMALLOC
     230      if (q == NULL)
     231        return NULL;
     232  #endif
     233      memcpy (q, curr_pathname, p - curr_pathname);
     234      q[p - curr_pathname] = '\0';
     235      curr_installdir = q;
     236    }
     237  
     238    /* Compute the current installation prefix by removing the trailing
     239       rel_installdir from it.  */
     240    {
     241      const char *rp = rel_installdir + strlen (rel_installdir);
     242      const char *cp = curr_installdir + strlen (curr_installdir);
     243      const char *cp_base =
     244        curr_installdir + FILE_SYSTEM_PREFIX_LEN (curr_installdir);
     245  
     246      while (rp > rel_installdir && cp > cp_base)
     247        {
     248          bool same = false;
     249          const char *rpi = rp;
     250          const char *cpi = cp;
     251  
     252          while (rpi > rel_installdir && cpi > cp_base)
     253            {
     254              rpi--;
     255              cpi--;
     256              if (ISSLASH (*rpi) || ISSLASH (*cpi))
     257                {
     258                  if (ISSLASH (*rpi) && ISSLASH (*cpi))
     259                    same = true;
     260                  break;
     261                }
     262              /* Do case-insensitive comparison if the file system is always or
     263                 often case-insensitive.  It's better to accept the comparison
     264                 if the difference is only in case, rather than to fail.  */
     265  #if defined _WIN32 || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
     266              /* Native Windows, Cygwin, OS/2, DOS - case insignificant file system */
     267              if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
     268                  != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
     269                break;
     270  #else
     271              if (*rpi != *cpi)
     272                break;
     273  #endif
     274            }
     275          if (!same)
     276            break;
     277          /* The last pathname component was the same.  rpi and cpi now point
     278             to the slash before it.  */
     279          rp = rpi;
     280          cp = cpi;
     281        }
     282  
     283      if (rp > rel_installdir)
     284        {
     285          /* Unexpected: The curr_installdir does not end with rel_installdir.  */
     286          free (curr_installdir);
     287          return NULL;
     288        }
     289  
     290      {
     291        size_t computed_curr_prefix_len = cp - curr_installdir;
     292        char *computed_curr_prefix;
     293  
     294        computed_curr_prefix = (char *) xmalloc (computed_curr_prefix_len + 1);
     295  #ifdef NO_XMALLOC
     296        if (computed_curr_prefix == NULL)
     297          {
     298            free (curr_installdir);
     299            return NULL;
     300          }
     301  #endif
     302        memcpy (computed_curr_prefix, curr_installdir, computed_curr_prefix_len);
     303        computed_curr_prefix[computed_curr_prefix_len] = '\0';
     304  
     305        free (curr_installdir);
     306  
     307        return computed_curr_prefix;
     308      }
     309    }
     310  }
     311  
     312  #endif /* !IN_LIBRARY || PIC */
     313  
     314  #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
     315  
     316  /* Full pathname of shared library, or NULL.  */
     317  static char *shared_library_fullname;
     318  
     319  #if defined _WIN32 && !defined __CYGWIN__
     320  /* Native Windows only.
     321     On Cygwin, it is better to use the Cygwin provided /proc interface, than
     322     to use native Windows API and cygwin_conv_to_posix_path, because it
     323     supports longer file names
     324     (see <https://cygwin.com/ml/cygwin/2011-01/msg00410.html>).  */
     325  
     326  /* Determine the full pathname of the shared library when it is loaded.
     327  
     328     Documentation:
     329     <https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain>  */
     330  
     331  BOOL WINAPI
     332  DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
     333  {
     334    (void) reserved;
     335  
     336    if (event == DLL_PROCESS_ATTACH)
     337      {
     338        /* The DLL is being loaded into an application's address range.  */
     339        static char location[MAX_PATH];
     340  
     341        if (!GetModuleFileName (module_handle, location, sizeof (location)))
     342          /* Shouldn't happen.  */
     343          return FALSE;
     344  
     345        if (!IS_FILE_NAME_WITH_DIR (location))
     346          /* Shouldn't happen.  */
     347          return FALSE;
     348  
     349        /* Avoid a memory leak when the same DLL get attached, detached,
     350           attached, detached, and so on.  This happens e.g. when a spell
     351           checker DLL is used repeatedly by a mail program.  */
     352        if (!(shared_library_fullname != NULL
     353              && strcmp (shared_library_fullname, location) == 0))
     354          /* Remember the full pathname of the shared library.  */
     355          shared_library_fullname = strdup (location);
     356      }
     357  
     358    return TRUE;
     359  }
     360  
     361  #elif defined __EMX__
     362  
     363  extern int  _CRT_init (void);
     364  extern void _CRT_term (void);
     365  extern void __ctordtorInit (void);
     366  extern void __ctordtorTerm (void);
     367  
     368  unsigned long _System
     369  _DLL_InitTerm (unsigned long hModule, unsigned long ulFlag)
     370  {
     371    static char location[CCHMAXPATH];
     372  
     373    switch (ulFlag)
     374      {
     375        case 0:
     376          if (_CRT_init () == -1)
     377            return 0;
     378  
     379          __ctordtorInit();
     380  
     381          /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
     382             for specification of DosQueryModuleName(). */
     383          if (DosQueryModuleName (hModule, sizeof (location), location))
     384            return 0;
     385  
     386          _fnslashify (location);
     387          shared_library_fullname = strdup (location);
     388          break;
     389  
     390        case 1:
     391          __ctordtorTerm();
     392  
     393          _CRT_term ();
     394          break;
     395      }
     396  
     397    return 1;
     398  }
     399  
     400  #else /* Unix */
     401  
     402  static void
     403  find_shared_library_fullname ()
     404  {
     405  #if (defined __linux__ && (__GLIBC__ >= 2 || defined __UCLIBC__)) || defined __CYGWIN__
     406    /* Linux has /proc/self/maps. glibc 2 and uClibc have the getline()
     407       function.
     408       Cygwin >= 1.5 has /proc/self/maps and the getline() function too.
     409       But it is costly: ca. 0.3 ms on Linux, 3 ms on Cygwin 1.5, and 5 ms on
     410       Cygwin 1.7.  */
     411    FILE *fp;
     412  
     413    /* Open the current process' maps file.  It describes one VMA per line.  */
     414    fp = fopen ("/proc/self/maps", "r");
     415    if (fp)
     416      {
     417        unsigned long address = (unsigned long) &find_shared_library_fullname;
     418        for (;;)
     419          {
     420            unsigned long start, end;
     421            int c;
     422  
     423            if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
     424              break;
     425            if (address >= start && address <= end - 1)
     426              {
     427                /* Found it.  Now see if this line contains a filename.  */
     428                while (c = getc (fp), c != EOF && c != '\n' && c != '/')
     429                  continue;
     430                if (c == '/')
     431                  {
     432                    size_t size;
     433                    int len;
     434  
     435                    ungetc (c, fp);
     436                    shared_library_fullname = NULL; size = 0;
     437                    len = getline (&shared_library_fullname, &size, fp);
     438                    if (len >= 0)
     439                      {
     440                        /* Success: filled shared_library_fullname.  */
     441                        if (len > 0 && shared_library_fullname[len - 1] == '\n')
     442                          shared_library_fullname[len - 1] = '\0';
     443                      }
     444                  }
     445                break;
     446              }
     447            while (c = getc (fp), c != EOF && c != '\n')
     448              continue;
     449          }
     450        fclose (fp);
     451      }
     452  #endif
     453  }
     454  
     455  #endif /* Native Windows / EMX / Unix */
     456  
     457  /* Return the full pathname of the current shared library.
     458     Return NULL if unknown.
     459     Guaranteed to work only on Linux, EMX, Cygwin, and native Windows.  */
     460  static char *
     461  get_shared_library_fullname ()
     462  {
     463  #if !(defined _WIN32 && !defined __CYGWIN__) && !defined __EMX__
     464    static bool tried_find_shared_library_fullname;
     465    if (!tried_find_shared_library_fullname)
     466      {
     467        find_shared_library_fullname ();
     468        tried_find_shared_library_fullname = true;
     469      }
     470  #endif
     471    return shared_library_fullname;
     472  }
     473  
     474  #endif /* PIC */
     475  
     476  /* Returns the pathname, relocated according to the current installation
     477     directory.
     478     The returned string is either PATHNAME unmodified or a freshly allocated
     479     string that you can free with free() after casting it to 'char *'.  */
     480  const char *
     481  relocate (const char *pathname)
     482  {
     483  #if defined PIC && defined INSTALLDIR && ENABLE_COSTLY_RELOCATABLE
     484    static int initialized;
     485  
     486    /* Initialization code for a shared library.  */
     487    if (!initialized)
     488      {
     489        /* At this point, orig_prefix and curr_prefix likely have already been
     490           set through the main program's set_program_name_and_installdir
     491           function.  This is sufficient in the case that the library has
     492           initially been installed in the same orig_prefix.  But we can do
     493           better, to also cover the cases that 1. it has been installed
     494           in a different prefix before being moved to orig_prefix and (later)
     495           to curr_prefix, 2. unlike the program, it has not moved away from
     496           orig_prefix.  */
     497        const char *orig_installprefix = INSTALLPREFIX;
     498        const char *orig_installdir = INSTALLDIR;
     499        char *curr_prefix_better;
     500  
     501        curr_prefix_better =
     502          compute_curr_prefix (orig_installprefix, orig_installdir,
     503                               get_shared_library_fullname ());
     504  
     505        set_relocation_prefix (orig_installprefix,
     506                               curr_prefix_better != NULL
     507                               ? curr_prefix_better
     508                               : curr_prefix);
     509  
     510        if (curr_prefix_better != NULL)
     511          free (curr_prefix_better);
     512  
     513        initialized = 1;
     514      }
     515  #endif
     516  
     517    /* Note: It is not necessary to perform case insensitive comparison here,
     518       even for DOS-like file systems, because the pathname argument was
     519       typically created from the same Makefile variable as orig_prefix came
     520       from.  */
     521    if (orig_prefix != NULL && curr_prefix != NULL
     522        && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
     523      {
     524        if (pathname[orig_prefix_len] == '\0')
     525          {
     526            /* pathname equals orig_prefix.  */
     527            char *result = (char *) xmalloc (strlen (curr_prefix) + 1);
     528  
     529  #ifdef NO_XMALLOC
     530            if (result != NULL)
     531  #endif
     532              {
     533                strcpy (result, curr_prefix);
     534                return result;
     535              }
     536          }
     537        else if (ISSLASH (pathname[orig_prefix_len]))
     538          {
     539            /* pathname starts with orig_prefix.  */
     540            const char *pathname_tail = &pathname[orig_prefix_len];
     541            char *result =
     542              (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
     543  
     544  #ifdef NO_XMALLOC
     545            if (result != NULL)
     546  #endif
     547              {
     548                memcpy (result, curr_prefix, curr_prefix_len);
     549                strcpy (result + curr_prefix_len, pathname_tail);
     550                return result;
     551              }
     552          }
     553      }
     554  
     555  #ifdef __EMX__
     556  # ifdef __KLIBC__
     557  #  undef strncmp
     558  
     559    if (strncmp (pathname, "/@unixroot", 10) == 0
     560        && (pathname[10] == '\0' || ISSLASH (pathname[10])))
     561      {
     562        /* kLIBC itself processes /@unixroot prefix */
     563        return pathname;
     564      }
     565    else
     566  # endif
     567    if (ISSLASH (pathname[0]))
     568      {
     569        const char *unixroot = getenv ("UNIXROOT");
     570  
     571        if (unixroot && HAS_DEVICE (unixroot) && unixroot[2] == '\0')
     572          {
     573            char *result = (char *) xmalloc (2 + strlen (pathname) + 1);
     574  #ifdef NO_XMALLOC
     575            if (result != NULL)
     576  #endif
     577              {
     578                memcpy (result, unixroot, 2);
     579                strcpy (result + 2, pathname);
     580                return result;
     581              }
     582          }
     583      }
     584  #endif
     585  
     586    /* Nothing to relocate.  */
     587    return pathname;
     588  }
     589  
     590  /* Returns the pathname, relocated according to the current installation
     591     directory.
     592     This function sets *ALLOCATEDP to the allocated memory, or to NULL if
     593     no memory allocation occurs.  So that, after you're done with the return
     594     value, to reclaim allocated memory, you can do: free (*ALLOCATEDP).  */
     595  const char *
     596  relocate2 (const char *pathname, char **allocatedp)
     597  {
     598    const char *result = relocate (pathname);
     599    *allocatedp = (result != pathname ? (char *) result : NULL);
     600    return result;
     601  }
     602  
     603  #endif