From b00d99e8c5b045acdacae73d946dd8147a8885c8 Mon Sep 17 00:00:00 2001 From: Erwin Waterlander Date: Wed, 5 Aug 2015 23:36:03 +0100 Subject: [PATCH 02/15] Relocate libintl The relocatex-libintl patch adds builtin relocation for executables to the libintl dll. With this patch the programs are automatically relocatable. There is no need anymore to add relocation code to your program when you use this libintl DLL. The patch was ported from the GnuWin32 port of libintl, which has also builtin relacation support. At the moment the relocation support is only active if you compile with MinGW for Windows. If you compile for Unix/Linux/Cygwin the functionality is unchanged. See also: http://waterlan.home.xs4all.nl/libintl.html http://sourceforge.net/tracker/?func=detail&atid=302435&aid=3003879&group_id=2435 GnuWin32: http://gnuwin32.sourceforge.net/ Great thanks to GnuWin32 maintainer Kees Zeelenberg. Erwin Waterlander waterlan@xs4all.nl http://waterlan.home.xs4all.nl/ Additional "bogus paths for *nix-style paths" fix by Alexey Pavlov [jes: fix for 64-bit] Signed-off-by: Johannes Schindelin --- intl/Makefile.in | 8 +- intl/bindtextdom.c | 22 ++++ intl/canonicalize.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++ intl/canonicalize.h | 18 +++ intl/relocatex.c | 284 +++++++++++++++++++++++++++++++++++++++++++ intl/relocatex.h | 41 +++++++ 6 files changed, 715 insertions(+), 1 deletion(-) create mode 100644 intl/canonicalize.c create mode 100644 intl/canonicalize.h create mode 100644 intl/relocatex.c create mode 100644 intl/relocatex.h diff --git a/intl/Makefile.in b/intl/Makefile.in index 3dd0b7f..e2b8666 100644 --- a/intl/Makefile.in +++ b/intl/Makefile.in @@ -53,6 +53,7 @@ DEFS = -DHAVE_CONFIG_H COMPILE = $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(DEFS-$@) $(INCLUDES) HEADERS = \ + canonicalize.h \ gmo.h \ gettextP.h \ hash-string.h \ @@ -61,6 +62,7 @@ HEADERS = \ eval-plural.h \ localcharset.h \ relocatable.h \ + relocatex.h \ libgnuintl.h SOURCES = \ bindtextdom.c \ @@ -81,6 +83,8 @@ SOURCES = \ plural-exp.c \ localcharset.c \ relocatable.c \ + relocatex.c \ + canonicalize.c \ localename.c \ log.c \ osdep.c \ @@ -104,6 +108,8 @@ OBJECTS = \ plural-exp.o \ localcharset.o \ relocatable.o \ + relocatex.o \ + canonicalize.o \ localename.o \ log.o \ osdep.o \ @@ -158,7 +164,7 @@ install-info install-dvi install-ps install-pdf install-html: $(OBJECTS): config.h libintl.h bindtextdom.o dcgettext.o dcigettext.o dcngettext.o dgettext.o \ dngettext.o finddomain.o gettext.o intl-compat.o loadmsgcat.o \ -localealias.o ngettext.o textdomain.o: gettextP.h gmo.h loadinfo.h +localealias.o ngettext.o textdomain.o: gettextP.h gmo.h loadinfo.h relocatex.h dcigettext.o loadmsgcat.o: hash-string.h explodename.o l10nflist.o: loadinfo.h dcigettext.o loadmsgcat.o plural.o plural-exp.o: plural-exp.h diff --git a/intl/bindtextdom.c b/intl/bindtextdom.c index 6faac57..b7a62d5 100644 --- a/intl/bindtextdom.c +++ b/intl/bindtextdom.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef _LIBC # include @@ -91,6 +92,12 @@ static void set_binding_values PARAMS ((const char *domainname, const char **dirnamep, const char **codesetp)); +#if ENABLE_RELOCATABLE +# include "relocatex.h" +#else +# define relocate(pathname) (pathname) +#endif + /* Specifies the directory name *DIRNAMEP and the output codeset *CODESETP to be used for the DOMAINNAME message catalog. If *DIRNAMEP or *CODESETP is NULL, the corresponding attribute is not @@ -352,8 +359,23 @@ BINDTEXTDOMAIN (domainname, dirname) const char *domainname; const char *dirname; { +/* set_binding_values (domainname, &dirname, NULL); return (char *) dirname; +*/ + if (!access (dirname, R_OK)) { + set_binding_values (domainname, &dirname, NULL); + return (char *) dirname; + } else { + char *locale_dirname, *installdir = strdup (dirname), *s; + if ((s = strrchr (installdir, '/'))) *s = '\0'; + if ((s = strrchr (installdir, '/'))) *s = '\0'; + locale_dirname = relocatex (installdir, dirname); + set_binding_values (domainname, (const char **) &locale_dirname, NULL); + if (installdir) + free (installdir); + return (char *) locale_dirname; + } } /* Specify the character encoding in which the messages from the diff --git a/intl/canonicalize.c b/intl/canonicalize.c new file mode 100644 index 0000000..5217f30 --- /dev/null +++ b/intl/canonicalize.c @@ -0,0 +1,343 @@ +/* Return the canonical absolute name of a given file. + Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __WIN32__ +# include +# include +//# include +#endif /* __WIN32__ */ +#include "canonicalize.h" + +#ifndef MAXSYMLINKS +# define MAXSYMLINKS 20 +#endif + +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +# ifdef VMS + /* We want the directory in Unix syntax, not in VMS syntax. */ +# define __getcwd(buf, max) getcwd (buf, max, 0) +# else +# define __getcwd getcwd +# endif + +#define weak_alias(local, symbol) + +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__ + /* Win32, Cygwin, OS/2, DOS */ +# define ISDIRSEP(C) ((C) == '/' || (C) == '\\') +#else + /* Unix */ +# define ISDIRSEP(C) ((C) == '/') +#endif + +#ifdef __WIN32__ +char *win2unixpath (char *FileName) +{ + char *s = FileName; + while (*s) { + if (*s == '\\') + *s = '/'; + *s++; + } + return FileName; +} +#endif + +/* Return the canonical absolute name of file NAME. A canonical name + does not contain any `.', `..' components nor any repeated path + separators ('/') or symlinks. All path components must exist. If + RESOLVED is null, the result is malloc'd; otherwise, if the + canonical name is PATH_MAX chars or more, returns null with `errno' + set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars, + returns the name in RESOLVED. If the name cannot be resolved and + RESOLVED is non-NULL, it contains the path of the first component + that cannot be resolved. If the path can be resolved, RESOLVED + holds the same value as the value returned. + RESOLVED must be at least PATH_MAX long */ + +static char * +canonicalize (const char *name, char *resolved) +{ + char *rpath, *dest, *extra_buf = NULL; + const char *start, *end, *rpath_limit; + long int path_max; + int num_links = 0, old_errno; + + if (name == NULL) + { + /* As per Single Unix Specification V2 we must return an error if + either parameter is a null pointer. We extend this to allow + the RESOLVED parameter to be NULL in case the we are expected to + allocate the room for the return value. */ + __set_errno (EINVAL); + return NULL; + } + + if (name[0] == '\0') + { + /* As per Single Unix Specification V2 we must return an error if + the name argument points to an empty string. */ + __set_errno (ENOENT); + return NULL; + } +#ifdef __WIN32__ + { + char *lpFilePart; + int len; +// fprintf(stderr, "name: %s\n", name); + rpath = resolved ? __builtin_alloca (MAX_PATH) : malloc (MAX_PATH); +// unix2winpath (name); +// fprintf(stderr, "name: %s\n", name); +len = GetFullPathName(name, MAX_PATH, rpath, &lpFilePart); +/* GetFullPathName returns bogus paths for *nix-style paths, like + * /foo/bar - it just prepends current drive to them. Keep them + * intact (they need to be for relocation to work!). + */ +if (name[0] == '/') { + strncpy (rpath, name, MAX_PATH - 1); + rpath[MAX_PATH - 1] = '\0'; + len = strlen (rpath); +} +// fprintf(stderr, "rpath: %s\n", rpath); + if (len == 0) { + //set_werrno; + return NULL; + } + if (len > MAX_PATH) { + if (resolved) + __set_errno(ENAMETOOLONG); + else { + rpath = realloc(rpath, len + 2); + GetFullPathName(name, len, rpath, &lpFilePart); +// fprintf(stderr, "rpath: %s\n", rpath); + } + } +// if ( ISDIRSEP(name[strlen(name)]) && !ISDIRSEP(rpath[len]) ) { +// rpath[len] = '\\'; +// rpath[len + 1] = 0; +// } + old_errno = errno; + //if (!access (rpath, D_OK) && !ISDIRSEP(rpath[len - 1]) ){ + if (!access (rpath, R_OK) && !ISDIRSEP(rpath[len - 1]) ){ + rpath[len] = '\\'; + rpath[len + 1] = 0; + } + errno = old_errno; + win2unixpath (rpath); +// fprintf(stderr, "rpath: %s\n", rpath); + return resolved ? strcpy(resolved, rpath) : rpath ; + } +#else /* __WIN32__ */ + +#ifdef PATH_MAX + path_max = PATH_MAX; +#else + path_max = pathconf (name, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 1024; +#endif + + rpath = resolved ? __builtin_alloca (path_max) : malloc (path_max); + rpath_limit = rpath + path_max; + + if (name[0] != '/') + { + if (!__getcwd (rpath, path_max)) + { + rpath[0] = '\0'; + goto error; + } + dest = strchr (rpath, '\0'); + } + else + { + rpath[0] = '/'; + dest = rpath + 1; + } + + for (start = end = name; *start; start = end) + { +#ifdef _LIBC + struct stat64 st; +#else + struct stat st; +#endif + int n; + + /* Skip sequence of multiple path-separators. */ + while (*start == '/') + ++start; + + /* Find end of path component. */ + for (end = start; *end && *end != '/'; ++end) + /* Nothing. */; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') + /* nothing */; + else if (end - start == 2 && start[0] == '.' && start[1] == '.') + { + /* Back up to previous component, ignore if at root already. */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/'); + } + else + { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) + { + ptrdiff_t dest_offset = dest - rpath; + + if (resolved) + { + __set_errno (ENAMETOOLONG); + if (dest > rpath + 1) + dest--; + *dest = '\0'; + goto error; + } + new_size = rpath_limit - rpath; + if (end - start + 1 > path_max) + new_size += end - start + 1; + else + new_size += path_max; + rpath = realloc (rpath, new_size); + rpath_limit = rpath + new_size; + if (rpath == NULL) + return NULL; + + dest = rpath + dest_offset; + } + +#ifdef _LIBC + dest = __mempcpy (dest, start, end - start); +#else + memcpy (dest, start, end - start); + dest += end - start; +#endif + *dest = '\0'; + +#ifdef _LIBC + if (__lxstat64 (_STAT_VER, rpath, &st) < 0) +#else + if (lstat (rpath, &st) < 0) +#endif + goto error; + +#if HAVE_READLINK + if (S_ISLNK (st.st_mode)) + { + char *buf = __builtin_alloca (path_max); + size_t len; + + if (++num_links > MAXSYMLINKS) + { + __set_errno (ELOOP); + goto error; + } + + n = __readlink (rpath, buf, path_max); + if (n < 0) + goto error; + buf[n] = '\0'; + + if (!extra_buf) + extra_buf = __builtin_alloca (path_max); + + len = strlen (end); + if ((long int) (n + len) >= path_max) + { + __set_errno (ENAMETOOLONG); + goto error; + } + + /* Careful here, end may be a pointer into extra_buf... */ + memmove (&extra_buf[n], end, len + 1); + name = end = memcpy (extra_buf, buf, n); + + if (buf[0] == '/') + dest = rpath + 1; /* It's an absolute symlink */ + else + /* Back up to previous component, ignore if at root already: */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/'); + } +#endif + } + } + if (dest > rpath + 1 && dest[-1] == '/') + --dest; + *dest = '\0'; + + return resolved ? memcpy (resolved, rpath, dest - rpath + 1) : rpath; + +error: + if (resolved) + strcpy (resolved, rpath); + else + free (rpath); + return NULL; + +#endif /* __WIN32__ */ +} + + +char * +__realpath (const char *name, char *resolved) +{ + if (resolved == NULL) + { + __set_errno (EINVAL); + return NULL; + } + + return canonicalize (name, resolved); +} +weak_alias (__realpath, realpath) + + +char * +__canonicalize_file_name (const char *name) +{ + return canonicalize (name, NULL); +} +weak_alias (__canonicalize_file_name, canonicalize_file_name) + +char * +canonicalize_file_name (const char *name) +{ + return canonicalize (name, NULL); +} diff --git a/intl/canonicalize.h b/intl/canonicalize.h new file mode 100644 index 0000000..ea707bf --- /dev/null +++ b/intl/canonicalize.h @@ -0,0 +1,18 @@ +#ifndef __CANONICALIZE_H__ +#define __CANONICALIZE_H__ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +char *canonicalize_file_name (const char *name); + +#ifdef __WIN32__ +char *win2unixpath (char *path); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __CANONICALIZE_H__ */ diff --git a/intl/relocatex.c b/intl/relocatex.c new file mode 100644 index 0000000..a2b7438 --- /dev/null +++ b/intl/relocatex.c @@ -0,0 +1,284 @@ +/* Provide relocatable packages. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + + +/* Specification. */ +#include +#define _GNU_SOURCE +#include +#include +#include +#include +/* #include */ +#include "relocatex.h" +#include "canonicalize.h" +/* #include */ + + +#if defined _WIN32 || defined __WIN32__ +# define WIN32_LEAN_AND_MEAN +# include +//# define __GW32__ +//# include +#endif +#define set_werrno + +#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__ + /* Win32, Cygwin, OS/2, DOS */ +# define ISDIRSEP(C) ((C) == '/' || (C) == '\\') +#else + /* Unix */ +# define ISDIRSEP(C) ((C) == '/') +#endif + +/* Original installation prefix. */ +static char *orig_prefix = NULL; +static size_t orig_prefix_len = 0; +/* Current installation prefix. */ +static char *curr_prefix = NULL; +static size_t curr_prefix_len = 0; +/* These prefixes do not end in a slash. Anything that will be concatenated + to them must start with a slash. */ + + +int win2posixpath (const char *winpath, char *posixpath) +{ + strcpy (posixpath, winpath); + win2unixpath (posixpath); + return 0; +} + + +/* Sets the original and the current installation prefix of this module. + Relocation simply replaces a pathname starting with the original prefix + by the corresponding pathname with the current prefix instead. Both + prefixes should be directory names without trailing slash (i.e. use "" + instead of "/"). */ +static char * +set_orig_prefix (const char *orig_prefix_arg) +{ + char *memory; +// printf ("orig_prefix_arg: %s\n", orig_prefix_arg); + if (!orig_prefix_arg) { + orig_prefix = NULL; + orig_prefix_len = 0; + return NULL; + } + if (orig_prefix) + free (orig_prefix); + + memory = canonicalize_file_name (orig_prefix_arg); +// printf ("memory: %s\n", memory); +// memory = (char *) malloc (orig_prefix_len + 1); + if (!memory) { + set_werrno; + orig_prefix = NULL; + orig_prefix_len = 0; + return NULL; + } + win2unixpath (memory); +// win2posixpath (orig_prefix_arg, memory); + orig_prefix = memory; + orig_prefix_len = strlen (orig_prefix); +// printf ("orig_prefix: %s\n", orig_prefix); + if (ISDIRSEP (orig_prefix[orig_prefix_len-1])) { + orig_prefix[orig_prefix_len-1] = '\0'; + orig_prefix_len--; + } +// printf ("orig_prefix: %s\n", orig_prefix); +// printf ("orig_prefix_len: %d\n", orig_prefix_len); + return orig_prefix; +} + +#if defined __WIN32__ +static char * +set_current_prefix (const char *ModuleName) +{ + LPTSTR curr_prefix_arg, q, lpFilePart; + DWORD len; + int nDIRSEP = 0; + + if (curr_prefix) + free (curr_prefix); + curr_prefix_arg = malloc (MAX_PATH * sizeof (TCHAR)); + if (!curr_prefix_arg) { + set_werrno; + curr_prefix = NULL; + curr_prefix_len = 0; + return NULL; + } + if (ModuleName) { +// printf ("ModuleName: %s\n", ModuleName); + len = SearchPath (NULL, ModuleName, ".DLL", MAX_PATH, curr_prefix_arg, &lpFilePart); + if (len) { +// printf ("ModulePath: %s\n", curr_prefix_arg); +// printf ("FilePart: %s\n", lpFilePart); + } + } + if (!ModuleName || !len) { + len = GetModuleFileName (NULL, curr_prefix_arg, MAX_PATH); + if (!len) { + set_werrno; + curr_prefix = NULL; + curr_prefix_len = 0; + return NULL; + } + } +// strncpy (curr_prefix_arg, ModuleName, MAX_PATH); +// printf ("curr_prefix_arg: %s\n", curr_prefix_arg); + win2posixpath (curr_prefix_arg, curr_prefix_arg); + curr_prefix = curr_prefix_arg; + q = curr_prefix_arg + len - 1; + /* strip name of executable and its directory */ + while (!ISDIRSEP (*q) && (q > curr_prefix_arg) && nDIRSEP < 2) { + q--; + if (ISDIRSEP (*q)) { + *q = '\0'; + nDIRSEP++; + } + } + curr_prefix_len = q - curr_prefix_arg; +// printf ("curr_prefix: %s\n", curr_prefix); +// printf ("curr_prefix_len: %d\n", curr_prefix_len); + return curr_prefix; +} + +char *getshortpath (const char *longpath) +{ + char *shortpath = NULL; + DWORD len, res; + +// printf ("longpath: %s\n", longpath); + len = GetShortPathName(longpath, shortpath, 0); +// printf ("len: %ld\n", len); + if (!len) { +// WinErr ("getshortpath: len = 0"); + set_werrno; + return (char *) longpath; + } + shortpath = (char *) malloc (len + 1); + if (!shortpath) { +// WinErr ("getshortpath: malloc"); + set_werrno; + return (char *) longpath; + } + res = GetShortPathName(longpath, shortpath, len); +// printf ("res: %ld\n", res); + if (!res) { +// WinErr ("getshortpath: res = 0"); + free (shortpath); + set_werrno; + return (char *) longpath; + } +// printf ("shortpath: %s\n", shortpath); + return shortpath; +} + +char *relocaten (const char *ModuleName, const char *path) +{ + char *relative_path, *relocated_path, *relocated_short_path; + int relative_path_len; + + if (!curr_prefix) + set_current_prefix (ModuleName); +// printf ("path: %s\n", path); +// printf ("orig_prefix: %s\n", orig_prefix); +// printf ("curr_prefix: %s\n", curr_prefix); +// if (strncmp (orig_prefix, path, orig_prefix_len)) +// if (strcmp (orig_prefix, path)) +// return (char *) path; + relative_path = (char *) path + orig_prefix_len; +// printf ("relative_path: %s\n", relative_path); + relative_path_len = strlen (relative_path); + relocated_path = malloc (curr_prefix_len + relative_path_len + 1); + strcpy (relocated_path, curr_prefix); + strcat (relocated_path, relative_path); +// printf ("relocated_path: %s\n", relocated_path); + relocated_short_path = getshortpath (relocated_path); +// printf ("relocated_short_path: %s\n", relocated_short_path); + if (relocated_short_path) { + if (relocated_short_path != relocated_path) + free (relocated_path); + return relocated_short_path; + } else + return relocated_path; +} + +#else // __WIN32__ +char *relocaten (const char *ModuleName, const char *path) +{ + // dummy function for Unix/Linux + return (char *)path; +} +#endif + +char *relocaten2 (const char *ModuleName, const char *installdir, const char *path) +{ + set_orig_prefix (installdir); + return relocaten (ModuleName, path); +} + +char *relocatenx (const char *ModuleName, const char *installdir, const char *path) +{ + char *p; + + set_orig_prefix (installdir); + if (access (path, R_OK)) + p = relocaten (ModuleName, path); + else + p = (char *) path; +// printf ("relocatenx: %s\n", p); + return p; +} + +char *relocate2 (const char *installdir, const char *path) +{ + return relocaten2 (NULL, installdir, path); +} + +char *relocatex (const char *installdir, const char *path) +{ + return relocatenx (NULL, installdir, path); +} + +char *relocatepx (const char *cprefix, const char *installdir, const char *path) +{ + if (curr_prefix) + free (curr_prefix); + curr_prefix = strdup (cprefix); + return relocatex (installdir, path); +} + +static char *get_orig_prefix (void) +{ + return orig_prefix; +} + +static char *get_curr_prefix (void) +{ + return curr_prefix; +} + +static char *set_curr_prefix (const char *ModuleName) +{ + if (curr_prefix) + free (curr_prefix); + set_current_prefix (ModuleName); + return curr_prefix; +} diff --git a/intl/relocatex.h b/intl/relocatex.h new file mode 100644 index 0000000..5cc7c51 --- /dev/null +++ b/intl/relocatex.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GnuWin C Library. + + The GnuWin C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GnuWin C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GnuWin32 C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef __RELOCATE_H__ +#define __RELOCATE_H__ 1 + +/* #include */ + +#ifdef __cplusplus +extern "C" { +#endif + +char *relocaten (const char *ModuleName, const char *path); +char *relocaten2 (const char *ModuleName, const char *installdir, const char *path); +char *relocatenx (const char *ModuleName, const char *installdir, const char *path); +char *relocate2 (const char *installdir, const char *path); +char *relocatex (const char *installdir, const char *path); + +#ifdef __cplusplus +} +#endif + +//#endif /* __GW32__ */ + +#endif /* __RELOCATE_H__ */ -- 2.8.1