[PATCH] Relocatable install, 1/n

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] Relocatable install, 1/n

Paolo Bonzini-2
This patch provides the meat for relocatable installs.  This means that
you can move an installation arbitrarily, for example with:

   # First way, copy to staging directory and move from there.
   ./configure
   make
   make install DESTDIR=`pwd`
   (cd usr/local && tar cvf - .) | (cd $HOME && tar xvf -)

This patch is mostly boring configury.  Some of it will have to be
refactored into something pleasant to see, but that can wait until
problems are fixed; in the meanwhile you can already run "./gst" from a
relocated installation, and that's a good part of the work.  The only
part that does not work yet is loading packages, because the kernel path
stored in the image is fixed and absolute (and does not move when you
restart gst), so the PackageLoader cannot find .star files.  This will
be fixed of course, but I don't want to rush in a bad design so I'll
give some more thought to it.


To get a relocatable install, several conditions should be fulfilled.
On glibc-based systems (e.g. most GNU/Linux distros) a simple
"./configure && make" does fulfill them.

1) the exec-prefix and prefix should be identical;

2) the installation should reside entirely within the prefix; e.g. no
--localstatedir=/var (I will see if this can be fixed);

3) on Windows, the bindir and libdir should be the same or shared
libraries should be disabled;

4) if neither on Windows nor under a glibc-based system, shared
libraries should be disabled (period).

If the above conditions are satisfied, and you want a relocatable
install, it is suggested that you configure with a non-existent prefix
such as "--prefix=/nonexistent".  You can then apply the above recipe
like this:

   ./configure --prefix=/nonexistent
   make
   make install DESTDIR=`pwd`
   (cd nonexistent && tar cvf - .) | (cd $HOME && tar xvf -)

There's some hope to add relocatability support for Solaris and Darwin.
  I'll look into the latter when I have time, the former should be the
same as Linux actually.

Paolo

diff --git a/Makefile.am b/Makefile.am
index ab0fa46..514e9b0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -94,15 +94,14 @@ AM_CPPFLAGS = -I$(top_srcdir)/libgst \
  -DCMD_ZIP="\"$(ZIP)\"" \
  -DCMD_INSTALL="\"$(INSTALL)\"" \
  -DCMD_LN_S="\"$(LN_S)\"" \
- -DKERNEL_PATH=\"$(pkgdatadir)/kernel\" \
- -DIMAGE_PATH=\"$(imagedir)\"
+ $(RELOC_CPPFLAGS)
 
 bin_PROGRAMS = gst
 
 gst_SOURCES = main.c
 gst_LDADD = libgst/libgst.la lib-src/library.la @ICON@
 gst_DEPENDENCIES = libgst/libgst.la lib-src/library.la @ICON@
-gst_LDFLAGS = -export-dynamic
+gst_LDFLAGS = -export-dynamic @RELOC_LDFLAGS@
 
 if ENABLE_DISASSEMBLER
 gst_LDADD += opcode/libdisass.la
@@ -116,7 +115,7 @@ noinst_PROGRAMS = gst-tool
 gst_tool_SOURCES = gst-tool.c
 gst_tool_LDADD = libgst/libgst.la lib-src/library.la
 gst_tool_DEPENDENCIES = libgst/libgst.la lib-src/library.la
-gst_tool_LDFLAGS = -export-dynamic
+gst_tool_LDFLAGS = -export-dynamic @RELOC_LDFLAGS@
 
 GST_EXTRA_TOOLS = gst-reload gst-sunit gst-blox gst-package gst-convert \
  gst-doc gst-remote
diff --git a/NEWS b/NEWS
index 7c35300..89b6fe1 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,30 @@ o   ObjectMemory>>#snapshot and ObjectMemory>>#snapshot: return false in
     snapshot.  Note that this does not apply to CallinProcesses, since
     those are stopped in saved images (will this be true in 3.1?).
 
