[m-rev.] for review: fix runtime program basename on Windows

Julien Fischer jfischer at opturion.com
Sun Oct 22 03:05:29 AEDT 2023


For review by anyone.

The other major use of the program basename in the runtime is the
default name of trace count files (which was actually why I started
looking at all this). I will fix that in my next change.

------------------------

Several places in the runtime make use of the program basename. This is
computed by stripping any directory qualification from the value of argv[0].
Currently, this is done by searching for uses of '/' as a directory separator.
This does not work on Windows where '\' is the directory separator and paths
may also be drive qualified. This diff adds a function that will handle
Windows-style paths.

Also, none of the uses of the program basename in the runtime currently account
for the presence of the .exe executable extension on Windows. This diff makes
it so that the .exe extension is not required in those uses.

Fix a bug in the handling of the runtime's --trace-count-if-exec and
--coverage-test-if-exec options on Unix-like systems. Only one level of
directory qualification was being removed from the program name before
comparing it against the option value.

runtime/mercury_runtime_util.[ch]:
     Add a function, MR_get_program_basename(), whose job is
     to strip any drive or directory qualification, and the executable
     extension from a given path.  On Windows, we use the OS's splitpath()
     function to do this.

     Add an XXX about the .exe extension on Cygwin.

runtime/mercury_wrapper.c:
     Use the new function when computing:
     - the name of the program-specific MERCURY_OPTIONS environment variable;
     - the value of the --trace-count-if-exec runtime option;
     - the value of the --coverage-test-if-exec runtime option.

doc/user_guide.texi:
     Document that the .exe extension is not expected in the three spots above.

Julien.

diff --git a/doc/user_guide.texi b/doc/user_guide.texi
index ed2a031..517c21f 100644
--- a/doc/user_guide.texi
+++ b/doc/user_guide.texi
@@ -10803,6 +10803,8 @@ The options given in this environment variable apply to every program;
  the options given in an environment variable
  whose name is of the form @env{MERCURY_OPTIONS_ at var{progname}}
  apply only to programs named @var{progname}.
+(Note that @var{progname} does @emph{not} include the @file{.exe} on those
+systems that use it.)
  Options may also be set for a particular executable at compile time
  by passing @samp{--runtime-flags} options
  to the invocations of @samp{ml} and @samp{c2init} which create that executable.
@@ -11253,7 +11255,7 @@ events in the output, even the ones that were not executed.
  @item --trace-count-if-exec @var{prog}
  @findex --trace-count-if-exec (runtime option)
  Act as the @samp{--trace-count} option, but only if the executable is named
- at var{prog}.
+ at var{prog} (excluding any @file{.exe} extension).
  This is to allow the collection of trace count information from only one
  Mercury program even if several Mercury programs are executed with the same
  setting of @env{MERCURY_OPTIONS}.
@@ -11262,7 +11264,7 @@ setting of @env{MERCURY_OPTIONS}.
  @item --coverage-test-if-exec @var{prog}
  @findex --coverage-test-if-exec (runtime option)
  Act as the @samp{--coverage-test} option, but only if the executable is named
- at var{prog}.
+ at var{prog} (excluding any @file{.exe} extension).
  This is to allow the collection of coverage test information from only one
  Mercury program even if several Mercury programs are executed with the same
  setting of @env{MERCURY_OPTIONS}.
diff --git a/runtime/mercury_runtime_util.c b/runtime/mercury_runtime_util.c
index 2b532cf..e1a7fb2 100644
--- a/runtime/mercury_runtime_util.c
+++ b/runtime/mercury_runtime_util.c
@@ -1,7 +1,7 @@
  // vim: ts=4 sw=4 expandtab ft=c

  // Copyright (C) 2001-2002, 2006 The University of Melbourne.
-// Copyright (C) 2014, 2016, 2018 The Mercury team.
+// Copyright (C) 2014, 2016, 2018, 2023 The Mercury team.
  // This file is distributed under the terms specified in COPYING.LIB.

  // This module contains utility functions for the rest of the Mercury runtime.
@@ -10,9 +10,14 @@

  #include    "mercury_imp.h"
  #include    "mercury_runtime_util.h"
+#include    "mercury_string.h"
+#include    "mercury_windows.h"

  #include    <stdio.h>
  #include    <string.h>
