Statistics
| Revision:

root / intl / relocatable.c @ 1

History | View | Annotate | Download (12.7 KB)

1
/* Provide relocatable packages.
2
   Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
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__
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 __EMX__ || defined __DJGPP__
76
  /* Win32, 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 FILESYSTEM_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 FILESYSTEM_PREFIX_LEN(P) 0
89
#endif
90

    
91
/* Original installation prefix.  */
92
static char *orig_prefix;
93
static size_t orig_prefix_len;
94
/* Current installation prefix.  */
95
static char *curr_prefix;
96
static 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 "/").  */
105
static void
106
set_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 "/").  */
143
void
144
set_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
168
static
169
#endif
170
const char *
171
compute_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 + FILESYSTEM_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 + FILESYSTEM_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
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
238
            /* Win32, OS/2, DOS - case insignificant filesystem */
239
            if ((*rpi >= 'a' && *rpi <= 'z' ? *rpi - 'a' + 'A' : *rpi)
240
                != (*cpi >= 'a' && *cpi <= 'z' ? *cpi - 'a' + 'A' : *cpi))
241
              break;
242
#else
243
            if (*rpi != *cpi)
244
              break;
245
#endif
246
          }
247
        if (!same)
248
          break;
249
        /* The last pathname component was the same.  opi and cpi now point
250
           to the slash before it.  */
251
        rp = rpi;
252
        cp = cpi;
253
      }
254

    
255
    if (rp > rel_installdir)
256
      /* Unexpected: The curr_installdir does not end with rel_installdir.  */
257
      return NULL;
258

    
259
    {
260
      size_t curr_prefix_len = cp - curr_installdir;
261
      char *curr_prefix;
262

    
263
      curr_prefix = (char *) xmalloc (curr_prefix_len + 1);
264
#ifdef NO_XMALLOC
265
      if (curr_prefix == NULL)
266
        return NULL;
267
#endif
268
      memcpy (curr_prefix, curr_installdir, curr_prefix_len);
269
      curr_prefix[curr_prefix_len] = '\0';
270

    
271
      return curr_prefix;
272
    }
273
  }
274
}
275

    
276
#endif /* !IN_LIBRARY || PIC */
277

    
278
#if defined PIC && defined INSTALLDIR
279

    
280
/* Full pathname of shared library, or NULL.  */
281
static char *shared_library_fullname;
282

    
283
#if defined _WIN32 || defined __WIN32__
284

    
285
/* Determine the full pathname of the shared library when it is loaded.  */
286

    
287
BOOL WINAPI
288
DllMain (HINSTANCE module_handle, DWORD event, LPVOID reserved)
289
{
290
  (void) reserved;
291

    
292
  if (event == DLL_PROCESS_ATTACH)
293
    {
294
      /* The DLL is being loaded into an application's address range.  */
295
      static char location[MAX_PATH];
296

    
297
      if (!GetModuleFileName (module_handle, location, sizeof (location)))
298
        /* Shouldn't happen.  */
299
        return FALSE;
300

    
301
      if (!IS_PATH_WITH_DIR (location))
302
        /* Shouldn't happen.  */
303
        return FALSE;
304

    
305
      shared_library_fullname = strdup (location);
306
    }
307

    
308
  return TRUE;
309
}
310

    
311
#else /* Unix */
312

    
313
static void
314
find_shared_library_fullname ()
315
{
316
#if defined __linux__ && __GLIBC__ >= 2
317
  /* Linux has /proc/self/maps. glibc 2 has the getline() function.  */
318
  FILE *fp;
319

    
320
  /* Open the current process' maps file.  It describes one VMA per line.  */
321
  fp = fopen ("/proc/self/maps", "r");
322
  if (fp)
323
    {
324
      unsigned long address = (unsigned long) &find_shared_library_fullname;
325
      for (;;)
326
        {
327
          unsigned long start, end;
328
          int c;
329

    
330
          if (fscanf (fp, "%lx-%lx", &start, &end) != 2)
331
            break;
332
          if (address >= start && address <= end - 1)
333
            {
334
              /* Found it.  Now see if this line contains a filename.  */
335
              while (c = getc (fp), c != EOF && c != '\n' && c != '/')
336
                continue;
337
              if (c == '/')
338
                {
339
                  size_t size;
340
                  int len;
341

    
342
                  ungetc (c, fp);
343
                  shared_library_fullname = NULL; size = 0;
344
                  len = getline (&shared_library_fullname, &size, fp);
345
                  if (len >= 0)
346
                    {
347
                      /* Success: filled shared_library_fullname.  */
348
                      if (len > 0 && shared_library_fullname[len - 1] == '\n')
349
                        shared_library_fullname[len - 1] = '\0';
350
                    }
351
                }
352
              break;
353
            }
354
          while (c = getc (fp), c != EOF && c != '\n')
355
            continue;
356
        }
357
      fclose (fp);
358
    }
359
#endif
360
}
361

    
362
#endif /* WIN32 / Unix */
363

    
364
/* Return the full pathname of the current shared library.
365
   Return NULL if unknown.
366
   Guaranteed to work only on Linux and Woe32.  */
