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