cmd/guru: add support for loading modified files

The -modified flag causes guru to read a simple archive file from stdin.
This archive specifies alternative contents for one or more file names.
The build.Context checks this table before delegating to the usual
behavior.

This will not work for files that import "C" since cgo accesses the
file system directly.

Added end-to-end test via Emacs.

Simplify findQueryPos (now: fileOffsetToPos)

Credit: Daniel Morsing, for the prototype of this feature.

Change-Id: I5ae818ed5e8bb81001781893dded2d085e9cf8d6
Reviewed-on: https://go-review.googlesource.com/19498
Reviewed-by: Daniel Morsing <daniel.morsing@gmail.com>
diff --git a/cmd/guru/pos.go b/cmd/guru/pos.go
index d9235b4..4566c06 100644
--- a/cmd/guru/pos.go
+++ b/cmd/guru/pos.go
@@ -66,26 +66,11 @@
 	return
 }
 
-// findQueryPos searches fset for filename and translates the
-// specified file-relative byte offsets into token.Pos form.  It
-// returns an error if the file was not found or the offsets were out
-// of bounds.
+// fileOffsetToPos translates the specified file-relative byte offsets
+// into token.Pos form.  It returns an error if the file was not found
+// or the offsets were out of bounds.
 //
-func findQueryPos(fset *token.FileSet, filename string, startOffset, endOffset int) (start, end token.Pos, err error) {
-	var file *token.File
-	fset.Iterate(func(f *token.File) bool {
-		if sameFile(filename, f.Name()) {
-			// (f.Name() is absolute)
-			file = f
-			return false // done
-		}
-		return true // continue
-	})
-	if file == nil {
-		err = fmt.Errorf("couldn't find file containing position")
-		return
-	}
-
+func fileOffsetToPos(file *token.File, startOffset, endOffset int) (start, end token.Pos, err error) {
 	// Range check [start..end], inclusive of both end-points.
 
 	if 0 <= startOffset && startOffset <= file.Size() {
@@ -119,8 +104,8 @@
 	return false
 }
 
-// fastQueryPos parses the position string and returns a QueryPos.
-// It parses only a single file, and does not run the type checker.
+// fastQueryPos parses the position string and returns a queryPos.
+// It parses only a single file and does not run the type checker.
 func fastQueryPos(pos string) (*queryPos, error) {
 	filename, startOffset, endOffset, err := parsePos(pos)
 	if err != nil {
@@ -133,7 +118,7 @@
 		return nil, err
 	}
 
-	start, end, err := findQueryPos(fset, filename, startOffset, endOffset)
+	start, end, err := fileOffsetToPos(fset.File(f.Pos()), startOffset, endOffset)
 	if err != nil {
 		return nil, err
 	}