diff options
Diffstat (limited to 'src/pkg/go/types/exportdata.go')
-rw-r--r-- | src/pkg/go/types/exportdata.go | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/src/pkg/go/types/exportdata.go b/src/pkg/go/types/exportdata.go new file mode 100644 index 000000000..cb08ffe18 --- /dev/null +++ b/src/pkg/go/types/exportdata.go @@ -0,0 +1,135 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements ExportData. + +package types + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + + +func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) { + // See $GOROOT/include/ar.h. + hdr := make([]byte, 64+12+6+6+8+10+2) + _, err = io.ReadFull(buf, hdr) + if err != nil { + return + } + if trace { + fmt.Printf("header: %s", hdr) + } + s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10])) + size, err = strconv.Atoi(s) + if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { + err = os.ErrorString("invalid archive header") + return + } + name = strings.TrimSpace(string(hdr[:64])) + return +} + + +type dataReader struct { + *bufio.Reader + io.Closer +} + + +// ExportData returns a readCloser positioned at the beginning of the +// export data section of the given object/archive file, or an error. +// It is the caller's responsibility to close the readCloser. +// +func ExportData(filename string) (rc io.ReadCloser, err os.Error) { + file, err := os.Open(filename) + if err != nil { + return + } + + defer func() { + if err != nil { + file.Close() + // Add file name to error. + err = fmt.Errorf("reading export data: %s: %v", filename, err) + } + }() + + buf := bufio.NewReader(file) + + // Read first line to make sure this is an object file. + line, err := buf.ReadSlice('\n') + if err != nil { + return + } + if string(line) == "!<arch>\n" { + // Archive file. Scan to __.PKGDEF, which should + // be second archive entry. + var name string + var size int + + // First entry should be __.SYMDEF. + // Read and discard. + if name, size, err = readGopackHeader(buf); err != nil { + return + } + if name != "__.SYMDEF" { + err = os.ErrorString("go archive does not begin with __.SYMDEF") + return + } + const block = 4096 + tmp := make([]byte, block) + for size > 0 { + n := size + if n > block { + n = block + } + _, err = io.ReadFull(buf, tmp[:n]) + if err != nil { + return + } + size -= n + } + + // Second entry should be __.PKGDEF. + if name, size, err = readGopackHeader(buf); err != nil { + return + } + if name != "__.PKGDEF" { + err = os.ErrorString("go archive is missing __.PKGDEF") + return + } + + // Read first line of __.PKGDEF data, so that line + // is once again the first line of the input. + line, err = buf.ReadSlice('\n') + if err != nil { + return + } + } + + // Now at __.PKGDEF in archive or still at beginning of file. + // Either way, line should begin with "go object ". + if !strings.HasPrefix(string(line), "go object ") { + err = os.ErrorString("not a go object file") + return + } + + // Skip over object header to export data. + // Begins after first line with $$. + for line[0] != '$' { + line, err = buf.ReadSlice('\n') + if err != nil { + return + } + } + + rc = &dataReader{buf, file} + return +} |