github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/go/main.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"flag"
    11  	"fmt"
    12  	"go/build"
    13  	"io"
    14  	"log"
    15  	"os"
    16  	"os/exec"
    17  	"path"
    18  	"path/filepath"
    19  	"regexp"
    20  	"runtime"
    21  	"strings"
    22  	"sync"
    23  	"text/template"
    24  	"unicode"
    25  	"unicode/utf8"
    26  )
    27  
    28  // A Command is an implementation of a go command
    29  // like go build or go fix.
    30  type Command struct {
    31  	// Run runs the command.
    32  	// The args are the arguments after the command name.
    33  	Run func(cmd *Command, args []string)
    34  
    35  	// UsageLine is the one-line usage message.
    36  	// The first word in the line is taken to be the command name.
    37  	UsageLine string
    38  
    39  	// Short is the short description shown in the 'go help' output.
    40  	Short string
    41  
    42  	// Long is the long message shown in the 'go help <this-command>' output.
    43  	Long string
    44  
    45  	// Flag is a set of flags specific to this command.
    46  	Flag flag.FlagSet
    47  
    48  	// CustomFlags indicates that the command will do its own
    49  	// flag parsing.
    50  	CustomFlags bool
    51  }
    52  
    53  // Name returns the command's name: the first word in the usage line.
    54  func (c *Command) Name() string {
    55  	name := c.UsageLine
    56  	i := strings.Index(name, " ")
    57  	if i >= 0 {
    58  		name = name[:i]
    59  	}
    60  	return name
    61  }
    62  
    63  func (c *Command) Usage() {
    64  	fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
    65  	fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
    66  	os.Exit(2)
    67  }
    68  
    69  // Runnable reports whether the command can be run; otherwise
    70  // it is a documentation pseudo-command such as importpath.
    71  func (c *Command) Runnable() bool {
    72  	return c.Run != nil
    73  }
    74  
    75  // Commands lists the available commands and help topics.
    76  // The order here is the order in which they are printed by 'go help'.
    77  var commands = []*Command{
    78  	cmdBuild,
    79  	cmdClean,
    80  	cmdDoc,
    81  	cmdEnv,
    82  	cmdFix,
    83  	cmdFmt,
    84  	cmdGenerate,
    85  	cmdGet,
    86  	cmdInstall,
    87  	cmdList,
    88  	cmdRun,
    89  	cmdTest,
    90  	cmdTool,
    91  	cmdVersion,
    92  	cmdVet,
    93  
    94  	helpC,
    95  	helpBuildmode,
    96  	helpFileType,
    97  	helpGopath,
    98  	helpEnvironment,
    99  	helpImportPath,
   100  	helpPackages,
   101  	helpTestflag,
   102  	helpTestfunc,
   103  }
   104  
   105  var exitStatus = 0
   106  var exitMu sync.Mutex
   107  
   108  func setExitStatus(n int) {
   109  	exitMu.Lock()
   110  	if exitStatus < n {
   111  		exitStatus = n
   112  	}
   113  	exitMu.Unlock()
   114  }
   115  
   116  var origEnv []string
   117  
   118  func main() {
   119  	_ = go11tag
   120  	flag.Usage = usage
   121  	flag.Parse()
   122  	log.SetFlags(0)
   123  
   124  	args := flag.Args()
   125  	if len(args) < 1 {
   126  		usage()
   127  	}
   128  
   129  	if args[0] == "help" {
   130  		help(args[1:])
   131  		return
   132  	}
   133  
   134  	// Diagnose common mistake: GOPATH==GOROOT.
   135  	// This setting is equivalent to not setting GOPATH at all,
   136  	// which is not what most people want when they do it.
   137  	if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() {
   138  		fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
   139  	} else {
   140  		for _, p := range filepath.SplitList(gopath) {
   141  			// Note: using HasPrefix instead of Contains because a ~ can appear
   142  			// in the middle of directory elements, such as /tmp/git-1.8.2~rc3
   143  			// or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell.
   144  			if strings.HasPrefix(p, "~") {
   145  				fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p)
   146  				os.Exit(2)
   147  			}
   148  			if !filepath.IsAbs(p) {
   149  				fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p)
   150  				os.Exit(2)
   151  			}
   152  		}
   153  	}
   154  
   155  	if fi, err := os.Stat(goroot); err != nil || !fi.IsDir() {
   156  		fmt.Fprintf(os.Stderr, "go: cannot find GOROOT directory: %v\n", goroot)
   157  		os.Exit(2)
   158  	}
   159  
   160  	// Set environment (GOOS, GOARCH, etc) explicitly.
   161  	// In theory all the commands we invoke should have
   162  	// the same default computation of these as we do,
   163  	// but in practice there might be skew
   164  	// This makes sure we all agree.
   165  	origEnv = os.Environ()
   166  	for _, env := range mkEnv() {
   167  		if os.Getenv(env.name) != env.value {
   168  			os.Setenv(env.name, env.value)
   169  		}
   170  	}
   171  
   172  	for _, cmd := range commands {
   173  		if cmd.Name() == args[0] && cmd.Runnable() {
   174  			cmd.Flag.Usage = func() { cmd.Usage() }
   175  			if cmd.CustomFlags {
   176  				args = args[1:]
   177  			} else {
   178  				cmd.Flag.Parse(args[1:])
   179  				args = cmd.Flag.Args()
   180  			}
   181  			cmd.Run(cmd, args)
   182  			exit()
   183  			return
   184  		}
   185  	}
   186  
   187  	fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0])
   188  	setExitStatus(2)
   189  	exit()
   190  }
   191  
   192  var usageTemplate = `Go is a tool for managing Go source code.
   193  
   194  Usage:
   195  
   196  	go command [arguments]
   197  
   198  The commands are:
   199  {{range .}}{{if .Runnable}}
   200  	{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
   201  
   202  Use "go help [command]" for more information about a command.
   203  
   204  Additional help topics:
   205  {{range .}}{{if not .Runnable}}
   206  	{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
   207  
   208  Use "go help [topic]" for more information about that topic.
   209  
   210  `
   211  
   212  var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}}
   213  
   214  {{end}}{{.Long | trim}}
   215  `
   216  
   217  var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reserved.
   218  // Use of this source code is governed by a BSD-style
   219  // license that can be found in the LICENSE file.
   220  
   221  // DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.
   222  // Edit the documentation in other files and rerun mkalldocs.sh to generate this one.
   223  
   224  /*
   225  {{range .}}{{if .Short}}{{.Short | capitalize}}
   226  
   227  {{end}}{{if .Runnable}}Usage:
   228  
   229  	go {{.UsageLine}}
   230  
   231  {{end}}{{.Long | trim}}
   232  
   233  
   234  {{end}}*/
   235  package main
   236  `
   237  
   238  // An errWriter wraps a writer, recording whether a write error occurred.
   239  type errWriter struct {
   240  	w   io.Writer
   241  	err error
   242  }
   243  
   244  func (w *errWriter) Write(b []byte) (int, error) {
   245  	n, err := w.w.Write(b)
   246  	if err != nil {
   247  		w.err = err
   248  	}
   249  	return n, err
   250  }
   251  
   252  // tmpl executes the given template text on data, writing the result to w.
   253  func tmpl(w io.Writer, text string, data interface{}) {
   254  	t := template.New("top")
   255  	t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
   256  	template.Must(t.Parse(text))
   257  	ew := &errWriter{w: w}
   258  	err := t.Execute(ew, data)
   259  	if ew.err != nil {
   260  		// I/O error writing. Ignore write on closed pipe.
   261  		if strings.Contains(ew.err.Error(), "pipe") {
   262  			os.Exit(1)
   263  		}
   264  		fatalf("writing output: %v", ew.err)
   265  	}
   266  	if err != nil {
   267  		panic(err)
   268  	}
   269  }
   270  
   271  func capitalize(s string) string {
   272  	if s == "" {
   273  		return s
   274  	}
   275  	r, n := utf8.DecodeRuneInString(s)
   276  	return string(unicode.ToTitle(r)) + s[n:]
   277  }
   278  
   279  func printUsage(w io.Writer) {
   280  	bw := bufio.NewWriter(w)
   281  	tmpl(bw, usageTemplate, commands)
   282  	bw.Flush()
   283  }
   284  
   285  func usage() {
   286  	// special case "go test -h"
   287  	if len(os.Args) > 1 && os.Args[1] == "test" {
   288  		os.Stderr.WriteString(testUsage + "\n\n" +
   289  			strings.TrimSpace(testFlag1) + "\n\n\t" +
   290  			strings.TrimSpace(testFlag2) + "\n")
   291  		os.Exit(2)
   292  	}
   293  	printUsage(os.Stderr)
   294  	os.Exit(2)
   295  }
   296  
   297  // help implements the 'help' command.
   298  func help(args []string) {
   299  	if len(args) == 0 {
   300  		printUsage(os.Stdout)
   301  		// not exit 2: succeeded at 'go help'.
   302  		return
   303  	}
   304  	if len(args) != 1 {
   305  		fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n")
   306  		os.Exit(2) // failed at 'go help'
   307  	}
   308  
   309  	arg := args[0]
   310  
   311  	// 'go help documentation' generates doc.go.
   312  	if arg == "documentation" {
   313  		buf := new(bytes.Buffer)
   314  		printUsage(buf)
   315  		usage := &Command{Long: buf.String()}
   316  		tmpl(os.Stdout, documentationTemplate, append([]*Command{usage}, commands...))
   317  		return
   318  	}
   319  
   320  	for _, cmd := range commands {
   321  		if cmd.Name() == arg {
   322  			tmpl(os.Stdout, helpTemplate, cmd)
   323  			// not exit 2: succeeded at 'go help cmd'.
   324  			return
   325  		}
   326  	}
   327  
   328  	fmt.Fprintf(os.Stderr, "Unknown help topic %#q.  Run 'go help'.\n", arg)
   329  	os.Exit(2) // failed at 'go help cmd'
   330  }
   331  
   332  // importPathsNoDotExpansion returns the import paths to use for the given
   333  // command line, but it does no ... expansion.
   334  func importPathsNoDotExpansion(args []string) []string {
   335  	if len(args) == 0 {
   336  		return []string{"."}
   337  	}
   338  	var out []string
   339  	for _, a := range args {
   340  		// Arguments are supposed to be import paths, but
   341  		// as a courtesy to Windows developers, rewrite \ to /
   342  		// in command-line arguments. Handles .\... and so on.
   343  		if filepath.Separator == '\\' {
   344  			a = strings.Replace(a, `\`, `/`, -1)
   345  		}
   346  
   347  		// Put argument in canonical form, but preserve leading ./.
   348  		if strings.HasPrefix(a, "./") {
   349  			a = "./" + path.Clean(a)
   350  			if a == "./." {
   351  				a = "."
   352  			}
   353  		} else {
   354  			a = path.Clean(a)
   355  		}
   356  		if isMetaPackage(a) {
   357  			out = append(out, allPackages(a)...)
   358  			continue
   359  		}
   360  		out = append(out, a)
   361  	}
   362  	return out
   363  }
   364  
   365  // importPaths returns the import paths to use for the given command line.
   366  func importPaths(args []string) []string {
   367  	args = importPathsNoDotExpansion(args)
   368  	var out []string
   369  	for _, a := range args {
   370  		if strings.Contains(a, "...") {
   371  			if build.IsLocalImport(a) {
   372  				out = append(out, allPackagesInFS(a)...)
   373  			} else {
   374  				out = append(out, allPackages(a)...)
   375  			}
   376  			continue
   377  		}
   378  		out = append(out, a)
   379  	}
   380  	return out
   381  }
   382  
   383  var atexitFuncs []func()
   384  
   385  func atexit(f func()) {
   386  	atexitFuncs = append(atexitFuncs, f)
   387  }
   388  
   389  func exit() {
   390  	for _, f := range atexitFuncs {
   391  		f()
   392  	}
   393  	os.Exit(exitStatus)
   394  }
   395  
   396  func fatalf(format string, args ...interface{}) {
   397  	errorf(format, args...)
   398  	exit()
   399  }
   400  
   401  func errorf(format string, args ...interface{}) {
   402  	log.Printf(format, args...)
   403  	setExitStatus(1)
   404  }
   405  
   406  func exitIfErrors() {
   407  	if exitStatus != 0 {
   408  		exit()
   409  	}
   410  }
   411  
   412  func run(cmdargs ...interface{}) {
   413  	cmdline := stringList(cmdargs...)
   414  	if buildN || buildX {
   415  		fmt.Printf("%s\n", strings.Join(cmdline, " "))
   416  		if buildN {
   417  			return
   418  		}
   419  	}
   420  
   421  	cmd := exec.Command(cmdline[0], cmdline[1:]...)
   422  	cmd.Stdout = os.Stdout
   423  	cmd.Stderr = os.Stderr
   424  	if err := cmd.Run(); err != nil {
   425  		errorf("%v", err)
   426  	}
   427  }
   428  
   429  // envForDir returns a copy of the environment
   430  // suitable for running in the given directory.
   431  // The environment is the current process's environment
   432  // but with an updated $PWD, so that an os.Getwd in the
   433  // child will be faster.
   434  func envForDir(dir string, base []string) []string {
   435  	// Internally we only use rooted paths, so dir is rooted.
   436  	// Even if dir is not rooted, no harm done.
   437  	return mergeEnvLists([]string{"PWD=" + dir}, base)
   438  }
   439  
   440  // mergeEnvLists merges the two environment lists such that
   441  // variables with the same name in "in" replace those in "out".
   442  // This always returns a newly allocated slice.
   443  func mergeEnvLists(in, out []string) []string {
   444  	out = append([]string(nil), out...)
   445  NextVar:
   446  	for _, inkv := range in {
   447  		k := strings.SplitAfterN(inkv, "=", 2)[0]
   448  		for i, outkv := range out {
   449  			if strings.HasPrefix(outkv, k) {
   450  				out[i] = inkv
   451  				continue NextVar
   452  			}
   453  		}
   454  		out = append(out, inkv)
   455  	}
   456  	return out
   457  }
   458  
   459  // matchPattern(pattern)(name) reports whether
   460  // name matches pattern. Pattern is a limited glob
   461  // pattern in which '...' means 'any string' and there
   462  // is no other special syntax.
   463  func matchPattern(pattern string) func(name string) bool {
   464  	re := regexp.QuoteMeta(pattern)
   465  	re = strings.Replace(re, `\.\.\.`, `.*`, -1)
   466  	// Special case: foo/... matches foo too.
   467  	if strings.HasSuffix(re, `/.*`) {
   468  		re = re[:len(re)-len(`/.*`)] + `(/.*)?`
   469  	}
   470  	reg := regexp.MustCompile(`^` + re + `$`)
   471  	return func(name string) bool {
   472  		return reg.MatchString(name)
   473  	}
   474  }
   475  
   476  // hasPathPrefix reports whether the path s begins with the
   477  // elements in prefix.
   478  func hasPathPrefix(s, prefix string) bool {
   479  	switch {
   480  	default:
   481  		return false
   482  	case len(s) == len(prefix):
   483  		return s == prefix
   484  	case len(s) > len(prefix):
   485  		if prefix != "" && prefix[len(prefix)-1] == '/' {
   486  			return strings.HasPrefix(s, prefix)
   487  		}
   488  		return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
   489  	}
   490  }
   491  
   492  // hasFilePathPrefix reports whether the filesystem path s begins with the
   493  // elements in prefix.
   494  func hasFilePathPrefix(s, prefix string) bool {
   495  	sv := strings.ToUpper(filepath.VolumeName(s))
   496  	pv := strings.ToUpper(filepath.VolumeName(prefix))
   497  	s = s[len(sv):]
   498  	prefix = prefix[len(pv):]
   499  	switch {
   500  	default:
   501  		return false
   502  	case sv != pv:
   503  		return false
   504  	case len(s) == len(prefix):
   505  		return s == prefix
   506  	case len(s) > len(prefix):
   507  		if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
   508  			return strings.HasPrefix(s, prefix)
   509  		}
   510  		return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
   511  	}
   512  }
   513  
   514  // expandPath returns the symlink-expanded form of path.
   515  func expandPath(p string) string {
   516  	x, err := filepath.EvalSymlinks(p)
   517  	if err == nil {
   518  		return x
   519  	}
   520  	return p
   521  }
   522  
   523  // treeCanMatchPattern(pattern)(name) reports whether
   524  // name or children of name can possibly match pattern.
   525  // Pattern is the same limited glob accepted by matchPattern.
   526  func treeCanMatchPattern(pattern string) func(name string) bool {
   527  	wildCard := false
   528  	if i := strings.Index(pattern, "..."); i >= 0 {
   529  		wildCard = true
   530  		pattern = pattern[:i]
   531  	}
   532  	return func(name string) bool {
   533  		return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
   534  			wildCard && strings.HasPrefix(name, pattern)
   535  	}
   536  }
   537  
   538  // allPackages returns all the packages that can be found
   539  // under the $GOPATH directories and $GOROOT matching pattern.
   540  // The pattern is either "all" (all packages), "std" (standard packages),
   541  // "cmd" (standard commands), or a path including "...".
   542  func allPackages(pattern string) []string {
   543  	pkgs := matchPackages(pattern)
   544  	if len(pkgs) == 0 {
   545  		fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
   546  	}
   547  	return pkgs
   548  }
   549  
   550  func matchPackages(pattern string) []string {
   551  	match := func(string) bool { return true }
   552  	treeCanMatch := func(string) bool { return true }
   553  	if !isMetaPackage(pattern) {
   554  		match = matchPattern(pattern)
   555  		treeCanMatch = treeCanMatchPattern(pattern)
   556  	}
   557  
   558  	have := map[string]bool{
   559  		"builtin": true, // ignore pseudo-package that exists only for documentation
   560  	}
   561  	if !buildContext.CgoEnabled {
   562  		have["runtime/cgo"] = true // ignore during walk
   563  	}
   564  	var pkgs []string
   565  
   566  	for _, src := range buildContext.SrcDirs() {
   567  		if (pattern == "std" || pattern == "cmd") && src != gorootSrc {
   568  			continue
   569  		}
   570  		src = filepath.Clean(src) + string(filepath.Separator)
   571  		root := src
   572  		if pattern == "cmd" {
   573  			root += "cmd" + string(filepath.Separator)
   574  		}
   575  		filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
   576  			if err != nil || !fi.IsDir() || path == src {
   577  				return nil
   578  			}
   579  
   580  			// Avoid .foo, _foo, and testdata directory trees.
   581  			_, elem := filepath.Split(path)
   582  			if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
   583  				return filepath.SkipDir
   584  			}
   585  
   586  			name := filepath.ToSlash(path[len(src):])
   587  			if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
   588  				// The name "std" is only the standard library.
   589  				// If the name is cmd, it's the root of the command tree.
   590  				return filepath.SkipDir
   591  			}
   592  			if !treeCanMatch(name) {
   593  				return filepath.SkipDir
   594  			}
   595  			if have[name] {
   596  				return nil
   597  			}
   598  			have[name] = true
   599  			if !match(name) {
   600  				return nil
   601  			}
   602  			_, err = buildContext.ImportDir(path, 0)
   603  			if err != nil {
   604  				if _, noGo := err.(*build.NoGoError); noGo {
   605  					return nil
   606  				}
   607  			}
   608  			pkgs = append(pkgs, name)
   609  			return nil
   610  		})
   611  	}
   612  	return pkgs
   613  }
   614  
   615  // allPackagesInFS is like allPackages but is passed a pattern
   616  // beginning ./ or ../, meaning it should scan the tree rooted
   617  // at the given directory. There are ... in the pattern too.
   618  func allPackagesInFS(pattern string) []string {
   619  	pkgs := matchPackagesInFS(pattern)
   620  	if len(pkgs) == 0 {
   621  		fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
   622  	}
   623  	return pkgs
   624  }
   625  
   626  func matchPackagesInFS(pattern string) []string {
   627  	// Find directory to begin the scan.
   628  	// Could be smarter but this one optimization
   629  	// is enough for now, since ... is usually at the
   630  	// end of a path.
   631  	i := strings.Index(pattern, "...")
   632  	dir, _ := path.Split(pattern[:i])
   633  
   634  	// pattern begins with ./ or ../.
   635  	// path.Clean will discard the ./ but not the ../.
   636  	// We need to preserve the ./ for pattern matching
   637  	// and in the returned import paths.
   638  	prefix := ""
   639  	if strings.HasPrefix(pattern, "./") {
   640  		prefix = "./"
   641  	}
   642  	match := matchPattern(pattern)
   643  
   644  	var pkgs []string
   645  	filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
   646  		if err != nil || !fi.IsDir() {
   647  			return nil
   648  		}
   649  		if path == dir {
   650  			// filepath.Walk starts at dir and recurses. For the recursive case,
   651  			// the path is the result of filepath.Join, which calls filepath.Clean.
   652  			// The initial case is not Cleaned, though, so we do this explicitly.
   653  			//
   654  			// This converts a path like "./io/" to "io". Without this step, running
   655  			// "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
   656  			// package, because prepending the prefix "./" to the unclean path would
   657  			// result in "././io", and match("././io") returns false.
   658  			path = filepath.Clean(path)
   659  		}
   660  
   661  		// Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
   662  		_, elem := filepath.Split(path)
   663  		dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
   664  		if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
   665  			return filepath.SkipDir
   666  		}
   667  
   668  		name := prefix + filepath.ToSlash(path)
   669  		if !match(name) {
   670  			return nil
   671  		}
   672  
   673  		// We keep the directory if we can import it, or if we can't import it
   674  		// due to invalid Go source files. This means that directories containing
   675  		// parse errors will be built (and fail) instead of being silently skipped
   676  		// as not matching the pattern. Go 1.5 and earlier skipped, but that
   677  		// behavior means people miss serious mistakes.
   678  		// See golang.org/issue/11407.
   679  		if p, err := buildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
   680  			if _, noGo := err.(*build.NoGoError); !noGo {
   681  				log.Print(err)
   682  			}
   683  			return nil
   684  		}
   685  		pkgs = append(pkgs, name)
   686  		return nil
   687  	})
   688  	return pkgs
   689  }
   690  
   691  // stringList's arguments should be a sequence of string or []string values.
   692  // stringList flattens them into a single []string.
   693  func stringList(args ...interface{}) []string {
   694  	var x []string
   695  	for _, arg := range args {
   696  		switch arg := arg.(type) {
   697  		case []string:
   698  			x = append(x, arg...)
   699  		case string:
   700  			x = append(x, arg)
   701  		default:
   702  			panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
   703  		}
   704  	}
   705  	return x
   706  }
   707  
   708  // toFold returns a string with the property that
   709  //	strings.EqualFold(s, t) iff toFold(s) == toFold(t)
   710  // This lets us test a large set of strings for fold-equivalent
   711  // duplicates without making a quadratic number of calls
   712  // to EqualFold. Note that strings.ToUpper and strings.ToLower
   713  // have the desired property in some corner cases.
   714  func toFold(s string) string {
   715  	// Fast path: all ASCII, no upper case.
   716  	// Most paths look like this already.
   717  	for i := 0; i < len(s); i++ {
   718  		c := s[i]
   719  		if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
   720  			goto Slow
   721  		}
   722  	}
   723  	return s
   724  
   725  Slow:
   726  	var buf bytes.Buffer
   727  	for _, r := range s {
   728  		// SimpleFold(x) cycles to the next equivalent rune > x
   729  		// or wraps around to smaller values. Iterate until it wraps,
   730  		// and we've found the minimum value.
   731  		for {
   732  			r0 := r
   733  			r = unicode.SimpleFold(r0)
   734  			if r <= r0 {
   735  				break
   736  			}
   737  		}
   738  		// Exception to allow fast path above: A-Z => a-z
   739  		if 'A' <= r && r <= 'Z' {
   740  			r += 'a' - 'A'
   741  		}
   742  		buf.WriteRune(r)
   743  	}
   744  	return buf.String()
   745  }
   746  
   747  // foldDup reports a pair of strings from the list that are
   748  // equal according to strings.EqualFold.
   749  // It returns "", "" if there are no such strings.
   750  func foldDup(list []string) (string, string) {
   751  	clash := map[string]string{}
   752  	for _, s := range list {
   753  		fold := toFold(s)
   754  		if t := clash[fold]; t != "" {
   755  			if s > t {
   756  				s, t = t, s
   757  			}
   758  			return s, t
   759  		}
   760  		clash[fold] = s
   761  	}
   762  	return "", ""
   763  }