[m-rev.] for review: improve usage and version messages in the slice directory

Julien Fischer jfischer at opturion.com
Sun Oct 1 12:55:39 AEDT 2023


Improve usage and version messages in the slice directory.

Improve the usage and version messages for the programs in the
slice directory by:

- adding --help and --version options for programs that did
   not already have them (Mantis issue #208);
- making version and usage message consistent in form with other
   programs in the system;
- on a usage error, writing the short usage message to the standard
   error stream and setting a non-zero exit status.

This also prepares the way for creating manual pages for the programs in the
slice directory.

slice/mcov.m:
slice/mslice.m:
slice/mdice.m:
slice/mtc_diff.m:
slice/mtc_union.m:
     As above.

Julien.

diff --git a/slice/mcov.m b/slice/mcov.m
index aab3ebd..a74db7e 100644
--- a/slice/mcov.m
+++ b/slice/mcov.m
@@ -140,7 +140,8 @@ do_coverage_testing(OptionTable, Args, !IO) :-
          )
      ;
          Args = [],
-        short_usage(StdOutStream, !IO)
+        short_usage(StdErrStream, !IO),
+        io.set_exit_status(1, !IO)
      ).

  :- func kind_warning = string.
@@ -439,7 +440,7 @@ write_path_port_for_user(OutStream, port_and_path(Port, Path), !IO) :-
  display_version(OutStream, !IO) :-
      Version = library.mercury_version,
      io.format(OutStream,
-        "Mercury Coverage Testing Tool, version %s",
+        "Mercury coverage testing tool, version %s",
          [s(Version)], !IO),
      Package = library.package_version,
      ( if Package = "" then
@@ -462,13 +463,18 @@ short_usage(OutStream, !IO) :-

  long_usage(OutStream, !IO) :-
      io.write_string(OutStream,
-        "Name: mcov - Mercury Coverage Testing Tool\n", !IO),
+        "Name: mcov - Mercury coverage testing tool\n", !IO),
      write_copyright_notice(OutStream, !IO),
      io.write_string(OutStream, "Usage: mcov [<options>] <arguments>\n", !IO),
