[m-rev.] for review: implement C version of mmc

Ian MacLarty maclarty at cs.mu.OZ.AU
Thu Oct 20 01:21:22 AEST 2005


On Mon, 3 Oct 2005, Julien Fischer wrote:

>
> On Sat, 1 Oct 2005, Ian MacLarty wrote:
>
> > For review by anyone.
> >
> > Estimated hours taken: 30
> > Branches: main and 0.12
> >
> > Implement a C version of mmc that can be run on Windows without a unix shell.
> >
> > The new mmc can also operate in a standalone mode where it determines where it
>
...
> > mode relative paths to the C compiler and linker must also be supplied.  These
> > are assumed to be packaged with the Mercury compiler in standalone mode.  mmc
> > will convert the relative paths to absolute paths at runtime and pass these
> > to the Mercury compiler.
> >
> > In non-stanalone mode the C version of mmc behaves exactly the same as the
> > shell script version.  The C version is now the version which is installed to
> > the bin directory, however the shell version is still used in the compiler
> > build process and for bootchecking.
> >
>
> Could we delay making the C version the default?  I think it should
> be tested a lot more extensively first.
>

No problem.

> > 	MinGW, so it is independent of Cygwin.
> >
> > configure.in:
> > 	Add options to enable the standalone version of mmc and to give
> > 	relative paths for the C compiler and the linker commands that generate
> > 	an executable and a shared library.
>
> What's the relationship between the C compiler used to build the Mercury
> compiler and the one used by the standalone version?
>

There is no relationship.

> > ===================================================================
> > RCS file: README.Standalone
> > diff -N README.Standalone
> > --- /dev/null	1 Jan 1970 00:00:00 -0000
> > +++ README.Standalone	1 Oct 2005 08:27:44 -0000
> > @@ -0,0 +1,94 @@
> > +-----------------------------------------------------------------------------
> > +
> > +INTRODUCTION
> > +
> > +This file describes how to build a version of the Mercury compiler that has
> > +a C compiler and linker packaged with it and so doesn't depend on any external
> > +programs.
>
> Doesn't it still need the sort utility in order to use fact tables?
>

