func readMetadata()

in go/tools/builders/pack.go [192:278]


func readMetadata(r *bufio.Reader, nameData *[]byte) (name string, size int64, err error) {
retry:
	// Each file is preceded by a 60-byte header that contains its metadata.
	// We only care about two fields, name and size. Other fields (mtime,
	// owner, group, mode) are ignored because they don't affect compilation.
	var entry [entryLength]byte
	if _, err := io.ReadFull(r, entry[:]); err != nil {
		return "", 0, err
	}

	sizeField := strings.TrimSpace(string(entry[48:58]))
	size, err = strconv.ParseInt(sizeField, 10, 64)
	if err != nil {
		return "", 0, err
	}

	nameField := strings.TrimRight(string(entry[:16]), " ")
	switch {
	case strings.HasPrefix(nameField, "#1/"):
		// BSD-style name. The number of bytes in the name is written here in
		// ASCII, right-padded with spaces. The actual name is stored at the
		// beginning of the file data, left-padded with NUL bytes.
		nameField = nameField[len("#1/"):]
		nameLen, err := strconv.ParseInt(nameField, 10, 64)
		if err != nil {
			return "", 0, err
		}
		nameBuf := make([]byte, nameLen)
		if _, err := io.ReadFull(r, nameBuf); err != nil {
			return "", 0, err
		}
		name = strings.TrimRight(string(nameBuf), "\x00")
		size -= nameLen

	case nameField == "//":
		// GNU / SysV-style name data. This is a fake file that contains names
		// for files with long names. We read this into nameData, then read
		// the next entry.
		*nameData = make([]byte, size)
		if _, err := io.ReadFull(r, *nameData); err != nil {
			return "", 0, err
		}
		if size%2 != 0 {
			// Files are aligned at 2-byte offsets. Discard the padding byte if the
			// size was odd.
			if _, err := r.ReadByte(); err != nil {
				return "", 0, err
			}
		}
		goto retry

	case nameField == "/":
		// GNU / SysV-style symbol lookup table. Skip.
		if err := skipFile(r, size); err != nil {
			return "", 0, err
		}
		goto retry

	case strings.HasPrefix(nameField, "/"):
		// GNU / SysV-style long file name. The number that follows the slash is
		// an offset into the name data that should have been read earlier.
		// The file name ends with a slash.
		nameField = nameField[1:]
		nameOffset, err := strconv.Atoi(nameField)
		if err != nil {
			return "", 0, err
		}
		if nameData == nil || nameOffset < 0 || nameOffset >= len(*nameData) {
			return "", 0, fmt.Errorf("invalid name length: %d", nameOffset)
		}
		i := bytes.IndexByte((*nameData)[nameOffset:], '/')
		if i < 0 {
			return "", 0, errors.New("file name does not end with '/'")
		}
		name = string((*nameData)[nameOffset : nameOffset+i])

	case strings.HasSuffix(nameField, "/"):
		// GNU / SysV-style short file name.
		name = nameField[:len(nameField)-1]

	default:
		// Common format name.
		name = nameField
	}

	return name, size, err
}