+o   If possible, the installation is made relocatable.  To this end,
+    the following conditions should be satisfied: 1) the exec-prefix
+    and prefix should be identical; 2) the installation should reside
+    entirely within the prefix; 3) on Windows, the bindir and libdir
+    should be the same or shared libraries should be disabled; 4) if
+    not on Windows or under a glibc-based system, shared libraries
+    should be disabled.
+
+    If the above conditions are satisfied, and you want a relocatable
+    install, it is suggested that you configure with a non-existent
+    prefix such as "--prefix=/nonexistent".  You can then install with
+    different prefixes, like this:
+
+        ./configure --prefix=/nonexistent
+        make
+        make install prefix=/usr/local
+        make install prefix=$HOME
+
+    In order to support relocatable installation, libgst clients should
+    call gst_set_executable_path *before* gst_initialize.  Failure to
+    do so won't cause any problem, except that relocatable installation
+    will be disabled and the program will look for its files only in
+    the configured prefix.
+
 o   The semantics of #on:do: were changed: executing off the end of an
     exception handler will always return from the associated #on:do:.
     Older versions of GNU Smalltalk either returned or resumed depending
diff --git a/build-aux/relocatable.m4 b/build-aux/relocatable.m4
index febd0f5..a473278 100644
--- a/build-aux/relocatable.m4
+++ b/build-aux/relocatable.m4
@@ -68,7 +68,6 @@ AC_DEFUN([AC_RELOCATABLE_LIBRARY],
 dnl Support for relocatable packages for which it is a nop.
 AC_DEFUN([AC_RELOCATABLE_NOP],
 [
-  AC_MSG_CHECKING([whether to activate relocatable installation])
   AC_ARG_ENABLE(relocatable,
     [  --enable-relocatable    install a package that can be moved in the filesystem],
     [if test "$enableval" != no; then
@@ -76,9 +75,8 @@ AC_DEFUN([AC_RELOCATABLE_NOP],
      else
        RELOCATABLE=no
      fi
-    ], RELOCATABLE=no)
+    ], RELOCATABLE=yes)
   AC_SUBST(RELOCATABLE)
-  AC_MSG_RESULT([$RELOCATABLE])
 ])
 
 dnl Determine the platform dependent parameters needed to use relocatability:
diff --git a/configure.ac b/configure.ac
index 17a779a..93d47f7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -467,6 +467,190 @@ fi
 GST_ARG_ENABLE_MODULES([Blox,TCP])
 
 dnl