-programs.  The directory containing the Mercury and C compilers can be moved
+programs (Mercury will still require the `sort' program to compile
+programs that make use of :- pragma fact_table).
+

> > The directory containing the Mercury and C compilers can be moved
> > +around and mmc will still work (although at this stage mmake, mdb, etc will
> > +not).  This property makes the standalone version of the compiler ideal for
> > +distribution in a binary package.
>
> You should make it clear that you are referring to the C version here
> not the shell version.
>

I don't see the need.  It shouldn't matter to the user.

> > +-----------------------------------------------------------------------------
> > +
> > +CONFIGURATION
> > +
> > +To enable mmc to operate in standalone mode, four options must be given to
> > +the configure script.
> > +
> > +1. --enable-standalone
> > +	This causes mmc to be compiled in standalone mode.
> > +2. --with-relative-cc=<relative path to C compiler>
> > +	This specifies the relative command to invoke the C compiler.  The
> > +	command should be relative to the installation directory (i.e. the
> > +	directory given with the --prefix configure option).
>
> What's the relationship between this option and the with-cc option?
>

There's no relationship.

> > Index: configure.in
> > ===================================================================
> > +AC_SUBST(SET_MACOSX_DEPLOYMENT_TARGET)
> >
> >  #-----------------------------------------------------------------------------#
> >  # Note that changes here may require changes in scripts/mgnuc.in.
> > @@ -4149,7 +4216,7 @@
> >  # if you add new .in files to either of those you *must* update
> >  # scripts/mercury_config.in.
> >  #
> > -AC_OUTPUT(Mmake.common scripts/Mmake.vars scripts/mmc
> > +AC_OUTPUT(Mmake.common scripts/Mmake.vars scripts/mmc util/mmc.c
> >  scripts/mercury.bat scripts/mprof scripts/mercury_update_interface
> >  scripts/mgnuc scripts/parse_ml_options.sh-subr scripts/ml
> >  scripts/c2init scripts/mmake scripts/mdb scripts/mdbrc scripts/mdprof
>
> Does this require any changes to scripts/mercury_config.in?

Initially I didn't think so, but yes it does.  In fact that script seemed
to be broken, so I have fixed and modified it to build and install the
C version of mmc if necessary.  See the diff at the end of this email.

> > Index: util/path_util.c
> > ===================================================================
...
> > +    }
> > +
> > +    switch (how) {
> > +        case ENVVAR_OVERWRITE:
> > +            set_string = (char*)malloc(strlen(var) + 1 + strlen(value) + 1);
> You'll need to check the return value of malloc here and below.  You
> should just define a safe version of malloc like we do in the runtime.
>

I've changed this to use the new_formatted_string function.

...
> > +    /*
> > +    ** We don't free set_string, since any changes to the contents of
> > +    ** it will alter the environment (according to the Linux manpage).
> > +    */
> What about non-Linux systems?
>

I've added an alternative version that works for systems that don't have
vsnprintf (such as visual c).  mmc.c now compiles under visual c.

I've also followed all your other suggestions.

Here is the interdiff (minus some boring bits).
If you want me to repost the full versions of any of the
files let me know.

Index: configure.in
(interdiff impossible, so this is the latest diff of configure.in)
===================================================================
RCS file: /home/mercury1/repository/mercury/configure.in,v
retrieving revision 1.435
diff -u -r1.435 configure.in
--- configure.in	4 Oct 2005 10:34:36 -0000	1.435
+++ configure.in	5 Oct 2005 08:38:32 -0000
@@ -129,6 +129,7 @@
 AC_SUBST(PREFIX)
 AC_SUBST(NONSHARED_LIB_DIR)
 AC_SUBST(LIBDIR)
+AC_DEFINE_UNQUOTED(MR_LIBDIR, "$LIBDIR")
 DEFAULT_MERCURY_DEBUGGER_INIT_DIR=$LIBDIR/mdb
 AC_SUBST(DEFAULT_MERCURY_DEBUGGER_INIT_DIR)
 #-----------------------------------------------------------------------------#
@@ -186,6 +187,7 @@
 CONFIG_LIBDIR="`$CYGPATH $CONFIG_LIBDIR`"
 AC_SUBST(CONFIG_PREFIX)
 AC_SUBST(CONFIG_LIBDIR)
+AC_DEFINE_UNQUOTED(MR_CONFIG_LIBDIR, "$CONFIG_LIBDIR")
 #-----------------------------------------------------------------------------#

 case "$host" in
@@ -330,6 +332,89 @@
 		CC="$mercury_cv_with_cc"
 		;;
 esac
+
+#-----------------------------------------------------------------------------#
+# Check if we should install the C version of mmc.
+AC_ARG_ENABLE(c-mmc,
+[  --enable-c-mmc          install a C version of the mmc program that
+                          invokes the Mercury compiler.  By default
+                          a unix shell script version of mmc is installed.
+                          The C version is more portable.],
+INSTALL_C_MMC="$enableval", INSTALL_C_MMC="no")
+case $INSTALL_C_MMC in
+	yes) ;;
+	no) ;;
+	*)	AC_MSG_ERROR(invalid option to --enable-c-mmc)
+		exit 1
+        	;;
+esac
+
+#-----------------------------------------------------------------------------#
+# Check if we should enable the standalone version of mmc.
+AC_ARG_ENABLE(standalone,
+[  --enable-standalone     enable the standalone version of mmc.  If this
+                          option is enabled then the --with-relative-cc,
+                          --with-relative-link-exec-cmd and
+                          --with-relative-link-shared-cmd options must also
+                          be set.],
+mercury_cv_enable_standalone="$enableval", mercury_cv_enable_standalone="no")
+AC_ARG_WITH(relative-cc,
+[  --with-relative-cc      specify the path to the C compiler relative to
+                          the Mercury installation directory.  The given C
+                          compiler is only used if the --enable-standalone
+                          option is given.],
+mercury_cv_with_relative_cc="$withval", mercury_cv_with_relative_cc="")
+AC_ARG_WITH(relative-link-exec-cmd,
+[  --with-relative-link-exec-cmd
+                          specify the command to link object files into an
+                          executable.  The command is relative to the Mercury
+                          installation directory.  The specified link command
+                          will only be used if the --enable-standalone option
+                          is also given.],
+mercury_cv_with_relative_link_exec_cmd="$withval",
+mercury_cv_with_relative_link_exec_cmd="")
+AC_ARG_WITH(relative-link-shared-cmd,
+[  --with-relative-link-shared-cmd
+                          specify the command to link object files into a
+                          shared library.  The command is relative to the
+                          Mercury installation directory.  The specified link
+                          command will only be used if the --enable-standalone
+                          option is also given.],
+mercury_cv_with_relative_link_shared_cmd="$withval",
+mercury_cv_with_relative_link_shared_cmd="")
+case "$mercury_cv_enable_standalone" in
+	yes)	case "$mercury_cv_with_relative_cc" in
+			"")	AC_MSG_ERROR(--with-relative-cc is required if --enable-standalone is given)
+				exit 1
+				;;
+			*)	AC_DEFINE_UNQUOTED(MR_C_COMPILER_RELATIVE_PATH, "$mercury_cv_with_relative_cc")
+				;;
+		esac
+		case "$mercury_cv_with_relative_link_exec_cmd" in
+			"")	AC_MSG_ERROR(--with-relative-link-exec-cmd is required if --enable-standalone is given)
+				exit 1
+				;;
+			*)	AC_DEFINE_UNQUOTED(MR_LINK_EXEC_CMD_RELATIVE_PATH, "$mercury_cv_with_relative_link_exec_cmd")
+				;;
+		esac
+		case "$mercury_cv_with_relative_link_shared_cmd" in
+			"")	AC_MSG_ERROR(--with-relative-link-shared-cmd is required if --enable-standalone is given)
+				exit 1
+				;;
+			*)	AC_DEFINE_UNQUOTED(MR_LINK_SHARED_LIB_CMD_RELATIVE_PATH, "$mercury_cv_with_relative_link_shared_cmd")
+				;;
+		esac
+		AC_DEFINE(MR_MMC_STANDALONE)
+		# Only the C version of mmc supports standalone mode.
+		INSTALL_C_MMC=yes
+		;;
+	no)	;;
+	*)	AC_MSG_ERROR(invalid option to --enable-standalone)
+		exit 1
+        	;;
+esac
+AC_SUBST(INSTALL_C_MMC)
+
 #-----------------------------------------------------------------------------#
 #
 # Find the GCC source code
@@ -601,6 +686,26 @@
 esac

 #-----------------------------------------------------------------------------#
+# Conditionally create a script to compile and install the C version of mmc.
+# The generated script takes one argument: the directory to install mmc to.
+# The script will be executed by mercury_config if it exists.
+# It is the mechanism configure uses to signal to mercury_config that it must
+# install the C version of mmc instead of the sh version.
+test -e util/reconf_install_c_mmc && rm -f util/reconf_install_c_mmc
+case "$INSTALL_C_MMC" in
+	yes)
+		cat > util/reconf_install_c_mmc << EOF
+#!/bin/sh
+# This script was automatically generated by configure.
+# It is invoked my mercury_config to build and install the C version of mmc.
+#
+$CC -o \$1/mmc -I../runtime mmc.c path_util.c
+EOF
+		chmod a+x util/reconf_install_c_mmc
+		;;
+esac
+
+#-----------------------------------------------------------------------------#

 cat > conftest.c << EOF
 	struct MR_TypeInfo_Almost_Struct {
@@ -3183,6 +3288,13 @@
 ERROR_UNDEFINED=""
 DEFAULT_LINKAGE="shared"

+# On Darwin the MACOSX_DEPLOYMENT_TARGET environment variable needs to be set
+# when linking with two level namespaces so we can use the `-undefined
+# dynamic_lookup' option.  If necessary the configure script will set the
+# following variable to 1 which will cause mmc to set the
+# MACOSX_DEPLOYMENT_TARGET environment variable.
+SET_MACOSX_DEPLOYMENT_TARGET=0
+
 case "$host" in
 	i*86-*-linux|i*86-*-linux-gnu)
 		case $ac_cv_prog_gcc in
@@ -3443,14 +3555,8 @@
 			    -a 7 -le;
 		    then
 		        AC_MSG_RESULT(yes)