-    io.write_string(OutStream, "Arguments:\n", !IO),
-    io.write_string(OutStream,
-        "\tArguments are assumed to Mercury trace count files.\n", !IO),
-    io.write_string(OutStream, "Options:\n", !IO),
+    io.write_string(OutStream, "\nDescription:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "`mcov' outputs a test coverage report."
+    ], !IO),
+    io.write_string(OutStream, "\nArguments:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "Arguments are assumed to Mercury trace count files."
+    ], !IO),
+    io.write_string(OutStream, "\nOptions:\n", !IO),
      io.write_prefixed_lines(OutStream, "\t", [
          "-?, -h, --help",
          "\tPrint help about using mcov (on the standard output) and exit",
@@ -476,7 +482,7 @@ long_usage(OutStream, !IO) :-
          "--version",
          "\tPrint version information.",
          "-v, --verbose",
-        "\tPrint the name of each trace count file as it is added to the union",
+        "\tPrint the name of each trace count file as it is added to the union.",
          "-d, --detailed",
          "\tPrint a report for each label that has not been executed, even",
          "\tif some other code has been executed in the same procedure.",
diff --git a/slice/mdice.m b/slice/mdice.m
index f664ca8..484d6c3 100644
--- a/slice/mdice.m
+++ b/slice/mdice.m
@@ -2,6 +2,7 @@
  % vim: ft=mercury ts=4 sw=4 expandtab
  %---------------------------------------------------------------------------%
  % Copyright (C) 2005-2006, 2012 The University of Melbourne.
+% Copyright (C) 2015-2016, 2019-2020, 2022-2023 The Mercury team.
  % This file may only be copied under the terms of the GNU General
  % Public License - see the file COPYING in the Mercury distribution.
  %---------------------------------------------------------------------------%
@@ -29,7 +30,9 @@
  :- import_module mdbcomp.slice_and_dice.
  :- import_module mdbcomp.shared_utilities.

+:- import_module bool.
  :- import_module getopt.
+:- import_module library.
  :- import_module maybe.
  :- import_module list.
  :- import_module string.
@@ -44,67 +47,146 @@ main(!IO) :-
      getopt.process_options(OptionOps, Args0, Args, GetoptResult),
      (
          GetoptResult = ok(OptionTable),
-        (
-            Args = [],
-            usage(StdOutStream, !IO)
-        ;
-            Args = [_],
-            usage(StdOutStream, !IO)
-        ;
-            Args = [PassFileName, FailFileName],
-            lookup_string_option(OptionTable, sort, SortStr),
-            lookup_string_option(OptionTable, modulename, Module),
-            lookup_int_option(OptionTable, max_row, MaxRow),
-            lookup_int_option(OptionTable, max_pred_column, MaxPredColumn),
-            lookup_int_option(OptionTable, max_path_column, MaxPathColumn),
-            lookup_int_option(OptionTable, max_file_column, MaxFileColumn),
-            ( if MaxPredColumn = 0 then
-                MaybeMaxPredColumn = no
-            else
-                MaybeMaxPredColumn = yes(MaxPredColumn)
-            ),
-            ( if MaxPathColumn = 0 then
-                MaybeMaxPathColumn = no
-            else
-                MaybeMaxPathColumn = yes(MaxPathColumn)
-            ),
-            ( if MaxFileColumn = 0 then
-                MaybeMaxFileColumn = no
-            else
-                MaybeMaxFileColumn = yes(MaxFileColumn)
-            ),
-            read_dice_to_string(PassFileName, FailFileName, SortStr, MaxRow,
-                MaybeMaxPredColumn, MaybeMaxPathColumn, MaybeMaxFileColumn,
-                Module, DiceStr, Problem, !IO),
-            ( if Problem = "" then
-                io.write_string(StdOutStream, DiceStr, !IO)
-            else
-                io.write_string(StdOutStream, Problem, !IO),
-                io.nl(StdOutStream, !IO),
-                io.set_exit_status(1, !IO)
-            )
-        ;
-            Args = [_, _, _ | _],
-            usage(StdOutStream, !IO)
+        ( if lookup_bool_option(OptionTable, help, yes) then
+            long_usage(StdOutStream, !IO)
+        else if lookup_bool_option(OptionTable, version, yes) then
+            display_version(StdOutStream, !IO)
+        else
+            main_2(StdOutStream, OptionTable, Args, !IO)
          )
      ;
          GetoptResult = error(GetoptError),
          GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO)
+        io.stderr_stream(StdErrStream, !IO),
+        io.format(StdErrStream, "%s\n", [s(GetoptErrorMsg)], !IO),
+        io.set_exit_status(1, !IO)
      ).

-:- pred usage(io.text_output_stream::in, io::di, io::uo) is det.
+:- pred main_2(io.text_output_stream::in, option_table::in, list(string)::in,
+    io::di, io::uo) is det.

-usage(OutStream, !IO) :-
+main_2(StdOutStream, OptionTable, Args, !IO) :-
+    (
+        ( Args = []
+        ; Args = [_]
+        ; Args = [_, _, _ | _]
+        ),
+        io.stderr_stream(StdErrStream, !IO),
+        short_usage(StdErrStream, !IO),
+        io.set_exit_status(1, !IO)
+    ;
+        Args = [PassFileName, FailFileName],
+        lookup_string_option(OptionTable, sort, SortStr),
+        lookup_string_option(OptionTable, modulename, Module),
+        lookup_int_option(OptionTable, max_row, MaxRow),
+        lookup_int_option(OptionTable, max_pred_column, MaxPredColumn),
+        lookup_int_option(OptionTable, max_path_column, MaxPathColumn),
+        lookup_int_option(OptionTable, max_file_column, MaxFileColumn),
+        ( if MaxPredColumn = 0 then
+            MaybeMaxPredColumn = no
+        else
+            MaybeMaxPredColumn = yes(MaxPredColumn)
+        ),
+        ( if MaxPathColumn = 0 then
+            MaybeMaxPathColumn = no
+        else
+            MaybeMaxPathColumn = yes(MaxPathColumn)
+        ),
+        ( if MaxFileColumn = 0 then
+            MaybeMaxFileColumn = no
+        else
+            MaybeMaxFileColumn = yes(MaxFileColumn)
+        ),
+        read_dice_to_string(PassFileName, FailFileName, SortStr, MaxRow,
+            MaybeMaxPredColumn, MaybeMaxPathColumn, MaybeMaxFileColumn,
+            Module, DiceStr, Problem, !IO),
+        ( if Problem = "" then
+            io.write_string(StdOutStream, DiceStr, !IO)
+        else
+            io.write_string(StdOutStream, Problem, !IO),
+            io.nl(StdOutStream, !IO),
+            io.set_exit_status(1, !IO)
+        )
+    ).
+
+%---------------------------------------------------------------------------%
+
+:- pred display_version(io.text_output_stream::in, io::di, io::uo) is det.
+
+display_version(OutStream, !IO) :-
+    Version = library.mercury_version,
+    io.format(OutStream, "Mercury dice tool, version %s",
+        [s(Version)], !IO),
+    Package = library.package_version,
+    ( if Package = "" then
+        io.nl(OutStream, !IO)
+    else
+        io.format(OutStream, " (%s)\n", [s(Package)], !IO)
+    ),
+    write_copyright_notice(OutStream, !IO).
+
+:- pred short_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+short_usage(OutStream, !IO) :-
+    io.write_strings(OutStream, [
+        "Usage: mdice [<options>] <passfile> <failfile>\n",
+        "Use `mdice --help' for more information.\n"
+    ], !IO).
+
+:- pred long_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+long_usage(OutStream, !IO) :-
+    io.write_string(OutStream, "Name: mdice - Mercury dice tool\n", !IO),
+    write_copyright_notice(OutStream, !IO),
+    io.write_string(OutStream, "\nUsage: mdice [<options>] <passfile> <failfile>\n", !IO),
+    io.write_string(OutStream, "\nDescription:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "`mdice' creates a comparison between passing and failing runs of a",
+        "program."
+    ], !IO),
+    io.write_string(OutStream, "\nArguments:\n", !IO),
      io.write_string(OutStream,
-        "Usage: mdice [-s sortspec] [-m module] [-l N] [-n N] [-p N] [-f N] "
-        ++ "passfile failfile\n",
-        !IO).
+        "\tArguments are assumed to Mercury trace count files.\n", !IO),
+    io.write_string(OutStream, "\nOptions:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "-?, -h, --help",
+        "\tPrint help about using mdice (on the standard output) and exit",
+        "\twithout doing any further processing.",
+        "--version",
+        "\tPrint version information.",
+        "-s <sortspec>, --sort <sortspec>",
+        "\tSpecify how the output should be sorted.",
+        "\t(See the Mercury User's Guide for details.)",
+        "-l <N>, --limit <N>",
+        "\tLimit the output to at most N lines.",
+        "-m <module>, --module <module>",
+        "\tRestrict the output to the given module and its submodules (if any).",
+        "-n <N>, --max-column-name <N>",
+        "\tThe maximum width of the column containing predicate names.",
+        "\tA value of zero means there is no maximum width.",
+        "-p <N>, --max-path-column <N>",
+        "\tThe maximum width of the column containing ports and goal paths.",
+        "\tA value of zero means there is no maximum width.",
+        "-f <N>, --max-file-column <N>",
+        "\tThe maximum width of the column containing file names and line numbers.",
+        "\tA value of zero means there is no maximum width."
+    ], !IO).
+
+:- pred write_copyright_notice(io.text_output_stream::in, io::di, io::uo)
+    is det.
+
+write_copyright_notice(OutStream, !IO) :-
+    io.write_strings(OutStream, [
+        "Copyright (C) 2005-2006, 2012 The University of Melbourne\n",
+        "Copyright (C) 2015-2016, 2019-2020, 2022-2023 The Mercury team\n"
+    ], !IO).

  %---------------------------------------------------------------------------%

  :- type option
-    --->    sort
+    --->    help
+    ;       version
+    ;       sort
      ;       max_row
      ;       max_pred_column
      ;       max_path_column
@@ -115,24 +197,30 @@ usage(OutStream, !IO) :-

  :- pred short_option(character::in, option::out) is semidet.

-short_option('s',               sort).
-short_option('l',               max_row).
-short_option('n',               max_pred_column).
-short_option('p',               max_path_column).
-short_option('f',               max_file_column).
-short_option('m',               modulename).
+short_option('?', help).
+short_option('h', help).
+short_option('s', sort).
+short_option('l', max_row).
+short_option('n', max_pred_column).
+short_option('p', max_path_column).
+short_option('f', max_file_column).
+short_option('m', modulename).

  :- pred long_option(string::in, option::out) is semidet.

-long_option("sort",             sort).
-long_option("limit",            max_row).
-long_option("max-name-column",  max_pred_column).
-long_option("max-path-column",  max_path_column).
-long_option("max-file-column",  max_file_column).
-long_option("module",           modulename).
+long_option("help",            help).
+long_option("version",         version).
+long_option("sort",            sort).
+long_option("limit",           max_row).
+long_option("max-name-column", max_pred_column).
+long_option("max-path-column", max_path_column).
+long_option("max-file-column", max_file_column).
+long_option("module",          modulename).

  :- pred option_default(option::out, option_data::out) is multi.

+option_default(help,            bool(no)).
+option_default(version,         bool(no)).
  option_default(sort,            string("S")).
  option_default(max_row,         int(100)).
  option_default(max_pred_column, int(35)).
diff --git a/slice/mslice.m b/slice/mslice.m
index 72cf225..1ebe2da 100644
--- a/slice/mslice.m
+++ b/slice/mslice.m
@@ -2,6 +2,7 @@
  % vim: ft=mercury ts=4 sw=4 expandtab
  %---------------------------------------------------------------------------%
  % Copyright (C) 2005-2006, 2012 The University of Melbourne.
+% Copyright (C) 2015-2016, 2019-2020, 2022-2023 The Mercury team.
  % This file may only be copied under the terms of the GNU General
  % Public License - see the file COPYING in the Mercury distribution.
  %---------------------------------------------------------------------------%
@@ -11,7 +12,6 @@
  %---------------------------------------------------------------------------%

  :- module mslice.
-
  :- interface.

  :- import_module io.
@@ -27,11 +27,15 @@
  :- import_module mdbcomp.slice_and_dice.
  :- import_module mdbcomp.shared_utilities.

+:- import_module bool.
  :- import_module getopt.
+:- import_module library.
  :- import_module list.
  :- import_module maybe.
  :- import_module string.

+%---------------------------------------------------------------------------%
+
  main(!IO) :-
      io.stdout_stream(StdOutStream, !IO),
      unlimit_stack(!IO),
@@ -40,64 +44,144 @@ main(!IO) :-
      getopt.process_options(OptionOps, Args0, Args, GetoptResult),
      (
          GetoptResult = ok(OptionTable),
-        (
-            Args = [],
-            usage(StdOutStream, !IO)
-        ;
-            Args = [FileName],
-            lookup_string_option(OptionTable, sort, SortStr),
-            lookup_int_option(OptionTable, max_row, MaxRow),
-            lookup_int_option(OptionTable, max_pred_column, MaxPredColumn),
-            lookup_int_option(OptionTable, max_path_column, MaxPathColumn),
-            lookup_int_option(OptionTable, max_file_column, MaxFileColumn),
-            lookup_string_option(OptionTable, modulename, Module),
-            ( if MaxPredColumn = 0 then
-                MaybeMaxPredColumn = no
-            else
-                MaybeMaxPredColumn = yes(MaxPredColumn)
-            ),
-            ( if MaxPathColumn = 0 then
-                MaybeMaxPathColumn = no
-            else
-                MaybeMaxPathColumn = yes(MaxPathColumn)
-            ),
-            ( if MaxFileColumn = 0 then
-                MaybeMaxFileColumn = no
-            else
-                MaybeMaxFileColumn = yes(MaxFileColumn)
-            ),
-            read_slice_to_string(FileName, SortStr, MaxRow,
-                MaybeMaxPredColumn, MaybeMaxPathColumn, MaybeMaxFileColumn,
-                Module, SliceStr, Problem, !IO),
-            ( if Problem = "" then
-                io.write_string(StdOutStream, SliceStr, !IO)
-            else
-                io.write_string(StdOutStream, Problem, !IO),
-                io.nl(StdOutStream, !IO),
-                io.set_exit_status(1, !IO)
-            )
-        ;
-            Args = [_, _ | _],
-            usage(StdOutStream, !IO)
+        ( if lookup_bool_option(OptionTable, help, yes) then
+            long_usage(StdOutStream, !IO)
+        else if lookup_bool_option(OptionTable, version, yes) then
+            display_version(StdOutStream, !IO)
+        else
+            main_2(StdOutStream, OptionTable, Args, !IO)
          )
      ;
          GetoptResult = error(GetoptError),
          GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO)
+        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO),
+        io.set_exit_status(1, !IO)
+    ).
+
+:- pred main_2(io.text_output_stream::in, option_table::in, list(string)::in,
+    io::di, io::uo) is det.
+
+main_2(StdOutStream, OptionTable, Args, !IO) :-
+    (
+        ( Args = []
+        ; Args = [_, _ | _]
+        ),
+        io.stderr_stream(StdErrStream, !IO),
+        short_usage(StdErrStream, !IO),
+        io.set_exit_status(1, !IO)
+    ;
+        Args = [FileName],
+        lookup_string_option(OptionTable, sort, SortStr),
+        lookup_int_option(OptionTable, max_row, MaxRow),
+        lookup_int_option(OptionTable, max_pred_column, MaxPredColumn),
+        lookup_int_option(OptionTable, max_path_column, MaxPathColumn),
+        lookup_int_option(OptionTable, max_file_column, MaxFileColumn),
+        lookup_string_option(OptionTable, modulename, Module),
+        ( if MaxPredColumn = 0 then
+            MaybeMaxPredColumn = no
+        else
+            MaybeMaxPredColumn = yes(MaxPredColumn)
+        ),
+        ( if MaxPathColumn = 0 then
+            MaybeMaxPathColumn = no
+        else
+            MaybeMaxPathColumn = yes(MaxPathColumn)
+        ),
+        ( if MaxFileColumn = 0 then
+            MaybeMaxFileColumn = no
+        else
+            MaybeMaxFileColumn = yes(MaxFileColumn)
+        ),
+        read_slice_to_string(FileName, SortStr, MaxRow,
+            MaybeMaxPredColumn, MaybeMaxPathColumn, MaybeMaxFileColumn,
+            Module, SliceStr, Problem, !IO),
+        ( if Problem = "" then
+            io.write_string(StdOutStream, SliceStr, !IO)
+        else
+            io.write_string(StdOutStream, Problem, !IO),
+            io.nl(StdOutStream, !IO),
+            io.set_exit_status(1, !IO)
+        )
      ).

-:- pred usage(io.text_output_stream::in, io::di, io::uo) is det.
+%---------------------------------------------------------------------------%

-usage(OutStream, !IO) :-
-    io.write_string(OutStream,
-        "Usage: mslice [-s sortspec] [-m module] [-l N] [-n N] [-p N] [-f N] "
-        ++ "filename\n",
-        !IO).
+:- pred display_version(io.text_output_stream::in, io::di, io::uo) is det.
+
+display_version(OutStream, !IO) :-
+    Version = library.mercury_version,
+    io.format(OutStream, "Mercury slice tool, version %s",
+        [s(Version)], !IO),
+    Package = library.package_version,
+    ( if Package = "" then
+        io.nl(OutStream, !IO)
+    else
+        io.format(OutStream, " (%s)\n", [s(Package)], !IO)
+    ),
+    write_copyright_notice(OutStream, !IO).
+
+:- pred short_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+short_usage(OutStream, !IO) :-
+    io.write_strings(OutStream, [
+        "Usage: mslice [<options>] <file>\n",
+        "Use `mslice --help' for more information.\n"
+    ], !IO).
+
+:- pred long_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+long_usage(OutStream, !IO) :-
+    io.write_string(OutStream, "Name: mslice - Mercury slice tool\n", !IO),
+    write_copyright_notice(OutStream, !IO),
+    io.write_string(OutStream, "\nUsage: mslice [<options>] <file>\n", !IO),
+    io.write_string(OutStream, "\nDescription:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "`mslice' is a tool that outputs views of program slices."
+    ], !IO),
+    io.write_string(OutStream, "\nArguments:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "The argument is assumed to be a Mercury trace count file."
+    ], !IO),
+    io.write_string(OutStream, "\nOptions:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "-?, -h, --help",
+        "\tPrint help about using mslice (on the standard output) and exit",
+        "\twithout doing any further processing.",
+        "--version",
+        "\tPrint version information.",
+        "-s <sortspec>, --sort <sortspec>",
+        "\tSpecify how the output should be sorted.",
+        "\t(See the Mercury User's Guide for details.)",
+        "-l <N>, --limit <N>",
+        "\tLimit the output to at most N lines.",
+        "-m <module>, --module <module>",
+        "\tRestrict the output to the given module and its submodules (if any).",
+        "-n <N>, --max-name-column <N>",
+        "\tThe maximum width of the column containing predicate names.",
+        "\tA value of zero means there is no maximum width.",
+        "-p <N>, --max-path-column <N>",
+        "\tThe maximum width of the column containing ports and goal paths.",
+        "\tA value of zero means there is no maximum width.",
+        "-f <N>, --max-file-column <N>",
+        "\tThe maximum width of the column containing file names and line numbers.",
+        "\tA value of zero means there is no maximum width."
+    ], !IO).
+
+:- pred write_copyright_notice(io.text_output_stream::in, io::di, io::uo)
+    is det.
+
+write_copyright_notice(OutStream, !IO) :-
+    io.write_strings(OutStream, [
+        "Copyright (C) 2005-2006, 2012 The University of Melbourne\n",
+        "Copyright (C) 2015-2016, 2019-2020, 2022-2023 The Mercury team\n"
+    ], !IO).

  %---------------------------------------------------------------------------%

  :- type option
-    --->    sort
+    --->    help
+    ;       version
+    ;       sort
      ;       max_row
      ;       max_pred_column
      ;       max_path_column
@@ -108,24 +192,30 @@ usage(OutStream, !IO) :-

  :- pred short_option(character::in, option::out) is semidet.

-short_option('s',               sort).
-short_option('l',               max_row).
-short_option('n',               max_pred_column).
-short_option('p',               max_path_column).
-short_option('f',               max_file_column).
-short_option('m',               modulename).
+short_option('?', help).
+short_option('h', help).
+short_option('s', sort).
+short_option('l', max_row).
+short_option('n', max_pred_column).
+short_option('p', max_path_column).
+short_option('f', max_file_column).
+short_option('m', modulename).

  :- pred long_option(string::in, option::out) is semidet.

-long_option("sort",             sort).
-long_option("limit",            max_row).
-long_option("max-name-column",  max_pred_column).
-long_option("max-path-column",  max_path_column).
-long_option("max-file-column",  max_file_column).
-long_option("module",           modulename).
+long_option("help",            help).
+long_option("version",         version).
+long_option("sort",            sort).
+long_option("limit",           max_row).
+long_option("max-name-column", max_pred_column).
+long_option("max-path-column", max_path_column).
+long_option("max-file-column", max_file_column).
+long_option("module",          modulename).

  :- pred option_default(option::out, option_data::out) is multi.

+option_default(help,            bool(no)).
+option_default(version,         bool(no)).
  option_default(sort,            string("C")).
  option_default(max_row,         int(100)).
  option_default(max_pred_column, int(35)).
diff --git a/slice/mtc_diff.m b/slice/mtc_diff.m
index 71d2d27..9c55fb3 100644
--- a/slice/mtc_diff.m
+++ b/slice/mtc_diff.m
@@ -2,6 +2,7 @@
  % vim: ft=mercury ts=4 sw=4 expandtab
  %---------------------------------------------------------------------------%
  % Copyright (C) 2006, 2012 The University of Melbourne.
+% Copyright (C) 2015-2017, 2019-2023 The Mercury team.
  % This file may only be copied under the terms of the GNU General
  % Public License - see the file COPYING in the Mercury distribution.
  %---------------------------------------------------------------------------%
@@ -29,7 +30,9 @@
  :- import_module mdbcomp.shared_utilities.
  :- import_module mdbcomp.trace_counts.

+:- import_module bool.
  :- import_module getopt.
+:- import_module library.
  :- import_module list.
  :- import_module map.
  :- import_module string.
@@ -45,81 +48,161 @@ main(!IO) :-
      getopt.process_options(OptionOps, Args0, Args, GetoptResult),
      (
          GetoptResult = ok(OptionTable),
-        lookup_string_option(OptionTable, output_filename, OutputFile),
+        ( if lookup_bool_option(OptionTable, help, yes) then
+            long_usage(StdOutStream, !IO)
+        else if lookup_bool_option(OptionTable, version, yes) then
+            display_version(StdOutStream, !IO)
+        else
+            main_2(StdErrStream, OptionTable, Args, !IO)
+        )
+    ;
+        GetoptResult = error(GetoptError),
+        GetoptErrorMsg = option_error_to_string(GetoptError),
+        io.format(StdErrStream, "%s\n", [s(GetoptErrorMsg)], !IO),
+        io.set_exit_status(1, !IO)
+    ).
+
+:- pred main_2(io.text_output_stream::in, option_table::in,
+    list(string)::in, io::di, io::uo) is det.
+
+main_2(StdErrStream, OptionTable, Args, !IO) :-
+    lookup_string_option(OptionTable, output_filename, OutputFile),
+    ( if
+        Args = [Arg1, Arg2],
+        OutputFile \= ""
+    then
+        read_trace_counts_source(Arg1, MaybeTraceCounts1, !IO),
+        (
+            MaybeTraceCounts1 = list_ok(_, _)
+        ;
+            MaybeTraceCounts1 = list_error_message(Msg1),
+            io.write_string(StdErrStream, Msg1, !IO),
+            io.nl(StdErrStream, !IO)
+        ),
+        read_trace_counts_source(Arg2, MaybeTraceCounts2, !IO),
+        (
+            MaybeTraceCounts2 = list_ok(_, _)
+        ;
+            MaybeTraceCounts2 = list_error_message(Msg2),
+            io.write_string(StdErrStream, Msg2, !IO),
+            io.nl(StdErrStream, !IO)
+        ),
          ( if
-            Args = [Arg1, Arg2],
-            OutputFile \= ""
+            MaybeTraceCounts1 = list_ok(Type1, TraceCounts1),
+            MaybeTraceCounts2 = list_ok(Type2, TraceCounts2)
          then
-            read_trace_counts_source(Arg1, MaybeTraceCounts1, !IO),
+            diff_trace_counts(TraceCounts1, TraceCounts2, TraceCounts),
+            write_trace_counts_to_file(diff_file(Type1, Type2),
+                TraceCounts, OutputFile, WriteResult, !IO),
              (
-                MaybeTraceCounts1 = list_ok(_, _)
+                WriteResult = ok
              ;
-                MaybeTraceCounts1 = list_error_message(Msg1),
-                io.write_string(StdErrStream, Msg1, !IO),
-                io.nl(StdErrStream, !IO)
-            ),
-            read_trace_counts_source(Arg2, MaybeTraceCounts2, !IO),
-            (
-                MaybeTraceCounts2 = list_ok(_, _)
-            ;
-                MaybeTraceCounts2 = list_error_message(Msg2),
-                io.write_string(StdErrStream, Msg2, !IO),
-                io.nl(StdErrStream, !IO)
-            ),
-            ( if
-                MaybeTraceCounts1 = list_ok(Type1, TraceCounts1),
-                MaybeTraceCounts2 = list_ok(Type2, TraceCounts2)
-            then
-                diff_trace_counts(TraceCounts1, TraceCounts2, TraceCounts),
-                write_trace_counts_to_file(diff_file(Type1, Type2),
-                    TraceCounts, OutputFile, WriteResult, !IO),
-                (
-                    WriteResult = ok
-                ;
-                    WriteResult = error(WriteErrorMsg),
-                    io.write_string(StdErrStream, "Error writing to " ++
-                        "file `" ++ OutputFile ++ "'" ++ ": " ++
-                        string(WriteErrorMsg), !IO),
-                    io.nl(StdErrStream, !IO)
-                )
-            else
-                % The error message has already been printed above.
-                true
+                WriteResult = error(WriteErrorMsg),
+                io.format(StdErrStream,
+                    "Error writing to file`%s': %s\n",
+                    [s(OutputFile), s(io.error_message(WriteErrorMsg))],
+                    !IO),
+                io.set_exit_status(1, !IO)
              )
          else