+dnl ------------------------------- RELOCATABILITY ------------
+
+# See if we can make the installed binaries relocatable
+
+AC_MSG_CHECKING([whether to enable relocatable install])
+AC_RELOCATABLE_NOP
+relocatable_reason=$RELOCATABLE
+
+# First of all, compute the final paths for the various components.
+
+AC_LIB_PREPARE_PREFIX
+
+acl_final_datadir=`echo "${datadir}" | sed \
+ -e "s,\\\${datarootdir},$datarootdir," \
+ -e "s,\\\${exec_prefix},$acl_final_exec_prefix," \
+ -e "s,\\\${prefix},$acl_final_prefix," `
+acl_final_bindir=`echo "${bindir}" | sed \
+ -e "s,\\\${exec_prefix},$acl_final_exec_prefix," \
+ -e "s,\\\${prefix},$acl_final_prefix," `
+acl_final_libdir=`echo "${libdir}" | sed \
+ -e "s,\\\${exec_prefix},$acl_final_exec_prefix," \
+ -e "s,\\\${prefix},$acl_final_prefix," `
+acl_final_libexecdir=`echo "${libexecdir}" | sed \
+ -e "s,\\\${exec_prefix},$acl_final_exec_prefix," \
+ -e "s,\\\${prefix},$acl_final_prefix," `
+
+acl_final_pkgdatadir="$acl_final_datadir/$PACKAGE"
+acl_final_pkglibdir="$acl_final_libdir/$PACKAGE"
+
+acl_final_imagedir=`echo "${imagedir}" | sed \
+ -e "s,\\\${localstatedir},$localstatedir," \
+ -e "s,\\\${pkgdatadir},$pkgdatadir," \
+ -e "s,\\\${datadir},$datadir," \
+ -e "s,\\\${docdir},$docdir," \
+ -e "s,\\\${datarootdir},$datarootdir," \
+ -e "s,\\\${pkglibdir},$acl_final_pkglibdir," \
+ -e "s,\\\${libdir},$acl_final_libdir," \
+ -e "s,\\\${exec_prefix},$acl_final_exec_prefix," \
+ -e "s,\\\${prefix},$acl_final_prefix," `
+acl_final_moduledir=`echo "${moduledir}" | sed \
+ -e "s,\\\${localstatedir},$localstatedir," \
+ -e "s,\\\${pkgdatadir},$pkgdatadir," \
+ -e "s,\\\${datadir},$datadir," \
+ -e "s,\\\${docdir},$docdir," \
+ -e "s,\\\${datarootdir},$datarootdir," \
+ -e "s,\\\${pkglibdir},$acl_final_pkglibdir," \
+ -e "s,\\\${libdir},$acl_final_libdir," \
+ -e "s,\\\${exec_prefix},$acl_final_exec_prefix," \
+ -e "s,\\\${prefix},$acl_final_prefix," `
+
+
+# If shared libraries are enabled, there are a few extra constraints.
+
+if test "$enable_shared" != no; then
+  case $host in
+    *-*-cygwin* | *-*-mingw*)
+      # For Windows, the shared library must be in the bindir
+      if test "$acl_final_libdir" != "${acl_final_bindir}"; then
+        relocatable_reason='no, libdir not equal to bindir'
+      fi
+      ;;
+
+    *-linux*)
+      # For Linux, we can use a relative rpath via -Wl,-rpath,...
+      case "$acl_final_libdir" in
+        "${acl_final_exec_prefix}"/*) ;;
+        /*) relocatable_reason='no, libdir outside exec-prefix' ;;
+        *) relocatable_reason='no, relative libdir' ;;
+      esac
+      ;;
+
+    *)
+      relocatable_reason="no,
+ relocatable shared libraries not supported on $host"
+      ;;
+  esac
+fi
+
+# Further OS-independent tests ensure that we can make relative
+# paths from the executable's location.
+
+if test "$relocatable_reason" = yes; then
+  case "${acl_final_bindir}" in
+    "${acl_final_exec_prefix}"/*) ;;
+    /*) relocatable_reason='no, bindir outside exec-prefix' ;;
+    *) relocatable_reason='no, relative bindir' ;;
+  esac
+  case "${acl_final_libexecdir}" in
+    ${acl_final_exec_prefix}/*) ;;
+    /*) relocatable_reason='no, libexecdir outside exec_prefix' ;;
+    *) relocatable_reason='no, relative libexecdir' ;;
+  esac
+  case "${acl_final_datadir}" in
+    ${acl_final_prefix}/*) ;;
+    /*) relocatable_reason='no, datadir outside prefix' ;;
+    *) relocatable_reason='no, relative datadir' ;;
+  esac
+  case "${acl_final_imagedir}" in
+    ${acl_final_prefix}/*) ;;
+    /*) relocatable_reason='no, imagedir outside prefix' ;;
+    *) relocatable_reason='no, relative imagedir' ;;
+  esac
+  case "${acl_final_moduledir}" in
+    ${acl_final_prefix}/*) ;;
+    /*) relocatable_reason='no, moduledir outside prefix' ;;
+    *) relocatable_reason='no, relative moduledir' ;;
+  esac
+
+  test "$acl_final_prefix" != "$acl_final_exec_prefix" && \
+    relocatable_reason='no, prefix does not match exec prefix'
+fi
+
+# echo the relative path from ${acl_final_bindir} to $1
+# (Works only if both are absolute.)
+[func_make_relpath ()
+{
+  dir=$1
+  idir=${acl_final_bindir}
+
+  while true; do
+    dfirst=`echo "$dir" | sed -n -e 's,^//*\([^/]*\).*$,/\1,p'`
+    ifirst=`echo "$idir" | sed -n -e 's,^//*\([^/]*\).*$,/\1,p'`
+    test x"$dfirst" = x && break
+    test x"$ifirst" = x && break
+    test "$dfirst" != "$ifirst" && break
+
+    dir=`echo "$dir" | sed -e 's,^//*[^/]*,,'`
+    idir=`echo "$idir" | sed -e 's,^//*[^/]*,,'`
+  done
+  idir=`echo "$idir" | sed -e 's,//*[^/]*,/..,g' -e 's,^/,,' `
+  if test x"$idir$dir" = x; then
+    echo .
+  else
+    echo "$idir$dir"
+  fi
+}]
+
+case "$relocatable_reason" in
+  yes)
+    # Command-line option to include a relative search path for
+    # shared libraries
+    if test "$enable_shared" != no; then
+      case "$host" in
+        *-linux*)
+          RELOC_LDFLAGS='-Wl,-rpath,"\$$ORIGIN/'`func_make_relpath ${acl_final_libdir}`'"'
+  ;;
+      esac
+    fi
+
+    KERNEL_PATH=`func_make_relpath ${acl_final_pkgdatadir}/kernel`
+    IMAGE_PATH=`func_make_relpath ${acl_final_imagedir}`
+    MODULE_PATH=`func_make_relpath ${acl_final_moduledir}`
+    LIBEXEC_PATH=`func_make_relpath "${acl_final_libexecdir}/${PACKAGE}"`
+    AC_DEFINE_UNQUOTED(KERNEL_PATH, "$KERNEL_PATH",
+       [The relative path from the program to the kernel path.
+        Defined only for relocatable installs.])
+    AC_DEFINE_UNQUOTED(IMAGE_PATH, "$IMAGE_PATH",
+       [The relative path from the program to the image path.
+        Defined only for relocatable installs.])
+    AC_DEFINE_UNQUOTED(MODULE_PATH, "$MODULE_PATH",
+       [The relative path from the program to the module path.
+        Defined only for relocatable installs.])
+    AC_DEFINE_UNQUOTED(LIBEXEC_PATH, "$LIBEXEC_PATH",
+       [The relative path from the program to the per-package
+ libexec path.  Defined only for relocatable installs.])
+    ;;
+
+  *)
+    # Pass paths on the command-line to allow specifying a prefix at "make"
+    # time.
+    RELOC_CPPFLAGS='-DKERNEL_PATH=\""${pkgdatadir}/kernel"\" \
+ -DIMAGE_PATH=\""${imagedir}"\" \
+ -DMODULE_PATH=\""${moduledir}"\" \
+ -DLIBEXEC_PATH=\""${libexecdir}/${PACKAGE}"\"'
+    ;;
+esac
+
+RELOC_CPPFLAGS=$RELOC_CPPFLAGS' \
+ -DDEFAULT_EXECUTABLE=\""${bindir}/gst${EXEEXT}"\"'
+AC_MSG_RESULT([$relocatable_reason])
+AC_SUBST(RELOC_CPPFLAGS)
+AC_SUBST(RELOC_LDFLAGS)
+
+dnl
 dnl ------------------------------- FILE GENERATION -----------
 
 { echo; echo "${term_bold}Output substitutions:${term_norm}"; } >& AS_MESSAGE_FD
diff --git a/doc/gst.texi b/doc/gst.texi
index d5e8b3c..c6d0a29 100644
--- a/doc/gst.texi
+++ b/doc/gst.texi
@@ -4573,7 +4573,8 @@ int     argc;
 char    **argv;
 @{
   gst_set_var (GST_VERBOSITY, 1);
-  gst_smalltalk_args (argc, argv);
+  gst_smalltalk_args (argc - 1, argv + 1);
+  gst_set_executable_path (argv[0]);
   result = gst_initialize ("@var{kernel-dir}", "@var{image-file}", GST_NO_TTY);
   if (result != 0)
     exit (result < 0 ? 1 : result);
diff --git a/gst-tool.c b/gst-tool.c
index ad5efa3..5f0dea2 100644
--- a/gst-tool.c
+++ b/gst-tool.c
@@ -498,6 +498,7 @@ main(int argc, const char **argv)
 
   gst_set_var (GST_VERBOSITY, 0);
   gst_smalltalk_args (smalltalk_argc, smalltalk_argv);
+  gst_set_executable_path (argv[0]);
   result = gst_initialize (kernel_dir, image_file, flags);
   if (result != 0)
     exit (result < 0 ? 1 : result);
diff --git a/libgst/Makefile.am b/libgst/Makefile.am
index c8be9cf..ba0de6f 100644
--- a/libgst/Makefile.am
+++ b/libgst/Makefile.am
@@ -4,11 +4,9 @@ LEX_OUTPUT_ROOT = lex.yy
 ## CFLAGS=-O0 -g
 AM_LFLAGS = -Cfe -o$(LEX_OUTPUT_ROOT).c
 AM_YFLAGS = -vy
-AM_CPPFLAGS = -DKERNEL_PATH=\"$(pkgdatadir)/kernel\" \
-  -DIMAGE_PATH=\"$(imagedir)\" -DMODULE_PATH=\"$(moduledir)\" \
-  -DLIBEXEC_PATH=\"$(libexecdir)/$(PACKAGE)\" \
-  -I$(top_srcdir)/lib-src $(INCFFI) $(INCLIGHTNING) \
-  $(INCSNPRINTFV) $(INCSIGSEGV) -I$(top_builddir)/lib-src
+AM_CPPFLAGS = $(RELOC_CPPFLAGS) \
+  -I$(top_srcdir)/lib-src -I$(top_builddir)/lib-src \
+  $(INCFFI) $(INCLIGHTNING) $(INCSNPRINTFV) $(INCSIGSEGV)
 
 if !HAVE_INSTALLED_LIGHTNING
 AM_CPPFLAGS += -I$(top_srcdir)/lightning -I$(top_builddir)/lightning \
diff --git a/libgst/cint.c b/libgst/cint.c
index 6169322..78e3715 100644
--- a/libgst/cint.c
+++ b/libgst/cint.c
@@ -456,7 +456,11 @@ init_dld (void)
 {
   char *modules;
   lt_dlinit ();
-  lt_dladdsearchdir (MODULE_PATH);
+
+  modules = _gst_relocate_path (MODULE_PATH);
+  lt_dladdsearchdir (modules);
+  free (modules);
+
   if ((modules = getenv ("SMALLTALK_MODULES")))
     lt_dladdsearchdir (modules);
 
diff --git a/libgst/dict.c b/libgst/dict.c
index 9188522..35dfab2 100644
--- a/libgst/dict.c
+++ b/libgst/dict.c
@@ -1057,9 +1057,17 @@ add_smalltalk (const char *globalName,
 void
 init_runtime_objects (void)
 {
+  char *s;
   add_smalltalk ("UserFileBasePath", _gst_string_new (_gst_user_file_base_path));
-  add_smalltalk ("ModulePath", _gst_string_new (MODULE_PATH));
-  add_smalltalk ("LibexecPath", _gst_string_new (LIBEXEC_PATH));
+
+  s = _gst_relocate_path (MODULE_PATH);
+  add_smalltalk ("ModulePath", _gst_string_new (s));
+  free (s);
+
+  s = _gst_relocate_path (LIBEXEC_PATH);
+  add_smalltalk ("LibexecPath", _gst_string_new (s));
+  free (s);
+
   add_smalltalk ("ImageFilePath", _gst_string_new (_gst_image_file_path));
   add_smalltalk ("ExecutableFileName", _gst_string_new (_gst_executable_path));
   add_smalltalk ("ImageFileName", _gst_string_new (_gst_binary_image_name));
diff --git a/libgst/files.c b/libgst/files.c
index 6278b9e..b6a1508 100644
--- a/libgst/files.c
+++ b/libgst/files.c
@@ -90,9 +90,6 @@ const char *_gst_image_file_path = NULL;
    home directory.  */
 const char *_gst_user_file_base_path = NULL;
 
