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)`