-            usage(StdOutStream, !IO)
+            % The error message has already been printed above.
+            true
          )
-    ;
-        GetoptResult = error(GetoptError),
-        GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO)
+    else
+        short_usage(StdErrStream, !IO),
+        io.set_exit_status(1, !IO)
      ).

-:- pred usage(io.text_output_stream::in, io::di, io::uo) is det.
+%---------------------------------------------------------------------------%

-usage(OutStream, !IO) :-
-    io.write_string(OutStream,
-        "Usage: mtc_diff -o outputfile tracecountfile1 tracecountfile2\n",
-        !IO).
+:- pred display_version(io.text_output_stream::in, io::di, io::uo) is det.
+
+display_version(OutStream, !IO) :-
+    Version = library.mercury_version,
+    io.format(OutStream, "Mercury trace count diff, version %s",
+        [s(Version)], !IO),
+    Package = library.package_version,
+    ( if Package = "" then
+        io.nl(OutStream, !IO)
+    else
+        io.format(OutStream, " (%s)\n", [s(Package)], !IO)
+    ),
+    write_copyright_notice(OutStream, !IO).
+
+:- pred short_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+short_usage(OutStream, !IO) :-
+    io.progname_base("mtc_diff", ProgName, !IO),
+    io.format(OutStream,
+        "Usage: %s [<options>] <tracecountfile1> <tracecountfile2>]\n",
+        [s(ProgName)], !IO),
+    io.format(OutStream, "Use `%s --help' for more information.\n",
+        [s(ProgName)], !IO).
+
+:- pred long_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+long_usage(OutStream, !IO) :-
+    io.progname_base("mtc_diff", ProgName, !IO),
+    io.write_string(OutStream, "Name: mtc_diff - Mercury trace count diff\n", !IO),
+    write_copyright_notice(OutStream, !IO),
+    io.write_strings(OutStream, [
+        "Usage: ", ProgName, " [<options>] <tracecountfile1> <tracecountfile2>\n",
+        "\n",
+        "Description:\n"
+    ], !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "`mtc_diff' combines multiple trace count files into a single trace",
+        "count file."
+    ], !IO),
+    io.write_string(OutStream, "\nArguments:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "<files> is the trace count files that are to be combined."
+    ], !IO),
+    io.write_string(OutStream, "\nOptions:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "-?, -h, --help",
+        "\tPrint this usage message.",
+        "--version",
+        "\tPrint version information.",
+        "-o <file>, --out <file>",
+        "\tWrite the diff of the input trace counts to the specified file."
+    ], !IO).
+
+:- pred write_copyright_notice(io.text_output_stream::in, io::di, io::uo)
+    is det.
+
+write_copyright_notice(OutStream, !IO) :-
+    io.write_strings(OutStream, [
+        "Copyright (C) 2006-2012 The University of Melbourne\n",
+        "Copyright (C) 2013-2023 The Mercury team\n"
+    ], !IO).

  %---------------------------------------------------------------------------%

  :- type option
