lib/cmdline: removing "help ..." from the space of possible command
arguments to make it possible to distinguish between binary-based
subcommands with and without children when generating recursive help

Change-Id: Id8e28055823bc9168466079c09aa3dcd707c208d
diff --git a/cmdline/cmdline.go b/cmdline/cmdline.go
index 549ac5b..61778dc 100644
--- a/cmdline/cmdline.go
+++ b/cmdline/cmdline.go
@@ -17,10 +17,15 @@
 // precedes it.  Flags registered on flag.CommandLine are considered global
 // flags, and are allowed anywhere a command-specific flag is allowed.
 //
-// Pretty usage documentation is automatically generated, and accessible either
-// via the standard -h / -help flags from the Go flag package, or a special help
-// command.  The help command is automatically appended to commands that already
-// have at least one child, and don't already have a "help" child.
+// Pretty usage documentation is automatically generated, and
+// accessible either via the standard -h / -help flags from the Go
+// flag package, or a special help command.  The help command is
+// automatically appended to commands that already have at least one
+// child, and don't already have a "help" child. Commands that do not
+// have any children will exit with an error if invoked with the "help
+// ..."  arguments; this behavior is relied on when generating
+// recursive help to distinguish between binary-based subcommands with
+// and without children.
 //
 // Pitfalls
 //
@@ -42,6 +47,7 @@
 	"io/ioutil"
 	"os"
 	"os/exec"
+	"reflect"
 	"strings"
 
 	"v.io/x/lib/envvar"
@@ -326,8 +332,12 @@
 			return nil, nil, env.UsageErrorf("%s: unknown command %q", cmdPath, subName)
 		}
 		return nil, nil, env.UsageErrorf("%s: doesn't take arguments", cmdPath)
+	case reflect.DeepEqual(args, []string{helpName, "..."}):
+		return nil, nil, env.UsageErrorf("%s: unsupported help invocation", cmdPath)
 	}
-	// INVARIANT: cmd.Runner != nil && len(args) > 0 && cmd.ArgsName != ""
+	// INVARIANT:
+	// cmd.Runner != nil && len(args) > 0 &&
+	// cmd.ArgsName != "" && args != []string{"help", "..."}
 	return cmd.Runner, args, nil
 }
 
diff --git a/cmdline/cmdline_test.go b/cmdline/cmdline_test.go
index d2c56ae..a119512 100644
--- a/cmdline/cmdline_test.go
+++ b/cmdline/cmdline_test.go
@@ -2695,3 +2695,6 @@
 	}
 	runTestCases(t, cmd, tests)
 }
+
+// TODO(toddw): Add a test for the case when "help ..." is passed to a
+// childless subcommand.