xref: /aosp_15_r20/external/boringssl/src/util/doc.go (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1*8fb009dcSAndroid Build Coastguard Worker//go:build ignore
2*8fb009dcSAndroid Build Coastguard Worker
3*8fb009dcSAndroid Build Coastguard Worker// doc generates HTML files from the comments in header files.
4*8fb009dcSAndroid Build Coastguard Worker//
5*8fb009dcSAndroid Build Coastguard Worker// doc expects to be given the path to a JSON file via the --config option.
6*8fb009dcSAndroid Build Coastguard Worker// From that JSON (which is defined by the Config struct) it reads a list of
7*8fb009dcSAndroid Build Coastguard Worker// header file locations and generates HTML files for each in the current
8*8fb009dcSAndroid Build Coastguard Worker// directory.
9*8fb009dcSAndroid Build Coastguard Workerpackage main
10*8fb009dcSAndroid Build Coastguard Worker
11*8fb009dcSAndroid Build Coastguard Workerimport (
12*8fb009dcSAndroid Build Coastguard Worker	"bufio"
13*8fb009dcSAndroid Build Coastguard Worker	"encoding/json"
14*8fb009dcSAndroid Build Coastguard Worker	"errors"
15*8fb009dcSAndroid Build Coastguard Worker	"flag"
16*8fb009dcSAndroid Build Coastguard Worker	"fmt"
17*8fb009dcSAndroid Build Coastguard Worker	"html/template"
18*8fb009dcSAndroid Build Coastguard Worker	"os"
19*8fb009dcSAndroid Build Coastguard Worker	"path/filepath"
20*8fb009dcSAndroid Build Coastguard Worker	"regexp"
21*8fb009dcSAndroid Build Coastguard Worker	"strconv"
22*8fb009dcSAndroid Build Coastguard Worker	"strings"
23*8fb009dcSAndroid Build Coastguard Worker	"unicode"
24*8fb009dcSAndroid Build Coastguard Worker)
25*8fb009dcSAndroid Build Coastguard Worker
26*8fb009dcSAndroid Build Coastguard Worker// Config describes the structure of the config JSON file.
27*8fb009dcSAndroid Build Coastguard Workertype Config struct {
28*8fb009dcSAndroid Build Coastguard Worker	// BaseDirectory is a path to which other paths in the file are
29*8fb009dcSAndroid Build Coastguard Worker	// relative.
30*8fb009dcSAndroid Build Coastguard Worker	BaseDirectory string
31*8fb009dcSAndroid Build Coastguard Worker	Sections      []ConfigSection
32*8fb009dcSAndroid Build Coastguard Worker}
33*8fb009dcSAndroid Build Coastguard Worker
34*8fb009dcSAndroid Build Coastguard Workertype ConfigSection struct {
35*8fb009dcSAndroid Build Coastguard Worker	Name string
36*8fb009dcSAndroid Build Coastguard Worker	// Headers is a list of paths to header files.
37*8fb009dcSAndroid Build Coastguard Worker	Headers []string
38*8fb009dcSAndroid Build Coastguard Worker}
39*8fb009dcSAndroid Build Coastguard Worker
40*8fb009dcSAndroid Build Coastguard Worker// HeaderFile is the internal representation of a header file.
41*8fb009dcSAndroid Build Coastguard Workertype HeaderFile struct {
42*8fb009dcSAndroid Build Coastguard Worker	// Name is the basename of the header file (e.g. "ex_data.html").
43*8fb009dcSAndroid Build Coastguard Worker	Name string
44*8fb009dcSAndroid Build Coastguard Worker	// Preamble contains a comment for the file as a whole. Each string
45*8fb009dcSAndroid Build Coastguard Worker	// is a separate paragraph.
46*8fb009dcSAndroid Build Coastguard Worker	Preamble []CommentBlock
47*8fb009dcSAndroid Build Coastguard Worker	Sections []HeaderSection
48*8fb009dcSAndroid Build Coastguard Worker	// AllDecls maps all decls to their URL fragments.
49*8fb009dcSAndroid Build Coastguard Worker	AllDecls map[string]string
50*8fb009dcSAndroid Build Coastguard Worker}
51*8fb009dcSAndroid Build Coastguard Worker
52*8fb009dcSAndroid Build Coastguard Workertype HeaderSection struct {
53*8fb009dcSAndroid Build Coastguard Worker	// Preamble contains a comment for a group of functions.
54*8fb009dcSAndroid Build Coastguard Worker	Preamble []CommentBlock
55*8fb009dcSAndroid Build Coastguard Worker	Decls    []HeaderDecl
56*8fb009dcSAndroid Build Coastguard Worker	// Anchor, if non-empty, is the URL fragment to use in anchor tags.
57*8fb009dcSAndroid Build Coastguard Worker	Anchor string
58*8fb009dcSAndroid Build Coastguard Worker	// IsPrivate is true if the section contains private functions (as
59*8fb009dcSAndroid Build Coastguard Worker	// indicated by its name).
60*8fb009dcSAndroid Build Coastguard Worker	IsPrivate bool
61*8fb009dcSAndroid Build Coastguard Worker}
62*8fb009dcSAndroid Build Coastguard Worker
63*8fb009dcSAndroid Build Coastguard Workertype HeaderDecl struct {
64*8fb009dcSAndroid Build Coastguard Worker	// Comment contains a comment for a specific function. Each string is a
65*8fb009dcSAndroid Build Coastguard Worker	// paragraph. Some paragraph may contain \n runes to indicate that they
66*8fb009dcSAndroid Build Coastguard Worker	// are preformatted.
67*8fb009dcSAndroid Build Coastguard Worker	Comment []CommentBlock
68*8fb009dcSAndroid Build Coastguard Worker	// Name contains the name of the function, if it could be extracted.
69*8fb009dcSAndroid Build Coastguard Worker	Name string
70*8fb009dcSAndroid Build Coastguard Worker	// Decl contains the preformatted C declaration itself.
71*8fb009dcSAndroid Build Coastguard Worker	Decl string
72*8fb009dcSAndroid Build Coastguard Worker	// Anchor, if non-empty, is the URL fragment to use in anchor tags.
73*8fb009dcSAndroid Build Coastguard Worker	Anchor string
74*8fb009dcSAndroid Build Coastguard Worker}
75*8fb009dcSAndroid Build Coastguard Worker
76*8fb009dcSAndroid Build Coastguard Workertype CommentBlockType int
77*8fb009dcSAndroid Build Coastguard Worker
78*8fb009dcSAndroid Build Coastguard Workerconst (
79*8fb009dcSAndroid Build Coastguard Worker	CommentParagraph CommentBlockType = iota
80*8fb009dcSAndroid Build Coastguard Worker	CommentOrderedListItem
81*8fb009dcSAndroid Build Coastguard Worker	CommentBulletListItem
82*8fb009dcSAndroid Build Coastguard Worker	CommentCode
83*8fb009dcSAndroid Build Coastguard Worker)
84*8fb009dcSAndroid Build Coastguard Worker
85*8fb009dcSAndroid Build Coastguard Workertype CommentBlock struct {
86*8fb009dcSAndroid Build Coastguard Worker	Type      CommentBlockType
87*8fb009dcSAndroid Build Coastguard Worker	Paragraph string
88*8fb009dcSAndroid Build Coastguard Worker}
89*8fb009dcSAndroid Build Coastguard Worker
90*8fb009dcSAndroid Build Coastguard Workerconst (
91*8fb009dcSAndroid Build Coastguard Worker	cppGuard     = "#if defined(__cplusplus)"
92*8fb009dcSAndroid Build Coastguard Worker	commentStart = "/* "
93*8fb009dcSAndroid Build Coastguard Worker	commentEnd   = " */"
94*8fb009dcSAndroid Build Coastguard Worker	lineComment  = "// "
95*8fb009dcSAndroid Build Coastguard Worker)
96*8fb009dcSAndroid Build Coastguard Worker
97*8fb009dcSAndroid Build Coastguard Workerfunc isComment(line string) bool {
98*8fb009dcSAndroid Build Coastguard Worker	return strings.HasPrefix(line, commentStart) || strings.HasPrefix(line, lineComment)
99*8fb009dcSAndroid Build Coastguard Worker}
100*8fb009dcSAndroid Build Coastguard Worker
101*8fb009dcSAndroid Build Coastguard Workerfunc commentSubject(line string) string {
102*8fb009dcSAndroid Build Coastguard Worker	if strings.HasPrefix(line, "A ") {
103*8fb009dcSAndroid Build Coastguard Worker		line = line[len("A "):]
104*8fb009dcSAndroid Build Coastguard Worker	} else if strings.HasPrefix(line, "An ") {
105*8fb009dcSAndroid Build Coastguard Worker		line = line[len("An "):]
106*8fb009dcSAndroid Build Coastguard Worker	}
107*8fb009dcSAndroid Build Coastguard Worker	idx := strings.IndexAny(line, " ,")
108*8fb009dcSAndroid Build Coastguard Worker	if idx < 0 {
109*8fb009dcSAndroid Build Coastguard Worker		return line
110*8fb009dcSAndroid Build Coastguard Worker	}
111*8fb009dcSAndroid Build Coastguard Worker	return line[:idx]
112*8fb009dcSAndroid Build Coastguard Worker}
113*8fb009dcSAndroid Build Coastguard Worker
114*8fb009dcSAndroid Build Coastguard Workerfunc extractCommentLines(lines []string, lineNo int) (comment []string, rest []string, restLineNo int, err error) {
115*8fb009dcSAndroid Build Coastguard Worker	if len(lines) == 0 {
116*8fb009dcSAndroid Build Coastguard Worker		return nil, lines, lineNo, nil
117*8fb009dcSAndroid Build Coastguard Worker	}
118*8fb009dcSAndroid Build Coastguard Worker
119*8fb009dcSAndroid Build Coastguard Worker	restLineNo = lineNo
120*8fb009dcSAndroid Build Coastguard Worker	rest = lines
121*8fb009dcSAndroid Build Coastguard Worker
122*8fb009dcSAndroid Build Coastguard Worker	var isBlock bool
123*8fb009dcSAndroid Build Coastguard Worker	if strings.HasPrefix(rest[0], commentStart) {
124*8fb009dcSAndroid Build Coastguard Worker		isBlock = true
125*8fb009dcSAndroid Build Coastguard Worker	} else if !strings.HasPrefix(rest[0], lineComment) {
126*8fb009dcSAndroid Build Coastguard Worker		panic("extractComment called on non-comment")
127*8fb009dcSAndroid Build Coastguard Worker	}
128*8fb009dcSAndroid Build Coastguard Worker	comment = []string{rest[0][len(commentStart):]}
129*8fb009dcSAndroid Build Coastguard Worker	rest = rest[1:]
130*8fb009dcSAndroid Build Coastguard Worker	restLineNo++
131*8fb009dcSAndroid Build Coastguard Worker
132*8fb009dcSAndroid Build Coastguard Worker	for len(rest) > 0 {
133*8fb009dcSAndroid Build Coastguard Worker		if isBlock {
134*8fb009dcSAndroid Build Coastguard Worker			last := &comment[len(comment)-1]
135*8fb009dcSAndroid Build Coastguard Worker			if i := strings.Index(*last, commentEnd); i >= 0 {
136*8fb009dcSAndroid Build Coastguard Worker				if i != len(*last)-len(commentEnd) {
137*8fb009dcSAndroid Build Coastguard Worker					err = fmt.Errorf("garbage after comment end on line %d", restLineNo)
138*8fb009dcSAndroid Build Coastguard Worker					return
139*8fb009dcSAndroid Build Coastguard Worker				}
140*8fb009dcSAndroid Build Coastguard Worker				*last = (*last)[:i]
141*8fb009dcSAndroid Build Coastguard Worker				return
142*8fb009dcSAndroid Build Coastguard Worker			}
143*8fb009dcSAndroid Build Coastguard Worker		}
144*8fb009dcSAndroid Build Coastguard Worker
145*8fb009dcSAndroid Build Coastguard Worker		line := rest[0]
146*8fb009dcSAndroid Build Coastguard Worker		if isBlock {
147*8fb009dcSAndroid Build Coastguard Worker			if !strings.HasPrefix(line, " *") {
148*8fb009dcSAndroid Build Coastguard Worker				err = fmt.Errorf("comment doesn't start with block prefix on line %d: %s", restLineNo, line)
149*8fb009dcSAndroid Build Coastguard Worker				return
150*8fb009dcSAndroid Build Coastguard Worker			}
151*8fb009dcSAndroid Build Coastguard Worker		} else if !strings.HasPrefix(line, "//") {
152*8fb009dcSAndroid Build Coastguard Worker			return
153*8fb009dcSAndroid Build Coastguard Worker		}
154*8fb009dcSAndroid Build Coastguard Worker		comment = append(comment, line[2:])
155*8fb009dcSAndroid Build Coastguard Worker		rest = rest[1:]
156*8fb009dcSAndroid Build Coastguard Worker		restLineNo++
157*8fb009dcSAndroid Build Coastguard Worker	}
158*8fb009dcSAndroid Build Coastguard Worker
159*8fb009dcSAndroid Build Coastguard Worker	err = errors.New("hit EOF in comment")
160*8fb009dcSAndroid Build Coastguard Worker	return
161*8fb009dcSAndroid Build Coastguard Worker}
162*8fb009dcSAndroid Build Coastguard Worker
163*8fb009dcSAndroid Build Coastguard Workerfunc removeBulletListMarker(line string) (string, bool) {
164*8fb009dcSAndroid Build Coastguard Worker	orig := line
165*8fb009dcSAndroid Build Coastguard Worker	line = strings.TrimSpace(line)
166*8fb009dcSAndroid Build Coastguard Worker	if !strings.HasPrefix(line, "+ ") && !strings.HasPrefix(line, "- ") && !strings.HasPrefix(line, "* ") {
167*8fb009dcSAndroid Build Coastguard Worker		return orig, false
168*8fb009dcSAndroid Build Coastguard Worker	}
169*8fb009dcSAndroid Build Coastguard Worker	return line[2:], true
170*8fb009dcSAndroid Build Coastguard Worker}
171*8fb009dcSAndroid Build Coastguard Worker
172*8fb009dcSAndroid Build Coastguard Workerfunc removeOrderedListMarker(line string) (rest string, num int, ok bool) {
173*8fb009dcSAndroid Build Coastguard Worker	orig := line
174*8fb009dcSAndroid Build Coastguard Worker	line = strings.TrimSpace(line)
175*8fb009dcSAndroid Build Coastguard Worker	if len(line) == 0 || !unicode.IsDigit(rune(line[0])) {
176*8fb009dcSAndroid Build Coastguard Worker		return orig, -1, false
177*8fb009dcSAndroid Build Coastguard Worker	}
178*8fb009dcSAndroid Build Coastguard Worker
179*8fb009dcSAndroid Build Coastguard Worker	l := 0
180*8fb009dcSAndroid Build Coastguard Worker	for l < len(line) && unicode.IsDigit(rune(line[l])) {
181*8fb009dcSAndroid Build Coastguard Worker		l++
182*8fb009dcSAndroid Build Coastguard Worker	}
183*8fb009dcSAndroid Build Coastguard Worker	num, err := strconv.Atoi(line[:l])
184*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
185*8fb009dcSAndroid Build Coastguard Worker		return orig, -1, false
186*8fb009dcSAndroid Build Coastguard Worker	}
187*8fb009dcSAndroid Build Coastguard Worker
188*8fb009dcSAndroid Build Coastguard Worker	line = line[l:]
189*8fb009dcSAndroid Build Coastguard Worker	if line, ok := strings.CutPrefix(line, ". "); ok {
190*8fb009dcSAndroid Build Coastguard Worker		return line, num, true
191*8fb009dcSAndroid Build Coastguard Worker	}
192*8fb009dcSAndroid Build Coastguard Worker	if line, ok := strings.CutPrefix(line, ") "); ok {
193*8fb009dcSAndroid Build Coastguard Worker		return line, num, true
194*8fb009dcSAndroid Build Coastguard Worker	}
195*8fb009dcSAndroid Build Coastguard Worker
196*8fb009dcSAndroid Build Coastguard Worker	return orig, -1, false
197*8fb009dcSAndroid Build Coastguard Worker}
198*8fb009dcSAndroid Build Coastguard Worker
199*8fb009dcSAndroid Build Coastguard Workerfunc removeCodeIndent(line string) (string, bool) {
200*8fb009dcSAndroid Build Coastguard Worker	return strings.CutPrefix(line, "   ")
201*8fb009dcSAndroid Build Coastguard Worker}
202*8fb009dcSAndroid Build Coastguard Worker
203*8fb009dcSAndroid Build Coastguard Workerfunc extractComment(lines []string, lineNo int) (comment []CommentBlock, rest []string, restLineNo int, err error) {
204*8fb009dcSAndroid Build Coastguard Worker	commentLines, rest, restLineNo, err := extractCommentLines(lines, lineNo)
205*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
206*8fb009dcSAndroid Build Coastguard Worker		return
207*8fb009dcSAndroid Build Coastguard Worker	}
208*8fb009dcSAndroid Build Coastguard Worker
209*8fb009dcSAndroid Build Coastguard Worker	// This syntax and parsing algorithm is loosely inspired by CommonMark,
210*8fb009dcSAndroid Build Coastguard Worker	// but reduced to a small subset with no nesting. Blocks being open vs.
211*8fb009dcSAndroid Build Coastguard Worker	// closed can be tracked implicitly. We're also much slopplier about how
212*8fb009dcSAndroid Build Coastguard Worker	// indentation. Additionally, rather than grouping list items into
213*8fb009dcSAndroid Build Coastguard Worker	// lists, our parser just emits a list items, which are grouped later at
214*8fb009dcSAndroid Build Coastguard Worker	// rendering time.
215*8fb009dcSAndroid Build Coastguard Worker	//
216*8fb009dcSAndroid Build Coastguard Worker	// If we later need more features, such as nested lists, this can evolve
217*8fb009dcSAndroid Build Coastguard Worker	// into a more complex implementation.
218*8fb009dcSAndroid Build Coastguard Worker	var numBlankLines int
219*8fb009dcSAndroid Build Coastguard Worker	for _, line := range commentLines {
220*8fb009dcSAndroid Build Coastguard Worker		// Defer blank lines until we know the next element.
221*8fb009dcSAndroid Build Coastguard Worker		if len(strings.TrimSpace(line)) == 0 {
222*8fb009dcSAndroid Build Coastguard Worker			numBlankLines++
223*8fb009dcSAndroid Build Coastguard Worker			continue
224*8fb009dcSAndroid Build Coastguard Worker		}
225*8fb009dcSAndroid Build Coastguard Worker
226*8fb009dcSAndroid Build Coastguard Worker		blankLinesSkipped := numBlankLines
227*8fb009dcSAndroid Build Coastguard Worker		numBlankLines = 0
228*8fb009dcSAndroid Build Coastguard Worker
229*8fb009dcSAndroid Build Coastguard Worker		// Attempt to continue the previous block.
230*8fb009dcSAndroid Build Coastguard Worker		if len(comment) > 0 {
231*8fb009dcSAndroid Build Coastguard Worker			last := &comment[len(comment)-1]
232*8fb009dcSAndroid Build Coastguard Worker			if last.Type == CommentCode {
233*8fb009dcSAndroid Build Coastguard Worker				l, ok := removeCodeIndent(line)
234*8fb009dcSAndroid Build Coastguard Worker				if ok {
235*8fb009dcSAndroid Build Coastguard Worker					for i := 0; i < blankLinesSkipped; i++ {
236*8fb009dcSAndroid Build Coastguard Worker						last.Paragraph += "\n"
237*8fb009dcSAndroid Build Coastguard Worker					}
238*8fb009dcSAndroid Build Coastguard Worker					last.Paragraph += l + "\n"
239*8fb009dcSAndroid Build Coastguard Worker					continue
240*8fb009dcSAndroid Build Coastguard Worker				}
241*8fb009dcSAndroid Build Coastguard Worker			} else if blankLinesSkipped == 0 {
242*8fb009dcSAndroid Build Coastguard Worker				_, isBulletList := removeBulletListMarker(line)
243*8fb009dcSAndroid Build Coastguard Worker				_, num, isOrderedList := removeOrderedListMarker(line)
244*8fb009dcSAndroid Build Coastguard Worker				if isOrderedList && last.Type == CommentParagraph && num != 1 {
245*8fb009dcSAndroid Build Coastguard Worker					// A list item can only interrupt a paragraph if the number is one.
246*8fb009dcSAndroid Build Coastguard Worker					// See the discussion in https://spec.commonmark.org/0.30/#lists.
247*8fb009dcSAndroid Build Coastguard Worker					// This avoids wrapping like "(See RFC\n5280)" turning into a list.
248*8fb009dcSAndroid Build Coastguard Worker					isOrderedList = false
249*8fb009dcSAndroid Build Coastguard Worker				}
250*8fb009dcSAndroid Build Coastguard Worker				if !isBulletList && !isOrderedList {
251*8fb009dcSAndroid Build Coastguard Worker					// This is a continuation line of the previous paragraph.
252*8fb009dcSAndroid Build Coastguard Worker					last.Paragraph += " " + strings.TrimSpace(line)
253*8fb009dcSAndroid Build Coastguard Worker					continue
254*8fb009dcSAndroid Build Coastguard Worker				}
255*8fb009dcSAndroid Build Coastguard Worker			}
256*8fb009dcSAndroid Build Coastguard Worker		}
257*8fb009dcSAndroid Build Coastguard Worker
258*8fb009dcSAndroid Build Coastguard Worker		// Make a new block.
259*8fb009dcSAndroid Build Coastguard Worker		if line, ok := removeBulletListMarker(line); ok {
260*8fb009dcSAndroid Build Coastguard Worker			comment = append(comment, CommentBlock{
261*8fb009dcSAndroid Build Coastguard Worker				Type:      CommentBulletListItem,
262*8fb009dcSAndroid Build Coastguard Worker				Paragraph: strings.TrimSpace(line),
263*8fb009dcSAndroid Build Coastguard Worker			})
264*8fb009dcSAndroid Build Coastguard Worker		} else if line, _, ok := removeOrderedListMarker(line); ok {
265*8fb009dcSAndroid Build Coastguard Worker			comment = append(comment, CommentBlock{
266*8fb009dcSAndroid Build Coastguard Worker				Type:      CommentOrderedListItem,
267*8fb009dcSAndroid Build Coastguard Worker				Paragraph: strings.TrimSpace(line),
268*8fb009dcSAndroid Build Coastguard Worker			})
269*8fb009dcSAndroid Build Coastguard Worker		} else if line, ok := removeCodeIndent(line); ok {
270*8fb009dcSAndroid Build Coastguard Worker			comment = append(comment, CommentBlock{
271*8fb009dcSAndroid Build Coastguard Worker				Type:      CommentCode,
272*8fb009dcSAndroid Build Coastguard Worker				Paragraph: line + "\n",
273*8fb009dcSAndroid Build Coastguard Worker			})
274*8fb009dcSAndroid Build Coastguard Worker		} else {
275*8fb009dcSAndroid Build Coastguard Worker			comment = append(comment, CommentBlock{
276*8fb009dcSAndroid Build Coastguard Worker				Type:      CommentParagraph,
277*8fb009dcSAndroid Build Coastguard Worker				Paragraph: strings.TrimSpace(line),
278*8fb009dcSAndroid Build Coastguard Worker			})
279*8fb009dcSAndroid Build Coastguard Worker		}
280*8fb009dcSAndroid Build Coastguard Worker	}
281*8fb009dcSAndroid Build Coastguard Worker
282*8fb009dcSAndroid Build Coastguard Worker	return
283*8fb009dcSAndroid Build Coastguard Worker}
284*8fb009dcSAndroid Build Coastguard Worker
285*8fb009dcSAndroid Build Coastguard Workerfunc extractDecl(lines []string, lineNo int) (decl string, rest []string, restLineNo int, err error) {
286*8fb009dcSAndroid Build Coastguard Worker	if len(lines) == 0 || len(lines[0]) == 0 {
287*8fb009dcSAndroid Build Coastguard Worker		return "", lines, lineNo, nil
288*8fb009dcSAndroid Build Coastguard Worker	}
289*8fb009dcSAndroid Build Coastguard Worker
290*8fb009dcSAndroid Build Coastguard Worker	rest = lines
291*8fb009dcSAndroid Build Coastguard Worker	restLineNo = lineNo
292*8fb009dcSAndroid Build Coastguard Worker
293*8fb009dcSAndroid Build Coastguard Worker	var stack []rune
294*8fb009dcSAndroid Build Coastguard Worker	for len(rest) > 0 {
295*8fb009dcSAndroid Build Coastguard Worker		line := rest[0]
296*8fb009dcSAndroid Build Coastguard Worker		for _, c := range line {
297*8fb009dcSAndroid Build Coastguard Worker			switch c {
298*8fb009dcSAndroid Build Coastguard Worker			case '(', '{', '[':
299*8fb009dcSAndroid Build Coastguard Worker				stack = append(stack, c)
300*8fb009dcSAndroid Build Coastguard Worker			case ')', '}', ']':
301*8fb009dcSAndroid Build Coastguard Worker				if len(stack) == 0 {
302*8fb009dcSAndroid Build Coastguard Worker					err = fmt.Errorf("unexpected %c on line %d", c, restLineNo)
303*8fb009dcSAndroid Build Coastguard Worker					return
304*8fb009dcSAndroid Build Coastguard Worker				}
305*8fb009dcSAndroid Build Coastguard Worker				var expected rune
306*8fb009dcSAndroid Build Coastguard Worker				switch c {
307*8fb009dcSAndroid Build Coastguard Worker				case ')':
308*8fb009dcSAndroid Build Coastguard Worker					expected = '('
309*8fb009dcSAndroid Build Coastguard Worker				case '}':
310*8fb009dcSAndroid Build Coastguard Worker					expected = '{'
311*8fb009dcSAndroid Build Coastguard Worker				case ']':
312*8fb009dcSAndroid Build Coastguard Worker					expected = '['
313*8fb009dcSAndroid Build Coastguard Worker				default:
314*8fb009dcSAndroid Build Coastguard Worker					panic("internal error")
315*8fb009dcSAndroid Build Coastguard Worker				}
316*8fb009dcSAndroid Build Coastguard Worker				if last := stack[len(stack)-1]; last != expected {
317*8fb009dcSAndroid Build Coastguard Worker					err = fmt.Errorf("found %c when expecting %c on line %d", c, last, restLineNo)
318*8fb009dcSAndroid Build Coastguard Worker					return
319*8fb009dcSAndroid Build Coastguard Worker				}
320*8fb009dcSAndroid Build Coastguard Worker				stack = stack[:len(stack)-1]
321*8fb009dcSAndroid Build Coastguard Worker			}
322*8fb009dcSAndroid Build Coastguard Worker		}
323*8fb009dcSAndroid Build Coastguard Worker		if len(decl) > 0 {
324*8fb009dcSAndroid Build Coastguard Worker			decl += "\n"
325*8fb009dcSAndroid Build Coastguard Worker		}
326*8fb009dcSAndroid Build Coastguard Worker		decl += line
327*8fb009dcSAndroid Build Coastguard Worker		rest = rest[1:]
328*8fb009dcSAndroid Build Coastguard Worker		restLineNo++
329*8fb009dcSAndroid Build Coastguard Worker
330*8fb009dcSAndroid Build Coastguard Worker		if len(stack) == 0 && (len(decl) == 0 || decl[len(decl)-1] != '\\') {
331*8fb009dcSAndroid Build Coastguard Worker			break
332*8fb009dcSAndroid Build Coastguard Worker		}
333*8fb009dcSAndroid Build Coastguard Worker	}
334*8fb009dcSAndroid Build Coastguard Worker
335*8fb009dcSAndroid Build Coastguard Worker	return
336*8fb009dcSAndroid Build Coastguard Worker}
337*8fb009dcSAndroid Build Coastguard Worker
338*8fb009dcSAndroid Build Coastguard Workerfunc skipLine(s string) string {
339*8fb009dcSAndroid Build Coastguard Worker	i := strings.Index(s, "\n")
340*8fb009dcSAndroid Build Coastguard Worker	if i > 0 {
341*8fb009dcSAndroid Build Coastguard Worker		return s[i:]
342*8fb009dcSAndroid Build Coastguard Worker	}
343*8fb009dcSAndroid Build Coastguard Worker	return ""
344*8fb009dcSAndroid Build Coastguard Worker}
345*8fb009dcSAndroid Build Coastguard Worker
346*8fb009dcSAndroid Build Coastguard Workervar stackOfRegexp = regexp.MustCompile(`STACK_OF\(([^)]*)\)`)
347*8fb009dcSAndroid Build Coastguard Workervar lhashOfRegexp = regexp.MustCompile(`LHASH_OF\(([^)]*)\)`)
348*8fb009dcSAndroid Build Coastguard Worker
349*8fb009dcSAndroid Build Coastguard Workerfunc getNameFromDecl(decl string) (string, bool) {
350*8fb009dcSAndroid Build Coastguard Worker	for strings.HasPrefix(decl, "#if") || strings.HasPrefix(decl, "#elif") {
351*8fb009dcSAndroid Build Coastguard Worker		decl = skipLine(decl)
352*8fb009dcSAndroid Build Coastguard Worker	}
353*8fb009dcSAndroid Build Coastguard Worker
354*8fb009dcSAndroid Build Coastguard Worker	if strings.HasPrefix(decl, "typedef ") {
355*8fb009dcSAndroid Build Coastguard Worker		return "", false
356*8fb009dcSAndroid Build Coastguard Worker	}
357*8fb009dcSAndroid Build Coastguard Worker
358*8fb009dcSAndroid Build Coastguard Worker	for _, prefix := range []string{"struct ", "enum ", "#define "} {
359*8fb009dcSAndroid Build Coastguard Worker		if !strings.HasPrefix(decl, prefix) {
360*8fb009dcSAndroid Build Coastguard Worker			continue
361*8fb009dcSAndroid Build Coastguard Worker		}
362*8fb009dcSAndroid Build Coastguard Worker
363*8fb009dcSAndroid Build Coastguard Worker		decl = strings.TrimPrefix(decl, prefix)
364*8fb009dcSAndroid Build Coastguard Worker
365*8fb009dcSAndroid Build Coastguard Worker		for len(decl) > 0 && decl[0] == ' ' {
366*8fb009dcSAndroid Build Coastguard Worker			decl = decl[1:]
367*8fb009dcSAndroid Build Coastguard Worker		}
368*8fb009dcSAndroid Build Coastguard Worker
369*8fb009dcSAndroid Build Coastguard Worker		// struct and enum types can be the return type of a
370*8fb009dcSAndroid Build Coastguard Worker		// function.
371*8fb009dcSAndroid Build Coastguard Worker		if prefix[0] != '#' && strings.Index(decl, "{") == -1 {
372*8fb009dcSAndroid Build Coastguard Worker			break
373*8fb009dcSAndroid Build Coastguard Worker		}
374*8fb009dcSAndroid Build Coastguard Worker
375*8fb009dcSAndroid Build Coastguard Worker		i := strings.IndexAny(decl, "( ")
376*8fb009dcSAndroid Build Coastguard Worker		if i < 0 {
377*8fb009dcSAndroid Build Coastguard Worker			return "", false
378*8fb009dcSAndroid Build Coastguard Worker		}
379*8fb009dcSAndroid Build Coastguard Worker		return decl[:i], true
380*8fb009dcSAndroid Build Coastguard Worker	}
381*8fb009dcSAndroid Build Coastguard Worker	decl = strings.TrimPrefix(decl, "OPENSSL_EXPORT ")
382*8fb009dcSAndroid Build Coastguard Worker	decl = strings.TrimPrefix(decl, "const ")
383*8fb009dcSAndroid Build Coastguard Worker	decl = stackOfRegexp.ReplaceAllString(decl, "STACK_OF_$1")
384*8fb009dcSAndroid Build Coastguard Worker	decl = lhashOfRegexp.ReplaceAllString(decl, "LHASH_OF_$1")
385*8fb009dcSAndroid Build Coastguard Worker	i := strings.Index(decl, "(")
386*8fb009dcSAndroid Build Coastguard Worker	if i < 0 {
387*8fb009dcSAndroid Build Coastguard Worker		return "", false
388*8fb009dcSAndroid Build Coastguard Worker	}
389*8fb009dcSAndroid Build Coastguard Worker	j := strings.LastIndex(decl[:i], " ")
390*8fb009dcSAndroid Build Coastguard Worker	if j < 0 {
391*8fb009dcSAndroid Build Coastguard Worker		return "", false
392*8fb009dcSAndroid Build Coastguard Worker	}
393*8fb009dcSAndroid Build Coastguard Worker	for j+1 < len(decl) && decl[j+1] == '*' {
394*8fb009dcSAndroid Build Coastguard Worker		j++
395*8fb009dcSAndroid Build Coastguard Worker	}
396*8fb009dcSAndroid Build Coastguard Worker	return decl[j+1 : i], true
397*8fb009dcSAndroid Build Coastguard Worker}
398*8fb009dcSAndroid Build Coastguard Worker
399*8fb009dcSAndroid Build Coastguard Workerfunc sanitizeAnchor(name string) string {
400*8fb009dcSAndroid Build Coastguard Worker	return strings.Replace(name, " ", "-", -1)
401*8fb009dcSAndroid Build Coastguard Worker}
402*8fb009dcSAndroid Build Coastguard Worker
403*8fb009dcSAndroid Build Coastguard Workerfunc isPrivateSection(name string) bool {
404*8fb009dcSAndroid Build Coastguard Worker	return strings.HasPrefix(name, "Private functions") || strings.HasPrefix(name, "Private structures") || strings.Contains(name, "(hidden)")
405*8fb009dcSAndroid Build Coastguard Worker}
406*8fb009dcSAndroid Build Coastguard Worker
407*8fb009dcSAndroid Build Coastguard Workerfunc isCollectiveComment(line string) bool {
408*8fb009dcSAndroid Build Coastguard Worker	return strings.HasPrefix(line, "The ") || strings.HasPrefix(line, "These ")
409*8fb009dcSAndroid Build Coastguard Worker}
410*8fb009dcSAndroid Build Coastguard Worker
411*8fb009dcSAndroid Build Coastguard Workerfunc (config *Config) parseHeader(path string) (*HeaderFile, error) {
412*8fb009dcSAndroid Build Coastguard Worker	headerPath := filepath.Join(config.BaseDirectory, path)
413*8fb009dcSAndroid Build Coastguard Worker
414*8fb009dcSAndroid Build Coastguard Worker	headerFile, err := os.Open(headerPath)
415*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
416*8fb009dcSAndroid Build Coastguard Worker		return nil, err
417*8fb009dcSAndroid Build Coastguard Worker	}
418*8fb009dcSAndroid Build Coastguard Worker	defer headerFile.Close()
419*8fb009dcSAndroid Build Coastguard Worker
420*8fb009dcSAndroid Build Coastguard Worker	scanner := bufio.NewScanner(headerFile)
421*8fb009dcSAndroid Build Coastguard Worker	var lines, oldLines []string
422*8fb009dcSAndroid Build Coastguard Worker	for scanner.Scan() {
423*8fb009dcSAndroid Build Coastguard Worker		lines = append(lines, scanner.Text())
424*8fb009dcSAndroid Build Coastguard Worker	}
425*8fb009dcSAndroid Build Coastguard Worker	if err := scanner.Err(); err != nil {
426*8fb009dcSAndroid Build Coastguard Worker		return nil, err
427*8fb009dcSAndroid Build Coastguard Worker	}
428*8fb009dcSAndroid Build Coastguard Worker
429*8fb009dcSAndroid Build Coastguard Worker	lineNo := 1
430*8fb009dcSAndroid Build Coastguard Worker	found := false
431*8fb009dcSAndroid Build Coastguard Worker	for i, line := range lines {
432*8fb009dcSAndroid Build Coastguard Worker		if line == cppGuard {
433*8fb009dcSAndroid Build Coastguard Worker			lines = lines[i+1:]
434*8fb009dcSAndroid Build Coastguard Worker			lineNo += i + 1
435*8fb009dcSAndroid Build Coastguard Worker			found = true
436*8fb009dcSAndroid Build Coastguard Worker			break
437*8fb009dcSAndroid Build Coastguard Worker		}
438*8fb009dcSAndroid Build Coastguard Worker	}
439*8fb009dcSAndroid Build Coastguard Worker
440*8fb009dcSAndroid Build Coastguard Worker	if !found {
441*8fb009dcSAndroid Build Coastguard Worker		return nil, errors.New("no C++ guard found")
442*8fb009dcSAndroid Build Coastguard Worker	}
443*8fb009dcSAndroid Build Coastguard Worker
444*8fb009dcSAndroid Build Coastguard Worker	if len(lines) == 0 || lines[0] != "extern \"C\" {" {
445*8fb009dcSAndroid Build Coastguard Worker		return nil, errors.New("no extern \"C\" found after C++ guard")
446*8fb009dcSAndroid Build Coastguard Worker	}
447*8fb009dcSAndroid Build Coastguard Worker	lineNo += 2
448*8fb009dcSAndroid Build Coastguard Worker	lines = lines[2:]
449*8fb009dcSAndroid Build Coastguard Worker
450*8fb009dcSAndroid Build Coastguard Worker	header := &HeaderFile{
451*8fb009dcSAndroid Build Coastguard Worker		Name:     filepath.Base(path),
452*8fb009dcSAndroid Build Coastguard Worker		AllDecls: make(map[string]string),
453*8fb009dcSAndroid Build Coastguard Worker	}
454*8fb009dcSAndroid Build Coastguard Worker
455*8fb009dcSAndroid Build Coastguard Worker	for i, line := range lines {
456*8fb009dcSAndroid Build Coastguard Worker		if len(line) > 0 {
457*8fb009dcSAndroid Build Coastguard Worker			lines = lines[i:]
458*8fb009dcSAndroid Build Coastguard Worker			lineNo += i
459*8fb009dcSAndroid Build Coastguard Worker			break
460*8fb009dcSAndroid Build Coastguard Worker		}
461*8fb009dcSAndroid Build Coastguard Worker	}
462*8fb009dcSAndroid Build Coastguard Worker
463*8fb009dcSAndroid Build Coastguard Worker	oldLines = lines
464*8fb009dcSAndroid Build Coastguard Worker	if len(lines) > 0 && isComment(lines[0]) {
465*8fb009dcSAndroid Build Coastguard Worker		comment, rest, restLineNo, err := extractComment(lines, lineNo)
466*8fb009dcSAndroid Build Coastguard Worker		if err != nil {
467*8fb009dcSAndroid Build Coastguard Worker			return nil, err
468*8fb009dcSAndroid Build Coastguard Worker		}
469*8fb009dcSAndroid Build Coastguard Worker
470*8fb009dcSAndroid Build Coastguard Worker		if len(rest) > 0 && len(rest[0]) == 0 {
471*8fb009dcSAndroid Build Coastguard Worker			if len(rest) < 2 || len(rest[1]) != 0 {
472*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("preamble comment should be followed by two blank lines")
473*8fb009dcSAndroid Build Coastguard Worker			}
474*8fb009dcSAndroid Build Coastguard Worker			header.Preamble = comment
475*8fb009dcSAndroid Build Coastguard Worker			lineNo = restLineNo + 2
476*8fb009dcSAndroid Build Coastguard Worker			lines = rest[2:]
477*8fb009dcSAndroid Build Coastguard Worker		} else {
478*8fb009dcSAndroid Build Coastguard Worker			lines = oldLines
479*8fb009dcSAndroid Build Coastguard Worker		}
480*8fb009dcSAndroid Build Coastguard Worker	}
481*8fb009dcSAndroid Build Coastguard Worker
482*8fb009dcSAndroid Build Coastguard Worker	allAnchors := make(map[string]struct{})
483*8fb009dcSAndroid Build Coastguard Worker
484*8fb009dcSAndroid Build Coastguard Worker	for {
485*8fb009dcSAndroid Build Coastguard Worker		// Start of a section.
486*8fb009dcSAndroid Build Coastguard Worker		if len(lines) == 0 {
487*8fb009dcSAndroid Build Coastguard Worker			return nil, errors.New("unexpected end of file")
488*8fb009dcSAndroid Build Coastguard Worker		}
489*8fb009dcSAndroid Build Coastguard Worker		line := lines[0]
490*8fb009dcSAndroid Build Coastguard Worker		if line == cppGuard {
491*8fb009dcSAndroid Build Coastguard Worker			break
492*8fb009dcSAndroid Build Coastguard Worker		}
493*8fb009dcSAndroid Build Coastguard Worker
494*8fb009dcSAndroid Build Coastguard Worker		if len(line) == 0 {
495*8fb009dcSAndroid Build Coastguard Worker			return nil, fmt.Errorf("blank line at start of section on line %d", lineNo)
496*8fb009dcSAndroid Build Coastguard Worker		}
497*8fb009dcSAndroid Build Coastguard Worker
498*8fb009dcSAndroid Build Coastguard Worker		var section HeaderSection
499*8fb009dcSAndroid Build Coastguard Worker
500*8fb009dcSAndroid Build Coastguard Worker		if isComment(line) {
501*8fb009dcSAndroid Build Coastguard Worker			comment, rest, restLineNo, err := extractComment(lines, lineNo)
502*8fb009dcSAndroid Build Coastguard Worker			if err != nil {
503*8fb009dcSAndroid Build Coastguard Worker				return nil, err
504*8fb009dcSAndroid Build Coastguard Worker			}
505*8fb009dcSAndroid Build Coastguard Worker			if len(rest) > 0 && len(rest[0]) == 0 {
506*8fb009dcSAndroid Build Coastguard Worker				heading := firstSentence(comment)
507*8fb009dcSAndroid Build Coastguard Worker				anchor := sanitizeAnchor(heading)
508*8fb009dcSAndroid Build Coastguard Worker				if len(anchor) > 0 {
509*8fb009dcSAndroid Build Coastguard Worker					if _, ok := allAnchors[anchor]; ok {
510*8fb009dcSAndroid Build Coastguard Worker						return nil, fmt.Errorf("duplicate anchor: %s", anchor)
511*8fb009dcSAndroid Build Coastguard Worker					}
512*8fb009dcSAndroid Build Coastguard Worker					allAnchors[anchor] = struct{}{}
513*8fb009dcSAndroid Build Coastguard Worker				}
514*8fb009dcSAndroid Build Coastguard Worker
515*8fb009dcSAndroid Build Coastguard Worker				section.Preamble = comment
516*8fb009dcSAndroid Build Coastguard Worker				section.IsPrivate = isPrivateSection(heading)
517*8fb009dcSAndroid Build Coastguard Worker				section.Anchor = anchor
518*8fb009dcSAndroid Build Coastguard Worker				lines = rest[1:]
519*8fb009dcSAndroid Build Coastguard Worker				lineNo = restLineNo + 1
520*8fb009dcSAndroid Build Coastguard Worker			}
521*8fb009dcSAndroid Build Coastguard Worker		}
522*8fb009dcSAndroid Build Coastguard Worker
523*8fb009dcSAndroid Build Coastguard Worker		for len(lines) > 0 {
524*8fb009dcSAndroid Build Coastguard Worker			line := lines[0]
525*8fb009dcSAndroid Build Coastguard Worker			if len(line) == 0 {
526*8fb009dcSAndroid Build Coastguard Worker				lines = lines[1:]
527*8fb009dcSAndroid Build Coastguard Worker				lineNo++
528*8fb009dcSAndroid Build Coastguard Worker				break
529*8fb009dcSAndroid Build Coastguard Worker			}
530*8fb009dcSAndroid Build Coastguard Worker			if line == cppGuard {
531*8fb009dcSAndroid Build Coastguard Worker				return nil, fmt.Errorf("hit ending C++ guard while in section on line %d (possibly missing two empty lines ahead of guard?)", lineNo)
532*8fb009dcSAndroid Build Coastguard Worker			}
533*8fb009dcSAndroid Build Coastguard Worker
534*8fb009dcSAndroid Build Coastguard Worker			var comment []CommentBlock
535*8fb009dcSAndroid Build Coastguard Worker			var decl string
536*8fb009dcSAndroid Build Coastguard Worker			if isComment(line) {
537*8fb009dcSAndroid Build Coastguard Worker				comment, lines, lineNo, err = extractComment(lines, lineNo)
538*8fb009dcSAndroid Build Coastguard Worker				if err != nil {
539*8fb009dcSAndroid Build Coastguard Worker					return nil, err
540*8fb009dcSAndroid Build Coastguard Worker				}
541*8fb009dcSAndroid Build Coastguard Worker			}
542*8fb009dcSAndroid Build Coastguard Worker			if len(lines) == 0 {
543*8fb009dcSAndroid Build Coastguard Worker				return nil, fmt.Errorf("expected decl at EOF on line %d", lineNo)
544*8fb009dcSAndroid Build Coastguard Worker			}
545*8fb009dcSAndroid Build Coastguard Worker			declLineNo := lineNo
546*8fb009dcSAndroid Build Coastguard Worker			decl, lines, lineNo, err = extractDecl(lines, lineNo)
547*8fb009dcSAndroid Build Coastguard Worker			if err != nil {
548*8fb009dcSAndroid Build Coastguard Worker				return nil, err
549*8fb009dcSAndroid Build Coastguard Worker			}
550*8fb009dcSAndroid Build Coastguard Worker			name, ok := getNameFromDecl(decl)
551*8fb009dcSAndroid Build Coastguard Worker			if !ok {
552*8fb009dcSAndroid Build Coastguard Worker				name = ""
553*8fb009dcSAndroid Build Coastguard Worker			}
554*8fb009dcSAndroid Build Coastguard Worker			if last := len(section.Decls) - 1; len(name) == 0 && len(comment) == 0 && last >= 0 {
555*8fb009dcSAndroid Build Coastguard Worker				section.Decls[last].Decl += "\n" + decl
556*8fb009dcSAndroid Build Coastguard Worker			} else {
557*8fb009dcSAndroid Build Coastguard Worker				// As a matter of style, comments should start
558*8fb009dcSAndroid Build Coastguard Worker				// with the name of the thing that they are
559*8fb009dcSAndroid Build Coastguard Worker				// commenting on. We make an exception here for
560*8fb009dcSAndroid Build Coastguard Worker				// collective comments.
561*8fb009dcSAndroid Build Coastguard Worker				sentence := firstSentence(comment)
562*8fb009dcSAndroid Build Coastguard Worker				if len(comment) > 0 &&
563*8fb009dcSAndroid Build Coastguard Worker					len(name) > 0 &&
564*8fb009dcSAndroid Build Coastguard Worker					!isCollectiveComment(sentence) {
565*8fb009dcSAndroid Build Coastguard Worker					subject := commentSubject(sentence)
566*8fb009dcSAndroid Build Coastguard Worker					ok := subject == name
567*8fb009dcSAndroid Build Coastguard Worker					if l := len(subject); l > 0 && subject[l-1] == '*' {
568*8fb009dcSAndroid Build Coastguard Worker						// Groups of names, notably #defines, are often
569*8fb009dcSAndroid Build Coastguard Worker						// denoted with a wildcard.
570*8fb009dcSAndroid Build Coastguard Worker						ok = strings.HasPrefix(name, subject[:l-1])
571*8fb009dcSAndroid Build Coastguard Worker					}
572*8fb009dcSAndroid Build Coastguard Worker					if !ok {
573*8fb009dcSAndroid Build Coastguard Worker						return nil, fmt.Errorf("comment for %q doesn't seem to match line %s:%d\n", name, path, declLineNo)
574*8fb009dcSAndroid Build Coastguard Worker					}
575*8fb009dcSAndroid Build Coastguard Worker				}
576*8fb009dcSAndroid Build Coastguard Worker				anchor := sanitizeAnchor(name)
577*8fb009dcSAndroid Build Coastguard Worker				// TODO(davidben): Enforce uniqueness. This is
578*8fb009dcSAndroid Build Coastguard Worker				// skipped because #ifdefs currently result in
579*8fb009dcSAndroid Build Coastguard Worker				// duplicate table-of-contents entries.
580*8fb009dcSAndroid Build Coastguard Worker				allAnchors[anchor] = struct{}{}
581*8fb009dcSAndroid Build Coastguard Worker
582*8fb009dcSAndroid Build Coastguard Worker				header.AllDecls[name] = anchor
583*8fb009dcSAndroid Build Coastguard Worker
584*8fb009dcSAndroid Build Coastguard Worker				section.Decls = append(section.Decls, HeaderDecl{
585*8fb009dcSAndroid Build Coastguard Worker					Comment: comment,
586*8fb009dcSAndroid Build Coastguard Worker					Name:    name,
587*8fb009dcSAndroid Build Coastguard Worker					Decl:    decl,
588*8fb009dcSAndroid Build Coastguard Worker					Anchor:  anchor,
589*8fb009dcSAndroid Build Coastguard Worker				})
590*8fb009dcSAndroid Build Coastguard Worker			}
591*8fb009dcSAndroid Build Coastguard Worker
592*8fb009dcSAndroid Build Coastguard Worker			if len(lines) > 0 && len(lines[0]) == 0 {
593*8fb009dcSAndroid Build Coastguard Worker				lines = lines[1:]
594*8fb009dcSAndroid Build Coastguard Worker				lineNo++
595*8fb009dcSAndroid Build Coastguard Worker			}
596*8fb009dcSAndroid Build Coastguard Worker		}
597*8fb009dcSAndroid Build Coastguard Worker
598*8fb009dcSAndroid Build Coastguard Worker		header.Sections = append(header.Sections, section)
599*8fb009dcSAndroid Build Coastguard Worker	}
600*8fb009dcSAndroid Build Coastguard Worker
601*8fb009dcSAndroid Build Coastguard Worker	return header, nil
602*8fb009dcSAndroid Build Coastguard Worker}
603*8fb009dcSAndroid Build Coastguard Worker
604*8fb009dcSAndroid Build Coastguard Workerfunc firstSentence(comment []CommentBlock) string {
605*8fb009dcSAndroid Build Coastguard Worker	if len(comment) == 0 {
606*8fb009dcSAndroid Build Coastguard Worker		return ""
607*8fb009dcSAndroid Build Coastguard Worker	}
608*8fb009dcSAndroid Build Coastguard Worker	s := comment[0].Paragraph
609*8fb009dcSAndroid Build Coastguard Worker	i := strings.Index(s, ". ")
610*8fb009dcSAndroid Build Coastguard Worker	if i >= 0 {
611*8fb009dcSAndroid Build Coastguard Worker		return s[:i]
612*8fb009dcSAndroid Build Coastguard Worker	}
613*8fb009dcSAndroid Build Coastguard Worker	if lastIndex := len(s) - 1; s[lastIndex] == '.' {
614*8fb009dcSAndroid Build Coastguard Worker		return s[:lastIndex]
615*8fb009dcSAndroid Build Coastguard Worker	}
616*8fb009dcSAndroid Build Coastguard Worker	return s
617*8fb009dcSAndroid Build Coastguard Worker}
618*8fb009dcSAndroid Build Coastguard Worker
619*8fb009dcSAndroid Build Coastguard Workerfunc markupComment(allDecls map[string]string, comment []CommentBlock) template.HTML {
620*8fb009dcSAndroid Build Coastguard Worker	var b strings.Builder
621*8fb009dcSAndroid Build Coastguard Worker	lastType := CommentParagraph
622*8fb009dcSAndroid Build Coastguard Worker	closeList := func() {
623*8fb009dcSAndroid Build Coastguard Worker		if lastType == CommentOrderedListItem {
624*8fb009dcSAndroid Build Coastguard Worker			b.WriteString("</ol>")
625*8fb009dcSAndroid Build Coastguard Worker		} else if lastType == CommentBulletListItem {
626*8fb009dcSAndroid Build Coastguard Worker			b.WriteString("</ul>")
627*8fb009dcSAndroid Build Coastguard Worker		}
628*8fb009dcSAndroid Build Coastguard Worker	}
629*8fb009dcSAndroid Build Coastguard Worker
630*8fb009dcSAndroid Build Coastguard Worker	for _, block := range comment {
631*8fb009dcSAndroid Build Coastguard Worker		// Group consecutive list items of the same type into a list.
632*8fb009dcSAndroid Build Coastguard Worker		if block.Type != lastType {
633*8fb009dcSAndroid Build Coastguard Worker			closeList()
634*8fb009dcSAndroid Build Coastguard Worker			if block.Type == CommentOrderedListItem {
635*8fb009dcSAndroid Build Coastguard Worker				b.WriteString("<ol>")
636*8fb009dcSAndroid Build Coastguard Worker			} else if block.Type == CommentBulletListItem {
637*8fb009dcSAndroid Build Coastguard Worker				b.WriteString("<ul>")
638*8fb009dcSAndroid Build Coastguard Worker			}
639*8fb009dcSAndroid Build Coastguard Worker		}
640*8fb009dcSAndroid Build Coastguard Worker		lastType = block.Type
641*8fb009dcSAndroid Build Coastguard Worker
642*8fb009dcSAndroid Build Coastguard Worker		switch block.Type {
643*8fb009dcSAndroid Build Coastguard Worker		case CommentParagraph:
644*8fb009dcSAndroid Build Coastguard Worker			if strings.HasPrefix(block.Paragraph, "WARNING:") {
645*8fb009dcSAndroid Build Coastguard Worker				b.WriteString("<p class=\"warning\">")
646*8fb009dcSAndroid Build Coastguard Worker			} else {
647*8fb009dcSAndroid Build Coastguard Worker				b.WriteString("<p>")
648*8fb009dcSAndroid Build Coastguard Worker			}
649*8fb009dcSAndroid Build Coastguard Worker			b.WriteString(string(markupParagraph(allDecls, block.Paragraph)))
650*8fb009dcSAndroid Build Coastguard Worker			b.WriteString("</p>")
651*8fb009dcSAndroid Build Coastguard Worker		case CommentOrderedListItem, CommentBulletListItem:
652*8fb009dcSAndroid Build Coastguard Worker			b.WriteString("<li>")
653*8fb009dcSAndroid Build Coastguard Worker			b.WriteString(string(markupParagraph(allDecls, block.Paragraph)))
654*8fb009dcSAndroid Build Coastguard Worker			b.WriteString("</li>")
655*8fb009dcSAndroid Build Coastguard Worker		case CommentCode:
656*8fb009dcSAndroid Build Coastguard Worker			b.WriteString("<pre>")
657*8fb009dcSAndroid Build Coastguard Worker			b.WriteString(block.Paragraph)
658*8fb009dcSAndroid Build Coastguard Worker			b.WriteString("</pre>")
659*8fb009dcSAndroid Build Coastguard Worker		default:
660*8fb009dcSAndroid Build Coastguard Worker			panic(block.Type)
661*8fb009dcSAndroid Build Coastguard Worker		}
662*8fb009dcSAndroid Build Coastguard Worker	}
663*8fb009dcSAndroid Build Coastguard Worker
664*8fb009dcSAndroid Build Coastguard Worker	closeList()
665*8fb009dcSAndroid Build Coastguard Worker	return template.HTML(b.String())
666*8fb009dcSAndroid Build Coastguard Worker}
667*8fb009dcSAndroid Build Coastguard Worker
668*8fb009dcSAndroid Build Coastguard Workerfunc markupParagraph(allDecls map[string]string, s string) template.HTML {
669*8fb009dcSAndroid Build Coastguard Worker	// TODO(davidben): Ideally the inline transforms would be unified into
670*8fb009dcSAndroid Build Coastguard Worker	// one pass, so that the HTML output of one pass does not interfere with
671*8fb009dcSAndroid Build Coastguard Worker	// the next.
672*8fb009dcSAndroid Build Coastguard Worker	ret := markupPipeWords(allDecls, s, true /* linkDecls */)
673*8fb009dcSAndroid Build Coastguard Worker	ret = markupFirstWord(ret)
674*8fb009dcSAndroid Build Coastguard Worker	ret = markupRFC(ret)
675*8fb009dcSAndroid Build Coastguard Worker	return ret
676*8fb009dcSAndroid Build Coastguard Worker}
677*8fb009dcSAndroid Build Coastguard Worker
678*8fb009dcSAndroid Build Coastguard Worker// markupPipeWords converts |s| into an HTML string, safe to be included outside
679*8fb009dcSAndroid Build Coastguard Worker// a tag, while also marking up words surrounded by |.
680*8fb009dcSAndroid Build Coastguard Workerfunc markupPipeWords(allDecls map[string]string, s string, linkDecls bool) template.HTML {
681*8fb009dcSAndroid Build Coastguard Worker	// It is safe to look for '|' in the HTML-escaped version of |s|
682*8fb009dcSAndroid Build Coastguard Worker	// below. The escaped version cannot include '|' instead tags because
683*8fb009dcSAndroid Build Coastguard Worker	// there are no tags by construction.
684*8fb009dcSAndroid Build Coastguard Worker	s = template.HTMLEscapeString(s)
685*8fb009dcSAndroid Build Coastguard Worker	ret := ""
686*8fb009dcSAndroid Build Coastguard Worker
687*8fb009dcSAndroid Build Coastguard Worker	for {
688*8fb009dcSAndroid Build Coastguard Worker		i := strings.Index(s, "|")
689*8fb009dcSAndroid Build Coastguard Worker		if i == -1 {
690*8fb009dcSAndroid Build Coastguard Worker			ret += s
691*8fb009dcSAndroid Build Coastguard Worker			break
692*8fb009dcSAndroid Build Coastguard Worker		}
693*8fb009dcSAndroid Build Coastguard Worker		ret += s[:i]
694*8fb009dcSAndroid Build Coastguard Worker		s = s[i+1:]
695*8fb009dcSAndroid Build Coastguard Worker
696*8fb009dcSAndroid Build Coastguard Worker		i = strings.Index(s, "|")
697*8fb009dcSAndroid Build Coastguard Worker		j := strings.Index(s, " ")
698*8fb009dcSAndroid Build Coastguard Worker		if i > 0 && (j == -1 || j > i) {
699*8fb009dcSAndroid Build Coastguard Worker			ret += "<tt>"
700*8fb009dcSAndroid Build Coastguard Worker			anchor, isLink := allDecls[s[:i]]
701*8fb009dcSAndroid Build Coastguard Worker			if linkDecls && isLink {
702*8fb009dcSAndroid Build Coastguard Worker				ret += fmt.Sprintf("<a href=\"%s\">%s</a>", template.HTMLEscapeString(anchor), s[:i])
703*8fb009dcSAndroid Build Coastguard Worker			} else {
704*8fb009dcSAndroid Build Coastguard Worker				ret += s[:i]
705*8fb009dcSAndroid Build Coastguard Worker			}
706*8fb009dcSAndroid Build Coastguard Worker			ret += "</tt>"
707*8fb009dcSAndroid Build Coastguard Worker			s = s[i+1:]
708*8fb009dcSAndroid Build Coastguard Worker		} else {
709*8fb009dcSAndroid Build Coastguard Worker			ret += "|"
710*8fb009dcSAndroid Build Coastguard Worker		}
711*8fb009dcSAndroid Build Coastguard Worker	}
712*8fb009dcSAndroid Build Coastguard Worker
713*8fb009dcSAndroid Build Coastguard Worker	return template.HTML(ret)
714*8fb009dcSAndroid Build Coastguard Worker}
715*8fb009dcSAndroid Build Coastguard Worker
716*8fb009dcSAndroid Build Coastguard Workerfunc markupFirstWord(s template.HTML) template.HTML {
717*8fb009dcSAndroid Build Coastguard Worker	if isCollectiveComment(string(s)) {
718*8fb009dcSAndroid Build Coastguard Worker		return s
719*8fb009dcSAndroid Build Coastguard Worker	}
720*8fb009dcSAndroid Build Coastguard Worker	start := 0
721*8fb009dcSAndroid Build Coastguard Workeragain:
722*8fb009dcSAndroid Build Coastguard Worker	end := strings.Index(string(s[start:]), " ")
723*8fb009dcSAndroid Build Coastguard Worker	if end > 0 {
724*8fb009dcSAndroid Build Coastguard Worker		end += start
725*8fb009dcSAndroid Build Coastguard Worker		w := strings.ToLower(string(s[start:end]))
726*8fb009dcSAndroid Build Coastguard Worker		// The first word was already marked up as an HTML tag. Don't
727*8fb009dcSAndroid Build Coastguard Worker		// mark it up further.
728*8fb009dcSAndroid Build Coastguard Worker		if strings.ContainsRune(w, '<') {
729*8fb009dcSAndroid Build Coastguard Worker			return s
730*8fb009dcSAndroid Build Coastguard Worker		}
731*8fb009dcSAndroid Build Coastguard Worker		if w == "a" || w == "an" {
732*8fb009dcSAndroid Build Coastguard Worker			start = end + 1
733*8fb009dcSAndroid Build Coastguard Worker			goto again
734*8fb009dcSAndroid Build Coastguard Worker		}
735*8fb009dcSAndroid Build Coastguard Worker		return s[:start] + "<span class=\"first-word\">" + s[start:end] + "</span>" + s[end:]
736*8fb009dcSAndroid Build Coastguard Worker	}
737*8fb009dcSAndroid Build Coastguard Worker	return s
738*8fb009dcSAndroid Build Coastguard Worker}
739*8fb009dcSAndroid Build Coastguard Worker
740*8fb009dcSAndroid Build Coastguard Workervar rfcRegexp = regexp.MustCompile("RFC ([0-9]+)")
741*8fb009dcSAndroid Build Coastguard Worker
742*8fb009dcSAndroid Build Coastguard Workerfunc markupRFC(html template.HTML) template.HTML {
743*8fb009dcSAndroid Build Coastguard Worker	s := string(html)
744*8fb009dcSAndroid Build Coastguard Worker	matches := rfcRegexp.FindAllStringSubmatchIndex(s, -1)
745*8fb009dcSAndroid Build Coastguard Worker	if len(matches) == 0 {
746*8fb009dcSAndroid Build Coastguard Worker		return html
747*8fb009dcSAndroid Build Coastguard Worker	}
748*8fb009dcSAndroid Build Coastguard Worker
749*8fb009dcSAndroid Build Coastguard Worker	var b strings.Builder
750*8fb009dcSAndroid Build Coastguard Worker	var idx int
751*8fb009dcSAndroid Build Coastguard Worker	for _, match := range matches {
752*8fb009dcSAndroid Build Coastguard Worker		start, end := match[0], match[1]
753*8fb009dcSAndroid Build Coastguard Worker		number := s[match[2]:match[3]]
754*8fb009dcSAndroid Build Coastguard Worker		b.WriteString(s[idx:start])
755*8fb009dcSAndroid Build Coastguard Worker		fmt.Fprintf(&b, "<a href=\"https://www.rfc-editor.org/rfc/rfc%s.html\">%s</a>", number, s[start:end])
756*8fb009dcSAndroid Build Coastguard Worker		idx = end
757*8fb009dcSAndroid Build Coastguard Worker	}
758*8fb009dcSAndroid Build Coastguard Worker	b.WriteString(s[idx:])
759*8fb009dcSAndroid Build Coastguard Worker	return template.HTML(b.String())
760*8fb009dcSAndroid Build Coastguard Worker}
761*8fb009dcSAndroid Build Coastguard Worker
762*8fb009dcSAndroid Build Coastguard Workerfunc generate(outPath string, config *Config) (map[string]string, error) {
763*8fb009dcSAndroid Build Coastguard Worker	allDecls := make(map[string]string)
764*8fb009dcSAndroid Build Coastguard Worker
765*8fb009dcSAndroid Build Coastguard Worker	headerTmpl := template.New("headerTmpl")
766*8fb009dcSAndroid Build Coastguard Worker	headerTmpl.Funcs(template.FuncMap{
767*8fb009dcSAndroid Build Coastguard Worker		"firstSentence":         firstSentence,
768*8fb009dcSAndroid Build Coastguard Worker		"markupPipeWordsNoLink": func(s string) template.HTML { return markupPipeWords(allDecls, s, false /* linkDecls */) },
769*8fb009dcSAndroid Build Coastguard Worker		"markupComment":         func(c []CommentBlock) template.HTML { return markupComment(allDecls, c) },
770*8fb009dcSAndroid Build Coastguard Worker	})
771*8fb009dcSAndroid Build Coastguard Worker	headerTmpl, err := headerTmpl.Parse(`<!DOCTYPE html>
772*8fb009dcSAndroid Build Coastguard Worker<html>
773*8fb009dcSAndroid Build Coastguard Worker  <head>
774*8fb009dcSAndroid Build Coastguard Worker    <title>BoringSSL - {{.Name}}</title>
775*8fb009dcSAndroid Build Coastguard Worker    <meta charset="utf-8">
776*8fb009dcSAndroid Build Coastguard Worker    <link rel="stylesheet" type="text/css" href="doc.css">
777*8fb009dcSAndroid Build Coastguard Worker  </head>
778*8fb009dcSAndroid Build Coastguard Worker
779*8fb009dcSAndroid Build Coastguard Worker  <body>
780*8fb009dcSAndroid Build Coastguard Worker    <div id="main">
781*8fb009dcSAndroid Build Coastguard Worker    <div class="title">
782*8fb009dcSAndroid Build Coastguard Worker      <h2>{{.Name}}</h2>
783*8fb009dcSAndroid Build Coastguard Worker      <a href="headers.html">All headers</a>
784*8fb009dcSAndroid Build Coastguard Worker    </div>
785*8fb009dcSAndroid Build Coastguard Worker
786*8fb009dcSAndroid Build Coastguard Worker    {{if .Preamble}}<div class="comment">{{.Preamble | markupComment}}</div>{{end}}
787*8fb009dcSAndroid Build Coastguard Worker
788*8fb009dcSAndroid Build Coastguard Worker    <ol class="toc">
789*8fb009dcSAndroid Build Coastguard Worker      {{range .Sections}}
790*8fb009dcSAndroid Build Coastguard Worker        {{if not .IsPrivate}}
791*8fb009dcSAndroid Build Coastguard Worker          {{if .Anchor}}<li class="header"><a href="#{{.Anchor}}">{{.Preamble | firstSentence | markupPipeWordsNoLink}}</a></li>{{end}}
792*8fb009dcSAndroid Build Coastguard Worker          {{range .Decls}}
793*8fb009dcSAndroid Build Coastguard Worker            {{if .Anchor}}<li><a href="#{{.Anchor}}"><tt>{{.Name}}</tt></a></li>{{end}}
794*8fb009dcSAndroid Build Coastguard Worker          {{end}}
795*8fb009dcSAndroid Build Coastguard Worker        {{end}}
796*8fb009dcSAndroid Build Coastguard Worker      {{end}}
797*8fb009dcSAndroid Build Coastguard Worker    </ol>
798*8fb009dcSAndroid Build Coastguard Worker
799*8fb009dcSAndroid Build Coastguard Worker    {{range .Sections}}
800*8fb009dcSAndroid Build Coastguard Worker      {{if not .IsPrivate}}
801*8fb009dcSAndroid Build Coastguard Worker        <div class="section" {{if .Anchor}}id="{{.Anchor}}"{{end}}>
802*8fb009dcSAndroid Build Coastguard Worker        {{if .Preamble}}<div class="sectionpreamble comment">{{.Preamble | markupComment}}</div>{{end}}
803*8fb009dcSAndroid Build Coastguard Worker
804*8fb009dcSAndroid Build Coastguard Worker        {{range .Decls}}
805*8fb009dcSAndroid Build Coastguard Worker          <div class="decl" {{if .Anchor}}id="{{.Anchor}}"{{end}}>
806*8fb009dcSAndroid Build Coastguard Worker            {{if .Comment}}<div class="comment">{{.Comment | markupComment}}</div>{{end}}
807*8fb009dcSAndroid Build Coastguard Worker            {{if .Decl}}<pre class="code">{{.Decl}}</pre>{{end}}
808*8fb009dcSAndroid Build Coastguard Worker          </div>
809*8fb009dcSAndroid Build Coastguard Worker        {{end}}
810*8fb009dcSAndroid Build Coastguard Worker        </div>
811*8fb009dcSAndroid Build Coastguard Worker      {{end}}
812*8fb009dcSAndroid Build Coastguard Worker    {{end}}
813*8fb009dcSAndroid Build Coastguard Worker    </div>
814*8fb009dcSAndroid Build Coastguard Worker  </body>
815*8fb009dcSAndroid Build Coastguard Worker</html>`)
816*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
817*8fb009dcSAndroid Build Coastguard Worker		return nil, err
818*8fb009dcSAndroid Build Coastguard Worker	}
819*8fb009dcSAndroid Build Coastguard Worker
820*8fb009dcSAndroid Build Coastguard Worker	headerDescriptions := make(map[string]string)
821*8fb009dcSAndroid Build Coastguard Worker	var headers []*HeaderFile
822*8fb009dcSAndroid Build Coastguard Worker
823*8fb009dcSAndroid Build Coastguard Worker	for _, section := range config.Sections {
824*8fb009dcSAndroid Build Coastguard Worker		for _, headerPath := range section.Headers {
825*8fb009dcSAndroid Build Coastguard Worker			header, err := config.parseHeader(headerPath)
826*8fb009dcSAndroid Build Coastguard Worker			if err != nil {
827*8fb009dcSAndroid Build Coastguard Worker				return nil, errors.New("while parsing " + headerPath + ": " + err.Error())
828*8fb009dcSAndroid Build Coastguard Worker			}
829*8fb009dcSAndroid Build Coastguard Worker			headerDescriptions[header.Name] = firstSentence(header.Preamble)
830*8fb009dcSAndroid Build Coastguard Worker			headers = append(headers, header)
831*8fb009dcSAndroid Build Coastguard Worker
832*8fb009dcSAndroid Build Coastguard Worker			for name, anchor := range header.AllDecls {
833*8fb009dcSAndroid Build Coastguard Worker				allDecls[name] = fmt.Sprintf("%s#%s", header.Name+".html", anchor)
834*8fb009dcSAndroid Build Coastguard Worker			}
835*8fb009dcSAndroid Build Coastguard Worker		}
836*8fb009dcSAndroid Build Coastguard Worker	}
837*8fb009dcSAndroid Build Coastguard Worker
838*8fb009dcSAndroid Build Coastguard Worker	for _, header := range headers {
839*8fb009dcSAndroid Build Coastguard Worker		filename := filepath.Join(outPath, header.Name+".html")
840*8fb009dcSAndroid Build Coastguard Worker		file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
841*8fb009dcSAndroid Build Coastguard Worker		if err != nil {
842*8fb009dcSAndroid Build Coastguard Worker			panic(err)
843*8fb009dcSAndroid Build Coastguard Worker		}
844*8fb009dcSAndroid Build Coastguard Worker		defer file.Close()
845*8fb009dcSAndroid Build Coastguard Worker		if err := headerTmpl.Execute(file, header); err != nil {
846*8fb009dcSAndroid Build Coastguard Worker			return nil, err
847*8fb009dcSAndroid Build Coastguard Worker		}
848*8fb009dcSAndroid Build Coastguard Worker	}
849*8fb009dcSAndroid Build Coastguard Worker
850*8fb009dcSAndroid Build Coastguard Worker	return headerDescriptions, nil
851*8fb009dcSAndroid Build Coastguard Worker}
852*8fb009dcSAndroid Build Coastguard Worker
853*8fb009dcSAndroid Build Coastguard Workerfunc generateIndex(outPath string, config *Config, headerDescriptions map[string]string) error {
854*8fb009dcSAndroid Build Coastguard Worker	indexTmpl := template.New("indexTmpl")
855*8fb009dcSAndroid Build Coastguard Worker	indexTmpl.Funcs(template.FuncMap{
856*8fb009dcSAndroid Build Coastguard Worker		"baseName": filepath.Base,
857*8fb009dcSAndroid Build Coastguard Worker		"headerDescription": func(header string) string {
858*8fb009dcSAndroid Build Coastguard Worker			return headerDescriptions[header]
859*8fb009dcSAndroid Build Coastguard Worker		},
860*8fb009dcSAndroid Build Coastguard Worker	})
861*8fb009dcSAndroid Build Coastguard Worker	indexTmpl, err := indexTmpl.Parse(`<!DOCTYPE html5>
862*8fb009dcSAndroid Build Coastguard Worker
863*8fb009dcSAndroid Build Coastguard Worker  <head>
864*8fb009dcSAndroid Build Coastguard Worker    <title>BoringSSL - Headers</title>
865*8fb009dcSAndroid Build Coastguard Worker    <meta charset="utf-8">
866*8fb009dcSAndroid Build Coastguard Worker    <link rel="stylesheet" type="text/css" href="doc.css">
867*8fb009dcSAndroid Build Coastguard Worker  </head>
868*8fb009dcSAndroid Build Coastguard Worker
869*8fb009dcSAndroid Build Coastguard Worker  <body>
870*8fb009dcSAndroid Build Coastguard Worker    <div id="main">
871*8fb009dcSAndroid Build Coastguard Worker      <div class="title">
872*8fb009dcSAndroid Build Coastguard Worker        <h2>BoringSSL Headers</h2>
873*8fb009dcSAndroid Build Coastguard Worker      </div>
874*8fb009dcSAndroid Build Coastguard Worker      <table>
875*8fb009dcSAndroid Build Coastguard Worker        {{range .Sections}}
876*8fb009dcSAndroid Build Coastguard Worker	  <tr class="header"><td colspan="2">{{.Name}}</td></tr>
877*8fb009dcSAndroid Build Coastguard Worker	  {{range .Headers}}
878*8fb009dcSAndroid Build Coastguard Worker	    <tr><td><a href="{{. | baseName}}.html">{{. | baseName}}</a></td><td>{{. | baseName | headerDescription}}</td></tr>
879*8fb009dcSAndroid Build Coastguard Worker	  {{end}}
880*8fb009dcSAndroid Build Coastguard Worker	{{end}}
881*8fb009dcSAndroid Build Coastguard Worker      </table>
882*8fb009dcSAndroid Build Coastguard Worker    </div>
883*8fb009dcSAndroid Build Coastguard Worker  </body>
884*8fb009dcSAndroid Build Coastguard Worker</html>`)
885*8fb009dcSAndroid Build Coastguard Worker
886*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
887*8fb009dcSAndroid Build Coastguard Worker		return err
888*8fb009dcSAndroid Build Coastguard Worker	}
889*8fb009dcSAndroid Build Coastguard Worker
890*8fb009dcSAndroid Build Coastguard Worker	file, err := os.OpenFile(filepath.Join(outPath, "headers.html"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
891*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
892*8fb009dcSAndroid Build Coastguard Worker		panic(err)
893*8fb009dcSAndroid Build Coastguard Worker	}
894*8fb009dcSAndroid Build Coastguard Worker	defer file.Close()
895*8fb009dcSAndroid Build Coastguard Worker
896*8fb009dcSAndroid Build Coastguard Worker	if err := indexTmpl.Execute(file, config); err != nil {
897*8fb009dcSAndroid Build Coastguard Worker		return err
898*8fb009dcSAndroid Build Coastguard Worker	}
899*8fb009dcSAndroid Build Coastguard Worker
900*8fb009dcSAndroid Build Coastguard Worker	return nil
901*8fb009dcSAndroid Build Coastguard Worker}
902*8fb009dcSAndroid Build Coastguard Worker
903*8fb009dcSAndroid Build Coastguard Workerfunc copyFile(outPath string, inFilePath string) error {
904*8fb009dcSAndroid Build Coastguard Worker	bytes, err := os.ReadFile(inFilePath)
905*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
906*8fb009dcSAndroid Build Coastguard Worker		return err
907*8fb009dcSAndroid Build Coastguard Worker	}
908*8fb009dcSAndroid Build Coastguard Worker	return os.WriteFile(filepath.Join(outPath, filepath.Base(inFilePath)), bytes, 0666)
909*8fb009dcSAndroid Build Coastguard Worker}
910*8fb009dcSAndroid Build Coastguard Worker
911*8fb009dcSAndroid Build Coastguard Workerfunc main() {
912*8fb009dcSAndroid Build Coastguard Worker	var (
913*8fb009dcSAndroid Build Coastguard Worker		configFlag *string = flag.String("config", "doc.config", "Location of config file")
914*8fb009dcSAndroid Build Coastguard Worker		outputDir  *string = flag.String("out", ".", "Path to the directory where the output will be written")
915*8fb009dcSAndroid Build Coastguard Worker		config     Config
916*8fb009dcSAndroid Build Coastguard Worker	)
917*8fb009dcSAndroid Build Coastguard Worker
918*8fb009dcSAndroid Build Coastguard Worker	flag.Parse()
919*8fb009dcSAndroid Build Coastguard Worker
920*8fb009dcSAndroid Build Coastguard Worker	if len(*configFlag) == 0 {
921*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("No config file given by --config\n")
922*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
923*8fb009dcSAndroid Build Coastguard Worker	}
924*8fb009dcSAndroid Build Coastguard Worker
925*8fb009dcSAndroid Build Coastguard Worker	if len(*outputDir) == 0 {
926*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("No output directory given by --out\n")
927*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
928*8fb009dcSAndroid Build Coastguard Worker	}
929*8fb009dcSAndroid Build Coastguard Worker
930*8fb009dcSAndroid Build Coastguard Worker	configBytes, err := os.ReadFile(*configFlag)
931*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
932*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("Failed to open config file: %s\n", err)
933*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
934*8fb009dcSAndroid Build Coastguard Worker	}
935*8fb009dcSAndroid Build Coastguard Worker
936*8fb009dcSAndroid Build Coastguard Worker	if err := json.Unmarshal(configBytes, &config); err != nil {
937*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("Failed to parse config file: %s\n", err)
938*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
939*8fb009dcSAndroid Build Coastguard Worker	}
940*8fb009dcSAndroid Build Coastguard Worker
941*8fb009dcSAndroid Build Coastguard Worker	headerDescriptions, err := generate(*outputDir, &config)
942*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
943*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("Failed to generate output: %s\n", err)
944*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
945*8fb009dcSAndroid Build Coastguard Worker	}
946*8fb009dcSAndroid Build Coastguard Worker
947*8fb009dcSAndroid Build Coastguard Worker	if err := generateIndex(*outputDir, &config, headerDescriptions); err != nil {
948*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("Failed to generate index: %s\n", err)
949*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
950*8fb009dcSAndroid Build Coastguard Worker	}
951*8fb009dcSAndroid Build Coastguard Worker
952*8fb009dcSAndroid Build Coastguard Worker	if err := copyFile(*outputDir, "doc.css"); err != nil {
953*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("Failed to copy static file: %s\n", err)
954*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
955*8fb009dcSAndroid Build Coastguard Worker	}
956*8fb009dcSAndroid Build Coastguard Worker}
957