-/* The path to the executable, derived from argv[0].  */
-const char *_gst_executable_path = NULL;
-
 /* Whether to look for user files.  */
 static mst_Boolean no_user_files = false;
 
@@ -308,7 +305,6 @@ _gst_smalltalk_args (int argc,
 {
   smalltalk_argc = argc;
   smalltalk_argv = argv;
-  _gst_executable_path = _gst_find_executable (argv[0]);
 }
 
   
@@ -330,6 +326,9 @@ _gst_initialize (const char *kernel_dir,
   _gst_smalltalk_initialized = true;
   _gst_init_snprintfv ();
 
+  if (!_gst_executable_path)
+    _gst_executable_path = DEFAULT_EXECUTABLE;
+
   /* By default, apply this kludge fpr OSes such as Windows and MS-DOS
      which have no concept of home directories.  */
   if (home == NULL)
@@ -352,6 +351,8 @@ _gst_initialize (const char *kernel_dir,
  }
     }
 
+  /* For the image file, it is okay to find none if we can/should rebuild
+     the image file.  */
   if (image_file
       && (flags & (GST_REBUILD_IMAGE | GST_MAYBE_REBUILD_IMAGE)) == 0
       && !_gst_file_is_readable (image_file))
@@ -365,6 +366,8 @@ _gst_initialize (const char *kernel_dir,
  }
     }
 