-			# The MACOSX_DEPLOYMENT_TARGET environment variable
-			# needs to be set when linking with two level
-			# namespaces so we can use the
-			# `-undefined dynamic_lookup' option.
-		        SET_MACOSX_DEPLOYMENT_TARGET="\
-				MACOSX_DEPLOYMENT_TARGET=10.3; \
-				export MACOSX_DEPLOYMENT_TARGET"
-			AC_SUBST(SET_MACOSX_DEPLOYMENT_TARGET)
+		        SET_MACOSX_DEPLOYMENT_TARGET=1
+			AC_DEFINE(SET_MACOSX_DEPLOYMENT_TARGET)
 		        LINK_SHARED_OBJ="$GCC_PROG -multiply_defined suppress \
 				 -dynamiclib -single_module"
 		        LINK_SHARED_OBJ_SH="$GCC_PROG -multiply_defined \
@@ -3483,6 +3589,7 @@
 		AC_MSG_RESULT(no)
 		;;
 esac
+AC_SUBST(SET_MACOSX_DEPLOYMENT_TARGET)

 #-----------------------------------------------------------------------------#
 # Note that changes here may require changes in scripts/mgnuc.in.
@@ -4198,11 +4305,12 @@

 # IMPORTANT NOTE
 # --------------
-# Any new entries here may need to be handled by scripts/mercury_config.in.
-# Failing to do this correctly may break the binary distributions
-# This is especially true of files in the runtime or java directories;
-# if you add new .in files to either of those you *must* update
-# scripts/mercury_config.in.
+# Each of the *.in files below must exist before configure is
+# run, otherwise configure will give an error.  scripts/mercury_config.in
+# must ensure these files exist before it calls configure.  If the
+# file is not needed for a reconfiguration then it suffices to create an
+# empty .in file in scripts/mercury_config.in.
+# Failing to do this correctly may break the binary distributions.
 #
 AC_OUTPUT(Mmake.common scripts/Mmake.vars scripts/mmc
 scripts/mercury.bat scripts/mprof scripts/mercury_update_interface
diff -u scripts/Mmakefile scripts/Mmakefile
--- scripts/Mmakefile	9 Sep 2005 06:02:09 -0000
+++ scripts/Mmakefile	4 Oct 2005 08:50:31 -0000
@@ -21,7 +21,7 @@
 	mtc \
 	vpath_find

-INSTALL_CONF_SCRIPTS = \
+INSTALL_CONF_SCRIPTS := \
 	c2init \
 	canonical_grade \
 	mdb \
@@ -36,8 +36,12 @@
 	mprof \
 	prepare_tmp_dir_fixed_part

-NONINSTALL_CONF_SCRIPTS = \
-	mmc
+ifeq ("$(INSTALL_C_MMC)", "yes")
+	NONINSTALL_CONF_SCRIPTS = mmc
+else
+	NONINSTALL_CONF_SCRIPTS =
+	INSTALL_CONF_SCRIPTS := $(INSTALL_CONF_SCRIPTS) mmc
+endif

 CONF_SCRIPTS = $(INSTALL_CONF_SCRIPTS) $(NONINSTALL_CONF_SCRIPTS)

diff -u util/Mmakefile util/Mmakefile
--- util/Mmakefile	1 Oct 2005 04:17:03 -0000
+++ util/Mmakefile	10 Oct 2005 07:57:17 -0000
@@ -34,13 +34,17 @@
 # mkinit.c needs `struct stat'
 MGNUCFLAGS-mkinit = --no-ansi

+# We want to include the local runtime/mercury_conf.h, not the installed
+# one.
+MGNUCFLAGS = --no-mercury-stdlib-dir
+
 #-----------------------------------------------------------------------------#

 all:	$(PROGS) $(TAGS_FILE_EXISTS) mmc$(EXT_FOR_EXE)

-mmc$(EXT_FOR_EXE): mmc.c
-	$(MGNUC) --no-ansi $(GRADEFLAGS) $(ALL_MGNUCFLAGS) \
-		-o mmc$(EXT_FOR_EXE) mmc.c path_util.c
+mmc$(EXT_FOR_EXE): mmc.c path_util.c path_util.h
+	$(MGNUC) --no-ansi $(GRADEFLAGS) \
+		$(ALL_MGNUCFLAGS) -o mmc$(EXT_FOR_EXE) mmc.c path_util.c

 .c:
 	$(MGNUC) $(GRADEFLAGS) $(ALL_MGNUCFLAGS) -o $@ $< $(GETOPT_SRC)
@@ -59,7 +63,12 @@
 install: $(PROGS) mmc$(EXT_FOR_EXE)
 	[ -d $(INSTALL_BINDIR) ] || mkdir -p $(INSTALL_BINDIR)
 	cp `vpath_find $(PROGFILENAMES)` $(INSTALL_BINDIR)
-	cp `vpath_find mmc$(EXT_FOR_EXE)` $(INSTALL_BINDIR)
+	case "$(INSTALL_C_MMC)" in \
+		yes) cp `vpath_find mmc$(EXT_FOR_EXE)` $(INSTALL_BINDIR) ;; \
+	esac
+	[ -d $(INSTALL_RECONF_DIR)/util ] || \
+		mkdir -p $(INSTALL_RECONF_DIR)/util
+	cp mmc.c path_util.h path_util.c $(INSTALL_RECONF_DIR)/util

 .PHONY: uninstall
 uninstall:
diff -u util/path_util.c util/path_util.c
--- util/path_util.c	1 Oct 2005 08:43:52 -0000
+++ util/path_util.c	19 Oct 2005 13:31:19 -0000
@@ -12,7 +12,12 @@
 ** Author: Ian MacLarty.
 **
 ** This file implements some utility functions for manipulating paths and
-** working out were an executable is being executed from.
+** working out were an executable is being run from.
+** The functions in this file are used by mmc.c when it is in standalone
+** mode to work out where it is being run from, so that it can pass the
+** absolute paths to the configuration and library directories and the
+** absolute paths of the C compiler and linker to mercury_compile.
+** It also contains a few string manipulation functions used by mmc.
 */


@@ -22,6 +27,7 @@
 #include <string.h>

 #include "mercury_conf.h"
+#include "mercury_conf_param.h"

 #include "path_util.h"

@@ -31,16 +37,16 @@

 #define MAX_PATH_SIZE   8128

