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