-    --->    output_filename.
+    --->    help
+    ;       version
+    ;       output_filename.

  :- type option_table == option_table(option).

  :- pred short_option(character::in, option::out) is semidet.

-short_option('o',               output_filename).
+short_option('?', help).
+short_option('h', help).
+short_option('o', output_filename).

  :- pred long_option(string::in, option::out) is semidet.

-long_option("out",              output_filename).
+long_option("help", help).
+long_option("version", version).
+long_option("out", output_filename).

  :- pred option_default(option::out, option_data::out) is multi.
  :- pragma no_determinism_warning(pred(option_default/2)).

+option_default(help, bool(no)).
+option_default(version, bool(no)).
  option_default(output_filename, string("")).

  %---------------------------------------------------------------------------%
diff --git a/slice/mtc_union.m b/slice/mtc_union.m
index 4b3c184..85bb6f1 100644
--- a/slice/mtc_union.m
+++ b/slice/mtc_union.m
@@ -2,6 +2,7 @@
  % vim: ft=mercury ts=4 sw=4 expandtab
  %---------------------------------------------------------------------------%
  % Copyright (C) 2005-2006, 2012 The University of Melbourne.
+% Copyright (C) 2015-2016, 2019-2023 The Mercury team.
  % This file may only be copied under the terms of the GNU General
  % Public License - see the file COPYING in the Mercury distribution.
  %---------------------------------------------------------------------------%
