Blame intl/relocatable.c

Packit bbfece
/* Provide relocatable packages.
Packit bbfece
   Copyright (C) 2003 Free Software Foundation, Inc.
Packit bbfece
   Written by Bruno Haible <bruno@clisp.org>, 2003.
Packit bbfece
Packit bbfece
   This program is free software; you can redistribute it and/or modify it
Packit bbfece
   under the terms of the GNU Library General Public License as published
Packit bbfece
   by the Free Software Foundation; either version 2, or (at your option)
Packit bbfece
   any later version.
Packit bbfece
Packit bbfece
   This program is distributed in the hope that it will be useful,
Packit bbfece
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit bbfece
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit bbfece
   Library General Public License for more details.
Packit bbfece
Packit bbfece
   You should have received a copy of the GNU Library General Public
Packit bbfece
   License along with this program; if not, write to the Free Software
Packit bbfece
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
Packit bbfece
   USA.  */
Packit bbfece
Packit bbfece
Packit bbfece
/* Tell glibc's <stdio.h> to provide a prototype for getline().
Packit bbfece
   This must come before <config.h> because <config.h> may include
Packit bbfece
   <features.h>, and once <features.h> has been included, it's too late.  */
Packit bbfece
#ifndef _GNU_SOURCE
Packit bbfece
# define _GNU_SOURCE	1
Packit bbfece
#endif
Packit bbfece
Packit bbfece
#ifdef HAVE_CONFIG_H
Packit bbfece
# include "config.h"
Packit bbfece
#endif
Packit bbfece
Packit bbfece
/* Specification.  */
Packit bbfece
#include "relocatable.h"
Packit bbfece
Packit bbfece
#if ENABLE_RELOCATABLE
Packit bbfece
Packit bbfece
#include <stddef.h>
Packit bbfece
#include <stdio.h>
Packit bbfece
#include <stdlib.h>
Packit bbfece
#include <string.h>
Packit bbfece
Packit bbfece
#ifdef NO_XMALLOC
Packit bbfece
# define xmalloc malloc
Packit bbfece
#else
Packit bbfece
# include "xmalloc.h"
Packit bbfece
#endif
Packit bbfece
Packit bbfece
#if DEPENDS_ON_LIBCHARSET
Packit bbfece
# include <libcharset.h>
Packit bbfece
#endif
Packit bbfece
#if DEPENDS_ON_LIBICONV && HAVE_ICONV
Packit bbfece
# include <iconv.h>
Packit bbfece
#endif
Packit bbfece
#if DEPENDS_ON_LIBINTL && ENABLE_NLS
Packit bbfece
# include <libintl.h>
Packit bbfece
#endif
Packit bbfece
Packit bbfece
/* Faked cheap 'bool'.  */
Packit bbfece
#undef bool
Packit bbfece
#undef false
Packit bbfece
#undef true
Packit bbfece
#define bool int
Packit bbfece
#define false 0
Packit bbfece
#define true 1
Packit bbfece
Packit bbfece
/* Pathname support.
Packit bbfece
   ISSLASH(C)           tests whether C is a directory separator character.
Packit bbfece
   IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
Packit bbfece
 */
Packit bbfece
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
Packit bbfece
  /* Win32, OS/2, DOS */
Packit bbfece
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
Packit bbfece
# define HAS_DEVICE(P) \
Packit bbfece
    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
Packit bbfece
     && (P)[1] == ':')
