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

Julien Fischer jfischer at opturion.com
Sun Oct 1 16:24:22 AEDT 2023


On Sun, 1 Oct 2023, Zoltan Somogyi wrote:

> That diff of mtc_diff.m is hard to read, because it resynchronized on an "if"
> that plays different roles in the before and after versions.
> Can you please resend it with -b --patience?

Attached.

Julien.
-------------- next part --------------
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,12 +47,33 @@ main(!IO) :-
     getopt.process_options(OptionOps, Args0, Args, GetoptResult),
     (
         GetoptResult = ok(OptionTable),
-        (
-            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)
+        )
     ;
-            Args = [_],
-            usage(StdOutStream, !IO)
+        GetoptResult = error(GetoptError),
+        GetoptErrorMsg = option_error_to_string(GetoptError),
+        io.stderr_stream(StdErrStream, !IO),
+        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(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),
@@ -83,28 +107,86 @@ main(!IO) :-
             io.nl(StdOutStream, !IO),
             io.set_exit_status(1, !IO)
         )
-        ;
-            Args = [_, _, _ | _],
-            usage(StdOutStream, !IO)
-        )
-    ;
-        GetoptResult = error(GetoptError),
-        GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO)
     ).
 
-:- pred usage(io.text_output_stream::in, io::di, io::uo) is det.
+%---------------------------------------------------------------------------%
 
-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 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,6 +197,8 @@ usage(OutStream, !IO) :-
 
 :- pred short_option(character::in, option::out) is semidet.
 
+short_option('?', help).
+short_option('h', help).
 short_option('s', sort).
 short_option('l', max_row).
 short_option('n', max_pred_column).
@@ -124,6 +208,8 @@ short_option('m',               modulename).
 
 :- pred long_option(string::in, option::out) is semidet.
 
+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).
@@ -133,6 +219,8 @@ 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,9 +44,31 @@ main(!IO) :-
     getopt.process_options(OptionOps, Args0, Args, GetoptResult),
     (
         GetoptResult = ok(OptionTable),
+        ( 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.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 = [],
-            usage(StdOutStream, !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),
@@ -76,28 +102,86 @@ main(!IO) :-
             io.nl(StdOutStream, !IO),
             io.set_exit_status(1, !IO)
         )
-        ;
-            Args = [_, _ | _],
-            usage(StdOutStream, !IO)
-        )
-    ;
-        GetoptResult = error(GetoptError),
-        GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !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,6 +192,8 @@ usage(OutStream, !IO) :-
 
 :- pred short_option(character::in, option::out) is semidet.
 
+short_option('?', help).
+short_option('h', help).
 short_option('s', sort).
 short_option('l', max_row).
 short_option('n', max_pred_column).
@@ -117,6 +203,8 @@ short_option('m',               modulename).
 
 :- pred long_option(string::in, option::out) is semidet.
 
+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).
@@ -126,6 +214,8 @@ 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,6 +48,24 @@ main(!IO) :-
     getopt.process_options(OptionOps, Args0, Args, GetoptResult),
     (
         GetoptResult = ok(OptionTable),
+        ( 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],
@@ -77,49 +98,111 @@ main(!IO) :-
                 WriteResult = ok
             ;
                 WriteResult = error(WriteErrorMsg),
-                    io.write_string(StdErrStream, "Error writing to " ++
-                        "file `" ++ OutputFile ++ "'" ++ ": " ++
-                        string(WriteErrorMsg), !IO),
-                    io.nl(StdErrStream, !IO)
+                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
             % The error message has already been printed above.
             true
         )
     else
-            usage(StdOutStream, !IO)
-        )
-    ;
-        GetoptResult = error(GetoptError),
-        GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO)
+        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('?', help).
+short_option('h', help).
 short_option('o', output_filename).
 
 :- pred long_option(string::in, option::out) is semidet.
 
+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,6 +50,24 @@ main(!IO) :-
     getopt.process_options(OptionOps, Args0, Args, GetoptResult),
     (
         GetoptResult = ok(OptionTable),
+        ( 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, 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, 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 = [_ | _],
@@ -76,51 +96,111 @@ main(!IO) :-
                 WriteResult = ok
             ;
                 WriteResult = error(WriteErrorMsg),
-                    io.write_string(StdErrStream,
-                        "Error writing to file `" ++ OutputFile ++ "'" ++
-                        ": " ++ string(WriteErrorMsg), !IO),
-                    io.nl(StdErrStream, !IO)
+                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)
-        )
-    ;
-        GetoptResult = error(GetoptError),
-        GetoptErrorMsg = option_error_to_string(GetoptError),
-        io.format(StdOutStream, "%s\n", [s(GetoptErrorMsg)], !IO)
+        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) :-
+:- 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