github.com/neonyo/sys@v0.0.0-20230720094341-b1ee14be3ce8/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 // +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). 11 package main 12 13 import ( 14 "bufio" 15 "fmt" 16 "io" 17 "io/ioutil" 18 "net/http" 19 "os" 20 "regexp" 21 "strings" 22 ) 23 24 var ( 25 goos, goarch string 26 ) 27 28 // cmdLine returns this programs's commandline arguments 29 func 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. 34 func goBuildTags() string { 35 return fmt.Sprintf("%s && %s", goarch, goos) 36 } 37 38 // plusBuildTags returns build tags in the +build format. 39 func plusBuildTags() string { 40 return fmt.Sprintf("%s,%s", goarch, goos) 41 } 42 43 func 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 51 type re struct { 52 str string // source string 53 sub []string // matched sub-string 54 } 55 56 // Match performs regular expression match 57 func (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 66 func 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 76 func readFile(path string) io.Reader { 77 file, err := os.Open(os.Args[1]) 78 checkErr(err) 79 return file 80 } 81 82 func 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 95 func 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)\s+({ \S+\s+(\w+).*)$`) { 148 num, proto := t.sub[1], t.sub[2] 149 name := fmt.Sprintf("SYS_%s", t.sub[3]) 150 // remove whitespace around parens 151 proto = regexp.MustCompile(`\( `).ReplaceAllString(proto, "(") 152 proto = regexp.MustCompile(` \)`).ReplaceAllString(proto, ")") 153 // remove SAL 2.0 annotations 154 proto = regexp.MustCompile(`_In[^ ]*[_)] `).ReplaceAllString(proto, "") 155 proto = regexp.MustCompile(`_Out[^ ]*[_)] `).ReplaceAllString(proto, "") 156 // remove double spaces at the source 157 proto = regexp.MustCompile(`\s{2}`).ReplaceAllString(proto, " ") 158 text += format(name, num, proto) 159 } 160 case "openbsd": 161 if t.Match(`^([0-9]+)\s+STD\s+(NOLOCK\s+)?({ \S+\s+\*?(\w+).*)$`) { 162 num, proto, name := t.sub[1], t.sub[3], t.sub[4] 163 text += format(name, num, proto) 164 } 165 case "netbsd": 166 if t.Match(`^([0-9]+)\s+((STD)|(NOERR))\s+(RUMP\s+)?({\s+\S+\s*\*?\s*\|(\S+)\|(\S*)\|(\w+).*\s+})(\s+(\S+))?$`) { 167 num, proto, compat := t.sub[1], t.sub[6], t.sub[8] 168 name := t.sub[7] + "_" + t.sub[9] 169 if t.sub[11] != "" { 170 name = t.sub[7] + "_" + t.sub[11] 171 } 172 name = strings.ToUpper(name) 173 if compat == "" || compat == "13" || compat == "30" || compat == "50" { 174 text += fmt.Sprintf(" %s = %s; // %s\n", name, num, proto) 175 } 176 } 177 default: 178 fmt.Fprintf(os.Stderr, "unrecognized GOOS=%s\n", goos) 179 os.Exit(1) 180 181 } 182 } 183 err := s.Err() 184 checkErr(err) 185 186 fmt.Printf(template, cmdLine(), goBuildTags(), plusBuildTags(), text) 187 } 188 189 const template = `// %s 190 // Code generated by the command above; see README.md. DO NOT EDIT. 191 192 //go:build %s 193 // +build %s 194 195 package unix 196 197 const( 198 %s)`