1/* Copyright (c) 2015, Google Inc. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15package main 16 17import ( 18 "bufio" 19 "bytes" 20 "errors" 21 "fmt" 22 "io" 23 "os" 24 "sort" 25 "strconv" 26 "strings" 27) 28 29// libraryNames must be kept in sync with the enum in err.h. The generated code 30// will contain static assertions to enforce this. 31var libraryNames = []string{ 32 "NONE", 33 "SYS", 34 "BN", 35 "RSA", 36 "DH", 37 "EVP", 38 "BUF", 39 "OBJ", 40 "PEM", 41 "DSA", 42 "X509", 43 "ASN1", 44 "CONF", 45 "CRYPTO", 46 "EC", 47 "SSL", 48 "BIO", 49 "PKCS7", 50 "PKCS8", 51 "X509V3", 52 "RAND", 53 "ENGINE", 54 "OCSP", 55 "UI", 56 "COMP", 57 "ECDSA", 58 "ECDH", 59 "HMAC", 60 "DIGEST", 61 "CIPHER", 62 "HKDF", 63 "USER", 64} 65 66// stringList is a map from uint32 -> string which can output data for a sorted 67// list as C literals. 68type stringList struct { 69 // entries is an array of keys and offsets into |stringData|. The 70 // offsets are in the bottom 15 bits of each uint32 and the key is the 71 // top 17 bits. 72 entries []uint32 73 // internedStrings contains the same strings as are in |stringData|, 74 // but allows for easy deduplication. It maps a string to its offset in 75 // |stringData|. 76 internedStrings map[string]uint32 77 stringData []byte 78} 79 80func newStringList() *stringList { 81 return &stringList{ 82 internedStrings: make(map[string]uint32), 83 } 84} 85 86// offsetMask is the bottom 15 bits. It's a mask that selects the offset from a 87// uint32 in entries. 88const offsetMask = 0x7fff 89 90func (st *stringList) Add(key uint32, value string) error { 91 if key&offsetMask != 0 { 92 return errors.New("need bottom 15 bits of the key for the offset") 93 } 94 offset, ok := st.internedStrings[value] 95 if !ok { 96 offset = uint32(len(st.stringData)) 97 if offset&offsetMask != offset { 98 return errors.New("stringList overflow") 99 } 100 st.stringData = append(st.stringData, []byte(value)...) 101 st.stringData = append(st.stringData, 0) 102 st.internedStrings[value] = offset 103 } 104 105 for _, existing := range st.entries { 106 if existing>>15 == key>>15 { 107 panic("duplicate entry") 108 } 109 } 110 st.entries = append(st.entries, key|offset) 111 return nil 112} 113 114// keySlice is a type that implements sorting of entries values. 115type keySlice []uint32 116 117func (ks keySlice) Len() int { 118 return len(ks) 119} 120 121func (ks keySlice) Less(i, j int) bool { 122 return (ks[i] >> 15) < (ks[j] >> 15) 123} 124 125func (ks keySlice) Swap(i, j int) { 126 ks[i], ks[j] = ks[j], ks[i] 127} 128 129func (st *stringList) buildList() []uint32 { 130 sort.Sort(keySlice(st.entries)) 131 return st.entries 132} 133 134type stringWriter interface { 135 io.Writer 136 WriteString(string) (int, error) 137} 138 139func (st *stringList) WriteTo(out stringWriter, name string) { 140 list := st.buildList() 141 fmt.Fprintf(os.Stderr, "%s: %d bytes of list and %d bytes of string data.\n", name, 4*len(list), len(st.stringData)) 142 143 values := "kOpenSSL" + name + "Values" 144 out.WriteString("const uint32_t " + values + "[] = {\n") 145 for _, v := range list { 146 fmt.Fprintf(out, " 0x%x,\n", v) 147 } 148 out.WriteString("};\n\n") 149 out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n") 150 151 stringData := "kOpenSSL" + name + "StringData" 152 out.WriteString("const char " + stringData + "[] =\n \"") 153 for i, c := range st.stringData { 154 if c == 0 { 155 out.WriteString("\\0\"\n \"") 156 continue 157 } 158 out.Write(st.stringData[i : i+1]) 159 } 160 out.WriteString("\";\n\n") 161} 162 163type errorData struct { 164 reasons *stringList 165 libraryMap map[string]uint32 166} 167 168func (e *errorData) readErrorDataFile(filename string) error { 169 inFile, err := os.Open(filename) 170 if err != nil { 171 return err 172 } 173 defer inFile.Close() 174 175 scanner := bufio.NewScanner(inFile) 176 comma := []byte(",") 177 178 lineNo := 0 179 for scanner.Scan() { 180 lineNo++ 181 182 line := scanner.Bytes() 183 if len(line) == 0 { 184 continue 185 } 186 parts := bytes.Split(line, comma) 187 if len(parts) != 3 { 188 return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts)) 189 } 190 libNum, ok := e.libraryMap[string(parts[0])] 191 if !ok { 192 return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename) 193 } 194 if libNum >= 64 { 195 return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename) 196 } 197 key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */) 198 if err != nil { 199 return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err) 200 } 201 if key >= 2048 { 202 return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename) 203 } 204 value := string(parts[2]) 205 206 listKey := libNum<<26 | uint32(key)<<15 207 208 err = e.reasons.Add(listKey, value) 209 if err != nil { 210 return err 211 } 212 } 213 214 return scanner.Err() 215} 216 217func main() { 218 e := &errorData{ 219 reasons: newStringList(), 220 libraryMap: make(map[string]uint32), 221 } 222 for i, name := range libraryNames { 223 e.libraryMap[name] = uint32(i) + 1 224 } 225 226 cwd, err := os.Open(".") 227 if err != nil { 228 panic(err) 229 } 230 names, err := cwd.Readdirnames(-1) 231 if err != nil { 232 panic(err) 233 } 234 235 sort.Strings(names) 236 for _, name := range names { 237 if !strings.HasSuffix(name, ".errordata") { 238 continue 239 } 240 if err := e.readErrorDataFile(name); err != nil { 241 panic(err) 242 } 243 } 244 245 out := os.Stdout 246 247 out.WriteString(`/* Copyright (c) 2015, Google Inc. 248 * 249 * Permission to use, copy, modify, and/or distribute this software for any 250 * purpose with or without fee is hereby granted, provided that the above 251 * copyright notice and this permission notice appear in all copies. 252 * 253 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 254 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 255 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 256 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 257 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 258 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 259 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 260 261 /* This file was generated by err_data_generate.go. */ 262 263#include <openssl/base.h> 264#include <openssl/err.h> 265#include <openssl/type_check.h> 266 267 268`) 269 270 for i, name := range libraryNames { 271 fmt.Fprintf(out, "OPENSSL_COMPILE_ASSERT(ERR_LIB_%s == %d, library_values_changed_%d);\n", name, i+1, i+1) 272 } 273 fmt.Fprintf(out, "OPENSSL_COMPILE_ASSERT(ERR_NUM_LIBS == %d, library_values_changed_num);\n", len(libraryNames)+1) 274 out.WriteString("\n") 275 276 e.reasons.WriteTo(out, "Reason") 277} 278