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