+  /* The image path can be used as the default kernel path, so we split
+     it anyway into directory+filename.  */
   if (image_file)
     {
       const char *p;
@@ -395,26 +398,31 @@ _gst_initialize (const char *kernel_dir,
     }
   else
     {
-      /* No image file given, we use the system default but revert to the
+      /* No image file given, we use the system default or revert to the
  current directory.  */
-      if (_gst_file_is_readable (IMAGE_PATH))
-        _gst_image_file_path = IMAGE_PATH;
+      str = _gst_relocate_path (IMAGE_PATH);
+      if (_gst_file_is_readable (str))
+        _gst_image_file_path = str;
       else
-        _gst_image_file_path = xstrdup (currentDirectory);
+ {
+          free (str);
+          _gst_image_file_path = xstrdup (currentDirectory);
+ }
+
       flags |= GST_IGNORE_BAD_IMAGE_PATH;
       image_file = "gst.im";
     }
 
   if (!kernel_dir)
     {
-      if (_gst_file_is_readable (KERNEL_PATH))
-        kernel_dir = KERNEL_PATH;
-      else
+      str = _gst_relocate_path (KERNEL_PATH);
+      if (!_gst_file_is_readable (str))
  {
-          char *kernel_file_path;
-  asprintf (&kernel_file_path, "%s/kernel", _gst_image_file_path);
-  kernel_dir = kernel_file_path;
+          free (str);
+  asprintf (&str, "%s/kernel", _gst_image_file_path);
  }
+
+      kernel_dir = str;
     }
 
   xfree (currentDirectory);
diff --git a/libgst/files.h b/libgst/files.h
index 169c89d..3a2d484 100644
--- a/libgst/files.h
+++ b/libgst/files.h
@@ -66,9 +66,6 @@ extern const char *_gst_image_file_path
    home directory.  */
 extern const char *_gst_user_file_base_path;
 
-/* The path to the executable.  */
-extern const char *_gst_executable_path;
-
 /* This is the name of the binary image to load.  If it is not NULL after the
    command line is parsed, the checking of the dates of the kernel source files
    against the image file date is overridden.  If it is NULL, it is set to
diff --git a/libgst/gstpub.c b/libgst/gstpub.c
index 8754961..6826741 100644
--- a/libgst/gstpub.c
+++ b/libgst/gstpub.c
@@ -110,7 +110,10 @@ VMProxy gst_interpreter_proxy = {
   _gst_process_stdin,
   _gst_process_file,
   _gst_get_var, _gst_set_var,
-  _gst_invoke_hook
+  _gst_invoke_hook,
+
+  /* New in 3.1.  */
+  _gst_relocate_path
 };
 
 /* Functions in comp.h.  */
