func shortPath(path string) string {
	if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
		return rel
	}
	return path
}
shortPath returns an absolute or relative name for path, whatever is shorter.
Plundered from $GOROOT/src/cmd/go/build.go.
package main
import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/format"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)
var (
	dryrun     = flag.Bool("n", false, "dry run: show changes, but don't apply them")
	badDomains = flag.String("baddomains", "code.google.com",
		"a comma-separated list of domains from which packages should not be imported")
	replaceFlag = flag.String("replace", "",
		"a comma-separated list of noncanonical=canonical pairs of package paths.  If both items in a pair end with '...', they are treated as path prefixes.")
)
var (
	stderr    io.Writer = os.Stderr
	writeFile           = ioutil.WriteFile
)
const usage = `fiximports: rewrite import paths to use canonical package names.
Usage: fiximports [-n] package...
The package... arguments specify a list of packages
in the style of the go tool; see "go help packages".
Hint: use "all" or "..." to match the entire workspace.
For details, see http: 
Flags:
  -n:	       dry run: show changes, but don't apply them
  -baddomains  a comma-separated list of domains from which packages
               should not be imported
`
func main() {
	flag.Parse()
	if len(flag.Args()) == 0 {
		fmt.Fprintf(stderr, usage)
		os.Exit(1)
	}
	if !fiximports(flag.Args()...) {
		os.Exit(1)
	}
}
type canonicalName struct{ path, name string }
// fiximports fixes imports in the specified packages.
// Invariant: a false result implies an error was already printed.
func fiximports(packages ...string) bool {
	// importedBy is the transpose of the package import graph.
	importedBy := make(map[string]map[*build.Package]bool)
	// addEdge adds an edge to the import graph.
	addEdge := func(from *build.Package, to string) {
		if to == "C" || to == "unsafe" {
			return // fake
		}
		pkgs := importedBy[to]
		if pkgs == nil {
			pkgs = make(map[*build.Package]bool)
			importedBy[to] = pkgs
		}
		pkgs[from] = true
	}
	// List metadata for all packages in the workspace.
	pkgs, err := list("...")
	if err != nil {
		fmt.Fprintf(stderr, "importfix: %v\n", err)
		return false
	}
	// packageName maps each package's path to its name.
	packageName := make(map[string]string)
	for _, p := range pkgs {
		packageName[p.ImportPath] = p.Package.Name
	}
	canonical := make(map[string]canonicalName)
	domains := strings.Split(*badDomains, ",")
	type replaceItem struct {
		old, new    string
		matchPrefix bool
	}
	var replace []replaceItem
	for _, pair := range strings.Split(*replaceFlag, ",") {
		if pair == "" {
			continue
		}
		words := strings.Split(pair, "=")
		if len(words) != 2 {
			fmt.Fprintf(stderr, "importfix: -replace: %q is not of the form \"canonical=noncanonical\".\n", pair)
			return false
		}
		replace = append(replace, replaceItem{
			old: strings.TrimSuffix(words[0], "..."),
			new: strings.TrimSuffix(words[1], "..."),
			matchPrefix: strings.HasSuffix(words[0], "...") &&
				strings.HasSuffix(words[1], "..."),
		})
	}
	for _, p := range pkgs {
		if p.Error != nil {
			msg := p.Error.Err
			if strings.Contains(msg, "code in directory") &&
				strings.Contains(msg, "expects import") {
			} else {
				fmt.Fprintln(stderr, msg)
			}
		}
		for _, imp := range p.Imports {
			addEdge(&p.Package, imp)
		}
		for _, imp := range p.TestImports {
			addEdge(&p.Package, imp)
		}
		for _, imp := range p.XTestImports {
			addEdge(&p.Package, imp)
		}
		if p.ImportComment != "" {
			if p.ImportComment != p.ImportPath {
				canonical[p.ImportPath] = canonicalName{
					path: p.Package.ImportComment,
					name: p.Package.Name,
				}
			}
		} else {
			var newPath string
			for _, item := range replace {
				if item.matchPrefix {
					if strings.HasPrefix(p.ImportPath, item.old) {
						newPath = item.new + p.ImportPath[len(item.old):]
						break
					}
				} else if p.ImportPath == item.old {
					newPath = item.new
					break
				}
			}
			if newPath != "" {
				newName := packageName[newPath]
				if newName == "" {
					newName = filepath.Base(newPath)  
				}
				canonical[p.ImportPath] = canonicalName{
					path: newPath,
					name: newName,
				}
				continue
			}
			for _, domain := range domains {
				slash := strings.Index(p.ImportPath, "/")
				if slash < 0 {
					continue  
				}
				if p.ImportPath[:slash] == domain {
					canonical[p.ImportPath] = canonicalName{}
				}
				break
			}
		}
	}
	clients := make(map[*build.Package]bool)
	for path := range canonical {
		for client := range importedBy[path] {
			clients[client] = true
		}
	}
	if len(packages) == 1 && (packages[0] == "all" || packages[0] == "...") {
	} else {
		pkgs, err := list(packages...)
		if err != nil {
			fmt.Fprintf(stderr, "importfix: %v\n", err)
			return false
		}
		seen := make(map[string]bool)
		for _, p := range pkgs {
			seen[p.ImportPath] = true
		}
		for client := range clients {
			if !seen[client.ImportPath] {
				delete(clients, client)
			}
		}
	}
	ok := true
	for client := range clients {
		if !rewritePackage(client, canonical) {
			ok = false
			seen := make(map[string]bool)
			var direct, indirect []string
			for p := range importedBy[client.ImportPath] {
				direct = append(direct, p.ImportPath)
				seen[p.ImportPath] = true
			}
			var visit func(path string)
			visit = func(path string) {
				for q := range importedBy[path] {
					qpath := q.ImportPath
					if !seen[qpath] {
						seen[qpath] = true
						indirect = append(indirect, qpath)
						visit(qpath)
					}
				}
			}
			if direct != nil {
				fmt.Fprintf(stderr, "\timported directly by:\n")
				sort.Strings(direct)
				for _, path := range direct {
					fmt.Fprintf(stderr, "\t\t%s\n", path)
					visit(path)
				}
				if indirect != nil {
					fmt.Fprintf(stderr, "\timported indirectly by:\n")
					sort.Strings(indirect)
					for _, path := range indirect {
						fmt.Fprintf(stderr, "\t\t%s\n", path)
					}
				}
			}
		}
	}
	return ok
}
func rewritePackage(client *build.Package, canonical map[string]canonicalName) bool {
	ok := true
	used := make(map[string]bool)
	var filenames []string
	filenames = append(filenames, client.GoFiles...)
	filenames = append(filenames, client.TestGoFiles...)
	filenames = append(filenames, client.XTestGoFiles...)
	var first bool
	for _, filename := range filenames {
		if !first {
			first = true
			fmt.Fprintf(stderr, "%s\n", client.ImportPath)
		}
		err := rewriteFile(filepath.Join(client.Dir, filename), canonical, used)
		if err != nil {
			fmt.Fprintf(stderr, "\tERROR: %v\n", err)
			ok = false
		}
	}
	var keys []string
	for key := range used {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		if p := canonical[key]; p.path != "" {
			fmt.Fprintf(stderr, "\tfixed: %s -> %s\n", key, p.path)
		} else {
			fmt.Fprintf(stderr, "\tERROR: %s has no import comment\n", key)
			ok = false
		}
	}
	return ok
}
func rewriteFile(filename string, canonical map[string]canonicalName, used map[string]bool) error {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
	if err != nil {
		return err
	}
	var changed bool
	for _, imp := range f.Imports {
		impPath, err := strconv.Unquote(imp.Path.Value)
		if err != nil {
			log.Printf("%s: bad import spec %q: %v",
				fset.Position(imp.Pos()), imp.Path.Value, err)
			continue
		}
		canon, ok := canonical[impPath]
		if !ok {
			continue  
		}
		used[impPath] = true
		if canon.path == "" {
			fmt.Fprintf(stderr, "\t%s:%d: import %q\n",
				shortPath(filename),
				fset.Position(imp.Pos()).Line, impPath)
			continue
		}
		changed = true
		imp.Path.Value = strconv.Quote(canon.path)
		newBase := path.Base(canon.path)
		if imp.Name == nil && newBase != canon.name {
			imp.Name = &ast.Ident{Name: canon.name}
		}
	}
	if changed && !*dryrun {
		var buf bytes.Buffer
		if err := format.Node(&buf, fset, f); err != nil {
			return fmt.Errorf("%s: couldn't format file: %v", filename, err)
		}
		return writeFile(filename, buf.Bytes(), 0644)
	}
	return nil
}
type listPackage struct {
	build.Package
	Error *packageError  
}
type packageError struct {
	ImportStack []string  
	Pos         string    
	Err         string    
}
func list(args ...string) ([]*listPackage, error) {
	cmd := exec.Command("go", append([]string{"list", "-e", "-json"}, args...)...)
	cmd.Stdout = new(bytes.Buffer)
	cmd.Stderr = stderr
	if err := cmd.Run(); err != nil {
		return nil, err
	}
	dec := json.NewDecoder(cmd.Stdout.(io.Reader))
	var pkgs []*listPackage
	for {
		var p listPackage
		if err := dec.Decode(&p); err == io.EOF {
			break
		} else if err != nil {
			return nil, err
		}
		pkgs = append(pkgs, &p)
	}
	return pkgs, nil
}
var cwd string
func init() {
	var err error
	cwd, err = os.Getwd()
	if err != nil {
		log.Fatalf("os.Getwd: %v", err)
	}
}