+#if defined(MR_WIN32) && !defined(MR_CYGWIN)
+   #include <wchar.h>
+#endif

  #ifdef MR_HAVE_UNISTD_H
    #include  <unistd.h>
@@ -148,3 +153,39 @@ MR_setenv(const char *name, const char *value, int overwrite)
    #error "MR_setenv: unable to define"
  #endif
  }
+
+// XXX TODO: Cygwin -- strip .exe extension if present.
+const char *
+MR_get_program_basename(const char *program_name)
+{
+    const char  *basename;
+
+    #if defined(MR_WIN32) && !defined(MR_CYGWIN)
+
+        wchar_t wname[_MAX_FNAME];
+
+        int err = _wsplitpath_s(MR_utf8_to_wide(program_name),
+            NULL, 0,  // Ignore drive.
+            NULL, 0,  // Ignore directories.
+            wname, _MAX_FNAME,
+            NULL, 0   // Ignore .exe extension.
+        );
+        if (err != 0) {
+            MR_fatal_error("Could not split path");
+        }
+        basename = MR_wide_to_utf8(wname, NULL);
+
+    #else
+
+        char    *slash;
+
+        basename = MR_copy_string(program_name);
+        slash = strrchr(basename, '/');
+        if (slash != NULL) {
+            basename = slash + 1;
+        }
+
+    #endif
+
+    return basename;
+}
diff --git a/runtime/mercury_runtime_util.h b/runtime/mercury_runtime_util.h
index 888154e..3f797f7 100644
--- a/runtime/mercury_runtime_util.h
+++ b/runtime/mercury_runtime_util.h
@@ -27,4 +27,8 @@ extern  void        MR_checked_atexit(void (*func)(void));
  extern  int         MR_setenv(const char *name, const char *value,
                          int overwrite);

+// Strip any directory components and the ".exe" extension (if present) from
+// the argument.
+extern const char   *MR_get_program_basename(const char *);
+
  #endif  // MERCURY_RUNTIME_UTIL_H
diff --git a/runtime/mercury_wrapper.c b/runtime/mercury_wrapper.c
index ae768e2..e6ba7bc 100644
--- a/runtime/mercury_wrapper.c
+++ b/runtime/mercury_wrapper.c
@@ -55,6 +55,7 @@ ENDINIT
  #include    "mercury_deep_profiling.h"
  #include    "mercury_memory.h"          // for MR_copy_string()
  #include    "mercury_memory_handlers.h" // for MR_default_handler
+#include    "mercury_runtime_util.h"    // for MR_get_program_basename()
  #include    "mercury_thread.h"          // for MR_debug_threads
  #include    "mercury_threadscope.h"

@@ -1076,17 +1077,9 @@ MR_process_environment_options(void)
          gen_env_options = (char *) "";
      }

-    // Find out the program's name, stripping off any directory names.
-    // XXX WINDOWS: the path separator is incorrect for non-Cygwin Windows.
-    // It also does not handle drive qualified paths. And presumably the
-    // name of the program-specific options should not include the .exe
-    // extension.
-    progname = MR_progname;
-    for (s = progname; *s != '\0'; s++) {
-        if (*s == '/') {
-            progname = s + 1;
-        }
-    }
+    // Find out the program's name, stripping off any directory names and the
+    // .exe extension on those systems that use it.
+    progname = MR_get_program_basename(MR_progname);

      // Build the program-specific option's name: MERCURY_OPTIONS_progname.
      mercury_options_len = strlen(MERCURY_OPTIONS);
@@ -1407,6 +1400,7 @@ struct MR_option MR_long_opts[] = {
      { NULL,                             0, 0, 0 }
  };

+
  static void
  MR_process_options(int argc, char **argv)
  {
@@ -2341,12 +2335,7 @@ MR_matches_exec_name(const char *option)
      char        *s;
      const char  *exec_name;

-    s = strrchr(MR_progname, '/');
-    if (s == NULL) {
-        exec_name = MR_progname;
-    } else {
-        exec_name = s + 1;
-    }
+    exec_name = MR_get_program_basename(MR_progname);

      if (MR_streq(option, exec_name)) {
          return MR_TRUE;


More information about the reviews mailing list