[nasm:nasm-2.16.xx] nasm.c: tidy up the help text and break it into topics

nasm-bot for H. Peter Anvin hpa at zytor.com
Thu Apr 25 15:18:07 PDT 2024


Commit-ID:  cd1fd8ac8e1ba1be52f93d2a87b5be31c55876fd
Gitweb:     http://repo.or.cz/w/nasm.git?a=commitdiff;h=cd1fd8ac8e1ba1be52f93d2a87b5be31c55876fd
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Thu, 25 Apr 2024 15:14:20 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Thu, 25 Apr 2024 15:14:20 -0700

nasm.c: tidy up the help text and break it into topics

The help output has gotten way too long to be shown on a single
command line. It can of course be piped to a pager, but to be a little
nicer to the user, break it up into subtopics that can be individually
displayed. --help all (-h all) can still show all the help information
as a single data dump.

Signed-off-by: H. Peter Anvin <hpa at zytor.com>


---
 asm/nasm.c | 468 +++++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 328 insertions(+), 140 deletions(-)

diff --git a/asm/nasm.c b/asm/nasm.c
index 97d926fc..e97a0af9 100644
--- a/asm/nasm.c
+++ b/asm/nasm.c
@@ -1,6 +1,6 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 1996-2022 The NASM Authors - All Rights Reserved
+ *   Copyright 1996-2024 The NASM Authors - All Rights Reserved
  *   See the file AUTHORS included with the NASM distribution for
  *   the specific copyright holders.
  *
@@ -76,7 +76,7 @@ static void parse_cmdline(int, char **, int);
 static void assemble_file(const char *, struct strlist *);
 static bool skip_this_pass(errflags severity);
 static void usage(void);
-static void help(FILE *);
+static void help(FILE *out, const char *what);
 
 struct error_format {
     const char *beforeline;     /* Before line number, if present */
@@ -752,8 +752,10 @@ int main(int argc, char **argv)
 /*
  * Get a parameter for a command line option.
  * First arg must be in the form of e.g. -f...
+ *
+ * get_param() errors on a missing argument; get_opt_param() does not.
  */
-static char *get_param(char *p, char *q, bool *advance)
+static char *get_opt_param(char *p, char *q, bool *advance)
 {
     *advance = false;
     if (p[2]) /* the parameter's in the option */
@@ -762,9 +764,15 @@ static char *get_param(char *p, char *q, bool *advance)
         *advance = true;
         return q;
     }
-    nasm_nonfatalf(ERR_USAGE, "option `-%c' requires an argument", p[1]);
     return NULL;
 }
+static char *get_param(char *p, char *q, bool *advance)
+{
+    char *r = get_opt_param(p, q, advance);
+    if (!r)
+        nasm_nonfatalf(ERR_USAGE, "option `-%c' requires an argument", p[1]);
+    return r;
+}
 
 /*
  * Copy a filename
@@ -956,7 +964,7 @@ struct textargs {
 static const struct textargs textopts[] = {
     {"v", OPT_VERSION, ARG_NO, 0},
     {"version", OPT_VERSION, ARG_NO, 0},
-    {"help",     OPT_HELP,  ARG_NO, 0},
+    {"help",     OPT_HELP,  ARG_MAYBE, 0},
     {"abort-on-panic", OPT_ABORT_ON_PANIC, ARG_NO, 0},
     {"prefix",   OPT_MANGLE, ARG_YES, LM_GPREFIX},
     {"postfix",  OPT_MANGLE, ARG_YES, LM_GSUFFIX},
@@ -1113,12 +1121,17 @@ static bool process_arg(char *p, char *q, int pass)
 
         case 'X':       /* specify error reporting format */
             if (pass == 1) {
-                if (!nasm_stricmp("vc", param) || !nasm_stricmp("msvc", param) || !nasm_stricmp("ms", param))
+                if (!nasm_stricmp("vc", param) ||
+                    !nasm_stricmp("msvc", param) ||
+                    !nasm_stricmp("ms", param))
                     errfmt = &errfmt_msvc;
-                else if (!nasm_stricmp("gnu", param) || !nasm_stricmp("gcc", param))
+                else if (!nasm_stricmp("gnu", param) ||
+                         !nasm_stricmp("gcc", param))
                     errfmt = &errfmt_gnu;
                 else
-                    nasm_fatalf(ERR_USAGE, "unrecognized error reporting format `%s'", param);
+                    nasm_fatalf(ERR_USAGE,
+                                "unrecognized error reporting format `%s'",
+                                param);
             }
             break;
 
