1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build ignore
6
7// Generate system call table for DragonFly, NetBSD,
8// FreeBSD or OpenBSD from master list (for example,
9// /usr/src/sys/kern/syscalls.master or sys/syscall.h).
10package main
11
12import (
13	"bufio"
14	"fmt"
15	"io"
16	"io/ioutil"
17	"net/http"
18	"os"
19	"regexp"
20	"strings"
21)
22
23var (
24	goos, goarch string
25)
26
27// cmdLine returns this programs's commandline arguments
28func cmdLine() string {
29	return "go run mksysnum.go " + strings.Join(os.Args[1:], " ")
30}
31
32// buildTags returns build tags
33func buildTags() string {
34	return fmt.Sprintf("%s,%s", goarch, goos)
35}
36
37func checkErr(err error) {
38	if err != nil {
39		fmt.Fprintf(os.Stderr, "%v\n", err)
40		os.Exit(1)
41	}
42}
43
44// source string and substring slice for regexp
45type re struct {
46	str string   // source string
47	sub []string // matched sub-string
48}
49
50// Match performs regular expression match
51func (r *re) Match(exp string) bool {
52	r.sub = regexp.MustCompile(exp).FindStringSubmatch(r.str)
53	if r.sub != nil {
54		return true
55	}
56	return false
57}
58
59// fetchFile fetches a text file from URL
60func fetchFile(URL string) io.Reader {
61	resp, err := http.Get(URL)
62	checkErr(err)
63	defer resp.Body.Close()
64	body, err := ioutil.ReadAll(resp.Body)
65	checkErr(err)
66	return strings.NewReader(string(body))
67}
68
69// readFile reads a text file from path
70func readFile(path string) io.Reader {
71	file, err := os.Open(os.Args[1])
72	checkErr(err)
73	return file
74}
75
76func format(name, num, proto string) string {
77	name = strings.ToUpper(name)
78	// There are multiple entries for enosys and nosys, so comment them out.
79	nm := re{str: name}
80	if nm.Match(`^SYS_E?NOSYS$`) {
81		name = fmt.Sprintf("// %s", name)
82	}
83	if name == `SYS_SYS_EXIT` {
84		name = `SYS_EXIT`
85	}
86	return fmt.Sprintf("	%s = %s;  // %s\n", name, num, proto)
87}
88
89func main() {
90	// Get the OS (using GOOS_TARGET if it exist)
91	goos = os.Getenv("GOOS_TARGET")
92	if goos == "" {
93		goos = os.Getenv("GOOS")
94	}
95	// Get the architecture (using GOARCH_TARGET if it exists)
96	goarch = os.Getenv("GOARCH_TARGET")
97	if goarch == "" {
98		goarch = os.Getenv("GOARCH")
99	}
100	// Check if GOOS and GOARCH environment variables are defined
101	if goarch == "" || goos == "" {
102		fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n")
103		os.Exit(1)
104	}
105
106	file := strings.TrimSpace(os.Args[1])
107	var syscalls io.Reader
108	if strings.HasPrefix(file, "https://") || strings.HasPrefix(file, "http://") {
109		// Download syscalls.master file
110		syscalls = fetchFile(file)
111	} else {
112		syscalls = readFile(file)
113	}
114
115	var text, line string
116	s := bufio.NewScanner(syscalls)
117	for s.Scan() {
118		t := re{str: line}
119		if t.Match(`^(.*)\\$`) {
120			// Handle continuation
121			line = t.sub[1]
122			line += strings.TrimLeft(s.Text(), " \t")
123		} else {
124			// New line
125			line = s.Text()
126		}
127		t = re{str: line}
128		if t.Match(`\\$`) {
129			continue
130		}
131		t = re{str: line}
132
133		switch goos {
134		case "dragonfly":
135			if t.Match(`^([0-9]+)\s+STD\s+({ \S+\s+(\w+).*)$`) {
136				num, proto := t.sub[1], t.sub[2]
137				name := fmt.Sprintf("SYS_%s", t.sub[3])
138				text += format(name, num, proto)
139			}
140		case "freebsd":
141			if t.Match(`^([0-9]+)\s+\S+\s+(?:(?:NO)?STD|COMPAT10)\s+({ \S+\s+(\w+).*)$`) {
142				num, proto := t.sub[1], t.sub[2]
143				name := fmt.Sprintf("SYS_%s", t.sub[3])
144				text += format(name, num, proto)
145			}
146		case "openbsd":
147			if t.Match(`^([0-9]+)\s+STD\s+(NOLOCK\s+)?({ \S+\s+\*?(\w+).*)$`) {
148				num, proto, name := t.sub[1], t.sub[3], t.sub[4]
149				text += format(name, num, proto)
150			}
151		case "netbsd":
152			if t.Match(`^([0-9]+)\s+((STD)|(NOERR))\s+(RUMP\s+)?({\s+\S+\s*\*?\s*\|(\S+)\|(\S*)\|(\w+).*\s+})(\s+(\S+))?$`) {
153				num, proto, compat := t.sub[1], t.sub[6], t.sub[8]
154				name := t.sub[7] + "_" + t.sub[9]
155				if t.sub[11] != "" {
156					name = t.sub[7] + "_" + t.sub[11]
157				}
158				name = strings.ToUpper(name)
159				if compat == "" || compat == "13" || compat == "30" || compat == "50" {
160					text += fmt.Sprintf("	%s = %s;  // %s\n", name, num, proto)
161				}
162			}
163		default:
164			fmt.Fprintf(os.Stderr, "unrecognized GOOS=%s\n", goos)
165			os.Exit(1)
166
167		}
168	}
169	err := s.Err()
170	checkErr(err)
171
172	fmt.Printf(template, cmdLine(), buildTags(), text)
173}
174
175const template = `// %s
176// Code generated by the command above; see README.md. DO NOT EDIT.
177
178// +build %s
179
180package unix
181
182const(
183%s)`
184