@@ -31,6 +32,7 @@

  :- import_module bool.
  :- import_module getopt.
+:- import_module library.
  :- import_module list.
  :- import_module map.
  :- import_module maybe.
@@ -48,79 +50,157 @@ main(!IO) :-
      getopt.process_options(OptionOps, Args0, Args, GetoptResult),
      (
          GetoptResult = ok(OptionTable),
-        lookup_string_option(OptionTable, output_filename, OutputFile),
-        ( if
-            Args = [_ | _],
-            OutputFile \= ""
-        then
-            lookup_bool_option(OptionTable, verbose, Verbose),
-            (
-                Verbose = yes,
-                ShowProgress = yes(StdOutStream)
-            ;
-                Verbose = no,
-                ShowProgress = no
-            ),
-            read_and_union_trace_counts(ShowProgress, Args, NumTests, Kinds,
-                TraceCounts, MaybeReadError, !IO),
-            (
-                MaybeReadError = yes(ReadErrorMsg),
-                io.write_string(StdErrStream, ReadErrorMsg, !IO),
-                io.nl(StdErrStream, !IO)
-            ;
-                MaybeReadError = no,
-                Type = union_file(NumTests, set.to_sorted_list(Kinds)),
-                write_trace_counts_to_file(Type, TraceCounts, OutputFile,
-                    WriteResult, !IO),
-                (
-                    WriteResult = ok
-                ;
-                    WriteResult = error(WriteErrorMsg),
-                    io.write_string(StdErrStream,
-                        "Error writing to file `" ++ OutputFile ++ "'" ++
-                        ": " ++ string(WriteErrorMsg), !IO),
-                    io.nl(StdErrStream, !IO)
-                )
-            )
+        ( if lookup_bool_option(OptionTable, help, yes) then
+            long_usage(StdOutStream, !IO)
+        else if lookup_bool_option(OptionTable, version, yes) then
+            display_version(StdOutStream, !IO)
          else
-            usage(StdOutStream, !IO)
+            main_2(StdOutStream, StdErrStream, OptionTable, Args, !IO)
          )
      ;
          GetoptResult = error(GetoptError),
          GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO)
+        io.format(StdErrStream, "%s\n", [s(GetoptErrorMsg)], !IO),
+        io.set_exit_status(1, !IO)
      ).