@@ -1131,13 +1144,14 @@ static bool process_arg(char *p, char *q, int pass)
             break;
 
         case 'h':
-            help(stdout);
+        case '?':
+            help(stdout, get_opt_param(p, q, &advance));
             exit(0);    /* never need usage message here */
             break;
 
         case 'y':
             /* legacy option */
-            dfmt_list(stdout);
+            help(stdout, "-F");
             exit(0);
             break;
 
@@ -1330,13 +1344,17 @@ static bool process_arg(char *p, char *q, int pass)
                     ppopt |= PP_NOLINE;
                     break;
                 case OPT_DEBUG:
-                    debug_nasm = param ? strtoul(param, NULL, 10) : debug_nasm+1;
+                    debug_nasm = param ?
+                        strtoul(param, NULL, 10) : debug_nasm+1;
                     break;
                 case OPT_REPRODUCIBLE:
                     reproducible = true;
                     break;
                 case OPT_HELP:
-                    help(stdout);
+                    /* Allow --help topic without *requiring* topic */
+                    if (!param)
+                        param = q;
+                    help(stdout, param);
                     exit(0);
                 default:
                     panic();
@@ -2210,143 +2228,313 @@ done:
 
 static void usage(void)
 {
-    fprintf(error_file, "Type %s -h for help.\n", _progname);
+    fprintf(error_file,
+            "Usage: %s [-@ response_file] [options...] [--] filename\n"
+            "   For additional help:\n"
+            "       %s -h [run|topics|all|-option]\n",
+            _progname, _progname);
 }
 
-static void help(FILE *out)
+enum help_with {
+    HW_ALL   = 0,
+    HW_OPT   = 1,
+    HW_ERR   = 2,
+    HW_LIMIT = 3
+};
+
+static inline bool help_is(int with, int h) {
+    return with == HW_ALL || with == h;
+}
+static inline bool help_opt(int with)
 {
-    int i;
+    return help_is(with, HW_OPT);
+}
+static inline bool help_optor(int with, int h)
+{
+    return help_opt(with) || with == h;
+}
 
