func()

in util/doc.go [411:602]


func (config *Config) parseHeader(path string) (*HeaderFile, error) {
	headerPath := filepath.Join(config.BaseDirectory, path)

	headerFile, err := os.Open(headerPath)
	if err != nil {
		return nil, err
	}
	defer headerFile.Close()

	scanner := bufio.NewScanner(headerFile)
	var lines, oldLines []string
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	lineNo := 1
	found := false
	for i, line := range lines {
		if line == cppGuard {
			lines = lines[i+1:]
			lineNo += i + 1
			found = true
			break
		}
	}

	if !found {
		return nil, errors.New("no C++ guard found")
	}

	if len(lines) == 0 || lines[0] != "extern \"C\" {" {
		return nil, errors.New("no extern \"C\" found after C++ guard")
	}
	lineNo += 2
	lines = lines[2:]

	header := &HeaderFile{
		Name:     filepath.Base(path),
		AllDecls: make(map[string]string),
	}

	for i, line := range lines {
		if len(line) > 0 {
			lines = lines[i:]
			lineNo += i
			break
		}
	}

	oldLines = lines
	if len(lines) > 0 && isComment(lines[0]) {
		comment, rest, restLineNo, err := extractComment(lines, lineNo)
		if err != nil {
			return nil, err
		}

		if len(rest) > 0 && len(rest[0]) == 0 {
			if len(rest) < 2 || len(rest[1]) != 0 {
				return nil, errors.New("preamble comment should be followed by two blank lines")
			}
			header.Preamble = comment
			lineNo = restLineNo + 2
			lines = rest[2:]
		} else {
			lines = oldLines
		}
	}

	allAnchors := make(map[string]struct{})

	for {
		// Start of a section.
		if len(lines) == 0 {
			return nil, errors.New("unexpected end of file")
		}
		line := lines[0]
		if line == cppGuard {
			break
		}

		if len(line) == 0 {
			return nil, fmt.Errorf("blank line at start of section on line %d", lineNo)
		}

		var section HeaderSection

		if isComment(line) {
			comment, rest, restLineNo, err := extractComment(lines, lineNo)
			if err != nil {
				return nil, err
			}
			if len(rest) > 0 && len(rest[0]) == 0 {
				heading := firstSentence(comment)
				anchor := sanitizeAnchor(heading)
				if len(anchor) > 0 {
					if _, ok := allAnchors[anchor]; ok {
						return nil, fmt.Errorf("duplicate anchor: %s", anchor)
					}
					allAnchors[anchor] = struct{}{}
				}

				section.Preamble = comment
				section.IsPrivate = isPrivateSection(heading)
				section.Anchor = anchor
				lines = rest[1:]
				lineNo = restLineNo + 1
			}
		}

		for len(lines) > 0 {
			line := lines[0]
			if len(line) == 0 {
				lines = lines[1:]
				lineNo++
				break
			}
			if line == cppGuard {
				return nil, fmt.Errorf("hit ending C++ guard while in section on line %d (possibly missing two empty lines ahead of guard?)", lineNo)
			}

			var comment []CommentBlock
			var decl string
			if isComment(line) {
				comment, lines, lineNo, err = extractComment(lines, lineNo)
				if err != nil {
					return nil, err
				}
			}
			if len(lines) == 0 {
				return nil, fmt.Errorf("expected decl at EOF on line %d", lineNo)
			}
			declLineNo := lineNo
			decl, lines, lineNo, err = extractDecl(lines, lineNo)
			if err != nil {
				return nil, err
			}
			name, ok := getNameFromDecl(decl)
			if !ok {
				name = ""
			}
			if last := len(section.Decls) - 1; len(name) == 0 && len(comment) == 0 && last >= 0 {
				section.Decls[last].Decl += "\n" + decl
			} else {
				// As a matter of style, comments should start
				// with the name of the thing that they are
				// commenting on. We make an exception here for
				// collective comments.
				sentence := firstSentence(comment)
				if len(comment) > 0 &&
					len(name) > 0 &&
					!isCollectiveComment(sentence) {
					subject := commentSubject(sentence)
					ok := subject == name
					if l := len(subject); l > 0 && subject[l-1] == '*' {
						// Groups of names, notably #defines, are often
						// denoted with a wildcard.
						ok = strings.HasPrefix(name, subject[:l-1])
					}
					if !ok {
						return nil, fmt.Errorf("comment for %q doesn't seem to match line %s:%d\n", name, path, declLineNo)
					}
				}
				anchor := sanitizeAnchor(name)
				// TODO(davidben): Enforce uniqueness. This is
				// skipped because #ifdefs currently result in
				// duplicate table-of-contents entries.
				allAnchors[anchor] = struct{}{}

				header.AllDecls[name] = anchor

				section.Decls = append(section.Decls, HeaderDecl{
					Comment: comment,
					Name:    name,
					Decl:    decl,
					Anchor:  anchor,
				})
			}

			if len(lines) > 0 && len(lines[0]) == 0 {
				lines = lines[1:]
				lineNo++
			}
		}

		header.Sections = append(header.Sections, section)
	}

	return header, nil
}