lib: Pass global and ancestor flags to external subcommands.

Updates the cmdline package to automatically pass through flags
set anywhere on the command line to external child commands.

This ensures that commands like "jiri -n go install all" pass the
-n flag through to jiri-go when it is invoked.

Also updated the semantics of setting cmdline.Command.Flags.
Previously if you defined a flag F on a command C, we'd only
allow parsing of F on the command line immediately after C.  Now
we allow F to be specified immediately after C, or after any
descendant of C.  Updated the help documentation accordingly.

Also updated documentation for help -style flag, and renamed the
"short" style to "shortonly" to make its purpose more clear.

And finally, updated the timer label "root" to the command path,
to make it easier to understand the output when -time is passed
through from jiri to its external subcommands.

Fixes https://github.com/vanadium/issues/issues/793

MultiPart: 3/4

Change-Id: I6d16d8dcf5592afec8d22bf2e53f9cbddcc396ce
diff --git a/cmdline/cmdline.go b/cmdline/cmdline.go
index a587e46..3c06f57 100644
--- a/cmdline/cmdline.go
+++ b/cmdline/cmdline.go
@@ -17,15 +17,13 @@
 // 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. 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.
+// 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
+// arguments "help ..."; this behavior is relied on when generating recursive
+// help to distinguish between external subcommands with and without children.
 //
 // Pitfalls
 //
@@ -48,6 +46,7 @@
 	"os"
 	"os/exec"
 	"reflect"
+	"sort"
 	"strings"
 	"syscall"
 
