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