-:- pred usage(io.text_output_stream::in, io::di, io::uo) is det.
+:- pred main_2(io.text_output_stream::in, io.text_output_stream::in,
+    option_table::in, list(string)::in, io::di, io::uo) is det.
+
+main_2(StdOutStream, StdErrStream, OptionTable, Args, !IO) :-
+    lookup_string_option(OptionTable, output_filename, OutputFile),
+    ( if
+        Args = [_ | _],
+        OutputFile \= ""
+    then
+        lookup_bool_option(OptionTable, verbose, Verbose),
+        (
+            Verbose = yes,
+            ShowProgress = yes(StdOutStream)
+        ;
+            Verbose = no,
+            ShowProgress = no
+        ),
+        read_and_union_trace_counts(ShowProgress, Args, NumTests, Kinds,
+            TraceCounts, MaybeReadError, !IO),
+        (
+            MaybeReadError = yes(ReadErrorMsg),
+            io.write_string(StdErrStream, ReadErrorMsg, !IO),
+            io.nl(StdErrStream, !IO)
+        ;
+            MaybeReadError = no,
+            Type = union_file(NumTests, set.to_sorted_list(Kinds)),
+            write_trace_counts_to_file(Type, TraceCounts, OutputFile,
+                WriteResult, !IO),
+            (
+                WriteResult = ok
+            ;
+                WriteResult = error(WriteErrorMsg),
+                io.format(StdErrStream,
+                    "Error writing to file `%s': %s\n",
+                    [s(OutputFile), s(io.error_message(WriteErrorMsg))],
+                    !IO),
+                io.set_exit_status(1, !IO)
+            )
+        )
+    else
+        short_usage(StdErrStream, !IO),
+        io.set_exit_status(1, !IO)
+    ).