Packit bbfece
# define IS_PATH_WITH_DIR(P) \
Packit bbfece
    (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
Packit bbfece
# define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
Packit bbfece
#else
Packit bbfece
  /* Unix */
Packit bbfece
# define ISSLASH(C) ((C) == '/')
Packit bbfece
# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
Packit bbfece
# define FILESYSTEM_PREFIX_LEN(P) 0
Packit bbfece
#endif
Packit bbfece
Packit bbfece
/* Original installation prefix.  */
Packit bbfece
static char *orig_prefix;
Packit bbfece
static size_t orig_prefix_len;
Packit bbfece
/* Current installation prefix.  */
Packit bbfece
static char *curr_prefix;
Packit bbfece
static size_t curr_prefix_len;
Packit bbfece
/* These prefixes do not end in a slash.  Anything that will be concatenated
Packit bbfece
   to them must start with a slash.  */
Packit bbfece
Packit bbfece
/* Sets the original and the current installation prefix of this module.
Packit bbfece
   Relocation simply replaces a pathname starting with the original prefix
Packit bbfece
   by the corresponding pathname with the current prefix instead.  Both
Packit bbfece
   prefixes should be directory names without trailing slash (i.e. use ""
Packit bbfece
   instead of "/").  */
Packit bbfece
static void
Packit bbfece
set_this_relocation_prefix (const char *orig_prefix_arg,
Packit bbfece
			    const char *curr_prefix_arg)
Packit bbfece
{
Packit bbfece
  if (orig_prefix_arg != NULL && curr_prefix_arg != NULL
Packit bbfece
      /* Optimization: if orig_prefix and curr_prefix are equal, the
Packit bbfece
	 relocation is a nop.  */
Packit bbfece
      && strcmp (orig_prefix_arg, curr_prefix_arg) != 0)
Packit bbfece
    {
Packit bbfece
      /* Duplicate the argument strings.  */
Packit bbfece
      char *memory;
Packit bbfece
Packit bbfece
      orig_prefix_len = strlen (orig_prefix_arg);
Packit bbfece
      curr_prefix_len = strlen (curr_prefix_arg);
Packit bbfece
      memory = (char *) xmalloc (orig_prefix_len + 1 + curr_prefix_len + 1);
Packit bbfece
#ifdef NO_XMALLOC
Packit bbfece
      if (memory != NULL)
Packit bbfece
#endif
Packit bbfece
	{
Packit bbfece
	  memcpy (memory, orig_prefix_arg, orig_prefix_len + 1);
Packit bbfece
	  orig_prefix = memory;
Packit bbfece
	  memory += orig_prefix_len + 1;
Packit bbfece
	  memcpy (memory, curr_prefix_arg, curr_prefix_len + 1);
Packit bbfece
	  curr_prefix = memory;
Packit bbfece
	  return;
Packit bbfece
	}
Packit bbfece
    }
Packit bbfece
  orig_prefix = NULL;
Packit bbfece
  curr_prefix = NULL;
Packit bbfece
  /* Don't worry about wasted memory here - this function is usually only
Packit bbfece
     called once.  */
Packit bbfece
}
Packit bbfece
Packit bbfece
/* Sets the original and the current installation prefix of the package.
Packit bbfece
   Relocation simply replaces a pathname starting with the original prefix
Packit bbfece
   by the corresponding pathname with the current prefix instead.  Both
Packit bbfece
   prefixes should be directory names without trailing slash (i.e. use ""
Packit bbfece
   instead of "/").  */
Packit bbfece
void
Packit bbfece
set_relocation_prefix (const char *orig_prefix_arg, const char *curr_prefix_arg)
Packit bbfece
{
Packit bbfece
  set_this_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
Packit bbfece
Packit bbfece
  /* Now notify all dependent libraries.  */
Packit bbfece
#if DEPENDS_ON_LIBCHARSET
Packit bbfece
  libcharset_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
Packit bbfece
#endif
Packit bbfece
#if DEPENDS_ON_LIBICONV && HAVE_ICONV && _LIBICONV_VERSION >= 0x0109
Packit bbfece
  libiconv_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
Packit bbfece
#endif
Packit bbfece
#if DEPENDS_ON_LIBINTL && ENABLE_NLS && defined libintl_set_relocation_prefix
Packit bbfece
  libintl_set_relocation_prefix (orig_prefix_arg, curr_prefix_arg);
Packit bbfece
#endif
Packit bbfece
}
Packit bbfece
Packit bbfece
/* Convenience function:
Packit bbfece
   Computes the current installation prefix, based on the original
Packit bbfece
   installation prefix, the original installation directory of a particular
Packit bbfece
   file, and the current pathname of this file.  Returns NULL upon failure.  */
Packit bbfece
#ifdef IN_LIBRARY
Packit bbfece
#define compute_curr_prefix local_compute_curr_prefix
Packit bbfece
static
Packit bbfece
#endif
Packit bbfece
const char *
Packit bbfece
compute_curr_prefix (const char *orig_installprefix,
Packit bbfece
		     const char *orig_installdir,
Packit bbfece
		     const char *curr_pathname)
Packit bbfece
{
Packit bbfece
  const char *curr_installdir;
Packit bbfece
  const char *rel_installdir;
Packit bbfece
Packit bbfece
  if (curr_pathname == NULL)
Packit bbfece
    return NULL;
Packit bbfece
Packit bbfece
  /* Determine the relative installation directory, relative to the prefix.
Packit bbfece
     This is simply the difference between orig_installprefix and
Packit bbfece
     orig_installdir.  */
Packit bbfece
  if (strncmp (orig_installprefix, orig_installdir, strlen (orig_installprefix))
Packit bbfece
      != 0)
Packit bbfece
    /* Shouldn't happen - nothing should be installed outside $(prefix).  */
Packit bbfece
    return NULL;
Packit bbfece
  rel_installdir = orig_installdir + strlen (orig_installprefix);
Packit bbfece
Packit bbfece
  /* Determine the current installation directory.  */
Packit bbfece
  {
Packit bbfece
    const char *p_base = curr_pathname + FILESYSTEM_PREFIX_LEN (curr_pathname);
Packit bbfece
    const char *p = curr_pathname + strlen (curr_pathname);
Packit bbfece
    char *q;
Packit bbfece
Packit bbfece
    while (p > p_base)
Packit bbfece
      {
Packit bbfece
	p--;
Packit bbfece
	if (ISSLASH (*p))
Packit bbfece
	  break;
Packit bbfece
      }
Packit bbfece
Packit bbfece
    q = (char *) xmalloc (p - curr_pathname + 1);
Packit bbfece
#ifdef NO_XMALLOC
Packit bbfece
    if (q == NULL)
Packit bbfece
      return NULL;
Packit bbfece
#endif
Packit bbfece
    memcpy (q, curr_pathname, p - curr_pathname);
Packit bbfece
    q[p - curr_pathname] = '\0';
Packit bbfece
    curr_installdir = q;
Packit bbfece
  }
Packit bbfece
Packit bbfece
  /* Compute the current installation prefix by removing the trailing
Packit bbfece
     rel_installdir from it.  */
Packit bbfece
  {
Packit bbfece
    const char *rp = rel_installdir + strlen (rel_installdir);
Packit bbfece
    const char *cp = curr_installdir + strlen (curr_installdir);
Packit bbfece
    const char *cp_base =
Packit bbfece
      curr_installdir + FILESYSTEM_PREFIX_LEN (curr_installdir);
Packit bbfece
Packit bbfece
    while (rp > rel_installdir && cp > cp_base)
Packit bbfece
      {
Packit bbfece
	bool same = false;
Packit bbfece
	const char *rpi = rp;
Packit bbfece
	const char *cpi = cp;
Packit bbfece
Packit bbfece
	while (rpi > rel_installdir && cpi > cp_base)
Packit bbfece
	  {
Packit bbfece
	    rpi--;
Packit bbfece
	    cpi--;
Packit bbfece
	    if (ISSLASH (*rpi) || ISSLASH (*cpi))
Packit bbfece
	      {
Packit bbfece
		if (ISSLASH (*rpi) && ISSLASH (*cpi))
Packit bbfece
		  same = true;
Packit bbfece
		break;
Packit bbfece
	      }
Packit bbfece
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
Packit bbfece
	    /* Win32, OS/2, DOS - case insignificant filesystem */
Packit bbfece
	    if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
Packit bbfece
		!= (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
Packit bbfece
	      break;
Packit bbfece
#else
Packit bbfece
	    if (*rpi != *cpi)
Packit bbfece
	      break;
Packit bbfece
#endif
Packit bbfece
	  }
Packit bbfece
	if (!same)
Packit bbfece
	  break;
Packit bbfece
	/* The last pathname component was the same.  opi and cpi now point
Packit bbfece
	   to the slash before it.  */
Packit bbfece
	rp = rpi;
Packit bbfece
	cp = cpi;
Packit bbfece
      }
Packit bbfece
Packit bbfece
    if (rp > rel_installdir)
Packit bbfece
      /* Unexpected: The curr_installdir does not end with rel_installdir.  */
Packit bbfece
      return NULL;
Packit bbfece
Packit bbfece
    {
Packit bbfece
      size_t curr_prefix_len = cp - curr_installdir;
Packit bbfece
      char *curr_prefix;
Packit bbfece
Packit bbfece
      curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
Packit bbfece
#ifdef NO_XMALLOC
Packit bbfece
      if (curr_prefix == NULL)
Packit bbfece
	return NULL;
Packit bbfece
#endif
Packit bbfece
      memcpy (curr_prefix, curr_installdir, curr_prefix_len);
Packit bbfece
      curr_prefix[curr_prefix_len] = '\0';
Packit bbfece
Packit bbfece
      return curr_prefix;
Packit bbfece
    }
Packit bbfece
  }
Packit bbfece
}
Packit bbfece
Packit bbfece
#if defined PIC && defined INSTALLDIR
Packit bbfece
Packit bbfece
/* Full pathname of shared library, or NULL.  */
Packit bbfece
static char *shared_library_fullname;
Packit bbfece
Packit bbfece
#if defined _WIN32 || defined __WIN32__
Packit bbfece
Packit bbfece
/* Determine the full pathname of the shared library when it is loaded.  */
Packit bbfece
Packit bbfece
BOOL WINAPI
Packit bbfece
DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
Packit bbfece
{
Packit bbfece
  (void) reserved;
Packit bbfece
Packit bbfece
  if (event == DLL_PROCESS_ATTACH)
Packit bbfece
    {
Packit bbfece
      /* The DLL is being loaded into an application's address range.  */
Packit bbfece
      static char location[MAX_PATH];
Packit bbfece
Packit bbfece
      if (!GetModuleFileName (module_handle, location, sizeof (location)))
Packit bbfece
	/* Shouldn't happen.  */
Packit bbfece
	return FALSE;
Packit bbfece
Packit bbfece
      if (!IS_PATH_WITH_DIR (location))
Packit bbfece
	/* Shouldn't happen.  */
Packit bbfece
	return FALSE;
Packit bbfece
Packit bbfece
      shared_library_fullname = strdup (location);
Packit bbfece
    }
Packit bbfece
Packit bbfece
  return TRUE;
Packit bbfece
}
Packit bbfece
Packit bbfece
#else /* Unix */
Packit bbfece
Packit bbfece
static void
Packit bbfece
find_shared_library_fullname ()
Packit bbfece
{
Packit bbfece
#ifdef __linux__
Packit bbfece
  FILE *fp;
Packit bbfece
Packit bbfece
  /* Open the current process' maps file.  It describes one VMA per line.  */
Packit bbfece
  fp = fopen ("/proc/self/maps", "r");
Packit bbfece
  if (fp)
Packit bbfece
    {
Packit bbfece
      unsigned long address = (unsigned long) &find_shared_library_fullname;
Packit bbfece
      for (;;)
Packit bbfece
	{
Packit bbfece
	  unsigned long start, end;
Packit bbfece
	  int c;
Packit bbfece
Packit bbfece
	  if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
Packit bbfece
	    break;
Packit bbfece
	  if (address >= start && address <= end - 1)
Packit bbfece
	    {
Packit bbfece
	      /* Found it.  Now see if this line contains a filename.  */
Packit bbfece
	      while (c = getc (fp), c != EOF && c != '\n' && c != '/')
Packit bbfece
		continue;
Packit bbfece
	      if (c == '/')
Packit bbfece
		{
Packit bbfece
		  size_t size;
Packit bbfece
		  int len;
Packit bbfece
Packit bbfece
		  ungetc (c, fp);
Packit bbfece
		  shared_library_fullname = NULL; size = 0;
Packit bbfece
		  len = getline (&shared_library_fullname, &size, fp);
Packit bbfece
		  if (len >= 0)
Packit bbfece
		    {
Packit bbfece
		      /* Success: filled shared_library_fullname.  */
Packit bbfece
		      if (len > 0 && shared_library_fullname[len - 1] == '\n')
Packit bbfece
			shared_library_fullname[len - 1] = '\0';
Packit bbfece
		    }
Packit bbfece
		}
Packit bbfece
	      break;
Packit bbfece
	    }
Packit bbfece
	  while (c = getc (fp), c != EOF && c != '\n')
Packit bbfece
	    continue;
Packit bbfece
	}
Packit bbfece
      fclose (fp);
Packit bbfece
    }
Packit bbfece
#endif
Packit bbfece
}
Packit bbfece
Packit bbfece
#endif /* WIN32 / Unix */
Packit bbfece
Packit bbfece
/* Return the full pathname of the current shared library.
Packit bbfece
   Return NULL if unknown.
Packit bbfece
   Guaranteed to work only on Linux and Woe32.  */
Packit bbfece
static char *
Packit bbfece
get_shared_library_fullname ()
Packit bbfece
{
Packit bbfece
#if !(defined _WIN32 || defined __WIN32__)
Packit bbfece
  static bool tried_find_shared_library_fullname;
Packit bbfece
  if (!tried_find_shared_library_fullname)
Packit bbfece
    {
Packit bbfece
      find_shared_library_fullname ();
Packit bbfece
      tried_find_shared_library_fullname = true;
Packit bbfece
    }
Packit bbfece
#endif
Packit bbfece
  return shared_library_fullname;
Packit bbfece
}
Packit bbfece
Packit bbfece
#endif /* PIC */
Packit bbfece
Packit bbfece
/* Returns the pathname, relocated according to the current installation
Packit bbfece
   directory.  */
Packit bbfece
const char *
Packit bbfece
relocate (const char *pathname)
Packit bbfece
{
Packit bbfece
#if defined PIC && defined INSTALLDIR
Packit bbfece
  static int initialized;
Packit bbfece
Packit bbfece
  /* Initialization code for a shared library.  */
Packit bbfece
  if (!initialized)
Packit bbfece
    {
Packit bbfece
      /* At this point, orig_prefix and curr_prefix likely have already been
Packit bbfece
	 set through the main program's set_program_name_and_installdir
Packit bbfece
	 function.  This is sufficient in the case that the library has
Packit bbfece
	 initially been installed in the same orig_prefix.  But we can do
Packit bbfece
	 better, to also cover the cases that 1. it has been installed
Packit bbfece
	 in a different prefix before being moved to orig_prefix and (later)
Packit bbfece
	 to curr_prefix, 2. unlike the program, it has not moved away from
Packit bbfece
	 orig_prefix.  */
Packit bbfece
      const char *orig_installprefix = INSTALLPREFIX;
Packit bbfece
      const char *orig_installdir = INSTALLDIR;
Packit bbfece
      const char *curr_prefix_better;
Packit bbfece
Packit bbfece
      curr_prefix_better =
Packit bbfece
	compute_curr_prefix (orig_installprefix, orig_installdir,
Packit bbfece
			     get_shared_library_fullname ());
Packit bbfece
      if (curr_prefix_better == NULL)
Packit bbfece
	curr_prefix_better = curr_prefix;
Packit bbfece
Packit bbfece
      set_relocation_prefix (orig_installprefix, curr_prefix_better);
Packit bbfece
Packit bbfece
      initialized = 1;
Packit bbfece
    }
Packit bbfece
#endif
Packit bbfece
Packit bbfece
  /* Note: It is not necessary to perform case insensitive comparison here,
Packit bbfece
     even for DOS-like filesystems, because the pathname argument was
Packit bbfece
     typically created from the same Makefile variable as orig_prefix came
Packit bbfece
     from.  */
Packit bbfece
  if (orig_prefix != NULL && curr_prefix != NULL
Packit bbfece
      && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
Packit bbfece
    {
Packit bbfece
      if (pathname[orig_prefix_len] == '\0')
Packit bbfece
	/* pathname equals orig_prefix.  */
Packit bbfece
	return curr_prefix;
Packit bbfece
      if (ISSLASH (pathname[orig_prefix_len]))
Packit bbfece
	{
Packit bbfece
	  /* pathname starts with orig_prefix.  */
Packit bbfece
	  const char *pathname_tail = &pathname[orig_prefix_len];
Packit bbfece
	  char *result =
Packit bbfece
	    (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
Packit bbfece
Packit bbfece
#ifdef NO_XMALLOC
Packit bbfece
	  if (result != NULL)
Packit bbfece
#endif
Packit bbfece
	    {
Packit bbfece
	      memcpy (result, curr_prefix, curr_prefix_len);
Packit bbfece
	      strcpy (result + curr_prefix_len, pathname_tail);
Packit bbfece
	      return result;
Packit bbfece
	    }
Packit bbfece
	}
Packit bbfece
    }
Packit bbfece
  /* Nothing to relocate.  */
Packit bbfece
  return pathname;
Packit bbfece
}
Packit bbfece
Packit bbfece
#endif