diff options
author | Adam Langley <agl@google.com> | 2015-01-22 14:27:53 -0800 |
---|---|---|
committer | Adam Langley <agl@google.com> | 2015-01-30 16:52:14 -0800 |
commit | d9e397b599b13d642138480a28c14db7a136bf05 (patch) | |
tree | 34bab61dc4ce323b123ad4614dbc07e86ea2f9ef /src/util | |
download | external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.zip external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.gz external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.bz2 |
Initial commit of BoringSSL for Android.
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/32-bit-toolchain.cmake | 7 | ||||
-rw-r--r-- | src/util/aarch64-toolchain.cmake | 6 | ||||
-rw-r--r-- | src/util/all_tests.sh | 85 | ||||
-rw-r--r-- | src/util/arm-toolchain.cmake | 6 | ||||
-rw-r--r-- | src/util/clang-toolchain.cmake | 2 | ||||
-rw-r--r-- | src/util/doc.config | 49 | ||||
-rw-r--r-- | src/util/doc.css | 67 | ||||
-rw-r--r-- | src/util/doc.go | 623 | ||||
-rw-r--r-- | src/util/make_errors.go | 462 |
9 files changed, 1307 insertions, 0 deletions
diff --git a/src/util/32-bit-toolchain.cmake b/src/util/32-bit-toolchain.cmake new file mode 100644 index 0000000..f3a10b7 --- /dev/null +++ b/src/util/32-bit-toolchain.cmake @@ -0,0 +1,7 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR "x86") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -msse2" CACHE STRING "c++ flags") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -msse2" CACHE STRING "c flags") +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32 -msse2" CACHE STRING "asm flags") diff --git a/src/util/aarch64-toolchain.cmake b/src/util/aarch64-toolchain.cmake new file mode 100644 index 0000000..77f33ab --- /dev/null +++ b/src/util/aarch64-toolchain.cmake @@ -0,0 +1,6 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR "aarch64") +set(CMAKE_CXX_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++") +set(CMAKE_C_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc") +set(CMAKE_EXE_LINKER_FLAGS "-static") diff --git a/src/util/all_tests.sh b/src/util/all_tests.sh new file mode 100644 index 0000000..bcb5c24 --- /dev/null +++ b/src/util/all_tests.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +# Copyright (c) 2014, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +SRC=.. +if [ "$#" -ge 1 ]; then + SRC=$1 +fi + +TESTS=" +./crypto/base64/base64_test +./crypto/bio/bio_test +./crypto/bn/bn_test +./crypto/bytestring/bytestring_test +./crypto/cipher/aead_test aes-128-gcm $SRC/crypto/cipher/test/aes_128_gcm_tests.txt +./crypto/cipher/aead_test aes-128-key-wrap $SRC/crypto/cipher/test/aes_128_key_wrap_tests.txt +./crypto/cipher/aead_test aes-256-gcm $SRC/crypto/cipher/test/aes_256_gcm_tests.txt +./crypto/cipher/aead_test aes-256-key-wrap $SRC/crypto/cipher/test/aes_256_key_wrap_tests.txt +./crypto/cipher/aead_test chacha20-poly1305 $SRC/crypto/cipher/test/chacha20_poly1305_tests.txt +./crypto/cipher/aead_test rc4-md5-tls $SRC/crypto/cipher/test/rc4_md5_tls_tests.txt +./crypto/cipher/aead_test rc4-sha1-tls $SRC/crypto/cipher/test/rc4_sha1_tls_tests.txt +./crypto/cipher/aead_test aes-128-cbc-sha1-tls $SRC/crypto/cipher/test/aes_128_cbc_sha1_tls_tests.txt +./crypto/cipher/aead_test aes-128-cbc-sha1-tls-implicit-iv $SRC/crypto/cipher/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt +./crypto/cipher/aead_test aes-128-cbc-sha256-tls $SRC/crypto/cipher/test/aes_128_cbc_sha256_tls_tests.txt +./crypto/cipher/aead_test aes-256-cbc-sha1-tls $SRC/crypto/cipher/test/aes_256_cbc_sha1_tls_tests.txt +./crypto/cipher/aead_test aes-256-cbc-sha1-tls-implicit-iv $SRC/crypto/cipher/test/aes_256_cbc_sha1_tls_implicit_iv_tests.txt +./crypto/cipher/aead_test aes-256-cbc-sha256-tls $SRC/crypto/cipher/test/aes_256_cbc_sha256_tls_tests.txt +./crypto/cipher/aead_test aes-256-cbc-sha384-tls $SRC/crypto/cipher/test/aes_256_cbc_sha384_tls_tests.txt +./crypto/cipher/aead_test des-ede3-cbc-sha1-tls $SRC/crypto/cipher/test/des_ede3_cbc_sha1_tls_tests.txt +./crypto/cipher/aead_test des-ede3-cbc-sha1-tls-implicit-iv $SRC/crypto/cipher/test/des_ede3_cbc_sha1_tls_implicit_iv_tests.txt +./crypto/cipher/aead_test rc4-md5-ssl3 $SRC/crypto/cipher/test/rc4_md5_ssl3_tests.txt +./crypto/cipher/aead_test rc4-sha1-ssl3 $SRC/crypto/cipher/test/rc4_sha1_ssl3_tests.txt +./crypto/cipher/aead_test aes-128-cbc-sha1-ssl3 $SRC/crypto/cipher/test/aes_128_cbc_sha1_ssl3_tests.txt +./crypto/cipher/aead_test aes-256-cbc-sha1-ssl3 $SRC/crypto/cipher/test/aes_256_cbc_sha1_ssl3_tests.txt +./crypto/cipher/aead_test des-ede3-cbc-sha1-ssl3 $SRC/crypto/cipher/test/des_ede3_cbc_sha1_ssl3_tests.txt +./crypto/cipher/cipher_test $SRC/crypto/cipher/test/cipher_test.txt +./crypto/constant_time_test +./crypto/dh/dh_test +./crypto/digest/digest_test +./crypto/dsa/dsa_test +./crypto/ec/ec_test +./crypto/ec/example_mul +./crypto/ecdsa/ecdsa_test +./crypto/err/err_test +./crypto/evp/evp_test +./crypto/evp/pbkdf_test +./crypto/hkdf/hkdf_test +./crypto/hmac/hmac_test +./crypto/lhash/lhash_test +./crypto/modes/gcm_test +./crypto/pkcs8/pkcs12_test +./crypto/rsa/rsa_test +./crypto/x509/pkcs7_test +./crypto/x509v3/tab_test +./crypto/x509v3/v3name_test +./ssl/pqueue/pqueue_test +./ssl/ssl_test +" + +IFS=$'\n' +for bin in $TESTS; do + echo $bin + out=$(bash -c "$bin" | tail -n 1) + if [ $? -ne 0 ]; then + echo $bin failed to complete. + exit 1 + fi + + if [ "x$out" != "xPASS" ]; then + echo $bin failed to print PASS on the last line. + exit 1 + fi +done diff --git a/src/util/arm-toolchain.cmake b/src/util/arm-toolchain.cmake new file mode 100644 index 0000000..2dfd2bd --- /dev/null +++ b/src/util/arm-toolchain.cmake @@ -0,0 +1,6 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR "arm") +set(CMAKE_CXX_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++") +set(CMAKE_C_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc") +set(CMAKE_EXE_LINKER_FLAGS "-static") diff --git a/src/util/clang-toolchain.cmake b/src/util/clang-toolchain.cmake new file mode 100644 index 0000000..8d81379 --- /dev/null +++ b/src/util/clang-toolchain.cmake @@ -0,0 +1,2 @@ +set(CMAKE_CXX_COMPILER "/agl/chromium/src/third_party/llvm-build/Release+Asserts/bin/clang++") +set(CMAKE_C_COMPILER "/agl/chromium/src/third_party/llvm-build/Release+Asserts/bin/clang") diff --git a/src/util/doc.config b/src/util/doc.config new file mode 100644 index 0000000..62db0f3 --- /dev/null +++ b/src/util/doc.config @@ -0,0 +1,49 @@ +{ + "BaseDirectory": "..", + "Sections": [{ + "Name": "Low-level infrastructure", + "Headers": [ + "include/openssl/base64.h", + "include/openssl/bio.h", + "include/openssl/buf.h", + "include/openssl/bytestring.h", + "include/openssl/err.h", + "include/openssl/cpu.h", + "include/openssl/ex_data.h", + "include/openssl/lhash.h", + "include/openssl/mem.h", + "include/openssl/obj.h", + "include/openssl/stack.h", + "include/openssl/thread.h", + "include/openssl/time_support.h" + ] + },{ + "Name": "Low-level crypto primitives", + "Headers": [ + "include/openssl/aes.h", + "include/openssl/bn.h", + "include/openssl/des.h", + "include/openssl/dh.h", + "include/openssl/dsa.h", + "include/openssl/ec.h", + "include/openssl/ec_key.h", + "include/openssl/ecdh.h", + "include/openssl/ecdsa.h", + "include/openssl/engine.h", + "include/openssl/hmac.h", + "include/openssl/md5.h", + "include/openssl/modes.h", + "include/openssl/rc4.h", + "include/openssl/rsa.h", + "include/openssl/sha.h" + ] + },{ + "Name": "Crypto interfaces", + "Headers": [ + "include/openssl/digest.h", + "include/openssl/cipher.h", + "include/openssl/aead.h", + "include/openssl/evp.h" + ] + }] +} diff --git a/src/util/doc.css b/src/util/doc.css new file mode 100644 index 0000000..ab653e4 --- /dev/null +++ b/src/util/doc.css @@ -0,0 +1,67 @@ +#main { + margin-left: auto; + margin-right: auto; + max-width: 55em; + text-align: justify; +} + +h2 { + font-family: monospace; + margin-bottom: 2em; + background-color: #b2c9db; + padding: 7px; + border-radius: 7px; +} + +ol { + list-style: none; + margin-bottom: 4em; +} + +li a { + color: black; +} + +li.header { + margin-top: 1em; + margin-bottom: 0.3em; + font-weight: bold; +} + +div.decl p:first-child { + margin-top: 2.5em; +} + +div.decl p:first-child .first-word { + font-weight: bold; + font-family: monospace; +} + +.sectionpreamble { + margin-top: 5em; +} + +.sectionpreamble p:first-child { + font-weight: bold; + font-size: 1.5em; +} + +.section pre { + background-color: #b2c9db; + padding: 5px; + border-radius: 5px; +} + +td { + padding: 2px; +} + +tr:nth-child(even) { + background-color: #eee; +} + +tr.header td { + font-weight: bold; + padding-top: 1em; + padding-bottom: 0.5em; +} diff --git a/src/util/doc.go b/src/util/doc.go new file mode 100644 index 0000000..7c96af8 --- /dev/null +++ b/src/util/doc.go @@ -0,0 +1,623 @@ +// 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 +} + +type HeaderSection struct { + // Preamble contains a comment for a group of functions. + Preamble []string + Decls []HeaderDecl + // Num is just the index of the section. It's included in order to help + // text/template generate anchors. + Num int + // 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 + // Num is an index for the declaration, but the value is unique for all + // declarations in a HeaderFile. It's included in order to help + // text/template generate anchors. + Num int +} + +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 + } + 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 { + 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 skipPast(s, skip string) string { + i := strings.Index(s, skip) + if i > 0 { + return s[len(skip):] + } + return s +} + +func getNameFromDecl(decl string) (string, bool) { + if strings.HasPrefix(decl, "struct ") { + return "", false + } + decl = skipPast(decl, "STACK_OF(") + decl = skipPast(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 (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 := 0 + found := false + for i, line := range lines { + lineNo++ + if line == cppGuard { + lines = lines[i+1:] + lineNo++ + 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), + } + + for i, line := range lines { + lineNo++ + if len(line) > 0 { + lines = lines[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 + } + } + + var sectionNumber, declNumber int + + 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) + } + + section := HeaderSection{ + Num: sectionNumber, + } + sectionNumber++ + + 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 { + section.Preamble = comment + section.IsPrivate = len(comment) > 0 && strings.HasPrefix(comment[0], "Private functions") + 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") + } + 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 { + section.Decls = append(section.Decls, HeaderDecl{ + Comment: comment, + Name: name, + Decl: decl, + Num: declNumber, + }) + declNumber++ + } + + 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(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>" + ret += s[:i] + 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])) + if w == "a" || w == "an" || w == "deprecated:" { + 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, " ", " ", -1) + return template.HTML(s) +} + +func generate(outPath string, config *Config) (map[string]string, error) { + headerTmpl := template.New("headerTmpl") + headerTmpl.Funcs(template.FuncMap{ + "firstSentence": firstSentence, + "markupPipeWords": markupPipeWords, + "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"> + <h2>{{.Name}}</h2> + + {{range .Preamble}}<p>{{. | html | markupPipeWords}}</p>{{end}} + + <ol> + {{range .Sections}} + {{if not .IsPrivate}} + {{if .Preamble}}<li class="header"><a href="#section-{{.Num}}">{{.Preamble | firstSentence | html | markupPipeWords}}</a></li>{{end}} + {{range .Decls}} + {{if .Name}}<li><a href="#decl-{{.Num}}"><tt>{{.Name}}</tt></a></li>{{end}} + {{end}} + {{end}} + {{end}} + </ol> + + {{range .Sections}} + {{if not .IsPrivate}} + <div class="section"> + {{if .Preamble}} + <div class="sectionpreamble"> + <a name="section-{{.Num}}"> + {{range .Preamble}}<p>{{. | html | markupPipeWords}}</p>{{end}} + </a> + </div> + {{end}} + + {{range .Decls}} + <div class="decl"> + <a name="decl-{{.Num}}"> + {{range .Comment}} + <p>{{. | html | markupPipeWords | newlinesToBR | markupFirstWord}}</p> + {{end}} + <pre>{{.Decl}}</pre> + </a> + </div> + {{end}} + </div> + {{end}} + {{end}} + </div> + </body> +</html>`) + if err != nil { + return nil, err + } + + headerDescriptions := make(map[string]string) + + 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) + 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"> + <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 main() { + var ( + configFlag *string = flag.String("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) + } +} diff --git a/src/util/make_errors.go b/src/util/make_errors.go new file mode 100644 index 0000000..5fd75e2 --- /dev/null +++ b/src/util/make_errors.go @@ -0,0 +1,462 @@ +// Copyright (c) 2014, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +package main + +import ( + "bufio" + "errors" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "unicode" +) + +// ssl.h reserves values 1000 and above for error codes corresponding to +// alerts. If automatically assigned reason codes exceed this value, this script +// will error. This must be kept in sync with SSL_AD_REASON_OFFSET in ssl.h. +const reservedReasonCode = 1000 + +var resetFlag *bool = flag.Bool("reset", false, "If true, ignore current assignments and reassign from scratch") + +func makeErrors(reset bool) error { + dirName, err := os.Getwd() + if err != nil { + return err + } + + lib := filepath.Base(dirName) + headerPath, err := findHeader(lib + ".h") + if err != nil { + return err + } + sourcePath := lib + "_error.c" + + headerFile, err := os.Open(headerPath) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("No header %s. Run in the right directory or touch the file.", headerPath) + } + + return err + } + + prefix := strings.ToUpper(lib) + functions, reasons, err := parseHeader(prefix, headerFile) + headerFile.Close() + + if reset { + err = nil + functions = make(map[string]int) + // Retain any reason codes above reservedReasonCode. + newReasons := make(map[string]int) + for key, value := range reasons { + if value >= reservedReasonCode { + newReasons[key] = value + } + } + reasons = newReasons + } + + if err != nil { + return err + } + + dir, err := os.Open(".") + if err != nil { + return err + } + defer dir.Close() + + filenames, err := dir.Readdirnames(-1) + if err != nil { + return err + } + + for _, name := range filenames { + if !strings.HasSuffix(name, ".c") || name == sourcePath { + continue + } + + if err := addFunctionsAndReasons(functions, reasons, name, prefix); err != nil { + return err + } + } + + assignNewValues(functions, -1) + assignNewValues(reasons, reservedReasonCode) + + headerFile, err = os.Open(headerPath) + if err != nil { + return err + } + defer headerFile.Close() + + newHeaderFile, err := os.OpenFile(headerPath+".tmp", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) + if err != nil { + return err + } + defer newHeaderFile.Close() + + if err := writeHeaderFile(newHeaderFile, headerFile, prefix, functions, reasons); err != nil { + return err + } + os.Rename(headerPath+".tmp", headerPath) + + sourceFile, err := os.OpenFile(sourcePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer sourceFile.Close() + + fmt.Fprintf(sourceFile, `/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/err.h> + +#include <openssl/%s.h> + +const ERR_STRING_DATA %s_error_string_data[] = { +`, lib, prefix) + outputStrings(sourceFile, lib, typeFunctions, functions) + outputStrings(sourceFile, lib, typeReasons, reasons) + + sourceFile.WriteString(" {0, NULL},\n};\n") + + return nil +} + +func findHeader(basename string) (path string, err error) { + includeDir := filepath.Join("..", "include") + + fi, err := os.Stat(includeDir) + if err != nil && os.IsNotExist(err) { + includeDir = filepath.Join("..", includeDir) + fi, err = os.Stat(includeDir) + } + if err != nil { + return "", errors.New("cannot find path to include directory") + } + if !fi.IsDir() { + return "", errors.New("include node is not a directory") + } + return filepath.Join(includeDir, "openssl", basename), nil +} + +type assignment struct { + key string + value int +} + +type assignmentsSlice []assignment + +func (a assignmentsSlice) Len() int { + return len(a) +} + +func (a assignmentsSlice) Less(i, j int) bool { + return a[i].value < a[j].value +} + +func (a assignmentsSlice) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func outputAssignments(w io.Writer, assignments map[string]int) { + var sorted assignmentsSlice + + for key, value := range assignments { + sorted = append(sorted, assignment{key, value}) + } + + sort.Sort(sorted) + + for _, assignment := range sorted { + fmt.Fprintf(w, "#define %s %d\n", assignment.key, assignment.value) + } +} + +func parseDefineLine(line, lib string) (typ int, key string, value int, ok bool) { + if !strings.HasPrefix(line, "#define ") { + return + } + + fields := strings.Fields(line) + if len(fields) != 3 { + return + } + + funcPrefix := lib + "_F_" + reasonPrefix := lib + "_R_" + + key = fields[1] + switch { + case strings.HasPrefix(key, funcPrefix): + typ = typeFunctions + case strings.HasPrefix(key, reasonPrefix): + typ = typeReasons + default: + return + } + + var err error + if value, err = strconv.Atoi(fields[2]); err != nil { + return + } + + ok = true + return +} + +func writeHeaderFile(w io.Writer, headerFile io.Reader, lib string, functions, reasons map[string]int) error { + var last []byte + var haveLast, sawDefine bool + newLine := []byte("\n") + + scanner := bufio.NewScanner(headerFile) + for scanner.Scan() { + line := scanner.Text() + _, _, _, ok := parseDefineLine(line, lib) + if ok { + sawDefine = true + continue + } + + if haveLast { + w.Write(last) + w.Write(newLine) + } + + if len(line) > 0 || !sawDefine { + last = []byte(line) + haveLast = true + } else { + haveLast = false + } + sawDefine = false + } + + if err := scanner.Err(); err != nil { + return err + } + + outputAssignments(w, functions) + outputAssignments(w, reasons) + w.Write(newLine) + + if haveLast { + w.Write(last) + w.Write(newLine) + } + + return nil +} + +const ( + typeFunctions = iota + typeReasons +) + +func outputStrings(w io.Writer, lib string, ty int, assignments map[string]int) { + lib = strings.ToUpper(lib) + prefixLen := len(lib + "_F_") + + keys := make([]string, 0, len(assignments)) + for key := range assignments { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, key := range keys { + var pack string + if ty == typeFunctions { + pack = key + ", 0" + } else { + pack = "0, " + key + } + + fmt.Fprintf(w, " {ERR_PACK(ERR_LIB_%s, %s), \"%s\"},\n", lib, pack, key[prefixLen:]) + } +} + +func assignNewValues(assignments map[string]int, reserved int) { + max := 99 + + for _, value := range assignments { + if reserved >= 0 && value >= reserved { + continue + } + if value > max { + max = value + } + } + + max++ + + for key, value := range assignments { + if value == -1 { + if reserved >= 0 && max >= reserved { + // If this happens, try passing + // -reset. Otherwise bump up reservedReasonCode. + panic("Automatically-assigned values exceeded limit!") + } + assignments[key] = max + max++ + } + } +} + +func handleDeclareMacro(line, join, macroName string, m map[string]int) { + if i := strings.Index(line, macroName); i >= 0 { + contents := line[i+len(macroName):] + if i := strings.Index(contents, ")"); i >= 0 { + contents = contents[:i] + args := strings.Split(contents, ",") + for i := range args { + args[i] = strings.TrimSpace(args[i]) + } + if len(args) != 2 { + panic("Bad macro line: " + line) + } + token := args[0] + join + args[1] + if _, ok := m[token]; !ok { + m[token] = -1 + } + } + } +} + +func addFunctionsAndReasons(functions, reasons map[string]int, filename, prefix string) error { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + prefix += "_" + reasonPrefix := prefix + "R_" + var currentFunction string + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + + if len(line) > 0 && unicode.IsLetter(rune(line[0])) { + /* Function start */ + fields := strings.Fields(line) + for _, field := range fields { + if i := strings.Index(field, "("); i != -1 { + f := field[:i] + // The return type of some functions is + // a macro that contains a "(". + if f == "STACK_OF" { + continue + } + currentFunction = f + for len(currentFunction) > 0 && currentFunction[0] == '*' { + currentFunction = currentFunction[1:] + } + break + } + } + } + + if strings.Contains(line, "OPENSSL_PUT_ERROR(") { + functionToken := prefix + "F_" + currentFunction + if _, ok := functions[functionToken]; !ok { + functions[functionToken] = -1 + } + } + + handleDeclareMacro(line, "_R_", "OPENSSL_DECLARE_ERROR_REASON(", reasons) + handleDeclareMacro(line, "_F_", "OPENSSL_DECLARE_ERROR_FUNCTION(", functions) + + for len(line) > 0 { + i := strings.Index(line, prefix) + if i == -1 { + break + } + + line = line[i:] + end := strings.IndexFunc(line, func(r rune) bool { + return !(r == '_' || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) + }) + if end == -1 { + end = len(line) + } + + var token string + token, line = line[:end], line[end:] + + switch { + case strings.HasPrefix(token, reasonPrefix): + if _, ok := reasons[token]; !ok { + reasons[token] = -1 + } + } + } + } + + return scanner.Err() +} + +func parseHeader(lib string, file io.Reader) (functions, reasons map[string]int, err error) { + functions = make(map[string]int) + reasons = make(map[string]int) + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + typ, key, value, ok := parseDefineLine(scanner.Text(), lib) + if !ok { + continue + } + + switch typ { + case typeFunctions: + functions[key] = value + case typeReasons: + reasons[key] = value + default: + panic("internal error") + } + } + + err = scanner.Err() + return +} + +func main() { + flag.Parse() + + if err := makeErrors(*resetFlag); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) + } +} |