@@ -61,17 +60,29 @@
 // each subcommand.  The command graph must be a tree; each command may either
 // have no parent (the root) or exactly one parent, and cycles are not allowed.
 type Command struct {
-	Name     string       // Name of the command.
-	Short    string       // Short description, shown in help called on parent.
-	Long     string       // Long description, shown in help called on itself.
-	Flags    flag.FlagSet // Flags for the command.
-	ArgsName string       // Name of the args, shown in usage line.
-	ArgsLong string       // Long description of the args, shown in help.
-	LookPath bool         // Check for subcommands in PATH.
+	Name     string // Name of the command.
+	Short    string // Short description, shown in help called on parent.
+	Long     string // Long description, shown in help called on itself.
+	ArgsName string // Name of the args, shown in usage line.
+	ArgsLong string // Long description of the args, shown in help.
+
+	// Flags defined for this command.  When a flag F is defined on a command C,
+	// we allow F to be specified on the command line immediately after C, or
+	// after any descendant of C.
+	Flags flag.FlagSet
 
 	// Children of the command.
 	Children []*Command
 
+	// LookPath indicates whether to look for external subcommands in the
+	// directories specified by the PATH environment variable.  The compiled-in
+	// children always take precedence; the check for external children only
+	// occurs if none of the compiled-in children match.
+	//
+	// All global flags and flags set on ancestor commands are passed through to
+	// the external child.
+	LookPath bool
+
 	// Runner that runs the command.
 	// Use RunnerFunc to adapt regular functions into Runners.
 	//
@@ -121,6 +132,9 @@
 //   }
 func Main(root *Command) {
 	env := EnvFromOS()
+	if env.Timer != nil && len(env.Timer.Intervals) > 0 {
+		env.Timer.Intervals[0].Name = pathName(env.prefix(), []*Command{root})
+	}
 	err := ParseAndRun(root, env, os.Args[1:])
 	code := ExitCode(err, env.Stderr)
 	if *flagTime && env.Timer != nil {
@@ -186,14 +200,14 @@
 	if err := checkTreeInvariants(path, env); err != nil {
 		return nil, nil, err
 	}
-	runner, args, err := root.parse(nil, env, args)
+	runner, args, err := root.parse(nil, env, args, make(map[string]string))
 	if err != nil {
 		return nil, nil, err
 	}
-	// Clear envvars that start with "CMDLINE_" when we're returning a
-	// user-specified runner, to avoid polluting the environment.  In particular
-	// CMDLINE_PREFIX and CMDLINE_FIRST_CALL are only meant to be passed to binary
-	// subcommands, and shouldn't be propagated through the user's runner.
+	// Clear envvars that start with "CMDLINE_" when returning a user-specified
+	// runner, to avoid polluting the environment.  In particular CMDLINE_PREFIX
+	// and CMDLINE_FIRST_CALL are only meant to be passed to external children,
+	// and shouldn't be propagated through the user's runner.
 	switch runner.(type) {
 	case helpRunner, binaryRunner:
 		// The help and binary runners need the envvars to be set.
@@ -318,19 +332,23 @@
 	return name
 }
 
-func (cmd *Command) parse(path []*Command, env *Env, args []string) (Runner, []string, error) {
+func (cmd *Command) parse(path []*Command, env *Env, args []string, setFlags map[string]string) (Runner, []string, error) {
 	path = append(path, cmd)
 	cmdPath := pathName(env.prefix(), path)
 	runHelp := makeHelpRunner(path, env)
 	env.Usage = runHelp.usageFunc
-	// Parse flags and retrieve the args remaining after the parse.
-	args, err := parseFlags(path, env, args)
+	// Parse flags and retrieve the args remaining after the parse, as well as the
+	// flags that were set.
+	args, setF, err := parseFlags(path, env, args)
 	switch {
 	case err == flag.ErrHelp:
 		return runHelp, nil, nil
 	case err != nil:
 		return nil, nil, env.UsageErrorf("%s: %v", cmdPath, err)
 	}
+	for key, val := range setF {
+		setFlags[key] = val
+	}
 	// First handle the no-args case.
 	if len(args) == 0 {
 		if cmd.Runner != nil {
@@ -344,19 +362,20 @@
 	if len(cmd.Children) > 0 {
 		for _, child := range cmd.Children {
 			if child.Name == subName {
-				return child.parse(path, env, subArgs)
+				return child.parse(path, env, subArgs, setFlags)
 			}
 		}
 		// Every non-leaf command gets a default help command.
 		if helpName == subName {
-			return runHelp.newCommand().parse(path, env, subArgs)
+			return runHelp.newCommand().parse(path, env, subArgs, setFlags)
 		}
 	}
 	if cmd.LookPath {
 		// Look for a matching executable in PATH.
 		subCmd := cmd.Name + "-" + subName
 		if lookPath(env, subCmd) {
-			return binaryRunner{subCmd, cmdPath}, subArgs, nil
+			extArgs := append(flagsAsArgs(setFlags), subArgs...)
+			return binaryRunner{subCmd, cmdPath}, extArgs, nil
 		}
 	}
 	// No matching subcommands, check various error cases.
@@ -377,7 +396,9 @@
 	return cmd.Runner, args, nil
 }
 
-func parseFlags(path []*Command, env *Env, args []string) ([]string, error) {
+// parseFlags parses the flags from args for the command with the given path and
+// env.  Returns the remaining non-flag args and the flags that were set.
+func parseFlags(path []*Command, env *Env, args []string) ([]string, map[string]string, error) {
 	cmd, isRoot := path[len(path)-1], len(path) == 1
 	// Parse the merged command-specific and global flags.
 	var flags *flag.FlagSet
@@ -391,7 +412,7 @@
 		mergeFlags(flags, &cmd.Flags)
 	} else {
 		// Command flags take precedence over global flags for non-root commands.
-		flags = copyFlags(&cmd.Flags)
+		flags = pathFlags(path)
 		mergeFlags(flags, globalFlags)
 	}
 	// Silence the many different ways flags.Parse can produce ugly output; we
@@ -413,9 +434,9 @@
 		}()
 	}
 	if err := flags.Parse(args); err != nil {
-		return nil, err
+		return nil, nil, err
 	}
-	return flags.Args(), nil
+	return flags.Args(), extractSetFlags(flags), nil
 }
 
 func mergeFlags(dst, src *flag.FlagSet) {
@@ -424,6 +445,7 @@
 		// Note that flag.Var will panic if it sees a collision.
 		if dst.Lookup(f.Name) == nil {
 			dst.Var(f.Value, f.Name, f.Usage)
+			dst.Lookup(f.Name).DefValue = f.DefValue
 		}
 	})
 }
@@ -434,6 +456,39 @@
 	return cp
 }
 
+// pathFlags returns the flags that are allowed for the last command in the
+// path.  Flags defined on ancestors are also allowed, except on "help".
+func pathFlags(path []*Command) *flag.FlagSet {
+	cmd := path[len(path)-1]
+	flags := copyFlags(&cmd.Flags)
+	if cmd.Name != helpName {
+		// Walk backwards to merge flags up to the root command.  If this takes too
+		// long, we could consider memoizing previous results.
+		for p := len(path) - 2; p >= 0; p-- {
+			mergeFlags(flags, &path[p].Flags)
+		}
+	}
+	return flags
+}
+
+func extractSetFlags(flags *flag.FlagSet) map[string]string {
+	// Use FlagSet.Visit rather than VisitAll to restrict to flags that are set.
+	setFlags := make(map[string]string)
+	flags.Visit(func(f *flag.Flag) {
+		setFlags[f.Name] = f.Value.String()
+	})
+	return setFlags
+}
+
+func flagsAsArgs(x map[string]string) []string {
+	var args []string
+	for key, val := range x {
+		args = append(args, "-"+key+"="+val)
+	}
+	sort.Strings(args)
+	return args
+}
+
 // subNames returns the sub names of c which should be ignored when using look
 // path to find external binaries.
 func (c *Command) subNames() map[string]bool {
diff --git a/cmdline/cmdline_test.go b/cmdline/cmdline_test.go
index 2df7ea7..77d6997 100644
--- a/cmdline/cmdline_test.go
+++ b/cmdline/cmdline_test.go
@@ -367,8 +367,8 @@
 Cmdrun has the echo command and a Run function with no args.
 
 Usage:
-   cmdrun
-   cmdrun <command>
+   cmdrun [flags]
+   cmdrun [flags] <command>
 
 The cmdrun commands are:
    echo        Print strings on stdout
@@ -387,8 +387,8 @@
 			Stdout: `Cmdrun has the echo command and a Run function with no args.
 
 Usage:
-   cmdrun
-   cmdrun <command>
+   cmdrun [flags]
+   cmdrun [flags] <command>
 
 The cmdrun commands are:
    echo        Print strings on stdout
@@ -407,7 +407,7 @@
 			Stdout: `Echo prints any strings passed in to stdout.
 
 Usage:
-   cmdrun echo [strings]
+   cmdrun echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 
@@ -423,8 +423,8 @@
 			Stdout: `Cmdrun has the echo command and a Run function with no args.
 
 Usage:
-   cmdrun
-   cmdrun <command>
+   cmdrun [flags]
+   cmdrun [flags] <command>
 
 The cmdrun commands are:
    echo        Print strings on stdout
@@ -442,7 +442,7 @@
 Echo prints any strings passed in to stdout.
 
 Usage:
-   cmdrun echo [strings]
+   cmdrun echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 ================================================================================
@@ -462,9 +462,10 @@
 The cmdrun help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -480,8 +481,8 @@
 Cmdrun has the echo command and a Run function with no args.
 
 Usage:
-   cmdrun
-   cmdrun <command>
+   cmdrun [flags]
+   cmdrun [flags] <command>
 
 The cmdrun commands are:
    echo        Print strings on stdout
@@ -511,7 +512,7 @@
 Echo prints any strings passed in to stdout.
 
 Usage:
-   cmdrun echo [strings]
+   cmdrun echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 
@@ -553,7 +554,7 @@
 Onecmd only has the echo command.
 
 Usage:
-   onecmd <command>
+   onecmd [flags] <command>
 
 The onecmd commands are:
    echo        Print strings on stdout
@@ -575,7 +576,7 @@
 Onecmd only has the echo command.
 
 Usage:
-   onecmd <command>
+   onecmd [flags] <command>
 
 The onecmd commands are:
    echo        Print strings on stdout
@@ -594,7 +595,7 @@
 			Stdout: `Onecmd only has the echo command.
 
 Usage:
-   onecmd <command>
+   onecmd [flags] <command>
 
 The onecmd commands are:
    echo        Print strings on stdout
@@ -613,7 +614,7 @@
 			Stdout: `Echo prints any strings passed in to stdout.
 
 Usage:
-   onecmd echo [strings]
+   onecmd echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 
@@ -640,9 +641,10 @@
 The onecmd help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -661,7 +663,7 @@
 			Stdout: `Onecmd only has the echo command.
 
 Usage:
-   onecmd <command>
+   onecmd [flags] <command>
 
 The onecmd commands are:
    echo        Print strings on stdout
@@ -679,7 +681,7 @@
 Echo prints any strings passed in to stdout.
 
 Usage:
-   onecmd echo [strings]
+   onecmd echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 ================================================================================
@@ -699,9 +701,10 @@
 The onecmd help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -717,7 +720,7 @@
 Onecmd only has the echo command.
 
 Usage:
-   onecmd <command>
+   onecmd [flags] <command>
 
 The onecmd commands are:
    echo        Print strings on stdout
@@ -747,7 +750,7 @@
 Echo prints any strings passed in to stdout.
 
 Usage:
-   onecmd echo [strings]
+   onecmd echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 
@@ -875,9 +878,11 @@
 Echo prints any strings passed in to stdout.
 
 Usage:
-   multi echo [strings]
+   multi echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
+
+Run "multi help -style=full echo" to show all flags.
 ================================================================================
 Multi echoopt - Print strings on stdout with opts
 
@@ -891,6 +896,8 @@
 The multi echoopt flags are:
  -n=false
    Do not output trailing newline
+
+Run "multi help -style=full echoopt" to show all flags.
 ================================================================================
 Multi help - Display help for commands or topics
 
@@ -908,9 +915,10 @@
 The multi help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -923,7 +931,7 @@
 			Stdout: `Echo prints any strings passed in to stdout.
 
 Usage:
-   multi echo [strings]
+   multi echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 
@@ -932,6 +940,8 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "multi help -style=full echo" to show all flags.
 `,
 		},
 		{
@@ -952,6 +962,8 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "multi help -style=full echoopt" to show all flags.
 `,
 		},
 		{
@@ -1037,7 +1049,7 @@
 Echo prints any strings passed in to stdout.
 
 Usage:
-   multi echo [strings]
+   multi echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
 
@@ -1046,6 +1058,8 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "multi help -style=full echo" to show all flags.
 `,
 		},
 		{
@@ -1242,15 +1256,19 @@
 The toplevelprog echoprog flags are:
  -extra=false
    Print an extra arg
+
+Run "toplevelprog echoprog help -style=full" to show all flags.
 ================================================================================
 Toplevelprog echoprog echo - Print strings on stdout
 
 Echo prints any strings passed in to stdout.
 
 Usage:
-   toplevelprog echoprog echo [strings]
+   toplevelprog echoprog echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
+
+Run "toplevelprog echoprog help -style=full echo" to show all flags.
 ================================================================================
 Toplevelprog echoprog echoopt - Print strings on stdout with opts
 
@@ -1264,6 +1282,8 @@
 The toplevelprog echoprog echoopt flags are:
  -n=false
    Do not output trailing newline
+
+Run "toplevelprog echoprog help -style=full echoopt" to show all flags.
 ================================================================================
 Toplevelprog echoprog topic3 - Help topic 3 short
 
@@ -1274,9 +1294,11 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   toplevelprog hello [strings]
+   toplevelprog hello [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
+
+Run "toplevelprog help -style=full hello" to show all flags.
 ================================================================================
 Toplevelprog help - Display help for commands or topics
 
@@ -1294,9 +1316,10 @@
 The toplevelprog help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -1338,6 +1361,8 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "toplevelprog echoprog help -style=full" to show all flags.
 `,
 		},
 		{
@@ -1376,15 +1401,19 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "toplevelprog echoprog help -style=full" to show all flags.
 ================================================================================
 Toplevelprog echoprog echo - Print strings on stdout
 
 Echo prints any strings passed in to stdout.
 
 Usage:
-   toplevelprog echoprog echo [strings]
+   toplevelprog echoprog echo [flags] [strings]
 
 [strings] are arbitrary strings that will be echoed.
+
+Run "toplevelprog echoprog help -style=full echo" to show all flags.
 ================================================================================
 Toplevelprog echoprog echoopt - Print strings on stdout with opts
 
@@ -1398,6 +1427,8 @@
 The toplevelprog echoprog echoopt flags are:
  -n=false
    Do not output trailing newline
+
+Run "toplevelprog echoprog help -style=full echoopt" to show all flags.
 ================================================================================
 Toplevelprog echoprog help - Display help for commands or topics
 
@@ -1415,9 +1446,10 @@
 The toplevelprog echoprog help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -1447,6 +1479,8 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "toplevelprog echoprog help -style=full echoopt" to show all flags.
 `,
 		},
 		{
@@ -1464,7 +1498,7 @@
 			Stdout: `Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   toplevelprog hello [strings]
+   toplevelprog hello [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -1473,6 +1507,8 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "toplevelprog help -style=full hello" to show all flags.
 `,
 		},
 		{
@@ -1559,7 +1595,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   toplevelprog hello [strings]
+   toplevelprog hello [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -1568,6 +1604,8 @@
    global test flag 1
  -global2=0
    global test flag 2
+
+Run "toplevelprog help -style=full hello" to show all flags.
 `,
 		},
 		{
@@ -1695,7 +1733,7 @@
 Prog1 has two variants of hello and a subprogram prog2.
 
 Usage:
-   prog1 <command>
+   prog1 [flags] <command>
 
 The prog1 commands are:
    hello11     Print strings on stdout preceded by Hello
@@ -1716,7 +1754,7 @@
 			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
 
 Usage:
-   prog1 <command>
+   prog1 [flags] <command>
 
 The prog1 commands are:
    hello11     Print strings on stdout preceded by Hello
@@ -1737,7 +1775,7 @@
 			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
 
 Usage:
-   prog1 <command>
+   prog1 [flags] <command>
 
 The prog1 commands are:
    hello11     Print strings on stdout preceded by Hello
@@ -1757,7 +1795,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 hello11 [strings]
+   prog1 hello11 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1766,7 +1804,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 hello12 [strings]
+   prog1 hello12 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1775,7 +1813,7 @@
 Prog2 has two variants of hello and a subprogram prog3.
 
 Usage:
-   prog1 prog2 <command>
+   prog1 prog2 [flags] <command>
 
 The prog1 prog2 commands are:
    hello21     Print strings on stdout preceded by Hello
@@ -1787,7 +1825,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 hello21 [strings]
+   prog1 prog2 hello21 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1796,7 +1834,7 @@
 Prog3 has two variants of hello.
 
 Usage:
-   prog1 prog2 prog3 <command>
+   prog1 prog2 prog3 [flags] <command>
 
 The prog1 prog2 prog3 commands are:
    hello31     Print strings on stdout preceded by Hello
@@ -1807,7 +1845,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello31 [strings]
+   prog1 prog2 prog3 hello31 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1816,7 +1854,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello32 [strings]
+   prog1 prog2 prog3 hello32 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1825,7 +1863,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 hello22 [strings]
+   prog1 prog2 hello22 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1845,9 +1883,10 @@
 The prog1 help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -1860,7 +1899,7 @@
 			Stdout: `Prog2 has two variants of hello and a subprogram prog3.
 
 Usage:
-   prog1 prog2 <command>
+   prog1 prog2 [flags] <command>
 
 The prog1 prog2 commands are:
    hello21     Print strings on stdout preceded by Hello
@@ -1880,7 +1919,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 hello21 [strings]
+   prog1 prog2 hello21 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1889,7 +1928,7 @@
 Prog3 has two variants of hello.
 
 Usage:
-   prog1 prog2 prog3 <command>
+   prog1 prog2 prog3 [flags] <command>
 
 The prog1 prog2 prog3 commands are:
    hello31     Print strings on stdout preceded by Hello
@@ -1900,7 +1939,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello31 [strings]
+   prog1 prog2 prog3 hello31 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1909,7 +1948,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello32 [strings]
+   prog1 prog2 prog3 hello32 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1918,7 +1957,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 hello22 [strings]
+   prog1 prog2 hello22 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1938,9 +1977,10 @@
 The prog1 prog2 help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -1953,7 +1993,7 @@
 			Stdout: `Prog3 has two variants of hello.
 
 Usage:
-   prog1 prog2 prog3 <command>
+   prog1 prog2 prog3 [flags] <command>
 
 The prog1 prog2 prog3 commands are:
    hello31     Print strings on stdout preceded by Hello
@@ -1972,7 +2012,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello31 [strings]
+   prog1 prog2 prog3 hello31 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -1981,7 +2021,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello32 [strings]
+   prog1 prog2 prog3 hello32 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -2001,9 +2041,10 @@
 The prog1 prog2 prog3 help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -2016,7 +2057,7 @@
 			Stdout: `Prog3 has two variants of hello.
 
 Usage:
-   prog1 prog2 prog3 <command>
+   prog1 prog2 prog3 [flags] <command>
 
 The prog1 prog2 prog3 commands are:
    hello31     Print strings on stdout preceded by Hello
@@ -2035,7 +2076,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello31 [strings]
+   prog1 prog2 prog3 hello31 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -2044,7 +2085,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello32 [strings]
+   prog1 prog2 prog3 hello32 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 ================================================================================
@@ -2064,9 +2105,10 @@
 The prog1 prog2 prog3 help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -2079,7 +2121,7 @@
 			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
 
 Usage:
-   prog1 <command>
+   prog1 [flags] <command>
 
 The prog1 commands are:
    hello11     Print strings on stdout preceded by Hello
@@ -2098,7 +2140,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 hello11 [strings]
+   prog1 hello11 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -2107,7 +2149,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 hello12 [strings]
+   prog1 hello12 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -2116,7 +2158,7 @@
 Prog2 has two variants of hello and a subprogram prog3.
 
 Usage:
-   prog1 prog2 <command>
+   prog1 prog2 [flags] <command>
 
 The prog1 prog2 commands are:
    hello21     Print strings on stdout preceded by Hello
@@ -2128,7 +2170,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 hello21 [strings]
+   prog1 prog2 hello21 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -2137,7 +2179,7 @@
 Prog3 has two variants of hello.
 
 Usage:
-   prog1 prog2 prog3 <command>
+   prog1 prog2 prog3 [flags] <command>
 
 The prog1 prog2 prog3 commands are:
    hello31     Print strings on stdout preceded by Hello
@@ -2148,7 +2190,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello31 [strings]
+   prog1 prog2 prog3 hello31 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -2157,7 +2199,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 prog3 hello32 [strings]
+   prog1 prog2 prog3 hello32 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -2166,7 +2208,7 @@
 Hello prints any strings passed in to stdout preceded by "Hello".
 
 Usage:
-   prog1 prog2 hello22 [strings]
+   prog1 prog2 hello22 [flags] [strings]
 
 [strings] are arbitrary strings that will be printed.
 
@@ -2186,9 +2228,10 @@
 The prog1 help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=<terminal width>
    Format output to this target width in runes, or unlimited if width < 0.
@@ -2225,7 +2268,7 @@
 			Stdout: `Test help strings when there are long commands.
 
 Usage:
-   program <command>
+   program [flags] <command>
 
 The program commands are:
    x                      description of short command.
@@ -2247,7 +2290,7 @@
 similarly have to be wrapped
 
 Usage:
-   program thisisaverylongcommand
+   program thisisaverylongcommand [flags]
 
 The global flags are:
  -global1=
@@ -2280,7 +2323,7 @@
 			Stdout: `Test hiding global flags.
 
 Usage:
-   program <command>
+   program [flags] <command>
 
 The program commands are:
    child       description of child command.
@@ -2291,7 +2334,7 @@
  -global2=0
    global test flag 2
 
-Run "program help -style=full" to show all global flags.
+Run "program help -style=full" to show all flags.
 `,
 		},
 		{
@@ -2299,13 +2342,13 @@
 			Stdout: `blah blah blah
 
 Usage:
-   program child
+   program child [flags]
 
 The global flags are:
  -global2=0
    global test flag 2
 
-Run "program help -style=full child" to show all global flags.
+Run "program help -style=full child" to show all flags.
 `,
 		},
 		{
@@ -2313,7 +2356,7 @@
 			Stdout: `Test hiding global flags.
 
 Usage:
-   program <command>
+   program [flags] <command>
 
 The program commands are:
    child       description of child command.
@@ -2333,7 +2376,7 @@
 			Stdout: `blah blah blah
 
 Usage:
-   program child
+   program child [flags]
 
 The global flags are:
  -global2=0
@@ -2362,13 +2405,13 @@
 			Stdout: `Test hiding global flags, root no children.
 
 Usage:
-   program
+   program [flags]
 
 The global flags are:
  -global2=0
    global test flag 2
 
-Run "CMDLINE_STYLE=full program -help" to show all global flags.
+Run "CMDLINE_STYLE=full program -help" to show all flags.
 `,
 		},
 		{
@@ -2377,7 +2420,7 @@
 			Stdout: `Test hiding global flags, root no children.
 
 Usage:
-   program
+   program [flags]
 
 The global flags are:
  -global2=0
@@ -2442,8 +2485,8 @@
 	}
 }
 
-func TestBinarySubcommand(t *testing.T) {
-	// Create a temporary directory for the binary subcommands.
+func TestExternalSubcommand(t *testing.T) {
+	// Create a temporary directory for the external subcommands.
 	tmpDir, err := ioutil.TempDir("", "cmdline-test")
 	if err != nil {
 		t.Fatalf("%v", err)
@@ -2458,8 +2501,8 @@
 	tokens = append([]string{tmpDir, tmpDir}, tokens...)
 	os.Setenv("PATH", strings.Join(tokens, string(os.PathListSeparator)))
 
-	// Build the binary subcommands.
-	for _, subCmd := range []string{"flat", "foreign", "nested", "repeated", "exitcode"} {
+	// Build the external subcommands.
+	for _, subCmd := range []string{"exitcode", "flags", "flat", "foreign", "nested", "repeated"} {
 		cmd := exec.Command("go", "build", "-o", filepath.Join(tmpDir, "unlikely-"+subCmd), filepath.Join(".", "testdata", subCmd+".go"))
 		if out, err := cmd.CombinedOutput(); err != nil {
 			t.Fatalf("%v, %v", string(out), err)
@@ -2487,6 +2530,7 @@
 			},
 		},
 	}
+	cmd.Flags.StringVar(new(string), "shared", "", "description of shared")
 
 	var tests = []testCase{
 		{
@@ -2497,7 +2541,7 @@
 			Stdout: `Long description of command unlikely.
 
 Usage:
-   unlikely <command>
+   unlikely [flags] <command>
 
 The unlikely commands are:
    dumpenv     Short description of command dumpenv
@@ -2505,11 +2549,16 @@
    help        Display help for commands or topics
 The unlikely external commands are:
    exitcode    Short description of command exitcode
+   flags       Short description of command flags
    flat        Short description of command flat
    foreign     No description available
    nested      Short description of command nested
 Run "unlikely help [command]" for command usage.
 
+The unlikely flags are:
+ -shared=
+   description of shared
+
 The global flags are:
  -global1=
    global test flag 1
@@ -2525,7 +2574,7 @@
 			Stdout: `Long description of command unlikely.
 
 Usage:
-   unlikely <command>
+   unlikely [flags] <command>
 
 The unlikely commands are:
    dumpenv     Short description of command dumpenv
@@ -2533,11 +2582,16 @@
    help        Display help for commands or topics
 The unlikely external commands are:
    exitcode    Short description of command exitcode
+   flags       Short description of command flags
    flat        Short description of command flat
    foreign     No description available
    nested      Short description of command nested
 Run "unlikely help [command]" for command usage.
 
+The unlikely flags are:
+ -shared=
+   description of shared
+
 The global flags are:
  -global1=
    global test flag 1
@@ -2553,7 +2607,7 @@
 			Stdout: `Long description of command unlikely.
 
 Usage:
-   unlikely <command>
+   unlikely [flags] <command>
 
 The unlikely commands are:
    dumpenv     Short description of command dumpenv
@@ -2561,11 +2615,16 @@
    help        Display help for commands or topics
 The unlikely external commands are:
    exitcode    Short description of command exitcode
+   flags       Short description of command flags
    flat        Short description of command flat
    foreign     No description available
    nested      Short description of command nested
 Run "unlikely help [command]" for command usage.
 
+The unlikely flags are:
+ -shared=
+   description of shared
+
 The global flags are:
  -global1=
    global test flag 1
@@ -2577,14 +2636,18 @@
 Long description of command dumpenv.
 
 Usage:
-   unlikely dumpenv
+   unlikely dumpenv [flags]
+
+Run "unlikely help -style=full dumpenv" to show all flags.
 ================================================================================
 Unlikely repeated - Repeated appears as both a child and as a binary
 
 Long description of command repeated.
 
 Usage:
-   unlikely repeated
+   unlikely repeated [flags]
+
+Run "unlikely help -style=full repeated" to show all flags.
 ================================================================================
 Unlikely help - Display help for commands or topics
 
@@ -2602,9 +2665,10 @@
 The unlikely help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=80
    Format output to this target width in runes, or unlimited if width < 0.
@@ -2616,16 +2680,33 @@
 Long description of command exitcode.
 
 Usage:
-   unlikely exitcode [args]
+   unlikely exitcode [flags] [args]
 
 [args] are ignored
 ================================================================================
+Unlikely flags - Short description of command flags
+
+Long description of command flags.
+
+Usage:
+   unlikely flags [flags] [args]
+
+[args] are ignored
+
+The unlikely flags flags are:
+ -global1=
+   description of global1
+ -local=
+   description of local
+ -shared=
+   description of shared
+================================================================================
 Unlikely flat - Short description of command flat
 
 Long description of command flat.
 
 Usage:
-   unlikely flat [args]
+   unlikely flat [flags] [args]
 
 [args] are ignored
 ================================================================================
@@ -2636,7 +2717,7 @@
 Long description of command nested.
 
 Usage:
-   unlikely nested <command>
+   unlikely nested [flags] <command>
 
 The unlikely nested commands are:
    child       Short description of command child
@@ -2646,7 +2727,7 @@
 Long description of command child.
 
 Usage:
-   unlikely nested child
+   unlikely nested child [flags]
 `,
 		},
 		{
@@ -2657,7 +2738,7 @@
 			Stdout: `Long description of command unlikely.
 
 Usage:
-   unlikely <command>
+   unlikely [flags] <command>
 
 The unlikely commands are:
    dumpenv     Short description of command dumpenv
@@ -2665,10 +2746,15 @@
    help        Display help for commands or topics
 The unlikely external commands are:
    exitcode    Short description of command exitcode
+   flags       Short description of command flags
    flat        Short description of command flat
    foreign     No description available
    nested      Short description of command nested
 
+The unlikely flags are:
+ -shared=
+   description of shared
+
 The global flags are:
  -global1=
    global test flag 1
@@ -2680,14 +2766,22 @@
 Long description of command dumpenv.
 
 Usage:
-   unlikely dumpenv
+   unlikely dumpenv [flags]
+
+The unlikely dumpenv flags are:
+ -shared=
+   description of shared
 
 Unlikely repeated - Repeated appears as both a child and as a binary
 
 Long description of command repeated.
 
 Usage:
-   unlikely repeated
+   unlikely repeated [flags]
+
+The unlikely repeated flags are:
+ -shared=
+   description of shared
 
 Unlikely help - Display help for commands or topics
 
@@ -2705,9 +2799,10 @@
 The unlikely help flags are:
  -style=compact
    The formatting style for help output:
-      compact - Good for compact cmdline output.
-      full    - Good for cmdline output, shows all global flags.
-      godoc   - Good for godoc processing.
+      compact   - Good for compact cmdline output.
+      full      - Good for cmdline output, shows all global flags.
+      godoc     - Good for godoc processing.
+      shortonly - Only output short description.
    Override the default by setting the CMDLINE_STYLE environment variable.
  -width=<terminal width>
    Format output to this target width in runes, or unlimited if width < 0.
@@ -2719,16 +2814,33 @@
 Long description of command exitcode.
 
 Usage:
-   unlikely exitcode [args]
+   unlikely exitcode [flags] [args]
 
 [args] are ignored
 
+Unlikely flags - Short description of command flags
+
+Long description of command flags.
+
+Usage:
+   unlikely flags [flags] [args]
+
+[args] are ignored
+
+The unlikely flags flags are:
+ -global1=
+   description of global1
+ -local=
+   description of local
+ -shared=
+   description of shared
+
 Unlikely flat - Short description of command flat
 
 Long description of command flat.
 
 Usage:
-   unlikely flat [args]
+   unlikely flat [flags] [args]
 
 [args] are ignored
 
@@ -2739,7 +2851,7 @@
 Long description of command nested.
 
 Usage:
-   unlikely nested <command>
+   unlikely nested [flags] <command>
 
 The unlikely nested commands are:
    child       Short description of command child
@@ -2749,7 +2861,7 @@
 Long description of command child.
 
 Usage:
-   unlikely nested child
+   unlikely nested child [flags]
 `,
 		},
 		{
@@ -2763,7 +2875,7 @@
 Long description of command flat.
 
 Usage:
-   unlikely flat [args]
+   unlikely flat [flags] [args]
 
 [args] are ignored
 
@@ -2785,7 +2897,7 @@
 Long description of command child.
 
 Usage:
-   unlikely nested child
+   unlikely nested child [flags]
 
 The global flags are:
  -metadata=<just specify -metadata to activate>
@@ -2813,6 +2925,49 @@
 			},
 			Err: "exit code 42",
 		},
+		{
+			Args: []string{"flags"},
+			Vars: map[string]string{
+				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
+			},
+			Stdout: `global1="" shared="" local="" []` + "\n",
+		},
+		{
+			Args: []string{"-global1=A B", "-shared=C D", "flags"},
+			Vars: map[string]string{
+				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
+			},
+			Stdout:      `global1="A B" shared="C D" local="" []` + "\n",
+			GlobalFlag1: "A B",
+		},
+		{
+			Args: []string{"flags", "-global1=A B", "-shared=C D"},
+			Vars: map[string]string{
+				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
+			},
+			Stdout: `global1="A B" shared="C D" local="" []` + "\n",
+		},
+		{
+			Args: []string{"flags", "-global1=A B", "-shared=C D", "-local=E F"},
+			Vars: map[string]string{
+				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
+			},
+			Stdout: `global1="A B" shared="C D" local="E F" []` + "\n",
+		},
+		{
+			Args: []string{"flags", "x", "y", "z"},
+			Vars: map[string]string{
+				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
+			},
+			Stdout: `global1="" shared="" local="" ["x" "y" "z"]` + "\n",
+		},
+		{
+			Args: []string{"flags", "-global1=A B", "-shared=C D", "-local=E F", "x", "y", "z"},
+			Vars: map[string]string{
+				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
+			},
+			Stdout: `global1="A B" shared="C D" local="E F" ["x" "y" "z"]` + "\n",
+		},
 	}
 	runTestCases(t, cmd, tests)
 }
diff --git a/cmdline/env.go b/cmdline/env.go
index b1f1802..a1cffe9 100644
--- a/cmdline/env.go
+++ b/cmdline/env.go
@@ -121,10 +121,10 @@
 type style int
 
 const (
-	styleCompact style = iota // Default style, good for compact cmdline output.
-	styleFull                 // Similar to compact but shows global flags.
-	styleGoDoc                // Style good for godoc processing.
-	styleShort                // Style good for displaying help of binary subcommands.
+	styleCompact   style = iota // Default style, good for compact cmdline output.
+	styleFull                   // Similar to compact but shows all global flags.
+	styleGoDoc                  // Good for godoc processing.
+	styleShortOnly              // Only output short description.
 )
 
 func (s *style) String() string {
@@ -135,8 +135,8 @@
 		return "full"
 	case styleGoDoc:
 		return "godoc"
-	case styleShort:
-		return "short"
+	case styleShortOnly:
+		return "shortonly"
 	default:
 		panic(fmt.Errorf("unhandled style %d", *s))
 	}
@@ -151,8 +151,8 @@
 		*s = styleFull
 	case "godoc":
 		*s = styleGoDoc
-	case "short":
-		*s = styleShort
+	case "shortonly":
+		*s = styleShortOnly
 	default:
 		return fmt.Errorf("unknown style %q", value)
 	}
diff --git a/cmdline/help.go b/cmdline/help.go
index 6ee2879..6be123c 100644
--- a/cmdline/help.go
+++ b/cmdline/help.go
@@ -21,9 +21,9 @@
 const missingDescription = "No description available"
 
 // helpRunner is a Runner that implements the "help" functionality.  Help is
-// requested for the last command in rootPath, which must not be empty.
+// requested for the last command in path, which must not be empty.
 type helpRunner struct {
-	rootPath []*Command
+	path []*Command
 	*helpConfig
 }
 
@@ -49,13 +49,13 @@
 func (h helpRunner) Run(env *Env, args []string) error {
 	w := textutil.NewUTF8LineWriter(env.Stdout, h.width)
 	defer w.Flush()
-	return runHelp(w, env, args, h.rootPath, h.helpConfig)
+	return runHelp(w, env, args, h.path, h.helpConfig)
 }
 
 // usageFunc is used as the implementation of the Env.Usage function.
 func (h helpRunner) usageFunc(env *Env, writer io.Writer) {
 	w := textutil.NewUTF8LineWriter(writer, h.width)
-	usage(w, env, h.rootPath, h.helpConfig, h.helpConfig.firstCall)
+	usage(w, env, h.path, h.helpConfig, h.helpConfig.firstCall)
 	w.Flush()
 }
 
@@ -84,9 +84,10 @@
 	}
 	help.Flags.Var(&h.style, "style", `
 The formatting style for help output:
-   compact - Good for compact cmdline output.
-   full    - Good for cmdline output, shows all global flags.
-   godoc   - Good for godoc processing.
+   compact   - Good for compact cmdline output.
+   full      - Good for cmdline output, shows all global flags.
+   godoc     - Good for godoc processing.
+   shortonly - Only output short description.
 Override the default by setting the CMDLINE_STYLE environment variable.
 `)
 	help.Flags.IntVar(&h.width, "width", h.width, `
@@ -279,7 +280,7 @@
 	cmd, cmdPath := path[len(path)-1], pathName(config.prefix, path)
 	env.TimerPush("usage " + cmdPath)
 	defer env.TimerPop()
-	if config.style == styleShort {
+	if config.style == styleShortOnly {
 		fmt.Fprintln(w, cmd.Short)
 		return
 	}
@@ -295,7 +296,7 @@
 	// Usage line.
 	fmt.Fprintln(w, "Usage:")
 	cmdPathF := "   " + cmdPath
-	if countFlags(&cmd.Flags, nil, true) > 0 {
+	if countFlags(pathFlags(path), nil, true) > 0 || countFlags(globalFlags, nil, true) > 0 {
 		cmdPathF += " [flags]"
 	}
 	if cmd.Runner != nil {
@@ -356,7 +357,7 @@
 			envCopy := env.clone()
 			envCopy.Stdout = &buffer
 			envCopy.Stderr = &buffer
-			envCopy.Vars["CMDLINE_STYLE"] = "short"
+			envCopy.Vars["CMDLINE_STYLE"] = "shortonly"
 			short := missingDescription
 			if err := runner.Run(envCopy, []string{"-help"}); err == nil {
 				// The external child supports "-help".
@@ -397,57 +398,78 @@
 			fmt.Fprintf(w, "Run \"%s help [topic]\" for topic details.\n", cmdPath)
 		}
 	}
-	flagsUsage(w, path, config, firstCall)
-}
-
-func flagsUsage(w *textutil.LineWriter, path []*Command, config *helpConfig, firstCall bool) {
-	cmd, cmdPath := path[len(path)-1], pathName(config.prefix, path)
-	// Flags.
-	if countFlags(&cmd.Flags, nil, true) > 0 {
-		fmt.Fprintln(w)
-		fmt.Fprintln(w, "The", cmdPath, "flags are:")
-		printFlags(w, &cmd.Flags, config.style, nil, true)
-	}
+	hidden := flagsUsage(w, path, config)
 	// Only show global flags on the first call.
-	if !firstCall {
-		return
+	if firstCall {
+		hidden = globalFlagsUsage(w, config) || hidden
 	}
-	hasCompact := countFlags(globalFlags, nonHiddenGlobalFlags, true) > 0
-	hasFull := countFlags(globalFlags, nonHiddenGlobalFlags, false) > 0
-	if config.style != styleCompact {
-		// Non-compact style, always show all global flags.
-		if hasCompact || hasFull {
-			fmt.Fprintln(w)
-			fmt.Fprintln(w, "The global flags are:")
-			printFlags(w, globalFlags, config.style, nonHiddenGlobalFlags, true)
-			if hasCompact && hasFull {
-				fmt.Fprintln(w)
-			}
-			printFlags(w, globalFlags, config.style, nonHiddenGlobalFlags, false)
-		}
-		return
-	}
-	// Compact style, only show compact flags and a reminder if there are more.
-	if hasCompact {
+	if hidden {
 		fmt.Fprintln(w)
-		fmt.Fprintln(w, "The global flags are:")
-		printFlags(w, globalFlags, config.style, nonHiddenGlobalFlags, true)
-	}
-	if hasFull {
-		fmt.Fprintln(w)
-		fullhelp := fmt.Sprintf(`Run "%s help -style=full" to show all global flags.`, cmdPath)
+		fullhelp := fmt.Sprintf(`Run "%s help -style=full" to show all flags.`, cmdPath)
 		if len(cmd.Children) == 0 {
 			if len(path) > 1 {
 				parentPath := pathName(config.prefix, path[:len(path)-1])
-				fullhelp = fmt.Sprintf(`Run "%s help -style=full %s" to show all global flags.`, parentPath, cmd.Name)
+				fullhelp = fmt.Sprintf(`Run "%s help -style=full %s" to show all flags.`, parentPath, cmd.Name)
 			} else {
-				fullhelp = fmt.Sprintf(`Run "CMDLINE_STYLE=full %s -help" to show all global flags.`, cmdPath)
+				fullhelp = fmt.Sprintf(`Run "CMDLINE_STYLE=full %s -help" to show all flags.`, cmdPath)
 			}
 		}
 		fmt.Fprintln(w, fullhelp)
 	}
 }
 
+func flagsUsage(w *textutil.LineWriter, path []*Command, config *helpConfig) bool {
+	cmd, cmdPath := path[len(path)-1], pathName(config.prefix, path)
+	allFlags := pathFlags(path)
+	numCompact := countFlags(&cmd.Flags, nil, true)
+	numFull := countFlags(allFlags, nil, true) - numCompact
+	if config.style == styleCompact {
+		// Compact style, only show compact flags.
+		if numCompact > 0 {
+			fmt.Fprintln(w)
+			fmt.Fprintln(w, "The", cmdPath, "flags are:")
+			printFlags(w, &cmd.Flags, nil, config.style, nil, true)
+		}
+		return numFull > 0
+	}
+	// Non-compact style, always show all flags.
+	if numCompact > 0 || numFull > 0 {
+		fmt.Fprintln(w)
+		fmt.Fprintln(w, "The", cmdPath, "flags are:")
+		printFlags(w, &cmd.Flags, nil, config.style, nil, true)
+		if numCompact > 0 && numFull > 0 {
+			fmt.Fprintln(w)
+		}
+		printFlags(w, allFlags, &cmd.Flags, config.style, nil, true)
+	}
+	return false
+}
+
+func globalFlagsUsage(w *textutil.LineWriter, config *helpConfig) bool {
+	numCompact := countFlags(globalFlags, nonHiddenGlobalFlags, true)
+	numFull := countFlags(globalFlags, nonHiddenGlobalFlags, false)
+	if config.style == styleCompact {
+		// Compact style, only show compact flags.
+		if numCompact > 0 {
+			fmt.Fprintln(w)
+			fmt.Fprintln(w, "The global flags are:")
+			printFlags(w, globalFlags, nil, config.style, nonHiddenGlobalFlags, true)
+		}
+		return numFull > 0
+	}
+	// Non-compact style, always show all global flags.
+	if numCompact > 0 || numFull > 0 {
+		fmt.Fprintln(w)
+		fmt.Fprintln(w, "The global flags are:")
+		printFlags(w, globalFlags, nil, config.style, nonHiddenGlobalFlags, true)
+		if numCompact > 0 && numFull > 0 {
+			fmt.Fprintln(w)
+		}
+		printFlags(w, globalFlags, nil, config.style, nonHiddenGlobalFlags, false)
+	}
+	return false
+}
+
 func countFlags(flags *flag.FlagSet, regexps []*regexp.Regexp, match bool) (num int) {
 	flags.VisitAll(func(f *flag.Flag) {
 		if match == matchRegexps(regexps, f.Name) {
@@ -457,8 +479,11 @@
 	return
 }
 
-func printFlags(w *textutil.LineWriter, flags *flag.FlagSet, style style, regexps []*regexp.Regexp, match bool) {
+func printFlags(w *textutil.LineWriter, flags, filter *flag.FlagSet, style style, regexps []*regexp.Regexp, match bool) {
 	flags.VisitAll(func(f *flag.Flag) {
+		if filter != nil && filter.Lookup(f.Name) != nil {
+			return
+		}
 		if match != matchRegexps(regexps, f.Name) {
 			return
 		}
diff --git a/cmdline/testdata/flags.go b/cmdline/testdata/flags.go
new file mode 100644
index 0000000..89c1519
--- /dev/null
+++ b/cmdline/testdata/flags.go
@@ -0,0 +1,37 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"fmt"
+
+	"v.io/x/lib/cmdline"
+)
+
+// cmdFlags represents the flags command.
+var cmdFlags = &cmdline.Command{
+	Runner:   cmdline.RunnerFunc(runFlags),
+	Name:     "flags",
+	Short:    "Short description of command flags",
+	Long:     "Long description of command flags.",
+	ArgsName: "[args]",
+	ArgsLong: "[args] are ignored",
+}
+
+func runFlags(env *cmdline.Env, args []string) error {
+	fmt.Fprintf(env.Stdout, "global1=%q shared=%q local=%q %q\n", flagGlobal1, flagShared, flagLocal, args)
+	return nil
+}
+
+var (
+	flagGlobal1, flagShared, flagLocal string
+)
+
+func main() {
+	cmdFlags.Flags.StringVar(&flagGlobal1, "global1", "", "description of global1")
+	cmdFlags.Flags.StringVar(&flagShared, "shared", "", "description of shared")
+	cmdFlags.Flags.StringVar(&flagLocal, "local", "", "description of local")
+	cmdline.Main(cmdFlags)
+}