@@ -500,6 +503,19 @@ gst_oop_at_put (OOP oop, size_t index, OOP new_oop)
 }
 
 
+/* Functions in systep.h.  */
+void
+gst_set_executable_path (const char *argv0)
+{
+  _gst_set_executable_path (argv0);
+}
+
+char *
+gst_relocate_path (const char *path)
+{
+  return _gst_relocate_path (path);
+}
+
 static void
 init_vmproxy (void)
 {
diff --git a/libgst/gstpub.h b/libgst/gstpub.h
index d9051dc..083d520 100644
--- a/libgst/gstpub.h
+++ b/libgst/gstpub.h
@@ -198,6 +198,9 @@ typedef struct VMProxy
   int (*getVar) (enum gst_var_index index);
   int (*setVar) (enum gst_var_index index, int value);
   void (*invokeHook) (enum gst_vm_hook);
+
+  /* 3.1+ functions.  */
+  char *(*relocatePath) (const char *);
 } VMProxy;
 
 /* Compatibility section */
@@ -232,6 +235,10 @@ extern int gst_set_var (enum gst_var_index index, int value);
 /* Functions in comp.h.  */
 extern void gst_invoke_hook (enum gst_vm_hook);
 
+/* Functions in sysdep.h.  */
+extern void gst_set_executable_path (const char *);
+extern char *gst_relocate_path (const char *);
+
 /* These are the library counterparts of the functions in
    gst_vm_proxy.  */
 extern OOP gst_msg_send (OOP receiver, OOP selector, ...);
