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