cmdline: do not expand environment variables in default cmdline flags
values when printing help only for the godoc style

MultiPart: 1/3

Change-Id: I5ef5bbcddb586eac3ed613d49e96ddf7b11553e8
diff --git a/cmdline/cmdline.go b/cmdline/cmdline.go
index d10c76a..29da34a 100644
--- a/cmdline/cmdline.go
+++ b/cmdline/cmdline.go
@@ -103,15 +103,15 @@
 type style int
 
 const (
-	styleText  style = iota // Default style, good for cmdline output.
-	styleGoDoc              // Style good for godoc processing.
+	styleDefault style = iota // Default style, good for cmdline output.
+	styleGoDoc                // Style good for godoc processing.
 )
 
 // String returns the human-readable representation of the style.
 func (s *style) String() string {
 	switch *s {
-	case styleText:
-		return "text"
+	case styleDefault:
+		return "default"
 	case styleGoDoc:
 		return "godoc"
 	default:
@@ -122,8 +122,8 @@
 // Set implements the flag.Value interface method.
 func (s *style) Set(value string) error {
 	switch value {
-	case "text":
-		*s = styleText
+	case "default":
+		*s = styleDefault
 	case "godoc":
 		*s = styleGoDoc
 	default:
@@ -132,6 +132,14 @@
 	return nil
 }
 
+// styleFromEnv returns the style value specified by the CMDLINE_STYLE
+// environment variable, falling back on the default style.
+func styleFromEnv() style {
+	style := styleDefault
+	style.Set(os.Getenv("CMDLINE_STYLE"))
+	return style
+}
+
 // Stdout is where output goes.  Typically os.Stdout.
 func (cmd *Command) Stdout() io.Writer {
 	return cmd.stdout
@@ -168,14 +176,14 @@
 
 func (cmd *Command) writeUsage(w io.Writer) {
 	lineWriter := textutil.NewUTF8LineWriter(w, outputWidth())
-	cmd.usage(lineWriter, true)
+	cmd.usage(lineWriter, styleFromEnv(), true)
 	lineWriter.Flush()
 }
 
 // usage prints the usage of cmd to the writer.  The firstCall boolean is set to
 // false when printing usage for multiple commands, and is used to avoid
 // printing redundant information (e.g. help command, global flags).
-func (cmd *Command) usage(w *textutil.LineWriter, firstCall bool) {
+func (cmd *Command) usage(w *textutil.LineWriter, style style, firstCall bool) {
 	fmt.Fprintln(w, cmd.Long)
 	fmt.Fprintln(w)
 	// Usage line.
@@ -255,13 +263,13 @@
 	if hasFlags {
 		fmt.Fprintln(w)
 		fmt.Fprintln(w, "The", path, "flags are:")
-		printFlags(w, &cmd.Flags)
+		printFlags(w, &cmd.Flags, style)
 	}
 	// Global flags.
 	if numFlags(flag.CommandLine) > 0 && firstCall {
 		fmt.Fprintln(w)
 		fmt.Fprintln(w, "The global flags are:")
-		printFlags(w, flag.CommandLine)
+		printFlags(w, flag.CommandLine, style)
 	}
 }
 
@@ -281,9 +289,24 @@
 	return
 }
 
-func printFlags(w *textutil.LineWriter, set *flag.FlagSet) {
+func printFlags(w *textutil.LineWriter, set *flag.FlagSet, style style) {
 	set.VisitAll(func(f *flag.Flag) {
-		fmt.Fprintf(w, " -%s=%s", f.Name, f.DefValue)
+		var value interface{}
+		// When using styleDefault, we want the current value of the flag.
+		// But when using styleGoDoc, we want the default value. This
+		// logic ensures the godoc style prints out default values of
+		// VariableFlags without expanding variables.
+		switch style {
+		case styleDefault:
+			if getter, ok := f.Value.(flag.Getter); ok {
+				value = getter.Get()
+			} else {
+				value = f.Value.String()
+			}
+		case styleGoDoc:
+			value = f.DefValue
+		}
+		fmt.Fprintf(w, " -%s=%v", f.Name, value)
 		w.SetIndents(spaces(3))
 		fmt.Fprintln(w, f.Usage)
 		w.SetIndents()
@@ -297,7 +320,7 @@
 // newDefaultHelp creates a new default help command.  We need to create new
 // instances since the parent for each help command is different.
 func newDefaultHelp() *Command {
-	helpStyle := styleText
+	helpStyle := styleFromEnv()
 	help := &Command{
 		Name:  helpName,
 		Short: "Display help for commands or topics",
@@ -326,7 +349,7 @@
 		},
 		isDefaultHelp: true,
 	}
-	help.Flags.Var(&helpStyle, "style", `The formatting style for help output, either "text" or "godoc".`)
+	help.Flags.Var(&helpStyle, "style", `The formatting style for help output, either "default" or "godoc".`)
 	return help
 }
 
@@ -335,7 +358,7 @@
 // runHelp runs the "help" command.
 func runHelp(w *textutil.LineWriter, cmd *Command, args []string, style style) error {
 	if len(args) == 0 {
-		cmd.usage(w, true)
+		cmd.usage(w, style, true)
 		return nil
 	}
 	if args[0] == "..." {
@@ -368,7 +391,7 @@
 		fmt.Fprintln(w, header)
 		fmt.Fprintln(w)
 	}
-	cmd.usage(w, firstCall)
+	cmd.usage(w, style, firstCall)
 	for _, child := range cmd.Children {
 		// Don't repeatedly print default help command.
 		if !child.isDefaultHelp || firstCall {
@@ -388,7 +411,7 @@
 func lineBreak(w *textutil.LineWriter, style style) {
 	w.Flush()
 	switch style {
-	case styleText:
+	case styleDefault:
 		width := w.Width()
 		if width < 0 {
 			// If the user has chosen an "unlimited" word-wrapping width, we still
diff --git a/cmdline/cmdline_test.go b/cmdline/cmdline_test.go
index 6cf6b0e..8d36f62 100644
--- a/cmdline/cmdline_test.go
+++ b/cmdline/cmdline_test.go
@@ -270,8 +270,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The onecmd help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 
 The global flags are:
  -global1=
@@ -327,8 +327,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The onecmd help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
@@ -534,8 +534,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The multi help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
@@ -918,8 +918,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The toplevelprog help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 ================================================================================
 Toplevelprog Topic1 - help topic
 
@@ -1037,8 +1037,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The toplevelprog echoprog help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 ================================================================================
 Toplevelprog Echoprog Topic3 - help topic
 
@@ -1465,8 +1465,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The prog1 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
@@ -1556,8 +1556,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The prog1 prog2 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
@@ -1617,8 +1617,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The prog1 prog2 prog3 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
@@ -1678,8 +1678,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The prog1 prog2 prog3 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
@@ -1799,8 +1799,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The prog1 help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 	}
@@ -1927,8 +1927,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The cmdargs help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
@@ -2119,8 +2119,8 @@
 [command/topic ...] optionally identifies a specific sub-command or help topic.
 
 The cmdrun help flags are:
- -style=text
-   The formatting style for help output, either "text" or "godoc".
+ -style=default
+   The formatting style for help output, either "default" or "godoc".
 `,
 		},
 		{
diff --git a/cmdline/flag_test.go b/cmdline/flag_test.go
index c0d1281..b0318db 100644
--- a/cmdline/flag_test.go
+++ b/cmdline/flag_test.go
@@ -58,13 +58,26 @@
 			t.Fatalf("%v failed:\n%v", strings.Join(cmd.Args, " "), string(output))
 		}
 	}
-	// Check that the substitution does not occur in the documentation.
+	// Check that the substitution occurs in the default documentation.
 	{
 		cmd := exec.Command("go", "run", filepath.Join("testdata", "flag.go"), "-help")
 		output, err := cmd.CombinedOutput()
 		if err != nil {
 			t.Fatalf("%v failed:\n%v", strings.Join(cmd.Args, " "), string(output))
 		}
+		if got, want := string(output), "-test=HELLO"; !strings.Contains(got, want) {
+			t.Fatalf("%q not found in:\n%v", want, got)
+		}
+	}
+	// Check that the substitution does not occur in the GoDoc
+	// documentation.
+	{
+		cmd := exec.Command("go", "run", filepath.Join("testdata", "flag.go"), "-help")
+		cmd.Env = append(os.Environ(), "CMDLINE_STYLE=godoc")
+		output, err := cmd.CombinedOutput()
+		if err != nil {
+			t.Fatalf("%v failed:\n%v", strings.Join(cmd.Args, " "), string(output))
+		}
 		if got, want := string(output), "-test=${TEST}"; !strings.Contains(got, want) {
 			t.Fatalf("%q not found in:\n%v", want, got)
 		}
diff --git a/cmdline/testdata/gendoc.go b/cmdline/testdata/gendoc.go
index 02ecc5c..f942ee3 100644
--- a/cmdline/testdata/gendoc.go
+++ b/cmdline/testdata/gendoc.go
@@ -56,13 +56,16 @@
 
 	// Use it to generate the documentation.
 	var out bytes.Buffer
+	env := os.Environ()
 	if len(os.Args) == 2 {
 		args = []string{"help", "-style=godoc", "..."}
 	} else {
 		args = os.Args[2:]
+		env = append(env, "CMDLINE_STYLE=godoc")
 	}
 	runCmd := exec.Command(gendocBin, args...)
 	runCmd.Stdout = &out
+	runCmd.Env = env
 	if err := runCmd.Run(); err != nil {
 		return fmt.Errorf("%q failed: %v\n%v\n", strings.Join(runCmd.Args, " "), err)
 	}