-usage(OutStream, !IO) :-
+%---------------------------------------------------------------------------%
+
+:- pred display_version(io.text_output_stream::in, io::di, io::uo) is det.
+
+display_version(OutStream, !IO) :-
+    Version = library.mercury_version,
+    io.format(OutStream, "Mercury trace count union, version %s",
+        [s(Version)], !IO),
+    Package = library.package_version,
+    ( if Package = "" then
+        io.nl(OutStream, !IO)
+    else
+        io.format(OutStream, " (%s)\n", [s(Package)], !IO)
+    ),
+    write_copyright_notice(OutStream, !IO).
+
+:- pred short_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+short_usage(OutStream, !IO) :-
+    io.progname_base("mtc_union", ProgName, !IO),
+    io.format(OutStream, "Usage: %s [<options>] [<files>]\n",
+        [s(ProgName)], !IO),
+    io.format(OutStream, "Use `%s --help' for more information.\n",
+        [s(ProgName)], !IO).
+
+:- pred long_usage(io.text_output_stream::in, io::di, io::uo) is det.
+
+long_usage(OutStream, !IO) :-
+    io.progname_base("mtc_union", ProgName, !IO),
+    io.write_string(OutStream, "Name: mtc_union - Mercury trace count union\n", !IO),
+    write_copyright_notice(OutStream, !IO),
+    io.write_strings(OutStream, [
+        "Usage: ", ProgName, " [<options>] [<files>]\n",
+        "\n",
+        "Description:\n"
+    ], !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "`mtc_union' combines multiple trace count files into a single trace",
+        "count file."
+    ], !IO),
+    io.write_string(OutStream, "\nArguments:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "<files> is the trace count files that are to be combined."
+    ], !IO),
+    io.write_string(OutStream, "\nOptions:\n", !IO),
+    io.write_prefixed_lines(OutStream, "\t", [
+        "-?, -h, --help",
+        "\tPrint this usage message.",
+        "--version",
+        "\tPrint version information.",
+        "-v, --verbose",
+        "\tPrint the name of each trace count file as it is added to the union",
+        "-o <file>, --out <file>",
+        "\tWrite the union of the input trace counts to the specified file."
+    ], !IO).
+
+:- pred write_copyright_notice(io.text_output_stream::in, io::di, io::uo)
+    is det.
+
+write_copyright_notice(OutStream, !IO) :-
      io.write_strings(OutStream, [
-        "Usage: mtc_union [-v] -o output_file file1 file2 ...\n",
-        "The -v or --verbose option causes each trace count file name\n",
-        "to be printed as it is added to the union.\n",
-        "file1, file2, etc should be trace count files.\n"],
-        !IO).
+        "Copyright (C) 2005-2012 The University of Melbourne\n",
+        "Copyright (C) 2013-2023 The Mercury team\n"
+    ], !IO).

  %---------------------------------------------------------------------------%

  :- type option
-    --->    output_filename
+    --->    help
+    ;       version
+    ;       output_filename
      ;       verbose.

  :- type option_table == option_table(option).

  :- pred short_option(character::in, option::out) is semidet.

+short_option('?',               help).
+short_option('h',               help).
  short_option('o',               output_filename).
  short_option('v',               verbose).

  :- pred long_option(string::in, option::out) is semidet.

+long_option("help",             help).
+long_option("version",          version).
  long_option("out",              output_filename).
  long_option("verbose",          verbose).

  :- pred option_default(option::out, option_data::out) is multi.

+option_default(help,            bool(no)).
+option_default(version,         bool(no)).
  option_default(output_filename, string("")).
  option_default(verbose,         bool(no)).



More information about the reviews mailing list