-static  void    split_path_into_dir_and_file(char *path, char **dir,
+static  void    split_path_into_dir_and_file(const char *path, char **dir,
                     char **filename);
-static  int     file_exists_and_is_executable(char* path);
-static  char    *search_path(char *path, const char *path_separator,
-                    char *filename);
-static  char*   copy_substring(char* str, int start, int end);
+static  int     file_exists_and_is_executable(const char *path);
+static  char    *search_path(const char *path, const char *path_separator,
+                    const char *filename);
+static  char*   copy_substring(const char *str, int start, int end);
 static  void    delete_quotes(char* str);

 char*
-find_exec_dir(char* cmd)
+find_exec_dir(const char *cmd)
 {
     char    *filename;
     char    *cmd_dir;
@@ -52,7 +58,7 @@
     split_path_into_dir_and_file(cmd, &cmd_dir, &filename);

     /*
-    ** Check if the first argument is an absolute path.
+    ** Check if the command is an absolute path.
     */
     if (IS_ABS_PATH(cmd_dir)) {
         free(filename);
@@ -60,12 +66,25 @@
     }

     /*
-    ** See if the first argument is a path relative to the current working
-    ** directory.
+    ** See if the first command is a path relative to the current working
+    ** directory by checking if the file exists relative to the current
+    ** working directory.
     */
     if (getcwd(working_dir, MAX_PATH_SIZE)) {
-        absolute_path = new_formatted_string("%s%s%s", working_dir,
-            DIR_SEP_STR, cmd);
+        if (working_dir[strlen(working_dir) - 1] == DIR_SEP_CHR) {
+            /*
+            ** There is already a directory separator at the end of the
+            ** working directory, so don't insert one.
+            */
+            absolute_path = new_formatted_string("%s%s", working_dir, cmd);
+        } else {
+            /*
+            ** There is no directory separator at the end of the working
+            ** directory, so add one.
+            */
+            absolute_path = new_formatted_string("%s%s%s", working_dir,
+                DIR_SEP_STR, cmd);
+        }
         if (file_exists_and_is_executable(absolute_path)) {
             free(filename);
             split_path_into_dir_and_file(absolute_path, &found_dir,
@@ -93,7 +112,7 @@
 void
 convert_dir_separators(char* dir)
 {
-#ifdef __WIN32
+#ifdef MR_WIN32
     char    *chr_ptr;

     chr_ptr = strchr(dir, '/');
@@ -101,14 +120,14 @@
         *chr_ptr = '\\';
         chr_ptr = strchr(dir, '/');
     }
-#endif /* __WIN32 */
+#endif /* not MR_WIN32 */
 }

 void
-set_env_var(const char *var, char* value, set_env_var_behaviour how)
+set_env_var(const char *var, const char *value, set_env_var_behaviour how)
 {
-    const char*   current_value;
-    char*   set_string;
+    const char*     current_value;
+    char*           set_string;

     current_value = getenv(var);
     if (current_value == NULL) {
@@ -119,42 +138,37 @@

     switch (how) {
         case ENVVAR_OVERWRITE:
-            set_string = (char*)malloc(strlen(var) + 1 + strlen(value) + 1);
-            sprintf(set_string, "%s=%s", var, value);
+            set_string = new_formatted_string("%s=%s", var, value);
             break;
         case ENVVAR_PREFIX:
-            set_string = (char*)malloc(strlen(var) + 1 + strlen(value)
-                + strlen(PATH_SEP_STR) + strlen(current_value) + 1);
-            sprintf(set_string, "%s=%s%s%s", var, value, PATH_SEP_STR,
-                current_value);
+            set_string = new_formatted_string("%s=%s%s%s", var, value,
+                PATH_SEP_STR, current_value);
             break;
         case ENVVAR_APPEND:
-            set_string = (char*)malloc(strlen(var) + 1 + strlen(current_value)
-                + strlen(PATH_SEP_STR) + strlen(value) + 1);
-            sprintf(set_string, "%s=%s%s%s", var, current_value, PATH_SEP_STR,
-                value);
+            set_string = new_formatted_string("%s=%s%s%s", var, current_value,
+                PATH_SEP_STR, value);
             break;
         case ENVVAR_CREATE_IF_UNSET:
-            set_string = (char*)malloc(strlen(var) + 1 + strlen(value) + 1);
-            sprintf(set_string, "%s=%s", var, value);
+            set_string = new_formatted_string("%s=%s", var, value);
             break;
         default:
             fprintf(stderr, "unknown set_env_var_behaviour value");
-            exit(1);
+            exit(EXIT_FAILURE);
     }

     if (putenv(set_string) != 0) {
         fprintf(stderr, "unable to set environment %s=%s.\n", var, value);
-        exit(1);
+        exit(EXIT_FAILURE);
     }
     /*
     ** We don't free set_string, since any changes to the contents of
     ** it will alter the environment (according to the Linux manpage).
+    ** XXX on non-Linux systems this may be a memory leak.
     */
 }

 char*
-quote_directories_with_spaces(char *path)
+quote_directories_with_spaces(const char *path)
 {
     int     length;
     char    *new_path;
@@ -166,10 +180,8 @@

     length = strlen(path);
     new_path = copy_string("");
-    found_space = 0;
-    dir_start = 0;

-    for (pos = 0; pos < length; pos++) {
+    for (pos = 0, found_space = 0, dir_start = 0; pos < length; pos++) {
         if (path[pos] == ' ') {
             found_space = 1;
         }
@@ -210,7 +222,7 @@
 */

 static void
-split_path_into_dir_and_file(char *path, char **dir, char **filename)
+split_path_into_dir_and_file(const char *path, char **dir, char **filename)
 {
     char    *last_dir_sep;

@@ -232,42 +244,41 @@
 */

 static int
-file_exists_and_is_executable(char* path)
+file_exists_and_is_executable(const char *path)
 {
     FILE    *fp;
     char    *exe_pos;
-    char    path_with_exe[MAX_PATH_SIZE];
+    char    *path_with_exe;

     if (strlen(path) >= MAX_PATH_SIZE) {
         return 0;
     }
-#if defined(__WIN32) || defined(__CYGWIN__)
+#if defined(MR_WIN32) || defined(MR_CYGWIN)
     exe_pos = strstr(path, ".exe");
     if (exe_pos == NULL || strcmp(exe_pos, ".exe") != 0) {
-        if (snprintf(path_with_exe, MAX_PATH_SIZE - 1, "%s%s", path, ".exe")
-            >= MAX_PATH_SIZE)
-        {
-            return 0;
-        }
+        path_with_exe = new_formatted_string("%s%s", path, ".exe");
     } else {
-        strncpy(path_with_exe, path, MAX_PATH_SIZE - 1);
+        path_with_exe = copy_string(path);
     }
-#else /* defined(__WIN32) || defined(__CYGWIN__) */
-    strncpy(path_with_exe, path, MAX_PATH_SIZE - 1);
-#endif /* defined(__WIN32) || defined(__CYGWIN__) */
+#else /* not (defined(MR_WIN32) || defined(MR_CYGWIN)) */
+    path_with_exe = copy_string(path);
+#endif /* not (defined(MR_WIN32) || defined(MR_CYGWIN)) */

 #ifdef MR_HAVE_UNISTD_H
     if (access(path_with_exe, X_OK) == 0) {
+        free(path_with_exe);
         return 1;
     }
-#else
+#else /* not MR_HAVE_UNISTD_H */
     fp = fopen(path_with_exe, "r");
     if (fp != NULL) {
         fclose(fp);
+        free(path_with_exe);
         return 1;
     }
-#endif /* MR_HAVE_UNISTD_H */
+#endif /* not MR_HAVE_UNISTD_H */

+    free(path_with_exe);
     return 0;
 }

@@ -280,7 +291,7 @@
 */

 static char*
-search_path(char *path, const char *path_separator, char *filename)
+search_path(const char *path, const char *path_separator, const char *filename)
 {
     char    *dir;
     char    *dirs;
@@ -288,16 +299,16 @@
     char    *absolute_path;

     dirs = copy_string(path);
-#ifdef _WIN32
+#ifdef MR_WIN32
     /*
     ** On Windows, directory names that need to be quoted (for example
     ** directory names with spaces) may have the quote characters in the PATH
     ** entry.
     */
     delete_quotes(dirs);
-#endif
+#endif /* not MR_WIN32 */

-    dir = (char*)strtok(dirs, path_separator);
+    dir = strtok(dirs, path_separator);
     do {
         absolute_path = new_formatted_string("%s/%s", dir, filename);
         convert_dir_separators(absolute_path);
@@ -306,7 +317,7 @@
             free(dirs);
             return found_dir;
         }
-        dir = (char*)strtok(NULL, path_separator);
+        dir = strtok(NULL, path_separator);
     } while (dir != NULL);

     free(dirs);
@@ -319,34 +330,62 @@
 */
 char *
 new_formatted_string(const char *fmt, ...) {
-    /* Guess we need no more than 100 bytes. */
-    int     n;
-    int     size = 100;
-    char    *p;
-    va_list ap;

-    if ((p = malloc (size)) == NULL) {
-        return NULL;
+#ifdef MR_HAVE_VSNPRINTF
+    int         formatted_string_length;
+    /* Guess we need no more than 100 bytes. */
+    size_t      size = 100;
+    char        *new_str;
+    va_list     ap;
+
+    if ((new_str = malloc (size)) == NULL) {
+        fprintf(stderr, "Error: Could not allocate %i bytes of memory\n",
+            size);
+        exit(EXIT_FAILURE);
     }
     while (1) {
         /* Try to print in the allocated space. */
         va_start(ap, fmt);
-        n = vsnprintf (p, size, fmt, ap);
+        formatted_string_length = vsnprintf(new_str, size, fmt, ap);
         va_end(ap);
         /* If that worked, return the string. */
-        if (n > -1 && n < size) {
-            return p;
+        if (formatted_string_length > -1 && formatted_string_length < size) {
+            return new_str;
         }
         /* Else try again with more space. */
-        if (n > -1) {   /* glibc 2.1 */
-            size = n+1; /* precisely what is needed */
-        } else {        /* glibc 2.0 */
-            size *= 2;  /* twice the old size */
+        if (formatted_string_length > -1) {      /* glibc 2.1 */
+            size = formatted_string_length + 1;  /* precisely what is needed */
+        } else {                                 /* glibc 2.0 */
+            size *= 2;                           /* twice the old size */
         }
-        if ((p = realloc (p, size)) == NULL) {
-            return NULL;
+        if ((new_str = realloc (new_str, size)) == NULL) {
+            fprintf(stderr, "Error: Could not realloc %i bytes of memory\n",
+                size);
+            exit(EXIT_FAILURE);
         }
     }
+#else /* not MR_HAVE_VSNPRINTF */
+    char        *new_str;
+    va_list     ap;
+
+    /*
+    ** If we don't have vsnprintf then just allocate a large chunk of memory
+    ** hope it is enough.
+    */
+    if ((new_str = malloc (MAX_PATH_SIZE)) == NULL) {
+        fprintf(stderr, "Error: Could not allocate %i bytes of memory\n",
+            MAX_PATH_SIZE);
+        exit(EXIT_FAILURE);
+    }
+    va_start(ap, fmt);
+    if (vsprintf(new_str, fmt, ap) > 0) {
+        return new_str;
+    } else {
+        free(new_str);
+        fprintf(stderr, "Error: unable to format string.\n");
+        exit(EXIT_FAILURE);
+    }
+#endif /* not MR_HAVE_VSNPRINTF */
 }

 char*
@@ -355,12 +394,17 @@
     char*   new_str;

     new_str = malloc(strlen(str) + 1);
+    if (new_str == NULL) {
+        fprintf(stderr, "Error: Could not allocate %i bytes of memory\n",
+            strlen(str) + 1);
+        exit(EXIT_FAILURE);
+    }
     strcpy(new_str, str);
     return new_str;
 }

 static  char*
-copy_substring(char* str, int start, int end)
+copy_substring(const char *str, int start, int end)
 {
     char*   new_str;

@@ -371,6 +415,11 @@
         new_str = malloc(end - start + 2);
         strncpy(new_str, &str[start], end - start + 1);
         new_str[end - start + 1] = '\0';
+        if (new_str == NULL) {
+            fprintf(stderr, "Error: Could not allocate %i bytes of memory\n",
+                strlen(str) + 1);
+            exit(EXIT_FAILURE);
+        }
         return new_str;
     }
 }
diff -u util/path_util.h util/path_util.h
--- util/path_util.h	1 Oct 2005 08:46:19 -0000
+++ util/path_util.h	5 Oct 2005 08:47:24 -0000
@@ -14,6 +14,8 @@
 ** working out were an executable is being executed from.
 */

+#include "mercury_conf_param.h"
+
 /*
 ** Find the location of the executable that was invoked with the
 ** command cmd.
@@ -25,11 +27,11 @@
 ** otherwise NULL is returned.
 */

-extern  char*   find_exec_dir(char* cmd);
+extern  char*   find_exec_dir(const char *cmd);

 /*
 ** This function converts any `/' characters in the given string to `\'
-** characters if __WIN32 is defined.  If __WIN32 is undefined then
+** characters if MR_WIN32 is defined.  If MR_WIN32 is undefined then
 ** the string is left unchanged.
 */

@@ -37,12 +39,12 @@

 /*
 ** Change the given environment variable.
-** The how argument says how the environment variable should be changed.
-** For ENVVAR_PREFIX and ENVVAR_APPEND, the default path seperator will be
-** used to seperate the old value from the prefixed or appended value
-** (on unix the path seperator is `:', while on Windows it is `;').
-** If memory could not be allocated for the environment variable then
-** this function displays an error and aborts the program.
+** The `how' argument says how the environment variable should be changed.
+** For ENVVAR_PREFIX and ENVVAR_APPEND, the default path separator will be
+** used to separate the old value from the prefixed or appended value
+** (on unix the path separator is `:', while on Windows it is `;').
+** If the environment variable could not be set then this function displays an
+** error and aborts the program.
 */

 typedef enum {
@@ -52,18 +54,19 @@
     ENVVAR_CREATE_IF_UNSET
 } set_env_var_behaviour;

-extern  void    set_env_var(const char *var, char* value, set_env_var_behaviour how);
+extern  void    set_env_var(const char *var, const char *value,
+                    set_env_var_behaviour how);

 /*
-** Put quotes around directories that contain spaces in the given path
-** and return a newly allocated string containing the result.
+** Return a newly allocated string containing the given path with quotes
+** placed around any directory names that contain spaces.
 */

-extern  char*   quote_directories_with_spaces(char *path);
+extern  char*   quote_directories_with_spaces(const char *path);

 /*
 ** new_formatted_string creates a new string based on the format string fmt
-** and the rest of its arguments and returns the result in a newly allocated
+** and the rest of its arguments, and returns the result in a newly allocated
 ** string.
 */

@@ -75,7 +78,7 @@

 extern  char*   copy_string(const char* str);

-#ifdef _WIN32
+#ifdef MR_WIN32

 #define IS_DIR_SEP(c) (c == '\\')
 #define DIR_SEP_STR "\\"
@@ -88,7 +91,7 @@
         p[1] == ':' && p[2] == '\\')                                          \
         || (strlen(p) >= 2 && p[0] == '\\' && p[1] == '\\'))

-#else /* ifdef _WIN32 */
+#else /* not MR_WIN32 */

 #define IS_DIR_SEP(c) (c == '/')
 #define DIR_SEP_STR "/"
@@ -98,3 +101,3 @@

-#endif /* ifdef _WIN32 */
+#endif /* not MR_WIN32 */

only in patch2:
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ util/mmc.c	5 Oct 2005 09:36:11 -0000
@@ -0,0 +1,218 @@
+/*
+** vim: ts=4 sw=4 expandtab
+*/
+/*
+** Copyright (C) 2005 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING in the Mercury distribution.
+*/
+
+/*
+** Author: Ian MacLarty.
+**
+** mmc - a wrapper for the Melbourne Mercury Compiler.
+**
+** NOTE:  Any changes to this file may also require changes to
+**        ../scripts/mmc.in.
+**        This is a C version of ../scripts/mmc.in.  This version also
+**        supports the standalone mode (see ../README.Standalone), which
+**        ../scripts/mmc.in does not.
+*/
+
+#include "mercury_conf.h"
+#include "path_util.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef MR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#define MERCURY_CONFIG_DIR_RELATIVE_PATH "lib/mercury"
+
+#define MERCURY_LINK_CMD_FLAG "--link-executable-command"
+
+#define MERCURY_LINK_EXEC_CMD_FLAG "--link-executable-command"
+
+#define MERCURY_LINK_SHARED_LIB_FLAG "--link-shared-lib-command"
+
+#define MACOSX_DEPLOYMENT_TARGET "10.3"
+
+int
+main(int argc, char **argv)
+{
+    char    *env_mercury_compiler;
+    char    *env_mercury_config_dir;
+    char    *env_mercury_stdlib_dir;
+    char    *mercury_compiler_absolute;
+    char    *mercury_config_dir_absolute;
+    char    *link_exec_cmd_absolute_path;
+    char    *link_shared_lib_cmd_absolute_path;
+    char    *tmp_ptr;
+    int     argnum;
+
+#ifdef MR_MMC_STANDALONE
+    {
+        char    *mercury_bin;
+        char    *mercury_home;
+        char    *mercury_c_compiler_absolute;
+
+        mercury_bin = find_exec_dir(argv[0]);
+
+        if (mercury_bin == NULL) {
+            fprintf(stderr, "mmc: Cannot find path to mmc.\n"
+                "Make sure the directory containing mmc is in your PATH.\n");
+            exit(EXIT_FAILURE);
+        }
+
+        tmp_ptr = mercury_bin;
+        mercury_bin = quote_directories_with_spaces(mercury_bin);
+        free(tmp_ptr);
+
+        set_env_var("PATH", mercury_bin, ENVVAR_PREFIX);
+
+        mercury_home = new_formatted_string("%s%s..", mercury_bin,
+            DIR_SEP_STR);
+
+        mercury_compiler_absolute = new_formatted_string(
+            "%s/lib/mercury/bin/%s/mercury_compile", mercury_home,
+            MR_FULLARCH);
+        convert_dir_separators(mercury_compiler_absolute);
+
+        mercury_config_dir_absolute = new_formatted_string("%s/%s",
+            mercury_home, MERCURY_CONFIG_DIR_RELATIVE_PATH);
+        convert_dir_separators(mercury_config_dir_absolute);
+        set_env_var("MERCURY_STDLIB_DIR", mercury_config_dir_absolute,
+            ENVVAR_CREATE_IF_UNSET);
+
+        mercury_c_compiler_absolute = new_formatted_string("%s/%s",
+            mercury_home, MR_C_COMPILER_RELATIVE_PATH);
+        convert_dir_separators(mercury_c_compiler_absolute);
+        set_env_var("MERCURY_C_COMPILER", mercury_c_compiler_absolute,
+            ENVVAR_CREATE_IF_UNSET);
+
+        link_exec_cmd_absolute_path = new_formatted_string("%s/%s",
+            mercury_home, MR_LINK_EXEC_CMD_RELATIVE_PATH);
+        convert_dir_separators(link_exec_cmd_absolute_path);
+
+        link_shared_lib_cmd_absolute_path = new_formatted_string("%s/%s",
+            mercury_home, MR_LINK_SHARED_LIB_CMD_RELATIVE_PATH);
+        convert_dir_separators(link_shared_lib_cmd_absolute_path);
+    }
+#else /* not MR_MMC_STANDALONE */
+    mercury_compiler_absolute = new_formatted_string(
+        "%s/bin/%s/mercury_compile", MR_LIBDIR, MR_FULLARCH);
+    mercury_config_dir_absolute = copy_string(MR_CONFIG_LIBDIR);
+    convert_dir_separators(mercury_compiler_absolute);
+    convert_dir_separators(mercury_config_dir_absolute);
+    link_exec_cmd_absolute_path = copy_string("");
+    link_shared_lib_cmd_absolute_path = copy_string("");
+#endif /* not MR_MMC_STANDALONE */
+
+    env_mercury_compiler = getenv("MERCURY_COMPILER");
+    env_mercury_config_dir = getenv("MERCURY_CONFIG_DIR");
+    env_mercury_stdlib_dir = getenv("MERCURY_STDLIB_DIR");
+
+    if (env_mercury_compiler == NULL) {
+        set_env_var("MERCURY_COMPILER", mercury_compiler_absolute,
+            ENVVAR_CREATE_IF_UNSET);
+    } else {
+        mercury_compiler_absolute = env_mercury_compiler;
+    }
+
+    if (env_mercury_config_dir == NULL) {
+        if (env_mercury_stdlib_dir == NULL) {
+            set_env_var("MERCURY_CONFIG_DIR", mercury_config_dir_absolute,
+                ENVVAR_CREATE_IF_UNSET);
+        } else {
+            set_env_var("MERCURY_CONFIG_DIR", env_mercury_stdlib_dir,
+                ENVVAR_CREATE_IF_UNSET);
+        }
+    }
+
+#ifdef MR_SET_MACOSX_DEPLOYMENT_TARGET
+    set_env_var("MACOSX_DEPLOYMENT_TARGET", MACOSX_DEPLOYMENT_TARGET,
+        ENVVAR_CREATE_IF_UNSET);
+#endif
+
+#ifdef MR_HAVE_UNISTD_H
+    /*
+    ** We have execv, so set up the argument vector and call execv.
+    */
+    {
+        char            **mercury_compiler_args;
+        int             num_args;
+        int             current_arg;
+        int             current_orig_arg;
+
+        num_args = 0;
+        if (strcmp(link_exec_cmd_absolute_path, "") != 0) {
+            num_args += 2;
+        }
+        if (strcmp(link_shared_lib_cmd_absolute_path, "") != 0) {
+            num_args += 2;
+        }
+        num_args += argc;
+        mercury_compiler_args = (char **)
+            calloc(num_args + 1, sizeof(char **));
+        mercury_compiler_args[num_args] = NULL;
+        mercury_compiler_args[0] = mercury_compiler_absolute;
+
+        current_arg = 1;
+        if (strcmp(link_exec_cmd_absolute_path, "") != 0) {
+            mercury_compiler_args[current_arg] =
+                (char *) MERCURY_LINK_EXEC_CMD_FLAG;
+            mercury_compiler_args[current_arg + 1] =
+                link_exec_cmd_absolute_path;
+            current_arg += 2;
+        }
+        if (strcmp(link_shared_lib_cmd_absolute_path, "") != 0) {
+            mercury_compiler_args[current_arg] =
+                (char *) MERCURY_LINK_SHARED_LIB_FLAG;
+            mercury_compiler_args[current_arg + 1] =
+                link_shared_lib_cmd_absolute_path;
+            current_arg += 2;
+        }
+
+        current_orig_arg = 1;
+        for(;current_arg < num_args; current_arg++) {
+            mercury_compiler_args[current_arg] = argv[current_orig_arg];
+            current_orig_arg++;
+        }
+
+        execv(mercury_compiler_absolute, (char *const *)mercury_compiler_args);
+        fprintf(stderr, "Error executing `%s': %s\n",
+            mercury_compiler_absolute, strerror(errno));
+        return EXIT_FAILURE;
+    }
+#else /* not MR_HAVE_UNISTD_H */
+    /*
+    ** We don't have execv, so use system instead.
+    */
+    {
+        char    *invoke_mercury_compiler_command;
+        char    *extra_mercury_compiler_flags;
+
+        extra_mercury_compiler_flags = new_formatted_string(
+            "%s %s %s %s",
+            MERCURY_LINK_EXEC_CMD_FLAG,
+            link_exec_cmd_absolute_path,
+            MERCURY_LINK_SHARED_LIB_FLAG,
+            link_shared_lib_cmd_absolute_path);
+        convert_dir_separators(extra_mercury_compiler_flags);
+
+        invoke_mercury_compiler_command = new_formatted_string("%s %s",
+            mercury_compiler_absolute, extra_mercury_compiler_flags);
+        for (argnum = 1; argnum < argc; argnum++) {
+            tmp_ptr = invoke_mercury_compiler_command;
+            invoke_mercury_compiler_command = new_formatted_string("%s %s",
+                invoke_mercury_compiler_command, argv[argnum]);
+            free(tmp_ptr);
+        }
+        return system(invoke_mercury_compiler_command);
+    }
+#endif /* not MR_HAVE_UNISTD_H */
+}
only in patch2:
--- scripts/mercury_config.in	13 Sep 2005 03:36:45 -0000	1.5
+++ scripts/mercury_config.in	5 Oct 2005 02:07:34 -0000
@@ -137,6 +137,22 @@
 mkdir $TMPDIR/java/runtime || exit 1
 touch $TMPDIR/java/runtime/Constants.java.in || exit 1
 touch $TMPDIR/java/runtime/Native.java.in || exit 1
+mkdir $TMPDIR/compiler || exit 1
+touch $TMPDIR/compiler/COMP_FLAGS.in || exit 1
+mkdir $TMPDIR/library || exit 1
+touch $TMPDIR/library/LIB_FLAGS.in || exit 1
+mkdir $TMPDIR/mdbcomp || exit 1
+touch $TMPDIR/mdbcomp/MDBCOMP_FLAGS.in || exit 1
+mkdir $TMPDIR/browser || exit 1
+touch $TMPDIR/browser/MDB_FLAGS.in || exit 1
+mkdir $TMPDIR/analysis || exit 1
+touch $TMPDIR/analysis/ANALYSIS_FLAGS.in || exit 1
+mkdir $TMPDIR/slice || exit 1
+touch $TMPDIR/slice/SLICE_FLAGS.in || exit 1
+mkdir $TMPDIR/profiler || exit 1
+touch $TMPDIR/profiler/PROF_FLAGS.in || exit 1
+mkdir $TMPDIR/deep_profiler || exit 1
+touch $TMPDIR/deep_profiler/DEEP_FLAGS.in || exit 1

 cd $TMPDIR
 case $# in
@@ -181,4 +197,9 @@
 			;;
 	esac
 done
+cd ../util
+# configure will create reconf_install_c_mmc if the C version of mmc
+# must be installed.  reconf_install_c_mmc will build and install the C
+# version of mmc.
+[ -e reconf_install_c_mmc ] && /bin/sh reconf_install_c_mmc $output_prefix/bin
 rm -rf $TMPDIR
only in patch2:
--- runtime/mercury_conf_param.h	13 Sep 2005 08:25:37 -0000	1.85
+++ runtime/mercury_conf_param.h	5 Oct 2005 03:59:12 -0000
@@ -831,6 +831,14 @@
   #define MR_BROKEN_ST_INO
 #endif

+/*
+** MR_CYGWIN -- We are compiling under Cygwin.
+** Note that MR_WIN32 is not defined under Cygwin.
+*/
+#ifdef __CYGWIN__
+  #define MR_CYGWIN
+#endif
+
 /*---------------------------------------------------------------------------*/

 #endif /* MERCURY_CONF_PARAM_H */
only in patch2:
--- runtime/mercury_conf.h.in	13 Sep 2005 08:25:37 -0000	1.52
+++ runtime/mercury_conf.h.in	5 Oct 2005 07:14:54 -0000
@@ -488,6 +488,43 @@
 */
 #undef	MR_NEW_MERCURYFILE_STRUCT

+/*
+** MR_MMC_STANDALONE
+**	Defined if the standalone version of mmc should be installed.
+** MR_C_COMPILER_RELATIVE_PATH
+**	Set by configure to the path to the C compiler.  The path is relative
+**	to the Mercury installation directory.
+** MR_LINK_EXEC_CMD_RELATIVE_PATH
+**	Set by configure to the command to link object files into an
+**	executable.  The path is relative to the Mercury installation
+**	directory.
+** MR_LINK_SHARED_LIB_CMD_RELATIVE_PATH
+**	Set by configure to the command to link object files into a
+**	shared library.  The path is relative to the Mercury installation
+**	directory.
+*/
+#undef	MR_MMC_STANDALONE
+#undef	MR_C_COMPILER_RELATIVE_PATH
+#undef	MR_LINK_EXEC_CMD_RELATIVE_PATH
+#undef	MR_LINK_SHARED_LIB_CMD_RELATIVE_PATH
+
+/*
+** MR_LIBDIR
+**	The absolute path to the library installation directory.
+** MR_CONFIG_LIBDIR
+**	The absolute path to the Mercury compiler configuration directory.
+*/
+#undef	MR_LIBDIR
+#undef	MR_CONFIG_LIBDIR
+
+/*
+** MR_SET_MACOSX_DEPLOYMENT_TARGET
+**	If this is set then the MACOSX_DEPLOYMENT_TARGET will be set to the
+**	value of this macro before mercury_compile is invoked.
+**	See the comments in configure.in for why this is done.
+*/
+#undef	MR_SET_MACOSX_DEPLOYMENT_TARGET
+
 /*---------------------------------------------------------------------------*/

 #include "mercury_conf_param.h"
only in patch2:
--- runtime/Mmakefile	12 Sep 2005 03:03:04 -0000	1.126
+++ runtime/Mmakefile	5 Oct 2005 02:38:59 -0000
@@ -470,6 +470,8 @@
 	cp `vpath_find mercury_conf.h` $(INSTALL_CONF_DIR)
 	-chmod u+w $(INSTALL_CONF_DIR)/mercury_conf.h
 	cp `vpath_find mercury_conf.h.in` $(INSTALL_RECONF_DIR)/runtime
+	cp `vpath_find mercury_conf_param.h` $(INSTALL_RECONF_DIR)/runtime
+	cp `vpath_find mercury_conf_bootstrap.h` $(INSTALL_RECONF_DIR)/runtime
 	cp `vpath_find $(MACHHDRS)` $(INSTALL_INC_DIR)/machdeps

 .PHONY: install_init
only in patch2:
--- Mmake.common.in	16 Aug 2005 15:51:28 -0000	1.83
+++ Mmake.common.in	4 Oct 2005 08:26:34 -0000
@@ -472,3 +472,8 @@
 		@chmod a-w $*_FLAGS

 #-----------------------------------------------------------------------------#
+
+# This variable determines whether to install the C or sh version of mmc.
+INSTALL_C_MMC=@INSTALL_C_MMC@
+
+#-----------------------------------------------------------------------------#
only in patch2:
--- .README.in	3 Oct 2005 12:53:08 -0000	1.15
+++ .README.in	5 Oct 2005 09:50:10 -0000
@@ -89,6 +89,9 @@
 	README.OSF1		DEC Alpha systems running OSF/1 version 3.x
 	README.Solaris		Solaris (SunOS) on SPARC or Intel x86.

+Documentation on how to package a C compiler with the Mercury compiler in
+a transportable way is contained in README.Standalone.
+
 The documentation sources are in the `doc' subdirectory.  The installation
 process will install INFO, HTML, and DVI versions in the locations specified
 in the file Mmake.common - by default these locations are


--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list