// To build a file only when using cgo, and only on Linux and OS X:
 //
 //     // +build linux,cgo darwin,cgo
-// 
+//
 // Such a file is usually paired with another file implementing the
 // default functionality for other systems, which in this case would
 // carry the constraint:
        "errors"
        "fmt"
        "go/ast"
+       "go/doc"
        "go/parser"
        "go/token"
+       "io"
        "io/ioutil"
        "log"
        "os"
-       "path"
+       pathpkg "path"
        "path/filepath"
        "runtime"
        "sort"
 type Context struct {
        GOARCH      string   // target architecture
        GOOS        string   // target operating system
+       GOROOT      string   // Go root
+       GOPATH      string   // Go path
        CgoEnabled  bool     // whether cgo can be used
        BuildTags   []string // additional tags to recognize in +build lines
        UseAllFiles bool     // use files regardless of +build lines, file names
-
-       // By default, ScanDir uses the operating system's
-       // file system calls to read directories and files.
-       // Callers can override those calls to provide other
-       // ways to read data by setting ReadDir and ReadFile.
-       // ScanDir does not make any assumptions about the
-       // format of the strings dir and file: they can be
-       // slash-separated, backslash-separated, even URLs.
+       Gccgo       bool     // assume use of gccgo when computing object paths
+
+       // By default, Import uses the operating system's file system calls
+       // to read directories and files.  To read from other sources,
+       // callers can set the following functions.  They all have default
+       // behaviors that use the local file system, so clients need only set
+       // the functions whose behaviors they wish to change.
+
+       // JoinPath joins the sequence of path fragments into a single path.
+       // If JoinPath is nil, Import uses filepath.Join.
+       JoinPath func(elem ...string) string
+
+       // SplitPathList splits the path list into a slice of individual paths.
+       // If SplitPathList is nil, Import uses filepath.SplitList.
+       SplitPathList func(list string) []string
+
+       // IsAbsPath reports whether path is an absolute path.
+       // If IsAbsPath is nil, Import uses filepath.IsAbs.
+       IsAbsPath func(path string) bool
+
+       // IsDir reports whether the path names a directory.
+       // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method.
+       IsDir func(path string) bool
+
+       // HasSubdir reports whether dir is a subdirectory of
+       // (perhaps multiple levels below) root.
+       // If so, HasSubdir sets rel to a slash-separated path that
+       // can be joined to root to produce a path equivalent to dir.
+       // If HasSubdir is nil, Import uses an implementation built on
+       // filepath.EvalSymlinks.
+       HasSubdir func(root, dir string) (rel string, ok bool)
 
        // ReadDir returns a slice of os.FileInfo, sorted by Name,
        // describing the content of the named directory.
-       // The dir argument is the argument to ScanDir.
-       // If ReadDir is nil, ScanDir uses io.ReadDir.
+       // If ReadDir is nil, Import uses io.ReadDir.
        ReadDir func(dir string) (fi []os.FileInfo, err error)
 
-       // ReadFile returns the content of the file named file
-       // in the directory named dir.  The dir argument is the
-       // argument to ScanDir, and the file argument is the
-       // Name field from an os.FileInfo returned by ReadDir.
-       // The returned path is the full name of the file, to be
-       // used in error messages.
-       //
-       // If ReadFile is nil, ScanDir uses filepath.Join(dir, file)
-       // as the path and ioutil.ReadFile to read the data.
-       ReadFile func(dir, file string) (path string, content []byte, err error)
+       // OpenFile opens a file (not a directory) for reading.
+       // If OpenFile is nil, Import uses os.Open.
+       OpenFile func(path string) (r io.ReadCloser, err error)
 }
 
