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