367
static char *
368
get_shared_library_fullname ()
369
{
370
#if !(defined _WIN32 || defined __WIN32__)
371
  static bool tried_find_shared_library_fullname;
372
  if (!tried_find_shared_library_fullname)
373
    {
374
      find_shared_library_fullname ();
375
      tried_find_shared_library_fullname = true;
376
    }
377
#endif
378
  return shared_library_fullname;
379
}
380

    
381
#endif /* PIC */
382

    
383
/* Returns the pathname, relocated according to the current installation
384
   directory.  */
385
const char *
386
relocate (const char *pathname)
387
{
388
#if defined PIC && defined INSTALLDIR
389
  static int initialized;
390

    
391
  /* Initialization code for a shared library.  */
392
  if (!initialized)
393
    {
394
      /* At this point, orig_prefix and curr_prefix likely have already been
395
         set through the main program's set_program_name_and_installdir
396
         function.  This is sufficient in the case that the library has
397
         initially been installed in the same orig_prefix.  But we can do
398
         better, to also cover the cases that 1. it has been installed
399
         in a different prefix before being moved to orig_prefix and (later)
400
         to curr_prefix, 2. unlike the program, it has not moved away from
401
         orig_prefix.  */
402
      const char *orig_installprefix = INSTALLPREFIX;
403
      const char *orig_installdir = INSTALLDIR;
404
      const char *curr_prefix_better;
405

    
406
      curr_prefix_better =
407
        compute_curr_prefix (orig_installprefix, orig_installdir,
408
                             get_shared_library_fullname ());
409
      if (curr_prefix_better == NULL)
410
        curr_prefix_better = curr_prefix;
411

    
412
      set_relocation_prefix (orig_installprefix, curr_prefix_better);
413

    
414
      initialized = 1;
415
    }
416
#endif
417

    
418
  /* Note: It is not necessary to perform case insensitive comparison here,
419
     even for DOS-like filesystems, because the pathname argument was
420
     typically created from the same Makefile variable as orig_prefix came
421
     from.  */
422
  if (orig_prefix != NULL && curr_prefix != NULL
423
      && strncmp (pathname, orig_prefix, orig_prefix_len) == 0)
424
    {
425
      if (pathname[orig_prefix_len] == '\0')
426
        /* pathname equals orig_prefix.  */
427
        return curr_prefix;
428
      if (ISSLASH (pathname[orig_prefix_len]))
429
        {
430
          /* pathname starts with orig_prefix.  */
431
          const char *pathname_tail = &pathname[orig_prefix_len];
432
          char *result =
433
            (char *) xmalloc (curr_prefix_len + strlen (pathname_tail) + 1);
434

    
435
#ifdef NO_XMALLOC
436
          if (result != NULL)
437
#endif
438
            {
439
              memcpy (result, curr_prefix, curr_prefix_len);
440
              strcpy (result + curr_prefix_len, pathname_tail);
441
              return result;
442
            }
443
        }
444
    }
445
  /* Nothing to relocate.  */
446
  return pathname;
447
}
448

    
449
#endif