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