-func (ctxt *Context) readDir(dir string) ([]os.FileInfo, error) {
+// joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join.
+func (ctxt *Context) joinPath(elem ...string) string {
+       if f := ctxt.JoinPath; f != nil {
+               return f(elem...)
+       }
+       return filepath.Join(elem...)
+}
+
+// splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList.
+func (ctxt *Context) splitPathList(s string) []string {
+       if f := ctxt.SplitPathList; f != nil {
+               return f(s)
+       }
+       return filepath.SplitList(s)
+}
+
+// isAbsPath calls ctxt.IsAbsSPath (if not nil) or else filepath.IsAbs.
+func (ctxt *Context) isAbsPath(path string) bool {
+       if f := ctxt.IsAbsPath; f != nil {
+               return f(path)
+       }
+       return filepath.IsAbs(path)
+}
+
+// isDir calls ctxt.IsDir (if not nil) or else uses os.Stat.
+func (ctxt *Context) isDir(path string) bool {
+       if f := ctxt.IsDir; f != nil {
+               return f(path)
+       }
+       fi, err := os.Stat(path)
+       return err == nil && fi.IsDir()
+}
+
+// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
+// the local file system to answer the question.
+func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
+       if f := ctxt.HasSubdir; f != nil {
+               return f(root, dir)
+       }
+
+       if p, err := filepath.EvalSymlinks(root); err == nil {
+               root = p
+       }
+       if p, err := filepath.EvalSymlinks(dir); err == nil {
+               dir = p
+       }
+       const sep = string(filepath.Separator)
+       root = filepath.Clean(root)
+       if !strings.HasSuffix(root, sep) {
+               root += sep
+       }
+       dir = filepath.Clean(dir)
+       if !strings.HasPrefix(dir, root) {
+               return "", false
+       }
+       return filepath.ToSlash(dir[len(root):]), true
+}
+
+// readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir.
+func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) {
        if f := ctxt.ReadDir; f != nil {
-               return f(dir)
+               return f(path)
+       }
+       return ioutil.ReadDir(path)
+}
+
+// openFile calls ctxt.OpenFile (if not nil) or else os.Open.
+func (ctxt *Context) openFile(path string) (io.ReadCloser, error) {
+       if fn := ctxt.OpenFile; fn != nil {
+               return fn(path)
+       }
+
+       f, err := os.Open(path)
+       if err != nil {
+               return nil, err // nil interface
        }
-       return ioutil.ReadDir(dir)
+       return f, nil
 }
 