-    fprintf(out,
-            "Usage: %s [-@ response_file] [options...] [--] filename\n"
-            "       %s -v (or --v)\n",
-            _progname, _progname);
-    fputs(
-        "\n"
-        "Options (values in brackets indicate defaults):\n"
-        "\n"
-        "    -h            show this text and exit (also --help)\n"
-        "    -v (or --v)   print the NASM version number and exit\n"
-        "    -@ file       response file; one command line option per line\n"
-        "\n"
-        "    -o outfile    write output to outfile\n"
-        "    --keep-all    output files will not be removed even if an error happens\n"
-        "\n"
-        "    -Xformat      specify error reporting format (gnu or vc)\n"
-        "    -s            redirect error messages to stdout\n"
-        "    -Zfile        redirect error messages to file\n"
-        "\n"
-        "    -M            generate Makefile dependencies on stdout\n"
-        "    -MG           d:o, missing files assumed generated\n"
-        "    -MF file      set Makefile dependency file\n"
-        "    -MD file      assemble and generate dependencies\n"
-        "    -MT file      dependency target name\n"
-        "    -MQ file      dependency target name (quoted)\n"
-        "    -MP           emit phony targets\n"
-        "\n"
-        "    -f format     select output file format\n"
-        , out);
-    ofmt_list(ofmt, out);
-    fputs(
-        "\n"
-        "    -g            generate debugging information\n"
-        "    -F format     select a debugging format (output format dependent)\n"
-        "    -gformat      same as -g -F format\n"
-        , out);
-    dfmt_list(out);
-    fputs(
-        "\n"
-        "    -l listfile   write listing to a list file\n"
-        "    -Lflags...    add optional information to the list file\n"
-        "       -Lb        show builtin macro packages (standard and %use)\n"
-        "       -Ld        show byte and repeat counts in decimal, not hex\n"
-        "       -Le        show the preprocessed output\n"
-        "       -Lf        ignore .nolist (force output)\n"
-        "       -Lm        show multi-line macro calls with expanded parameters\n"
-        "       -Lp        output a list file every pass, in case of errors\n"
-        "       -Ls        show all single-line macro definitions\n"
-        "       -Lw        flush the output after every line (very slow!)\n"
-        "       -L+        enable all listing options except -Lw (very verbose!)\n"
-        "\n"
-        "    -Oflags...    optimize opcodes, immediates and branch offsets\n"
-        "       -O0        no optimization\n"
-        "       -O1        minimal optimization\n"
-        "       -Ox        multipass optimization (default)\n"
-        "       -Ov        display the number of passes executed at the end\n"
-        "    -t            assemble in limited SciTech TASM compatible mode\n"
-        "\n"
-        "    -E (or -e)    preprocess only (writes output to stdout by default)\n"
-        "    -a            don't preprocess (assemble only)\n"
-        "    -Ipath        add a pathname to the include file path\n"
-        "    -Pfile        pre-include a file (also --include)\n"
-        "    -Dmacro[=str] pre-define a macro\n"
-        "    -Umacro       undefine a macro\n"
-        "   --pragma str   pre-executes a specific %%pragma\n"
-        "   --before str   add line (usually a preprocessor statement) before the input\n"
-        "   --no-line      ignore %line directives in input\n"
-        "\n"
-        "   --prefix str   prepend the given string to the names of all extern,\n"
-        "                  common and global symbols (also --gprefix)\n"
-        "   --suffix str   append the given string to the names of all extern,\n"
-        "                  common and global symbols (also --gprefix)\n"
-        "   --lprefix str  prepend the given string to local symbols\n"
-        "   --lpostfix str append the given string to local symbols\n"
-        "\n"
-        "   --reproducible attempt to produce run-to-run identical output\n"
-        "\n"
-        "    -w+x          enable warning x (also -Wx)\n"
-        "    -w-x          disable warning x (also -Wno-x)\n"
-        "    -w[+-]error   promote all warnings to errors (also -Werror)\n"
-        "    -w[+-]error=x promote warning x to errors (also -Werror=x)\n"
-        , out);
-
-    fprintf(out, "       %-20s %s\n",
-            warning_name[WARN_IDX_ALL], warning_help[WARN_IDX_ALL]);
-
-    for (i = 1; i < WARN_IDX_ALL; i++) {
-        const char *me   = warning_name[i];
-        const char *prev = warning_name[i-1];
-        const char *next = warning_name[i+1];
-
-        if (prev) {
-            int prev_len = strlen(prev);
-            const char *dash = me;
-
-            while ((dash = strchr(dash+1, '-'))) {
-                int prefix_len = dash - me; /* Not including final dash */
-                if (strncmp(next, me, prefix_len+1)) {
-                    /* Only one or last option with this prefix */
-                    break;
-                }
-                if (prefix_len >= prev_len ||
-                    strncmp(prev, me, prefix_len) ||
-                    (prev[prefix_len] != '-' && prev[prefix_len] != '\0')) {
-                    /* This prefix is different from the previous option */
-                    fprintf(out, "       %-20.*s all warnings prefixed with \"%.*s\"\n",
-                            prefix_len, me, prefix_len+1, me);
-                }
-            }
+static void help(FILE *out, const char *what)
+{
+    int i;
+    int with;
+
+#define SEE(x) (with == HW_OPT ? " (see -h " x ")" : "")
+
+    with = HW_ERR;
+
+    if (!what || !*what || !strcmp(what, "-") || !strcmp(what, "--") ||
+        !nasm_stricmp(what, "opt") ||
+        !nasm_stricmp(what, "run") ||
+        !nasm_stricmp(what, "cmd")) {
+        with = HW_OPT;
+    } else if (!nasm_stricmp(what, "all")) {
+        with = HW_ALL;
+    } else if (!nasm_strnicmp(what, "--limit", 7) ||
+               !nasm_strnicmp(what, "limit", 5)) {
+        with = HW_LIMIT;
+    } else if (!nasm_stricmp(what, "--help") ||
+               !nasm_stricmp(what, "help") ||
+               !nasm_stricmp(what, "list") ||
+               !nasm_stricmp(what, "topics") ||
+               !nasm_stricmp(what, "index")) {
+        with = 'h';
+    } else if ((what[0] == '-' && what[1] && !what[2]) ||
+               (what[0] && !what[1])) {
+        with = what[0] == '-' ? what[1] : what[0];
+        switch (with) {
+        case 'h':
+        case 'X':
+        case 'L':
+        case 'f':
+        case 'F':
+        case 'O':
+        case 'w':
+        case 'M':
+            break;
+        case 'g':
+            with = 'F';
+            break;
+        case 'W':
+            with = 'w';
+            break;
+        case '?':
+            with = 'h';
+            break;
+        case '*':
+            with = HW_ALL;
+            break;
+        default:
+            with = HW_ERR;
+            break;
         }
-
-        fprintf(out, "       %-20s %s%s\n",
-                warning_name[i], warning_help[i],
-                (warning_default[i] & WARN_ST_ERROR) ? " [error]" :
-                (warning_default[i] & WARN_ST_ENABLED) ? " [on]" : " [off]");
     }
 
-    fputs(
-        "\n"
-        "   --limit-X val  set execution limit X\n"
-        , out);
+    if (with == HW_ERR) {
+        fprintf(out, "Sorry, no help available for `%s'\n", what);
+        return;
+    }
 
+    if (help_opt(with)) {
+        fprintf(out,
+                "Usage: %s [-@ response_file] [options...] [--] filename\n"
+                "  Options:\n"
+                "    -v (or --v)    print the NASM version number and exit\n"
+                "    -@ file        response file; one command line option per line\n",
+                _progname);
+    }
+    if (help_optor(with, 'h')) {
+        fputs(
+            "    -h             list command line options and exit (also --help)\n"
+            , out);
+        if (with == HW_OPT) {
+            fputs(
+                "    -h -opt        show additional help for option \"-opt\"\n"
+                , out);
+        }
+        fputs(
+            "    -h all         show all available command line help\n"
+            "    -h topics      show list of help topics (also -h list)\n"
+            , out);
+    }
+    if (help_is(with, 'h')) {
+        fputs(
+            "    -h -X          show list of error message formats\n"
+            "    -h -M          show list of dependency generation options\n"
+            "    -h -f          show list of object code output formats\n"
+            "    -h -F          show list of debug information formats\n"
+            "    -h -L          show list of list file option flags\n"
+            "    -h -O          show list of optimization flags\n"
+            "    -h -w          show list of warning classes and defaults\n"
+            "    -h --limit     show list of resource limits and defaults\n"
+            , out);
+    }
+    if (help_opt(with)) {
+        fputs(
+            "    -o outfile     write output to outfile\n"
+            "    --keep-all     output files will not be removed even if an error happens\n"
+            , out);
+    }
+    if (help_optor(with, 'X')) {
+        fprintf(out,
+                "    -Xformat       specify error reporting format%s\n",
+                SEE("-X"));
+    }
+    if (help_is(with, 'X')) {
+        fputs(
+            "    -Xgnu          report errors in GNU format (default)\n"
+            "    -Xgcc          alias for -Xgnu\n"
+            "    -Xvc           report errors in Visual Studio format\n"
+            "    -Xmsvc         alias for -Xvc\n"
+            "    -Xms           alias for -Xvc\n"
+            , out);
+    }
+    if (help_opt(with)) {
+        fputs(
+            "    -s             redirect error messages to stdout\n"
+            "    -Zfile         redirect error messages to file\n"
+            , out);
+    }
+    if (help_optor(with, 'M')) {
+        fprintf(out,
+                "    -M...          generate Makefile dependencies%s\n",
+                with == HW_OPT
+                ? " (see -h -M)"
+                : " (multiple options permitted):");
+    }
+    if (help_is(with, 'M')) {
+        fputs(
+            "    -M             generate Makefile dependencies on stdout\n"
+            "    -MG            d:o, missing files assumed generated\n"
+            "    -MF file       set Makefile dependency file\n"
+            "    -MD file       assemble and generate dependencies\n"
+            "    -MT file       dependency target name\n"
+            "    -MQ file       dependency target name (quoted)\n"
+            "    -MP            emit phony targets\n"
+            "    -MW            generate output in Watcom wmake syntax\n"
+            , out);
+    }
+    if (help_optor(with, 'f')) {
+        fprintf(out,
+                "    -f format      select output file format%s\n",
+                SEE("-f"));
+    }
+    if (help_is(with, 'f')) {
+        ofmt_list(ofmt, out);
+    }
+    if (help_optor(with, 'F')) {
+        fprintf(out,
+                "    -g             generate debugging information\n"
+                "    -F format      select a debugging format%s\n"
+                "    -gformat       same as -g -F format\n",
+                SEE("-F"));
+    }
+    if (help_is(with, 'F')) {
+        dfmt_list(out);
+    }
+    if (help_optor(with, 'L')) {
+        fprintf(out,
+                "    -l listfile    write listing to a list file\n"
+                "    -Lflags...     add information to the list file%s\n",
+                SEE("-L"));
+    }
+    if (help_is(with, 'L')) {
+        fputs(
+            "       -Lb         show builtin macro packages (standard and %use)\n"
+            "       -Ld         show byte and repeat counts in decimal, not hex\n"
+            "       -Le         show the preprocessed output\n"
+            "       -Lf         ignore .nolist (force output)\n"
+            "       -Lm         show multi-line macro calls with expanded parameters\n"
+            "       -Lp         output a list file every pass, in case of errors\n"
+            "       -Ls         show all single-line macro definitions\n"
+            "       -Lw         flush the output after every line (very slow!)\n"
+            "       -L+         enable all listing options except -Lw (very verbose!)\n"
+            , out);
+    }
+    if (help_optor(with, 'O')) {
+        fprintf(out,
+                "    -Oflags...     select optimization%s\n", SEE("-O"));
+    }
+    if (help_is(with, 'O')) {
+        fputs(
+            "       -O0       no optimization\n"
+            "       -O1       minimal optimization\n"
+            "       -Ox       multipass optimization (default, recommended)\n"
+            "       -Ov       display the number of passes executed at the end\n"
+            , out);
+    }
+    if (help_opt(with)) {
+        fputs(
+            "    -t             assemble in limited SciTech TASM compatible mode\n"
+            "    -E (or -e)     preprocess only (writes output to stdout by default)\n"
+            "    -a             don't preprocess (assemble only)\n"
+            "    -Ipath         add a pathname to the include file path\n"
+            "    -Pfile         pre-include a file (also --include)\n"
+            "    -Dmacro[=str]  pre-define a macro\n"
+            "    -Umacro        undefine a macro\n"
+            , out);
+    }
+    if (help_optor(with, 'w')) {
+        fprintf(out,
+                "    -w+x           enable warning x %s(also -Wx)\n"
+                "    -w-x           disable warning x (also -Wno-x)\n"
+                "    -w[+-]error    promote all warnings to errors (also -Werror)\n"
+                "    -w[+-]error=x  promote warning x to errors (also -Werror=x)\n",
+                SEE("-w"));
+    }
+    if (help_is(with, 'w')) {
+        fputs("      Defaults in brackets:\n", out);
+
+        fprintf(out, "       %-20s %s\n",
+                warning_name[WARN_IDX_ALL], warning_help[WARN_IDX_ALL]);
+
+        for (i = 1; i < WARN_IDX_ALL; i++) {
+            static const char nl_indent[] =
+                "\n                            ";
+            const char *me   = warning_name[i];
+            const char *prev = warning_name[i-1];
+            const char *next = warning_name[i+1];
+
+            if (prev) {
+                int prev_len = strlen(prev);
+                const char *dash = me;
+
+                while ((dash = strchr(dash+1, '-'))) {
+                    int prefix_len = dash - me; /* Not including final dash */
+                    if (strncmp(next, me, prefix_len+1)) {
+                        /* Only one or last option with this prefix */
+                        break;
+                    }
+                    if (prefix_len >= prev_len ||
+                        strncmp(prev, me, prefix_len) ||
+                        (prev[prefix_len] != '-' && prev[prefix_len] != '\0')) {
+                        /* This prefix is different from the previous option */
+                        fprintf(out, "       %-20.*s%sall warnings prefixed with \"%.*s\"\n",
+                                prefix_len, me,
+                                prefix_len > 19 ? nl_indent : " ",
+                                prefix_len+1, me);
+                    }
+                }
+            }
 
-    for (i = 0; i <= LIMIT_MAX; i++) {
-        fprintf(out, "       %-20s %s [",
-                limit_info[i].name, limit_info[i].help);
-        if (nasm_limit[i] < LIMIT_MAX_VAL) {
-            fprintf(out, "%"PRId64"]\n", nasm_limit[i]);
-        } else {
-            fputs("unlimited]\n", out);
+            fprintf(out, "       %-20s%s%s%s\n",
+                    warning_name[i],
+                    strlen(warning_name[i]) > 19 ? nl_indent : " ",
+                    warning_help[i],
+                    (warning_default[i] & WARN_ST_ERROR) ? " [error]" :
+                    (warning_default[i] & WARN_ST_ENABLED) ? " [on]" : " [off]");
+        }
+    }
+    if (help_opt(with)) {
+        fputs(
+            "    --pragma str   pre-executes a specific %pragma\n"
+            "    --before str   add line (usually a preprocessor statement) before the input\n"
+            "    --no-line      ignore %line directives in input\n"
+            "    --prefix str   prepend the given string to the names of all extern,\n"
+            "                   common and global symbols (also --gprefix)\n"
+            "    --suffix str   append the given string to the names of all extern,\n"
+            "                   common and global symbols (also --gprefix)\n"
+            "    --lprefix str  prepend the given string to local symbols\n"
+            "    --lpostfix str append the given string to local symbols\n"
+            "    --reproducible attempt to produce run-to-run identical output\n"
+            , out);
+    }
+    if (help_optor(with, HW_LIMIT)) {
+        fprintf(out, "    --limit-X val  set execution limit X%s\n",
+                SEE("--limit"));
+    }
+    if (help_is(with, HW_LIMIT)) {
+        fputs("      Defaults in brackets:\n", out);
+
+        for (i = 0; i <= LIMIT_MAX; i++) {
+            fprintf(out, "       %-20s %s [",
+                    limit_info[i].name, limit_info[i].help);
+            if (nasm_limit[i] < LIMIT_MAX_VAL) {
+                fprintf(out, "%"PRId64"]\n", nasm_limit[i]);
+            } else {
+                fputs("unlimited]\n", out);
+            }
         }
     }
+#undef SEE
 }


More information about the Nasm-commits mailing list