github.com/euank/go@v0.0.0-20160829210321-495514729181/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 = `{{range .}}{{if .Short}}{{.Short | capitalize}} 218 219 {{end}}{{if .Runnable}}Usage: 220 221 go {{.UsageLine}} 222 223 {{end}}{{.Long | trim}} 224 225 226 {{end}}` 227 228 // commentWriter writes a Go comment to the underlying io.Writer, 229 // using line comment form (//). 230 type commentWriter struct { 231 W io.Writer 232 wroteSlashes bool // Wrote "//" at the beginning of the current line. 233 } 234 235 func (c *commentWriter) Write(p []byte) (int, error) { 236 var n int 237 for i, b := range p { 238 if !c.wroteSlashes { 239 s := "//" 240 if b != '\n' { 241 s = "// " 242 } 243 if _, err := io.WriteString(c.W, s); err != nil { 244 return n, err 245 } 246 c.wroteSlashes = true 247 } 248 n0, err := c.W.Write(p[i : i+1]) 249 n += n0 250 if err != nil { 251 return n, err 252 } 253 if b == '\n' { 254 c.wroteSlashes = false 255 } 256 } 257 return len(p), nil 258 } 259 260 // An errWriter wraps a writer, recording whether a write error occurred. 261 type errWriter struct { 262 w io.Writer 263 err error 264 } 265 266 func (w *errWriter) Write(b []byte) (int, error) { 267 n, err := w.w.Write(b) 268 if err != nil { 269 w.err = err 270 } 271 return n, err 272 } 273 274 // tmpl executes the given template text on data, writing the result to w. 275 func tmpl(w io.Writer, text string, data interface{}) { 276 t := template.New("top") 277 t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) 278 template.Must(t.Parse(text)) 279 ew := &errWriter{w: w} 280 err := t.Execute(ew, data) 281 if ew.err != nil { 282 // I/O error writing. Ignore write on closed pipe. 283 if strings.Contains(ew.err.Error(), "pipe") { 284 os.Exit(1) 285 } 286 fatalf("writing output: %v", ew.err) 287 } 288 if err != nil { 289 panic(err) 290 } 291 } 292 293 func capitalize(s string) string { 294 if s == "" { 295 return s 296 } 297 r, n := utf8.DecodeRuneInString(s) 298 return string(unicode.ToTitle(r)) + s[n:] 299 } 300 301 func printUsage(w io.Writer) { 302 bw := bufio.NewWriter(w) 303 tmpl(bw, usageTemplate, commands) 304 bw.Flush() 305 } 306 307 func usage() { 308 // special case "go test -h" 309 if len(os.Args) > 1 && os.Args[1] == "test" { 310 os.Stderr.WriteString(testUsage + "\n\n" + 311 strings.TrimSpace(testFlag1) + "\n\n\t" + 312 strings.TrimSpace(testFlag2) + "\n") 313 os.Exit(2) 314 } 315 printUsage(os.Stderr) 316 os.Exit(2) 317 } 318 319 // help implements the 'help' command. 320 func help(args []string) { 321 if len(args) == 0 { 322 printUsage(os.Stdout) 323 // not exit 2: succeeded at 'go help'. 324 return 325 } 326 if len(args) != 1 { 327 fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n") 328 os.Exit(2) // failed at 'go help' 329 } 330 331 arg := args[0] 332 333 // 'go help documentation' generates doc.go. 334 if arg == "documentation" { 335 fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.") 336 fmt.Println("// Use of this source code is governed by a BSD-style") 337 fmt.Println("// license that can be found in the LICENSE file.") 338 fmt.Println() 339 fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.") 340 fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.") 341 fmt.Println() 342 buf := new(bytes.Buffer) 343 printUsage(buf) 344 usage := &Command{Long: buf.String()} 345 tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*Command{usage}, commands...)) 346 fmt.Println("package main") 347 return 348 } 349 350 for _, cmd := range commands { 351 if cmd.Name() == arg { 352 tmpl(os.Stdout, helpTemplate, cmd) 353 // not exit 2: succeeded at 'go help cmd'. 354 return 355 } 356 } 357 358 fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg) 359 os.Exit(2) // failed at 'go help cmd' 360 } 361 362 // importPathsNoDotExpansion returns the import paths to use for the given 363 // command line, but it does no ... expansion. 364 func importPathsNoDotExpansion(args []string) []string { 365 if len(args) == 0 { 366 return []string{"."} 367 } 368 var out []string 369 for _, a := range args { 370 // Arguments are supposed to be import paths, but 371 // as a courtesy to Windows developers, rewrite \ to / 372 // in command-line arguments. Handles .\... and so on. 373 if filepath.Separator == '\\' { 374 a = strings.Replace(a, `\`, `/`, -1) 375 } 376 377 // Put argument in canonical form, but preserve leading ./. 378 if strings.HasPrefix(a, "./") { 379 a = "./" + path.Clean(a) 380 if a == "./." { 381 a = "." 382 } 383 } else { 384 a = path.Clean(a) 385 } 386 if isMetaPackage(a) { 387 out = append(out, allPackages(a)...) 388 continue 389 } 390 out = append(out, a) 391 } 392 return out 393 } 394 395 // importPaths returns the import paths to use for the given command line. 396 func importPaths(args []string) []string { 397 args = importPathsNoDotExpansion(args) 398 var out []string 399 for _, a := range args { 400 if strings.Contains(a, "...") { 401 if build.IsLocalImport(a) { 402 out = append(out, allPackagesInFS(a)...) 403 } else { 404 out = append(out, allPackages(a)...) 405 } 406 continue 407 } 408 out = append(out, a) 409 } 410 return out 411 } 412 413 var atexitFuncs []func() 414 415 func atexit(f func()) { 416 atexitFuncs = append(atexitFuncs, f) 417 } 418 419 func exit() { 420 for _, f := range atexitFuncs { 421 f() 422 } 423 os.Exit(exitStatus) 424 } 425 426 func fatalf(format string, args ...interface{}) { 427 errorf(format, args...) 428 exit() 429 } 430 431 func errorf(format string, args ...interface{}) { 432 log.Printf(format, args...) 433 setExitStatus(1) 434 } 435 436 func exitIfErrors() { 437 if exitStatus != 0 { 438 exit() 439 } 440 } 441 442 func run(cmdargs ...interface{}) { 443 cmdline := stringList(cmdargs...) 444 if buildN || buildX { 445 fmt.Printf("%s\n", strings.Join(cmdline, " ")) 446 if buildN { 447 return 448 } 449 } 450 451 cmd := exec.Command(cmdline[0], cmdline[1:]...) 452 cmd.Stdout = os.Stdout 453 cmd.Stderr = os.Stderr 454 if err := cmd.Run(); err != nil { 455 errorf("%v", err) 456 } 457 } 458 459 // envForDir returns a copy of the environment 460 // suitable for running in the given directory. 461 // The environment is the current process's environment 462 // but with an updated $PWD, so that an os.Getwd in the 463 // child will be faster. 464 func envForDir(dir string, base []string) []string { 465 // Internally we only use rooted paths, so dir is rooted. 466 // Even if dir is not rooted, no harm done. 467 return mergeEnvLists([]string{"PWD=" + dir}, base) 468 } 469 470 // mergeEnvLists merges the two environment lists such that 471 // variables with the same name in "in" replace those in "out". 472 // This always returns a newly allocated slice. 473 func mergeEnvLists(in, out []string) []string { 474 out = append([]string(nil), out...) 475 NextVar: 476 for _, inkv := range in { 477 k := strings.SplitAfterN(inkv, "=", 2)[0] 478 for i, outkv := range out { 479 if strings.HasPrefix(outkv, k) { 480 out[i] = inkv 481 continue NextVar 482 } 483 } 484 out = append(out, inkv) 485 } 486 return out 487 } 488 489 // matchPattern(pattern)(name) reports whether 490 // name matches pattern. Pattern is a limited glob 491 // pattern in which '...' means 'any string' and there 492 // is no other special syntax. 493 func matchPattern(pattern string) func(name string) bool { 494 re := regexp.QuoteMeta(pattern) 495 re = strings.Replace(re, `\.\.\.`, `.*`, -1) 496 // Special case: foo/... matches foo too. 497 if strings.HasSuffix(re, `/.*`) { 498 re = re[:len(re)-len(`/.*`)] + `(/.*)?` 499 } 500 reg := regexp.MustCompile(`^` + re + `$`) 501 return func(name string) bool { 502 return reg.MatchString(name) 503 } 504 } 505 506 // hasPathPrefix reports whether the path s begins with the 507 // elements in prefix. 508 func hasPathPrefix(s, prefix string) bool { 509 switch { 510 default: 511 return false 512 case len(s) == len(prefix): 513 return s == prefix 514 case len(s) > len(prefix): 515 if prefix != "" && prefix[len(prefix)-1] == '/' { 516 return strings.HasPrefix(s, prefix) 517 } 518 return s[len(prefix)] == '/' && s[:len(prefix)] == prefix 519 } 520 } 521 522 // hasFilePathPrefix reports whether the filesystem path s begins with the 523 // elements in prefix. 524 func hasFilePathPrefix(s, prefix string) bool { 525 sv := strings.ToUpper(filepath.VolumeName(s)) 526 pv := strings.ToUpper(filepath.VolumeName(prefix)) 527 s = s[len(sv):] 528 prefix = prefix[len(pv):] 529 switch { 530 default: 531 return false 532 case sv != pv: 533 return false 534 case len(s) == len(prefix): 535 return s == prefix 536 case len(s) > len(prefix): 537 if prefix != "" && prefix[len(prefix)-1] == filepath.Separator { 538 return strings.HasPrefix(s, prefix) 539 } 540 return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix 541 } 542 } 543 544 // expandPath returns the symlink-expanded form of path. 545 func expandPath(p string) string { 546 x, err := filepath.EvalSymlinks(p) 547 if err == nil { 548 return x 549 } 550 return p 551 } 552 553 // treeCanMatchPattern(pattern)(name) reports whether 554 // name or children of name can possibly match pattern. 555 // Pattern is the same limited glob accepted by matchPattern. 556 func treeCanMatchPattern(pattern string) func(name string) bool { 557 wildCard := false 558 if i := strings.Index(pattern, "..."); i >= 0 { 559 wildCard = true 560 pattern = pattern[:i] 561 } 562 return func(name string) bool { 563 return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || 564 wildCard && strings.HasPrefix(name, pattern) 565 } 566 } 567 568 // allPackages returns all the packages that can be found 569 // under the $GOPATH directories and $GOROOT matching pattern. 570 // The pattern is either "all" (all packages), "std" (standard packages), 571 // "cmd" (standard commands), or a path including "...". 572 func allPackages(pattern string) []string { 573 pkgs := matchPackages(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 matchPackages(pattern string) []string { 581 match := func(string) bool { return true } 582 treeCanMatch := func(string) bool { return true } 583 if !isMetaPackage(pattern) { 584 match = matchPattern(pattern) 585 treeCanMatch = treeCanMatchPattern(pattern) 586 } 587 588 have := map[string]bool{ 589 "builtin": true, // ignore pseudo-package that exists only for documentation 590 } 591 if !buildContext.CgoEnabled { 592 have["runtime/cgo"] = true // ignore during walk 593 } 594 var pkgs []string 595 596 for _, src := range buildContext.SrcDirs() { 597 if (pattern == "std" || pattern == "cmd") && src != gorootSrc { 598 continue 599 } 600 src = filepath.Clean(src) + string(filepath.Separator) 601 root := src 602 if pattern == "cmd" { 603 root += "cmd" + string(filepath.Separator) 604 } 605 filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { 606 if err != nil || !fi.IsDir() || path == src { 607 return nil 608 } 609 610 // Avoid .foo, _foo, and testdata directory trees. 611 _, elem := filepath.Split(path) 612 if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { 613 return filepath.SkipDir 614 } 615 616 name := filepath.ToSlash(path[len(src):]) 617 if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { 618 // The name "std" is only the standard library. 619 // If the name is cmd, it's the root of the command tree. 620 return filepath.SkipDir 621 } 622 if !treeCanMatch(name) { 623 return filepath.SkipDir 624 } 625 if have[name] { 626 return nil 627 } 628 have[name] = true 629 if !match(name) { 630 return nil 631 } 632 _, err = buildContext.ImportDir(path, 0) 633 if err != nil { 634 if _, noGo := err.(*build.NoGoError); noGo { 635 return nil 636 } 637 } 638 pkgs = append(pkgs, name) 639 return nil 640 }) 641 } 642 return pkgs 643 } 644 645 // allPackagesInFS is like allPackages but is passed a pattern 646 // beginning ./ or ../, meaning it should scan the tree rooted 647 // at the given directory. There are ... in the pattern too. 648 func allPackagesInFS(pattern string) []string { 649 pkgs := matchPackagesInFS(pattern) 650 if len(pkgs) == 0 { 651 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 652 } 653 return pkgs 654 } 655 656 func matchPackagesInFS(pattern string) []string { 657 // Find directory to begin the scan. 658 // Could be smarter but this one optimization 659 // is enough for now, since ... is usually at the 660 // end of a path. 661 i := strings.Index(pattern, "...") 662 dir, _ := path.Split(pattern[:i]) 663 664 // pattern begins with ./ or ../. 665 // path.Clean will discard the ./ but not the ../. 666 // We need to preserve the ./ for pattern matching 667 // and in the returned import paths. 668 prefix := "" 669 if strings.HasPrefix(pattern, "./") { 670 prefix = "./" 671 } 672 match := matchPattern(pattern) 673 674 var pkgs []string 675 filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 676 if err != nil || !fi.IsDir() { 677 return nil 678 } 679 if path == dir { 680 // filepath.Walk starts at dir and recurses. For the recursive case, 681 // the path is the result of filepath.Join, which calls filepath.Clean. 682 // The initial case is not Cleaned, though, so we do this explicitly. 683 // 684 // This converts a path like "./io/" to "io". Without this step, running 685 // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io 686 // package, because prepending the prefix "./" to the unclean path would 687 // result in "././io", and match("././io") returns false. 688 path = filepath.Clean(path) 689 } 690 691 // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". 692 _, elem := filepath.Split(path) 693 dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." 694 if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { 695 return filepath.SkipDir 696 } 697 698 name := prefix + filepath.ToSlash(path) 699 if !match(name) { 700 return nil 701 } 702 703 // We keep the directory if we can import it, or if we can't import it 704 // due to invalid Go source files. This means that directories containing 705 // parse errors will be built (and fail) instead of being silently skipped 706 // as not matching the pattern. Go 1.5 and earlier skipped, but that 707 // behavior means people miss serious mistakes. 708 // See golang.org/issue/11407. 709 if p, err := buildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) { 710 if _, noGo := err.(*build.NoGoError); !noGo { 711 log.Print(err) 712 } 713 return nil 714 } 715 pkgs = append(pkgs, name) 716 return nil 717 }) 718 return pkgs 719 } 720 721 // stringList's arguments should be a sequence of string or []string values. 722 // stringList flattens them into a single []string. 723 func stringList(args ...interface{}) []string { 724 var x []string 725 for _, arg := range args { 726 switch arg := arg.(type) { 727 case []string: 728 x = append(x, arg...) 729 case string: 730 x = append(x, arg) 731 default: 732 panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg)) 733 } 734 } 735 return x 736 } 737 738 // toFold returns a string with the property that 739 // strings.EqualFold(s, t) iff toFold(s) == toFold(t) 740 // This lets us test a large set of strings for fold-equivalent 741 // duplicates without making a quadratic number of calls 742 // to EqualFold. Note that strings.ToUpper and strings.ToLower 743 // have the desired property in some corner cases. 744 func toFold(s string) string { 745 // Fast path: all ASCII, no upper case. 746 // Most paths look like this already. 747 for i := 0; i < len(s); i++ { 748 c := s[i] 749 if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { 750 goto Slow 751 } 752 } 753 return s 754 755 Slow: 756 var buf bytes.Buffer 757 for _, r := range s { 758 // SimpleFold(x) cycles to the next equivalent rune > x 759 // or wraps around to smaller values. Iterate until it wraps, 760 // and we've found the minimum value. 761 for { 762 r0 := r 763 r = unicode.SimpleFold(r0) 764 if r <= r0 { 765 break 766 } 767 } 768 // Exception to allow fast path above: A-Z => a-z 769 if 'A' <= r && r <= 'Z' { 770 r += 'a' - 'A' 771 } 772 buf.WriteRune(r) 773 } 774 return buf.String() 775 } 776 777 // foldDup reports a pair of strings from the list that are 778 // equal according to strings.EqualFold. 779 // It returns "", "" if there are no such strings. 780 func foldDup(list []string) (string, string) { 781 clash := map[string]string{} 782 for _, s := range list { 783 fold := toFold(s) 784 if t := clash[fold]; t != "" { 785 if s > t { 786 s, t = t, s 787 } 788 return s, t 789 } 790 clash[fold] = s 791 } 792 return "", "" 793 }