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