-func (ctxt *Context) readFile(dir, file string) (string, []byte, error) {
-       if f := ctxt.ReadFile; f != nil {
-               return f(dir, file)
+// isFile determines whether path is a file by trying to open it.
+// It reuses openFile instead of adding another function to the
+// list in Context.
+func (ctxt *Context) isFile(path string) bool {
+       f, err := ctxt.openFile(path)
+       if err != nil {
+               return false
        }
-       p := filepath.Join(dir, file)
-       content, err := ioutil.ReadFile(p)
-       return p, content, err
+       f.Close()
+       return true
 }
 
-// The DefaultContext is the default Context for builds.
-// It uses the GOARCH and GOOS environment variables
-// if set, or else the compiled code's GOARCH and GOOS.
-var DefaultContext Context = defaultContext()
+// gopath returns the list of Go path directories.
+func (ctxt *Context) gopath() []string {
+       var all []string
+       for _, p := range ctxt.splitPathList(ctxt.GOPATH) {
+               if p == "" || p == ctxt.GOROOT {
+                       // Empty paths are uninteresting.
+                       // If the path is the GOROOT, ignore it.
+                       // People sometimes set GOPATH=$GOROOT, which is useless
+                       // but would cause us to find packages with import paths
+                       // like "pkg/math".
+                       // Do not get confused by this common mistake.
+                       continue
+               }
+               all = append(all, p)
+       }
+       return all
+}
+
+// SrcDirs returns a list of package source root directories.
+// It draws from the current Go root and Go path but omits directories
+// that do not exist.
+func (ctxt *Context) SrcDirs() []string {
+       var all []string
+       if ctxt.GOROOT != "" {
+               dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg")
+               if ctxt.isDir(dir) {
+                       all = append(all, dir)
+               }
+       }
+       for _, p := range ctxt.gopath() {
+               dir := ctxt.joinPath(p, "src")
+               if ctxt.isDir(dir) {
+                       all = append(all, dir)
+               }
+       }
+       return all
+}
+
+// Default is the default Context for builds.
+// It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables
+// if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
+var Default Context = defaultContext()
 
 var cgoEnabled = map[string]bool{
        "darwin/386":    true,
 
        c.GOARCH = envOr("GOARCH", runtime.GOARCH)
        c.GOOS = envOr("GOOS", runtime.GOOS)
+       c.GOROOT = runtime.GOROOT()
+       c.GOPATH = envOr("GOPATH", "")
 
-       s := os.Getenv("CGO_ENABLED")
-       switch s {
+       switch os.Getenv("CGO_ENABLED") {
        case "1":
                c.CgoEnabled = true
        case "0":
        return s
 }
 
-type DirInfo struct {
-       Package        string                      // Name of package in dir
-       PackageComment *ast.CommentGroup           // Package comments from GoFiles
-       ImportPath     string                      // Import path of package in dir
-       Imports        []string                    // All packages imported by GoFiles
-       ImportPos      map[string][]token.Position // Source code location of imports
+// An ImportMode controls the behavior of the Import method.
+type ImportMode uint
+
+const (
+       // If FindOnly is set, Import stops after locating the directory
+       // that should contain the sources for a package.  It does not
+       // read any files in the directory.
+       FindOnly ImportMode = 1 << iota
+
+       // If AllowBinary is set, Import can be satisfied by a compiled
+       // package object without corresponding sources.
+       AllowBinary
+)
+
+// A Package describes the Go package found in a directory.
+type Package struct {
+       Dir        string // directory containing package sources
+       Name       string // package name
+       Doc        string // documentation synopsis
+       ImportPath string // import path of package ("" if unknown)
+       Root       string // root of Go tree where this package lives
+       SrcRoot    string // package source root directory ("" if unknown)
+       PkgRoot    string // package install root directory ("" if unknown)
+       BinDir     string // command install directory ("" if unknown)
+       Goroot     bool   // package found in Go root
+       PkgObj     string // installed .a file
 
        // Source files
-       GoFiles  []string // .go files in dir (excluding CgoFiles, TestGoFiles, XTestGoFiles)
-       HFiles   []string // .h files in dir
-       CFiles   []string // .c files in dir
-       SFiles   []string // .s (and, when using cgo, .S files in dir)
-       CgoFiles []string // .go files that import "C"
+       GoFiles  []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+       CgoFiles []string // .go source files that import "C"
+       CFiles   []string // .c source files
+       HFiles   []string // .h source files
+       SFiles   []string // .s source files
 
        // Cgo directives
        CgoPkgConfig []string // Cgo pkg-config directives
        CgoCFLAGS    []string // Cgo CFLAGS directives
        CgoLDFLAGS   []string // Cgo LDFLAGS directives
 
+       // Dependency information
+       Imports   []string                    // imports from GoFiles, CgoFiles
+       ImportPos map[string][]token.Position // line information for Imports
+
        // Test information
-       TestGoFiles   []string // _test.go files in package
-       XTestGoFiles  []string // _test.go files outside package
-       TestImports   []string // All packages imported by (X)TestGoFiles
-       TestImportPos map[string][]token.Position
+       TestGoFiles    []string                    // _test.go files in package
+       TestImports    []string                    // imports from TestGoFiles
+       TestImportPos  map[string][]token.Position // line information for TestImports
+       XTestGoFiles   []string                    // _test.go files outside package
+       XTestImports   []string                    // imports from XTestGoFiles
+       XTestImportPos map[string][]token.Position // line information for XTestImports
 }
 
-func (d *DirInfo) IsCommand() bool {
-       // TODO(rsc): This is at least a little bogus.
-       return d.Package == "main"
+// IsCommand reports whether the package is considered a
+// command to be installed (not just a library).
+// Packages named "main" are treated as commands.
+func (p *Package) IsCommand() bool {
+       return p.Name == "main"
 }
 
-// ScanDir calls DefaultContext.ScanDir.
-func ScanDir(dir string) (info *DirInfo, err error) {
-       return DefaultContext.ScanDir(dir)
+// ImportDir is like Import but processes the Go package found in
+// the named directory.
+func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
+       return ctxt.Import(".", dir, mode)
 }
 
-// TODO(rsc): Move this comment to a more appropriate place.
-
-// ScanDir returns a structure with details about the Go package
-// found in the given directory.
+// Import returns details about the Go package named by the import path,
+// interpreting local import paths relative to the src directory.  If the path
+// is a local import path naming a package that can be imported using a
+// standard import path, the returned package will set p.ImportPath to
+// that path.
 //
-// Most .go, .c, .h, and .s files in the directory are considered part
-// of the package.  The exceptions are:
+// In the directory containing the package, .go, .c, .h, and .s files are
+// considered part of the package except for:
 //
-//     - .go files in package main (unless no other package is found)
 //     - .go files in package documentation
 //     - files starting with _ or .
 //     - files with build constraints not satisfied by the context
 //
-func (ctxt *Context) ScanDir(dir string) (info *DirInfo, err error) {
-       dirs, err := ctxt.readDir(dir)
+// If an error occurs, Import returns a non-nil error also returns a non-nil
+// *Package containing partial information.
+//
+func (ctxt *Context) Import(path string, src string, mode ImportMode) (*Package, error) {
+       p := &Package{
+               ImportPath: path,
+       }
+
+       var pkga string
+       if ctxt.Gccgo {
+               dir, elem := pathpkg.Split(p.ImportPath)
+               pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a"
+       } else {
+               pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + p.ImportPath + ".a"
+       }
+
+       binaryOnly := false
+       if IsLocalImport(path) {
+               if src == "" {
+                       return p, fmt.Errorf("import %q: import relative to unknown directory", path)
+               }
+               if !ctxt.isAbsPath(path) {
+                       p.Dir = ctxt.joinPath(src, path)
+               }
+               // Determine canonical import path, if any.
+               if ctxt.GOROOT != "" {
+                       root := ctxt.joinPath(ctxt.GOROOT, "src", "pkg")
+                       if sub, ok := ctxt.hasSubdir(root, p.Dir); ok {
+                               p.Goroot = true
+                               p.ImportPath = sub
+                               p.Root = ctxt.GOROOT
+                               goto Found
+                       }
+               }
+               all := ctxt.gopath()
+               for i, root := range all {
+                       rootsrc := ctxt.joinPath(root, "src")
+                       if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok {
+                               // We found a potential import path for dir,
+                               // but check that using it wouldn't find something
+                               // else first.
+                               if ctxt.GOROOT != "" {
+                                       if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) {
+                                               goto Found
+                                       }
+                               }
+                               for _, earlyRoot := range all[:i] {
+                                       if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
+                                               goto Found
+                                       }
+                               }
+
+                               // sub would not name some other directory instead of this one.
+                               // Record it.
+                               p.ImportPath = sub
+                               p.Root = root
+                               goto Found
+                       }
+               }
+               // It's okay that we didn't find a root containing dir.
+               // Keep going with the information we have.
+       } else {
+               if strings.HasPrefix(path, "/") {
+                       return p, fmt.Errorf("import %q: cannot import absolute path", path)
+               }
+               // Determine directory from import path.
+               if ctxt.GOROOT != "" {
+                       dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", path)
+                       isDir := ctxt.isDir(dir)
+                       binaryOnly = !isDir && mode&AllowBinary != 0 && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
+                       if isDir || binaryOnly {
+                               p.Dir = dir
+                               p.Goroot = true
+                               p.Root = ctxt.GOROOT
+                               goto Found
+                       }
+               }
+               for _, root := range ctxt.gopath() {
+                       dir := ctxt.joinPath(root, "src", path)
+                       isDir := ctxt.isDir(dir)
+                       binaryOnly = !isDir && mode&AllowBinary != 0 && ctxt.isFile(ctxt.joinPath(root, pkga))
+                       if isDir || binaryOnly {
+                               p.Dir = dir
+                               p.Root = root
+                               goto Found
+                       }
+               }
+               return p, fmt.Errorf("import %q: cannot find package", path)
+       }
+
+Found:
+       if p.Root != "" {
+               if p.Goroot {
+                       p.SrcRoot = ctxt.joinPath(p.Root, "src", "pkg")
+               } else {
+                       p.SrcRoot = ctxt.joinPath(p.Root, "src")
+               }
+               p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
+               p.BinDir = ctxt.joinPath(p.Root, "bin")
+               p.PkgObj = ctxt.joinPath(p.Root, pkga)
+       }
+
+       if mode&FindOnly != 0 {
+               return p, nil
+       }
+       if binaryOnly && (mode&AllowBinary) != 0 {
+               return p, nil
+       }
+
+       dirs, err := ctxt.readDir(p.Dir)
        if err != nil {
-               return nil, err
+               return p, err
        }
 
        var Sfiles []string // files with ".S" (capital S)
-       var di DirInfo
        var firstFile string
        imported := make(map[string][]token.Position)
        testImported := make(map[string][]token.Position)
+       xTestImported := make(map[string][]token.Position)
        fset := token.NewFileSet()
        for _, d := range dirs {
                if d.IsDir() {
                        continue
                }
 
-               ext := path.Ext(name)
+               i := strings.LastIndex(name, ".")
+               if i < 0 {
+                       i = len(name)
+               }
+               ext := name[i:]
                switch ext {
                case ".go", ".c", ".s", ".h", ".S":
                        // tentatively okay
                        continue
                }
 
-               filename, data, err := ctxt.readFile(dir, name)
+               filename := ctxt.joinPath(p.Dir, name)
+               f, err := ctxt.openFile(filename)
                if err != nil {
-                       return nil, err
+                       return p, err
+               }
+               data, err := ioutil.ReadAll(f)
+               f.Close()
+               if err != nil {
+                       return p, fmt.Errorf("read %s: %v", filename, err)
                }
 
                // Look for +build comments to accept or reject the file.
                // Going to save the file.  For non-Go files, can stop here.
                switch ext {
                case ".c":
-                       di.CFiles = append(di.CFiles, name)
+                       p.CFiles = append(p.CFiles, name)
                        continue
                case ".h":
-                       di.HFiles = append(di.HFiles, name)
+                       p.HFiles = append(p.HFiles, name)
                        continue
                case ".s":
-                       di.SFiles = append(di.SFiles, name)
+                       p.SFiles = append(p.SFiles, name)
                        continue
                case ".S":
                        Sfiles = append(Sfiles, name)
 
                pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
                if err != nil {
-                       return nil, err
+                       return p, err
                }
 
                pkg := string(pf.Name.Name)
                }
 
                isTest := strings.HasSuffix(name, "_test.go")
+               isXTest := false
                if isTest && strings.HasSuffix(pkg, "_test") {
+                       isXTest = true
                        pkg = pkg[:len(pkg)-len("_test")]
                }
 
-               if di.Package == "" {
-                       di.Package = pkg
+               if p.Name == "" {
+                       p.Name = pkg
                        firstFile = name
-               } else if pkg != di.Package {
-                       return nil, fmt.Errorf("%s: found packages %s (%s) and %s (%s)", dir, di.Package, firstFile, pkg, name)
-               }
-               if pf.Doc != nil {
-                       if di.PackageComment != nil {
-                               di.PackageComment.List = append(di.PackageComment.List, pf.Doc.List...)
-                       } else {
-                               di.PackageComment = pf.Doc
-                       }
+               } else if pkg != p.Name {
+                       return p, fmt.Errorf("found packages %s (%s) and %s (%s) in %s", p.Name, firstFile, pkg, name, p.Dir)
+               }
+               if pf.Doc != nil && p.Doc == "" {
+                       p.Doc = doc.Synopsis(pf.Doc.Text())
                }
 
                // Record imports and information about cgo.
                                if err != nil {
                                        log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
                                }
-                               if isTest {
+                               if isXTest {
+                                       xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos()))
+                               } else if isTest {
                                        testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
                                } else {
                                        imported[path] = append(imported[path], fset.Position(spec.Pos()))
                                }
                                if path == "C" {
                                        if isTest {
-                                               return nil, fmt.Errorf("%s: use of cgo in test not supported", filename)
+                                               return p, fmt.Errorf("use of cgo in test %s not supported", filename)
                                        }
                                        cg := spec.Doc
                                        if cg == nil && len(d.Specs) == 1 {
                                                cg = d.Doc
                                        }
                                        if cg != nil {
-                                               if err := ctxt.saveCgo(filename, &di, cg); err != nil {
-                                                       return nil, err
+                                               if err := ctxt.saveCgo(filename, p, cg); err != nil {
+                                                       return p, err
                                                }
                                        }
                                        isCgo = true
                }
                if isCgo {
                        if ctxt.CgoEnabled {
-                               di.CgoFiles = append(di.CgoFiles, name)
+                               p.CgoFiles = append(p.CgoFiles, name)
                        }
+               } else if isXTest {
+                       p.XTestGoFiles = append(p.XTestGoFiles, name)
                } else if isTest {
-                       if pkg == string(pf.Name.Name) {
-                               di.TestGoFiles = append(di.TestGoFiles, name)
-                       } else {
-                               di.XTestGoFiles = append(di.XTestGoFiles, name)
-                       }
+                       p.TestGoFiles = append(p.TestGoFiles, name)
                } else {
-                       di.GoFiles = append(di.GoFiles, name)
+                       p.GoFiles = append(p.GoFiles, name)
                }
        }
-       if di.Package == "" {
-               return nil, fmt.Errorf("%s: no Go source files", dir)
-       }
-       di.Imports = make([]string, len(imported))
-       di.ImportPos = imported
-       i := 0
-       for p := range imported {
-               di.Imports[i] = p
-               i++
-       }
-       di.TestImports = make([]string, len(testImported))
-       di.TestImportPos = testImported
-       i = 0
-       for p := range testImported {
-               di.TestImports[i] = p
-               i++
+       if p.Name == "" {
+               return p, fmt.Errorf("no Go source files in %s", p.Dir)
        }
 
+       p.Imports, p.ImportPos = cleanImports(imported)
+       p.TestImports, p.TestImportPos = cleanImports(testImported)
+       p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
+
        // add the .S files only if we are using cgo
        // (which means gcc will compile them).
        // The standard assemblers expect .s files.
-       if len(di.CgoFiles) > 0 {
-               di.SFiles = append(di.SFiles, Sfiles...)
-               sort.Strings(di.SFiles)
+       if len(p.CgoFiles) > 0 {
+               p.SFiles = append(p.SFiles, Sfiles...)
+               sort.Strings(p.SFiles)
+       }
+
+       return p, nil
+}
+
+func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
+       all := make([]string, 0, len(m))
+       for path := range m {
+               all = append(all, path)
        }
+       sort.Strings(all)
+       return all, m
+}
 
-       // File name lists are sorted because ReadDir sorts.
-       sort.Strings(di.Imports)
-       sort.Strings(di.TestImports)
-       return &di, nil
+// Import is shorthand for Default.Import.
+func Import(path, src string, mode ImportMode) (*Package, error) {
+       return Default.Import(path, src, mode)
+}
+
+// ImportDir is shorthand for Default.ImportDir.
+func ImportDir(dir string, mode ImportMode) (*Package, error) {
+       return Default.ImportDir(dir, mode)
 }
 
 var slashslash = []byte("//")
 //
 // TODO(rsc): This duplicates code in cgo.
 // Once the dust settles, remove this code from cgo.
-func (ctxt *Context) saveCgo(filename string, di *DirInfo, cg *ast.CommentGroup) error {
+func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
        text := cg.Text()
        for _, line := range strings.Split(text, "\n") {
                orig := line
 // ToolDir is the directory containing build tools.
 var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
 
-// isLocalPath returns whether the given path is local (/foo ./foo ../foo . ..)
-// Windows paths that starts with drive letter (c:\foo c:foo) are considered local.
-func isLocalPath(s string) bool {
-       const sep = string(filepath.Separator)
-       return s == "." || s == ".." ||
-               filepath.HasPrefix(s, sep) ||
-               filepath.HasPrefix(s, "."+sep) || filepath.HasPrefix(s, ".."+sep) ||
-               filepath.VolumeName(s) != ""
+// IsLocalImport reports whether the import path is
+// a local import path, like ".", "..", "./foo", or "../foo".
+func IsLocalImport(path string) bool {
+       return path == "." || path == ".." ||
+               strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
 }
 
 // ArchChar returns the architecture character for the given goarch.