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