diff --git a/libgst/sysdep.c b/libgst/sysdep.c
index 4faa20d..ecb8bf3 100644
--- a/libgst/sysdep.c
+++ b/libgst/sysdep.c
@@ -1011,6 +1011,9 @@ _gst_file_is_executable (const char *fileName)
 static int executable_fd = -1;
 #endif
 
+/* The path to the executable, derived from argv[0].  */
+const char *_gst_executable_path = NULL;
+
 /* Tests whether a given pathname may belong to the executable.  */
 static mst_Boolean
 maybe_executable (const char *filename)
@@ -1044,8 +1047,8 @@ maybe_executable (const char *filename)
    Return NULL if unknown.  Guaranteed to work on Linux and Win32, Mac OS X.
    Likely to work on the other Unixes (maybe except BeOS), under most
    conditions.  */
-char *
-_gst_find_executable (const char *argv0)
+static char *
+find_executable (const char *argv0)
 {
 #if defined WIN32
   char location[MAX_PATH];
@@ -1158,6 +1161,46 @@ _gst_find_executable (const char *argv0)
 #endif
 }
 
+void
+_gst_set_executable_path (const char *argv0)
+{
+  _gst_executable_path = find_executable (argv0);
+}
+
+char *
+_gst_relocate_path (const char *path)
+{
+  const char *p;
+  char *s;
+
+  /* Detect absolute paths.  */
+#if defined(MSDOS) || defined(WIN32) || defined(__OS2__)
+  if ((path[0] && path[1] == ':')
+      || path[0] == '/' || path[0] == '\\')
+    return xstrdup (path);
+#else
+  if (path[0] == '/')
+    return xstrdup (path);
+#endif
+
+  /* Remove filename from executable path.  */
+  p = _gst_executable_path + strlen (_gst_executable_path);
+  do
+    --p;
+  while (p >= _gst_executable_path
+         && *p != '/'
+#if defined(MSDOS) || defined(WIN32) || defined(__OS2__)
+ && *p != '\\'
+#endif
+ );
+  p++;
+
+  /* Now p points just past the last separator (if any).  */
+  s = alloca (p - _gst_executable_path + strlen (path) + 1);
+  sprintf (s, "%.*s%s", p - _gst_executable_path, _gst_executable_path, path);
+  return _gst_get_full_file_name (s);
+}
+
 
 /* Code to use PTY's did not work on Mac OS.  I'm keeping the Unix code,
    but it should not be used.  */
diff --git a/libgst/sysdep.h b/libgst/sysdep.h
index 0966456..da36083 100644
--- a/libgst/sysdep.h
+++ b/libgst/sysdep.h
@@ -81,6 +81,9 @@
 
 typedef RETSIGTYPE (*SigHandler) ();
 
+/* The path to the executable.  */
+extern const char *_gst_executable_path;
+
 /* Saves and returns the current state of the software interrupt
    system.  Disables all interrupts.  */
 extern void _gst_disable_interrupts (void)
@@ -185,7 +188,12 @@ extern mst_Boolean _gst_file_is_executable (const char *fileName)
   ATTRIBUTE_HIDDEN;
 
 /* Return a path to the executable given argv[0].  */
-extern char *_gst_find_executable (const char *argv0)
+extern void _gst_set_executable_path (const char *argv0)
+  ATTRIBUTE_HIDDEN;
+
+/* Return the absolute path for PATH, interpreted relative to the
+   executable.  */
+char *_gst_relocate_path (const char *path)
   ATTRIBUTE_HIDDEN;
 
 /* Answer true if the file descriptor FD is associated to a pipe
diff --git a/main.c b/main.c
index d770c3a..adabd2e 100644
--- a/main.c
+++ b/main.c
@@ -290,8 +290,9 @@ parse_args (int argc,
   break;
 
  case 'v':
-  printf (copyright_and_legal_stuff_text, VERSION, KERNEL_PATH,
-  IMAGE_PATH);
+  printf (copyright_and_legal_stuff_text, VERSION,
+  gst_relocate_path (KERNEL_PATH),
+  gst_relocate_path (IMAGE_PATH));
   exit (0);
 
  case '\1':
@@ -337,6 +338,7 @@ main(int argc, const char **argv)
   loaded_files =
     (struct loaded_file *) alloca (sizeof (struct loaded_file) * argc);
 
+  gst_set_executable_path (argv[0]);
   parse_args (argc, argv);
 
   /* These might go away in the next release.  */

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk