blob: 3c94766cd46b3a7d29ce80910efe5db774358b66 [file] [log] [blame]
Adam Langley95c29f32014-06-20 12:00:00 -07001// Copyright (c) 2014, 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 "flag"
20 "fmt"
21 "io"
22 "os"
23 "path/filepath"
24 "sort"
25 "strconv"
26 "strings"
27 "unicode"
28)
29
30var resetFlag *bool = flag.Bool("reset", false, "If true, ignore current assignments and reassign from scratch")
31
32func makeErrors(reset bool) error {
33 dirName, err := os.Getwd()
34 if err != nil {
35 return err
36 }
37
38 lib := filepath.Base(dirName)
39 headerPath := lib + ".h"
40 sourcePath := lib + "_error.c"
41
42 headerFile, err := os.Open(headerPath)
43 if err != nil {
44 if os.IsNotExist(err) {
45 return fmt.Errorf("No %s in current directory. Run in the right directory or touch the file.", headerPath)
46 }
47
48 return err
49 }
50
51 prefix := strings.ToUpper(lib)
52 functions, reasons, err := parseHeader(prefix, headerFile)
53 headerFile.Close()
54
55 if reset {
56 err = nil
57 functions = make(map[string]int)
58 reasons = make(map[string]int)
59 }
60
61 if err != nil {
62 return err
63 }
64
65 dir, err := os.Open(".")
66 if err != nil {
67 return err
68 }
69 defer dir.Close()
70
71 filenames, err := dir.Readdirnames(-1)
72 if err != nil {
73 return err
74 }
75
76 for _, name := range filenames {
77 if !strings.HasSuffix(name, ".c") {
78 continue
79 }
80
81 if err := addFunctionsAndReasons(functions, reasons, name, prefix); err != nil {
82 return err
83 }
84 }
85
86 assignNewValues(functions)
87 assignNewValues(reasons)
88
89 headerFile, err = os.Open(headerPath)
90 if err != nil {
91 return err
92 }
93 defer headerFile.Close()
94
95 newHeaderFile, err := os.OpenFile(headerPath + ".tmp", os.O_CREATE | os.O_WRONLY | os.O_TRUNC, 0666)
96 if err != nil {
97 return err
98 }
99 defer newHeaderFile.Close()
100
101 if err := writeHeaderFile(newHeaderFile, headerFile, prefix, functions, reasons); err != nil {
102 return err
103 }
104 os.Rename(headerPath + ".tmp", headerPath)
105
106 sourceFile, err := os.OpenFile(sourcePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
107 if err != nil {
108 return err
109 }
110 defer sourceFile.Close()
111
112 fmt.Fprintf(sourceFile, `/* Copyright (c) 2014, Google Inc.
113 *
114 * Permission to use, copy, modify, and/or distribute this software for any
115 * purpose with or without fee is hereby granted, provided that the above
116 * copyright notice and this permission notice appear in all copies.
117 *
118 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
119 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
120 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
121 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
122 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
123 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
124 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
125
126#include <openssl/err.h>
127
128#include "%s.h"
129
130const ERR_STRING_DATA %s_error_string_data[] = {
131`, lib, prefix)
132 outputStrings(sourceFile, lib, typeFunctions, functions)
133 outputStrings(sourceFile, lib, typeReasons, reasons)
134
135 sourceFile.WriteString(" {0, NULL},\n};\n")
136
137 return nil
138}
139
140type assignment struct {
141 key string
142 value int
143}
144
145type assignmentsSlice []assignment
146
147func (a assignmentsSlice) Len() int {
148 return len(a)
149}
150
151func (a assignmentsSlice) Less(i, j int) bool {
152 return a[i].value < a[j].value
153}
154
155func (a assignmentsSlice) Swap(i, j int) {
156 a[i], a[j] = a[j], a[i]
157}
158
159func outputAssignments(w io.Writer, assignments map[string]int) {
160 var sorted assignmentsSlice
161
162 for key, value := range assignments {
163 sorted = append(sorted, assignment{key, value})
164 }
165
166 sort.Sort(sorted)
167
168 for _, assignment := range sorted {
169 fmt.Fprintf(w, "#define %s %d\n", assignment.key, assignment.value)
170 }
171}
172
173func parseDefineLine(line, lib string) (typ int, key string, value int, ok bool) {
174 if !strings.HasPrefix(line, "#define ") {
175 return
176 }
177
178 fields := strings.Fields(line)
179 if len(fields) != 3 {
180 return
181 }
182
183 funcPrefix := lib + "_F_"
184 reasonPrefix := lib + "_R_"
185
186 key = fields[1]
187 switch {
188 case strings.HasPrefix(key, funcPrefix):
189 typ = typeFunctions
190 case strings.HasPrefix(key, reasonPrefix):
191 typ = typeReasons
192 default:
193 return
194 }
195
196 var err error
197 if value, err = strconv.Atoi(fields[2]); err != nil {
198 return
199 }
200
201 ok = true
202 return
203}
204
205func writeHeaderFile(w io.Writer, headerFile io.Reader, lib string, functions, reasons map[string]int) error {
206 var last []byte
207 var haveLast, sawDefine bool
208 newLine := []byte("\n")
209
210 scanner := bufio.NewScanner(headerFile)
211 for scanner.Scan() {
212 line := scanner.Text()
213 _, _, _, ok := parseDefineLine(line, lib)
214 if ok {
215 sawDefine = true
216 continue
217 }
218
219 if haveLast {
220 w.Write(last)
221 w.Write(newLine)
222 }
223
224 if len(line) > 0 || !sawDefine {
225 last = []byte(line)
226 haveLast = true
227 } else {
228 haveLast = false
229 }
230 sawDefine = false
231 }
232
233 if err := scanner.Err(); err != nil {
234 return err
235 }
236
237 outputAssignments(w, functions)
238 outputAssignments(w, reasons)
239 w.Write(newLine)
240
241 if haveLast {
242 w.Write(last)
243 w.Write(newLine)
244 }
245
246 return nil
247}
248
249const (
250 typeFunctions = iota
251 typeReasons
252)
253
254func outputStrings(w io.Writer, lib string, ty int, assignments map[string]int) {
255 lib = strings.ToUpper(lib)
256 prefixLen := len(lib + "_F_")
257
258 keys := make([]string, 0, len(assignments))
259 for key := range assignments {
260 keys = append(keys, key)
261 }
262 sort.Strings(keys)
263
264 for _, key := range keys {
265 var pack string
266 if ty == typeFunctions {
267 pack = key + ", 0"
268 } else {
269 pack = "0, " + key
270 }
271
272 fmt.Fprintf(w, " {ERR_PACK(ERR_LIB_%s, %s), \"%s\"},\n", lib, pack, key[prefixLen:])
273 }
274}
275
276func assignNewValues(assignments map[string]int) {
277 max := 99
278
279 for _, value := range assignments {
280 if value > max {
281 max = value
282 }
283 }
284
285 max++
286
287 for key, value := range assignments {
288 if value == -1 {
289 assignments[key] = max
290 max++
291 }
292 }
293}
294
295func handleDeclareMacro(line, join, macroName string, m map[string]int) {
296 if i := strings.Index(line, macroName); i >= 0 {
297 contents := line[i + len(macroName):]
298 if i := strings.Index(contents, ")"); i >= 0 {
299 contents = contents[:i]
300 args := strings.Split(contents, ",")
301 for i := range args {
302 args[i] = strings.TrimSpace(args[i])
303 }
304 if len(args) != 2 {
305 panic("Bad macro line: " + line)
306 }
307 token := args[0] + join + args[1]
308 if _, ok := m[token]; !ok {
309 m[token] = -1
310 }
311 }
312 }
313}
314
315func addFunctionsAndReasons(functions, reasons map[string]int, filename, prefix string) error {
316 file, err := os.Open(filename)
317 if err != nil {
318 return err
319 }
320 defer file.Close()
321
322 prefix += "_"
323 reasonPrefix := prefix + "R_"
324 var currentFunction string
325
326 scanner := bufio.NewScanner(file)
327 for scanner.Scan() {
328 line := scanner.Text()
329
330 if len(line) > 0 && unicode.IsLetter(rune(line[0])) {
331 /* Function start */
332 fields := strings.Fields(line)
333 for _, field := range fields {
334 if i := strings.Index(field, "("); i != -1 {
335 f := field[:i]
336 // The return type of some functions is
337 // a macro that contains a "(".
338 if f == "STACK_OF" {
339 continue
340 }
341 currentFunction = f
342 for len(currentFunction) > 0 && currentFunction[0] == '*' {
343 currentFunction = currentFunction[1:]
344 }
345 break
346 }
347 }
348 }
349
350 if strings.Contains(line, "OPENSSL_PUT_ERROR(") {
351 functionToken := prefix + "F_" + currentFunction
352 if _, ok := functions[functionToken]; !ok {
353 functions[functionToken] = -1
354 }
355 }
356
357 handleDeclareMacro(line, "_R_", "OPENSSL_DECLARE_ERROR_REASON(", reasons)
358 handleDeclareMacro(line, "_F_", "OPENSSL_DECLARE_ERROR_FUNCTION(", functions)
359
360 for len(line) > 0 {
361 i := strings.Index(line, prefix)
362 if i == -1 {
363 break
364 }
365
366 line = line[i:]
367 end := strings.IndexFunc(line, func(r rune) bool {
368 return !(r == '_' || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9'))
369 })
370 if end == -1 {
371 end = len(line)
372 }
373
374 var token string
375 token, line = line[:end], line[end:]
376
377 switch {
378 case strings.HasPrefix(token, reasonPrefix):
379 if _, ok := reasons[token]; !ok {
380 reasons[token] = -1
381 }
382 }
383 }
384 }
385
386 return scanner.Err()
387}
388
389func parseHeader(lib string, file io.Reader) (functions, reasons map[string]int, err error) {
390 functions = make(map[string]int)
391 reasons = make(map[string]int)
392
393 scanner := bufio.NewScanner(file)
394 for scanner.Scan() {
395 typ, key, value, ok := parseDefineLine(scanner.Text(), lib)
396 if !ok {
397 continue
398 }
399
400 switch typ {
401 case typeFunctions:
402 functions[key] = value
403 case typeReasons:
404 reasons[key] = value
405 default:
406 panic("internal error")
407 }
408 }
409
410 err = scanner.Err()
411 return
412}
413
414func main() {
415 flag.Parse()
416
417 if err := makeErrors(*resetFlag); err != nil {
418 fmt.Fprintf(os.Stderr, "%s\n", err)
419 os.Exit(1)
420 }
421}