aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/util/doc.go
diff options
context:
space:
mode:
Diffstat (limited to 'roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/util/doc.go')
-rw-r--r--roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/util/doc.go728
1 files changed, 728 insertions, 0 deletions
diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/util/doc.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/util/doc.go
new file mode 100644
index 000000000..987794c92
--- /dev/null
+++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/util/doc.go
@@ -0,0 +1,728 @@
+// doc generates HTML files from the comments in header files.
+//
+// doc expects to be given the path to a JSON file via the --config option.
+// From that JSON (which is defined by the Config struct) it reads a list of
+// header file locations and generates HTML files for each in the current
+// directory.
+
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "errors"
+ "flag"
+ "fmt"
+ "html/template"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// Config describes the structure of the config JSON file.
+type Config struct {
+ // BaseDirectory is a path to which other paths in the file are
+ // relative.
+ BaseDirectory string
+ Sections []ConfigSection
+}
+
+type ConfigSection struct {
+ Name string
+ // Headers is a list of paths to header files.
+ Headers []string
+}
+
+// HeaderFile is the internal representation of a header file.
+type HeaderFile struct {
+ // Name is the basename of the header file (e.g. "ex_data.html").
+ Name string
+ // Preamble contains a comment for the file as a whole. Each string
+ // is a separate paragraph.
+ Preamble []string
+ Sections []HeaderSection
+ // AllDecls maps all decls to their URL fragments.
+ AllDecls map[string]string
+}
+
+type HeaderSection struct {
+ // Preamble contains a comment for a group of functions.
+ Preamble []string
+ Decls []HeaderDecl
+ // Anchor, if non-empty, is the URL fragment to use in anchor tags.
+ Anchor string
+ // IsPrivate is true if the section contains private functions (as
+ // indicated by its name).
+ IsPrivate bool
+}
+
+type HeaderDecl struct {
+ // Comment contains a comment for a specific function. Each string is a
+ // paragraph. Some paragraph may contain \n runes to indicate that they
+ // are preformatted.
+ Comment []string
+ // Name contains the name of the function, if it could be extracted.
+ Name string
+ // Decl contains the preformatted C declaration itself.
+ Decl string
+ // Anchor, if non-empty, is the URL fragment to use in anchor tags.
+ Anchor string
+}
+
+const (
+ cppGuard = "#if defined(__cplusplus)"
+ commentStart = "/* "
+ commentEnd = " */"
+)
+
+func extractComment(lines []string, lineNo int) (comment []string, rest []string, restLineNo int, err error) {
+ if len(lines) == 0 {
+ return nil, lines, lineNo, nil
+ }
+
+ restLineNo = lineNo
+ rest = lines
+
+ if !strings.HasPrefix(rest[0], commentStart) {
+ panic("extractComment called on non-comment")
+ }
+ commentParagraph := rest[0][len(commentStart):]
+ rest = rest[1:]
+ restLineNo++
+
+ for len(rest) > 0 {
+ i := strings.Index(commentParagraph, commentEnd)
+ if i >= 0 {
+ if i != len(commentParagraph)-len(commentEnd) {
+ err = fmt.Errorf("garbage after comment end on line %d", restLineNo)
+ return
+ }
+ commentParagraph = commentParagraph[:i]
+ if len(commentParagraph) > 0 {
+ comment = append(comment, commentParagraph)
+ }
+ return
+ }
+
+ line := rest[0]
+ if !strings.HasPrefix(line, " *") {
+ err = fmt.Errorf("comment doesn't start with block prefix on line %d: %s", restLineNo, line)
+ return
+ }
+ if len(line) == 2 || line[2] != '/' {
+ line = line[2:]
+ }
+ if strings.HasPrefix(line, " ") {
+ /* Identing the lines of a paragraph marks them as
+ * preformatted. */
+ if len(commentParagraph) > 0 {
+ commentParagraph += "\n"
+ }
+ line = line[3:]
+ }
+ if len(line) > 0 {
+ commentParagraph = commentParagraph + line
+ if len(commentParagraph) > 0 && commentParagraph[0] == ' ' {
+ commentParagraph = commentParagraph[1:]
+ }
+ } else {
+ comment = append(comment, commentParagraph)
+ commentParagraph = ""
+ }
+ rest = rest[1:]
+ restLineNo++
+ }
+
+ err = errors.New("hit EOF in comment")
+ return
+}
+
+func extractDecl(lines []string, lineNo int) (decl string, rest []string, restLineNo int, err error) {
+ if len(lines) == 0 || len(lines[0]) == 0 {
+ return "", lines, lineNo, nil
+ }
+
+ rest = lines
+ restLineNo = lineNo
+
+ var stack []rune
+ for len(rest) > 0 {
+ line := rest[0]
+ for _, c := range line {
+ switch c {
+ case '(', '{', '[':
+ stack = append(stack, c)
+ case ')', '}', ']':
+ if len(stack) == 0 {
+ err = fmt.Errorf("unexpected %c on line %d", c, restLineNo)
+ return
+ }
+ var expected rune
+ switch c {
+ case ')':
+ expected = '('
+ case '}':
+ expected = '{'
+ case ']':
+ expected = '['
+ default:
+ panic("internal error")
+ }
+ if last := stack[len(stack)-1]; last != expected {
+ err = fmt.Errorf("found %c when expecting %c on line %d", c, last, restLineNo)
+ return
+ }
+ stack = stack[:len(stack)-1]
+ }
+ }
+ if len(decl) > 0 {
+ decl += "\n"
+ }
+ decl += line
+ rest = rest[1:]
+ restLineNo++
+
+ if len(stack) == 0 && (len(decl) == 0 || decl[len(decl)-1] != '\\') {
+ break
+ }
+ }
+
+ return
+}
+
+func skipLine(s string) string {
+ i := strings.Index(s, "\n")
+ if i > 0 {
+ return s[i:]
+ }
+ return ""
+}
+
+func getNameFromDecl(decl string) (string, bool) {
+ for strings.HasPrefix(decl, "#if") || strings.HasPrefix(decl, "#elif") {
+ decl = skipLine(decl)
+ }
+
+ if strings.HasPrefix(decl, "typedef ") {
+ return "", false
+ }
+
+ for _, prefix := range []string{"struct ", "enum ", "#define "} {
+ if !strings.HasPrefix(decl, prefix) {
+ continue
+ }
+
+ decl = strings.TrimPrefix(decl, prefix)
+
+ for len(decl) > 0 && decl[0] == ' ' {
+ decl = decl[1:]
+ }
+
+ // struct and enum types can be the return type of a
+ // function.
+ if prefix[0] != '#' && strings.Index(decl, "{") == -1 {
+ break
+ }
+
+ i := strings.IndexAny(decl, "( ")
+ if i < 0 {
+ return "", false
+ }
+ return decl[:i], true
+ }
+ decl = strings.TrimPrefix(decl, "OPENSSL_EXPORT ")
+ decl = strings.TrimPrefix(decl, "STACK_OF(")
+ decl = strings.TrimPrefix(decl, "LHASH_OF(")
+ i := strings.Index(decl, "(")
+ if i < 0 {
+ return "", false
+ }
+ j := strings.LastIndex(decl[:i], " ")
+ if j < 0 {
+ return "", false
+ }
+ for j+1 < len(decl) && decl[j+1] == '*' {
+ j++
+ }
+ return decl[j+1 : i], true
+}
+
+func sanitizeAnchor(name string) string {
+ return strings.Replace(name, " ", "-", -1)
+}
+
+func isPrivateSection(name string) bool {
+ return strings.HasPrefix(name, "Private functions") || strings.HasPrefix(name, "Private structures") || strings.Contains(name, "(hidden)")
+}
+
+func (config *Config) parseHeader(path string) (*HeaderFile, error) {
+ headerPath := filepath.Join(config.BaseDirectory, path)
+
+ headerFile, err := os.Open(headerPath)
+ if err != nil {
+ return nil, err
+ }
+ defer headerFile.Close()
+
+ scanner := bufio.NewScanner(headerFile)
+ var lines, oldLines []string
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+
+ lineNo := 1
+ found := false
+ for i, line := range lines {
+ if line == cppGuard {
+ lines = lines[i+1:]
+ lineNo += i + 1
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ return nil, errors.New("no C++ guard found")
+ }
+
+ if len(lines) == 0 || lines[0] != "extern \"C\" {" {
+ return nil, errors.New("no extern \"C\" found after C++ guard")
+ }
+ lineNo += 2
+ lines = lines[2:]
+
+ header := &HeaderFile{
+ Name: filepath.Base(path),
+ AllDecls: make(map[string]string),
+ }
+
+ for i, line := range lines {
+ if len(line) > 0 {
+ lines = lines[i:]
+ lineNo += i
+ break
+ }
+ }
+
+ oldLines = lines
+ if len(lines) > 0 && strings.HasPrefix(lines[0], commentStart) {
+ comment, rest, restLineNo, err := extractComment(lines, lineNo)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(rest) > 0 && len(rest[0]) == 0 {
+ if len(rest) < 2 || len(rest[1]) != 0 {
+ return nil, errors.New("preamble comment should be followed by two blank lines")
+ }
+ header.Preamble = comment
+ lineNo = restLineNo + 2
+ lines = rest[2:]
+ } else {
+ lines = oldLines
+ }
+ }
+
+ allAnchors := make(map[string]struct{})
+
+ for {
+ // Start of a section.
+ if len(lines) == 0 {
+ return nil, errors.New("unexpected end of file")
+ }
+ line := lines[0]
+ if line == cppGuard {
+ break
+ }
+
+ if len(line) == 0 {
+ return nil, fmt.Errorf("blank line at start of section on line %d", lineNo)
+ }
+
+ var section HeaderSection
+
+ if strings.HasPrefix(line, commentStart) {
+ comment, rest, restLineNo, err := extractComment(lines, lineNo)
+ if err != nil {
+ return nil, err
+ }
+ if len(rest) > 0 && len(rest[0]) == 0 {
+ anchor := sanitizeAnchor(firstSentence(comment))
+ if len(anchor) > 0 {
+ if _, ok := allAnchors[anchor]; ok {
+ return nil, fmt.Errorf("duplicate anchor: %s", anchor)
+ }
+ allAnchors[anchor] = struct{}{}
+ }
+
+ section.Preamble = comment
+ section.IsPrivate = len(comment) > 0 && isPrivateSection(comment[0])
+ section.Anchor = anchor
+ lines = rest[1:]
+ lineNo = restLineNo + 1
+ }
+ }
+
+ for len(lines) > 0 {
+ line := lines[0]
+ if len(line) == 0 {
+ lines = lines[1:]
+ lineNo++
+ break
+ }
+ if line == cppGuard {
+ return nil, errors.New("hit ending C++ guard while in section")
+ }
+
+ var comment []string
+ var decl string
+ if strings.HasPrefix(line, commentStart) {
+ comment, lines, lineNo, err = extractComment(lines, lineNo)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if len(lines) == 0 {
+ return nil, errors.New("expected decl at EOF")
+ }
+ declLineNo := lineNo
+ decl, lines, lineNo, err = extractDecl(lines, lineNo)
+ if err != nil {
+ return nil, err
+ }
+ name, ok := getNameFromDecl(decl)
+ if !ok {
+ name = ""
+ }
+ if last := len(section.Decls) - 1; len(name) == 0 && len(comment) == 0 && last >= 0 {
+ section.Decls[last].Decl += "\n" + decl
+ } else {
+ // As a matter of style, comments should start
+ // with the name of the thing that they are
+ // commenting on. We make an exception here for
+ // #defines (because we often have blocks of
+ // them) and collective comments, which are
+ // detected by starting with “The” or “These”.
+ if len(comment) > 0 &&
+ !strings.HasPrefix(comment[0], name) &&
+ !strings.HasPrefix(comment[0], "A "+name) &&
+ !strings.HasPrefix(comment[0], "An "+name) &&
+ !strings.HasPrefix(decl, "#define ") &&
+ !strings.HasPrefix(comment[0], "The ") &&
+ !strings.HasPrefix(comment[0], "These ") {
+ return nil, fmt.Errorf("Comment for %q doesn't seem to match line %s:%d\n", name, path, declLineNo)
+ }
+ anchor := sanitizeAnchor(name)
+ // TODO(davidben): Enforce uniqueness. This is
+ // skipped because #ifdefs currently result in
+ // duplicate table-of-contents entries.
+ allAnchors[anchor] = struct{}{}
+
+ header.AllDecls[name] = anchor
+
+ section.Decls = append(section.Decls, HeaderDecl{
+ Comment: comment,
+ Name: name,
+ Decl: decl,
+ Anchor: anchor,
+ })
+ }
+
+ if len(lines) > 0 && len(lines[0]) == 0 {
+ lines = lines[1:]
+ lineNo++
+ }
+ }
+
+ header.Sections = append(header.Sections, section)
+ }
+
+ return header, nil
+}
+
+func firstSentence(paragraphs []string) string {
+ if len(paragraphs) == 0 {
+ return ""
+ }
+ s := paragraphs[0]
+ i := strings.Index(s, ". ")
+ if i >= 0 {
+ return s[:i]
+ }
+ if lastIndex := len(s) - 1; s[lastIndex] == '.' {
+ return s[:lastIndex]
+ }
+ return s
+}
+
+func markupPipeWords(allDecls map[string]string, s string) template.HTML {
+ ret := ""
+
+ for {
+ i := strings.Index(s, "|")
+ if i == -1 {
+ ret += s
+ break
+ }
+ ret += s[:i]
+ s = s[i+1:]
+
+ i = strings.Index(s, "|")
+ j := strings.Index(s, " ")
+ if i > 0 && (j == -1 || j > i) {
+ ret += "<tt>"
+ anchor, isLink := allDecls[s[:i]]
+ if isLink {
+ ret += fmt.Sprintf("<a href=\"%s\">", template.HTMLEscapeString(anchor))
+ }
+ ret += s[:i]
+ if isLink {
+ ret += "</a>"
+ }
+ ret += "</tt>"
+ s = s[i+1:]
+ } else {
+ ret += "|"
+ }
+ }
+
+ return template.HTML(ret)
+}
+
+func markupFirstWord(s template.HTML) template.HTML {
+ start := 0
+again:
+ end := strings.Index(string(s[start:]), " ")
+ if end > 0 {
+ end += start
+ w := strings.ToLower(string(s[start:end]))
+ // The first word was already marked up as an HTML tag. Don't
+ // mark it up further.
+ if strings.ContainsRune(w, '<') {
+ return s
+ }
+ if w == "a" || w == "an" {
+ start = end + 1
+ goto again
+ }
+ return s[:start] + "<span class=\"first-word\">" + s[start:end] + "</span>" + s[end:]
+ }
+ return s
+}
+
+func newlinesToBR(html template.HTML) template.HTML {
+ s := string(html)
+ if !strings.Contains(s, "\n") {
+ return html
+ }
+ s = strings.Replace(s, "\n", "<br>", -1)
+ s = strings.Replace(s, " ", "&nbsp;", -1)
+ return template.HTML(s)
+}
+
+func generate(outPath string, config *Config) (map[string]string, error) {
+ allDecls := make(map[string]string)
+
+ headerTmpl := template.New("headerTmpl")
+ headerTmpl.Funcs(template.FuncMap{
+ "firstSentence": firstSentence,
+ "markupPipeWords": func(s string) template.HTML { return markupPipeWords(allDecls, s) },
+ "markupFirstWord": markupFirstWord,
+ "newlinesToBR": newlinesToBR,
+ })
+ headerTmpl, err := headerTmpl.Parse(`<!DOCTYPE html>
+<html>
+ <head>
+ <title>BoringSSL - {{.Name}}</title>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="doc.css">
+ </head>
+
+ <body>
+ <div id="main">
+ <div class="title">
+ <h2>{{.Name}}</h2>
+ <a href="headers.html">All headers</a>
+ </div>
+
+ {{range .Preamble}}<p>{{. | html | markupPipeWords}}</p>{{end}}
+
+ <ol>
+ {{range .Sections}}
+ {{if not .IsPrivate}}
+ {{if .Anchor}}<li class="header"><a href="#{{.Anchor}}">{{.Preamble | firstSentence | html | markupPipeWords}}</a></li>{{end}}
+ {{range .Decls}}
+ {{if .Anchor}}<li><a href="#{{.Anchor}}"><tt>{{.Name}}</tt></a></li>{{end}}
+ {{end}}
+ {{end}}
+ {{end}}
+ </ol>
+
+ {{range .Sections}}
+ {{if not .IsPrivate}}
+ <div class="section" {{if .Anchor}}id="{{.Anchor}}"{{end}}>
+ {{if .Preamble}}
+ <div class="sectionpreamble">
+ {{range .Preamble}}<p>{{. | html | markupPipeWords}}</p>{{end}}
+ </div>
+ {{end}}
+
+ {{range .Decls}}
+ <div class="decl" {{if .Anchor}}id="{{.Anchor}}"{{end}}>
+ {{range .Comment}}
+ <p>{{. | html | markupPipeWords | newlinesToBR | markupFirstWord}}</p>
+ {{end}}
+ <pre>{{.Decl}}</pre>
+ </div>
+ {{end}}
+ </div>
+ {{end}}
+ {{end}}
+ </div>
+ </body>
+</html>`)
+ if err != nil {
+ return nil, err
+ }
+
+ headerDescriptions := make(map[string]string)
+ var headers []*HeaderFile
+
+ for _, section := range config.Sections {
+ for _, headerPath := range section.Headers {
+ header, err := config.parseHeader(headerPath)
+ if err != nil {
+ return nil, errors.New("while parsing " + headerPath + ": " + err.Error())
+ }
+ headerDescriptions[header.Name] = firstSentence(header.Preamble)
+ headers = append(headers, header)
+
+ for name, anchor := range header.AllDecls {
+ allDecls[name] = fmt.Sprintf("%s#%s", header.Name+".html", anchor)
+ }
+ }
+ }
+
+ for _, header := range headers {
+ filename := filepath.Join(outPath, header.Name+".html")
+ file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ panic(err)
+ }
+ defer file.Close()
+ if err := headerTmpl.Execute(file, header); err != nil {
+ return nil, err
+ }
+ }
+
+ return headerDescriptions, nil
+}
+
+func generateIndex(outPath string, config *Config, headerDescriptions map[string]string) error {
+ indexTmpl := template.New("indexTmpl")
+ indexTmpl.Funcs(template.FuncMap{
+ "baseName": filepath.Base,
+ "headerDescription": func(header string) string {
+ return headerDescriptions[header]
+ },
+ })
+ indexTmpl, err := indexTmpl.Parse(`<!DOCTYPE html5>
+
+ <head>
+ <title>BoringSSL - Headers</title>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="doc.css">
+ </head>
+
+ <body>
+ <div id="main">
+ <div class="title">
+ <h2>BoringSSL Headers</h2>
+ </div>
+ <table>
+ {{range .Sections}}
+ <tr class="header"><td colspan="2">{{.Name}}</td></tr>
+ {{range .Headers}}
+ <tr><td><a href="{{. | baseName}}.html">{{. | baseName}}</a></td><td>{{. | baseName | headerDescription}}</td></tr>
+ {{end}}
+ {{end}}
+ </table>
+ </div>
+ </body>
+</html>`)
+
+ if err != nil {
+ return err
+ }
+
+ file, err := os.OpenFile(filepath.Join(outPath, "headers.html"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ panic(err)
+ }
+ defer file.Close()
+
+ if err := indexTmpl.Execute(file, config); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func copyFile(outPath string, inFilePath string) error {
+ bytes, err := ioutil.ReadFile(inFilePath)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(filepath.Join(outPath, filepath.Base(inFilePath)), bytes, 0666)
+}
+
+func main() {
+ var (
+ configFlag *string = flag.String("config", "doc.config", "Location of config file")
+ outputDir *string = flag.String("out", ".", "Path to the directory where the output will be written")
+ config Config
+ )
+
+ flag.Parse()
+
+ if len(*configFlag) == 0 {
+ fmt.Printf("No config file given by --config\n")
+ os.Exit(1)
+ }
+
+ if len(*outputDir) == 0 {
+ fmt.Printf("No output directory given by --out\n")
+ os.Exit(1)
+ }
+
+ configBytes, err := ioutil.ReadFile(*configFlag)
+ if err != nil {
+ fmt.Printf("Failed to open config file: %s\n", err)
+ os.Exit(1)
+ }
+
+ if err := json.Unmarshal(configBytes, &config); err != nil {
+ fmt.Printf("Failed to parse config file: %s\n", err)
+ os.Exit(1)
+ }
+
+ headerDescriptions, err := generate(*outputDir, &config)
+ if err != nil {
+ fmt.Printf("Failed to generate output: %s\n", err)
+ os.Exit(1)
+ }
+
+ if err := generateIndex(*outputDir, &config, headerDescriptions); err != nil {
+ fmt.Printf("Failed to generate index: %s\n", err)
+ os.Exit(1)
+ }
+
+ if err := copyFile(*outputDir, "doc.css"); err != nil {
+ fmt.Printf("Failed to copy static file: %s\n", err)
+ os.Exit(1)
+ }
+}