cmd: improve help for programs without subcommands 83/10983/8
authorMichael Meffie <mmeffie@sinenomine.net>
Fri, 4 Apr 2014 14:27:10 +0000 (10:27 -0400)
committerBenjamin Kaduk <kaduk@mit.edu>
Fri, 5 Oct 2018 13:27:19 +0000 (09:27 -0400)
Some programs do not have subcommands (other than the standard "help",
and "version" subcommands).  The cmd library provides the "noopcode"
mechanism for new subcommand-less programs, but older programs take
advantage of the optional "initcmd" token to simulate subcommand-less
programs.  The "initcmd" token is optional to run the command, however
it is required to display the command help.

For example, running the xstat_cm_test program without any options gives
a syntax error:

    $ xstat_cm_test
    xstat_cm_test: Missing required parameter '-cmname'
    ...

Retrying with -help (or help, -h, --help), gives the rather unhelpful
output:

    $ xstat_cm_test -help
    xstat_cm_test: Commands are:
    apropos         search by help text
    help            get help on commands
    initcmd         initialize the program

It is not obvious to the user how to get the command usage for the
program, nor that the initcmd subcommand to "initialize the program" is
actually is a placeholder to run the program.

Instead, display the command usage when help is requested and initcmd is
the only defined subcommand for a program.

For example:

    $ xstat_cm_test -help
    Usage: src/xstat/xstat_cm_test [initcmd]
             -cmname <Cache Manager name(s) to monitor>+
             -collID <Collection(s) to fetch>+ [-onceonly]
             [-frequency <poll frequency, in seconds>]
             [-period <data collection time, in minutes>] [-debug] [-help]
    Where: -onceonly  Collect results exactly once, then quit
           -debug     turn on debugging output

The libcmd library now supports an "noopcode", which should used for
future subcommand-less programs, but converting old programs to remove
the initcmd opcode could break scripts which actually specify the
optional initcmd token.

This commit adds a new libcmd flag called CMD_IMPLICIT which is used to
denote built-in subcommands such as "version" and "help".

Change-Id: Iee9cb2761254543f74166e5c240685f85b6915b6
Reviewed-on: https://gerrit.openafs.org/10983
Reviewed-by: Michael Meffie <mmeffie@sinenomine.net>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>

src/cmd/cmd.c
src/cmd/cmd.p.h

index e2e8e3a..ffe49aa 100644 (file)
@@ -357,11 +357,35 @@ HelpProc(struct cmd_syndesc *as, void *arock)
     int code = 0;
 
     if (as->parms[0].items == 0) {
-       printf("%sCommands are:\n", NName(as->a0name, ": "));
+       struct cmd_syndesc *initcmd = NULL;
+       int count = 0;
+
+       /*
+        * Print the usage of the initcmd command when it is the only
+        * non-hidden, explicit command, otherwise, list the all the commands.
+        */
        for (ts = allSyntax; ts; ts = ts->next) {
-           if ((ts->flags & CMD_ALIAS) || (ts->flags & CMD_HIDDEN))
+           if (ts->flags & (CMD_ALIAS | CMD_HIDDEN | CMD_IMPLICIT)) {
+               continue; /* skip aliases, hidden, and implied commands */
+           }
+           if (strcmp(ts->name, initcmd_opcode) == 0) {
+               initcmd = ts; /* save the initcmd */
                continue;
-           printf("%-15s %s\n", ts->name, (ts->help ? ts->help : ""));
+           }
+           count++;
+       }
+       if (initcmd && count == 0) {
+           initcmd->a0name = as->a0name;
+           PrintAliases(initcmd);
+           PrintSyntax(initcmd);
+           PrintFlagHelp(initcmd);
+       } else {
+           printf("%sCommands are:\n", NName(as->a0name, ": "));
+           for (ts = allSyntax; ts; ts = ts->next) {
+               if ((ts->flags & CMD_ALIAS) || (ts->flags & CMD_HIDDEN))
+                   continue;
+               printf("%-15s %s\n", ts->name, (ts->help ? ts->help : ""));
+           }
        }
     } else {
        /* print out individual help topics */
@@ -439,7 +463,7 @@ SortSyntax(struct cmd_syndesc *as)
  * \param[in] aname  name used to invoke the command
  * \param[in] aproc  procedure to be called when command is invoked
  * \param[in] arock  opaque data pointer to be passed to aproc
- * \param[in] aflags command option flags (CMD_HIDDEN)
+ * \param[in] aflags command option flags
  * \param[in] ahelp  help string to display for this command
  *
  * \return a pointer to the cmd_syndesc or NULL if error.
@@ -456,7 +480,7 @@ cmd_CreateSyntax(char *aname,
        return NULL;
 
     /* Allow only valid cmd flags. */
-    if (aflags & ~CMD_HIDDEN) {
+    if (aflags & ~(CMD_HIDDEN | CMD_IMPLICIT)) {
        return NULL;
     }
 
@@ -763,20 +787,20 @@ initSyntax(void)
     struct cmd_syndesc *ts;
 
     if (!noOpcodes) {
-       ts = cmd_CreateSyntax("help", HelpProc, NULL, 0,
+       ts = cmd_CreateSyntax("help", HelpProc, NULL, CMD_IMPLICIT,
                              "get help on commands");
        cmd_AddParm(ts, "-topic", CMD_LIST, CMD_OPTIONAL, "help string");
 
-       ts = cmd_CreateSyntax("apropos", AproposProc, NULL, 0,
+       ts = cmd_CreateSyntax("apropos", AproposProc, NULL, CMD_IMPLICIT,
                              "search by help text");
        cmd_AddParm(ts, "-topic", CMD_SINGLE, CMD_REQUIRED, "help string");
 
-       cmd_CreateSyntax("version", VersionProc, NULL, 0,
+       cmd_CreateSyntax("version", VersionProc, NULL, CMD_IMPLICIT,
                         "show version");
-       cmd_CreateSyntax("-version", VersionProc, NULL, CMD_HIDDEN, NULL);
-       cmd_CreateSyntax("-help", HelpProc, NULL, CMD_HIDDEN, NULL);
-       cmd_CreateSyntax("--version", VersionProc, NULL, CMD_HIDDEN, NULL);
-       cmd_CreateSyntax("--help", HelpProc, NULL, CMD_HIDDEN, NULL);
+       cmd_CreateSyntax("-version", VersionProc, NULL, (CMD_HIDDEN | CMD_IMPLICIT), NULL);
+       cmd_CreateSyntax("-help", HelpProc, NULL, (CMD_HIDDEN | CMD_IMPLICIT), NULL);
+       cmd_CreateSyntax("--version", VersionProc, NULL, (CMD_HIDDEN | CMD_IMPLICIT), NULL);
+       cmd_CreateSyntax("--help", HelpProc, NULL, (CMD_HIDDEN | CMD_IMPLICIT), NULL);
     }
 }
 
index 4a5f303..30ce7d1 100644 (file)
@@ -19,6 +19,7 @@
 /* syndesc flags */
 #define        CMD_ALIAS       1       /* this is an alias */
 #define CMD_HIDDEN      4      /* A hidden command - similar to CMD_HIDE */
+#define CMD_IMPLICIT    8      /* A built-in command - apropos, help, version (and variations) */
 
 #define CMD_HELPPARM   (CMD_MAXPARMS-1)        /* last one is used by -help switch */
 #define        CMD_MAXPARMS    64      /* max number of parm types to a cmd line */