github.com/nibnait/go-learn@v0.0.0-20220227013611-dfa47ea6d2da/src/pkg/mod/golang.org/x/sys@v0.0.0-20210630005230-0f9fa26af87c/windows/mkwinsyscall/mkwinsyscall.go (about)

     1  // Copyright 2013 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  /*
     6  mkwinsyscall generates windows system call bodies
     7  
     8  It parses all files specified on command line containing function
     9  prototypes (like syscall_windows.go) and prints system call bodies
    10  to standard output.
    11  
    12  The prototypes are marked by lines beginning with "//sys" and read
    13  like func declarations if //sys is replaced by func, but:
    14  
    15  * The parameter lists must give a name for each argument. This
    16    includes return parameters.
    17  
    18  * The parameter lists must give a type for each argument:
    19    the (x, y, z int) shorthand is not allowed.
    20  
    21  * If the return parameter is an error number, it must be named err.
    22  
    23  * If go func name needs to be different from its winapi dll name,
    24    the winapi name could be specified at the end, after "=" sign, like
    25    //sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA
    26  
    27  * Each function that returns err needs to supply a condition, that
    28    return value of winapi will be tested against to detect failure.
    29    This would set err to windows "last-error", otherwise it will be nil.
    30    The value can be provided at end of //sys declaration, like
    31    //sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA
    32    and is [failretval==0] by default.
    33  
    34  * If the function name ends in a "?", then the function not existing is non-
    35    fatal, and an error will be returned instead of panicking.
    36  
    37  Usage:
    38  	mkwinsyscall [flags] [path ...]
    39  
    40  The flags are:
    41  	-output
    42  		Specify output file name (outputs to console if blank).
    43  	-trace
    44  		Generate print statement after every syscall.
    45  */
    46  package main
    47  
    48  import (
    49  	"bufio"
    50  	"bytes"
    51  	"errors"
    52  	"flag"
    53  	"fmt"
    54  	"go/format"
    55  	"go/parser"
    56  	"go/token"
    57  	"io"
    58  	"io/ioutil"
    59  	"log"
    60  	"os"
    61  	"path/filepath"
    62  	"runtime"
    63  	"sort"
    64  	"strconv"
    65  	"strings"
    66  	"text/template"
    67  )
    68  
    69  var (
    70  	filename       = flag.String("output", "", "output file name (standard output if omitted)")
    71  	printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
    72  	systemDLL      = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory")
    73  )
    74  
    75  func trim(s string) string {
    76  	return strings.Trim(s, " \t")
    77  }
    78  
    79  var packageName string
    80  
    81  func packagename() string {
    82  	return packageName
    83  }
    84  
    85  func windowsdot() string {
    86  	if packageName == "windows" {
    87  		return ""
    88  	}
    89  	return "windows."
    90  }
    91  
    92  func syscalldot() string {
    93  	if packageName == "syscall" {
    94  		return ""
    95  	}
    96  	return "syscall."
    97  }
    98  
    99  // Param is function parameter
   100  type Param struct {
   101  	Name      string
   102  	Type      string
   103  	fn        *Fn
   104  	tmpVarIdx int
   105  }
   106  
   107  // tmpVar returns temp variable name that will be used to represent p during syscall.
   108  func (p *Param) tmpVar() string {
   109  	if p.tmpVarIdx < 0 {
   110  		p.tmpVarIdx = p.fn.curTmpVarIdx
   111  		p.fn.curTmpVarIdx++
   112  	}
   113  	return fmt.Sprintf("_p%d", p.tmpVarIdx)
   114  }
   115  
   116  // BoolTmpVarCode returns source code for bool temp variable.
   117  func (p *Param) BoolTmpVarCode() string {
   118  	const code = `var %[1]s uint32
   119  	if %[2]s {
   120  		%[1]s = 1
   121  	}`
   122  	return fmt.Sprintf(code, p.tmpVar(), p.Name)
   123  }
   124  
   125  // BoolPointerTmpVarCode returns source code for bool temp variable.
   126  func (p *Param) BoolPointerTmpVarCode() string {
   127  	const code = `var %[1]s uint32
   128  	if *%[2]s {
   129  		%[1]s = 1
   130  	}`
   131  	return fmt.Sprintf(code, p.tmpVar(), p.Name)
   132  }
   133  
   134  // SliceTmpVarCode returns source code for slice temp variable.
   135  func (p *Param) SliceTmpVarCode() string {
   136  	const code = `var %s *%s
   137  	if len(%s) > 0 {
   138  		%s = &%s[0]
   139  	}`
   140  	tmp := p.tmpVar()
   141  	return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name)
   142  }
   143  
   144  // StringTmpVarCode returns source code for string temp variable.
   145  func (p *Param) StringTmpVarCode() string {
   146  	errvar := p.fn.Rets.ErrorVarName()
   147  	if errvar == "" {
   148  		errvar = "_"
   149  	}
   150  	tmp := p.tmpVar()
   151  	const code = `var %s %s
   152  	%s, %s = %s(%s)`
   153  	s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name)
   154  	if errvar == "-" {
   155  		return s
   156  	}
   157  	const morecode = `
   158  	if %s != nil {
   159  		return
   160  	}`
   161  	return s + fmt.Sprintf(morecode, errvar)
   162  }
   163  
   164  // TmpVarCode returns source code for temp variable.
   165  func (p *Param) TmpVarCode() string {
   166  	switch {
   167  	case p.Type == "bool":
   168  		return p.BoolTmpVarCode()
   169  	case p.Type == "*bool":
   170  		return p.BoolPointerTmpVarCode()
   171  	case strings.HasPrefix(p.Type, "[]"):
   172  		return p.SliceTmpVarCode()
   173  	default:
   174  		return ""
   175  	}
   176  }
   177  
   178  // TmpVarReadbackCode returns source code for reading back the temp variable into the original variable.
   179  func (p *Param) TmpVarReadbackCode() string {
   180  	switch {
   181  	case p.Type == "*bool":
   182  		return fmt.Sprintf("*%s = %s != 0", p.Name, p.tmpVar())
   183  	default:
   184  		return ""
   185  	}
   186  }
   187  
   188  // TmpVarHelperCode returns source code for helper's temp variable.
   189  func (p *Param) TmpVarHelperCode() string {
   190  	if p.Type != "string" {
   191  		return ""
   192  	}
   193  	return p.StringTmpVarCode()
   194  }
   195  
   196  // SyscallArgList returns source code fragments representing p parameter
   197  // in syscall. Slices are translated into 2 syscall parameters: pointer to
   198  // the first element and length.
   199  func (p *Param) SyscallArgList() []string {
   200  	t := p.HelperType()
   201  	var s string
   202  	switch {
   203  	case t == "*bool":
   204  		s = fmt.Sprintf("unsafe.Pointer(&%s)", p.tmpVar())
   205  	case t[0] == '*':
   206  		s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name)
   207  	case t == "bool":
   208  		s = p.tmpVar()
   209  	case strings.HasPrefix(t, "[]"):
   210  		return []string{
   211  			fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()),
   212  			fmt.Sprintf("uintptr(len(%s))", p.Name),
   213  		}
   214  	default:
   215  		s = p.Name
   216  	}
   217  	return []string{fmt.Sprintf("uintptr(%s)", s)}
   218  }
   219  
   220  // IsError determines if p parameter is used to return error.
   221  func (p *Param) IsError() bool {
   222  	return p.Name == "err" && p.Type == "error"
   223  }
   224  
   225  // HelperType returns type of parameter p used in helper function.
   226  func (p *Param) HelperType() string {
   227  	if p.Type == "string" {
   228  		return p.fn.StrconvType()
   229  	}
   230  	return p.Type
   231  }
   232  
   233  // join concatenates parameters ps into a string with sep separator.
   234  // Each parameter is converted into string by applying fn to it
   235  // before conversion.
   236  func join(ps []*Param, fn func(*Param) string, sep string) string {
   237  	if len(ps) == 0 {
   238  		return ""
   239  	}
   240  	a := make([]string, 0)
   241  	for _, p := range ps {
   242  		a = append(a, fn(p))
   243  	}
   244  	return strings.Join(a, sep)
   245  }
   246  
   247  // Rets describes function return parameters.
   248  type Rets struct {
   249  	Name          string
   250  	Type          string
   251  	ReturnsError  bool
   252  	FailCond      string
   253  	fnMaybeAbsent bool
   254  }
   255  
   256  // ErrorVarName returns error variable name for r.
   257  func (r *Rets) ErrorVarName() string {
   258  	if r.ReturnsError {
   259  		return "err"
   260  	}
   261  	if r.Type == "error" {
   262  		return r.Name
   263  	}
   264  	return ""
   265  }
   266  
   267  // ToParams converts r into slice of *Param.
   268  func (r *Rets) ToParams() []*Param {
   269  	ps := make([]*Param, 0)
   270  	if len(r.Name) > 0 {
   271  		ps = append(ps, &Param{Name: r.Name, Type: r.Type})
   272  	}
   273  	if r.ReturnsError {
   274  		ps = append(ps, &Param{Name: "err", Type: "error"})
   275  	}
   276  	return ps
   277  }
   278  
   279  // List returns source code of syscall return parameters.
   280  func (r *Rets) List() string {
   281  	s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ")
   282  	if len(s) > 0 {
   283  		s = "(" + s + ")"
   284  	} else if r.fnMaybeAbsent {
   285  		s = "(err error)"
   286  	}
   287  	return s
   288  }
   289  
   290  // PrintList returns source code of trace printing part correspondent
   291  // to syscall return values.
   292  func (r *Rets) PrintList() string {
   293  	return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
   294  }
   295  
   296  // SetReturnValuesCode returns source code that accepts syscall return values.
   297  func (r *Rets) SetReturnValuesCode() string {
   298  	if r.Name == "" && !r.ReturnsError {
   299  		return ""
   300  	}
   301  	retvar := "r0"
   302  	if r.Name == "" {
   303  		retvar = "r1"
   304  	}
   305  	errvar := "_"
   306  	if r.ReturnsError {
   307  		errvar = "e1"
   308  	}
   309  	return fmt.Sprintf("%s, _, %s := ", retvar, errvar)
   310  }
   311  
   312  func (r *Rets) useLongHandleErrorCode(retvar string) string {
   313  	const code = `if %s {
   314  		err = errnoErr(e1)
   315  	}`
   316  	cond := retvar + " == 0"
   317  	if r.FailCond != "" {
   318  		cond = strings.Replace(r.FailCond, "failretval", retvar, 1)
   319  	}
   320  	return fmt.Sprintf(code, cond)
   321  }
   322  
   323  // SetErrorCode returns source code that sets return parameters.
   324  func (r *Rets) SetErrorCode() string {
   325  	const code = `if r0 != 0 {
   326  		%s = %sErrno(r0)
   327  	}`
   328  	const ntstatus = `if r0 != 0 {
   329  		ntstatus = %sNTStatus(r0)
   330  	}`
   331  	if r.Name == "" && !r.ReturnsError {
   332  		return ""
   333  	}
   334  	if r.Name == "" {
   335  		return r.useLongHandleErrorCode("r1")
   336  	}
   337  	if r.Type == "error" && r.Name == "ntstatus" {
   338  		return fmt.Sprintf(ntstatus, windowsdot())
   339  	}
   340  	if r.Type == "error" {
   341  		return fmt.Sprintf(code, r.Name, syscalldot())
   342  	}
   343  	s := ""
   344  	switch {
   345  	case r.Type[0] == '*':
   346  		s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type)
   347  	case r.Type == "bool":
   348  		s = fmt.Sprintf("%s = r0 != 0", r.Name)
   349  	default:
   350  		s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type)
   351  	}
   352  	if !r.ReturnsError {
   353  		return s
   354  	}
   355  	return s + "\n\t" + r.useLongHandleErrorCode(r.Name)
   356  }
   357  
   358  // Fn describes syscall function.
   359  type Fn struct {
   360  	Name        string
   361  	Params      []*Param
   362  	Rets        *Rets
   363  	PrintTrace  bool
   364  	dllname     string
   365  	dllfuncname string
   366  	src         string
   367  	// TODO: get rid of this field and just use parameter index instead
   368  	curTmpVarIdx int // insure tmp variables have uniq names
   369  }
   370  
   371  // extractParams parses s to extract function parameters.
   372  func extractParams(s string, f *Fn) ([]*Param, error) {
   373  	s = trim(s)
   374  	if s == "" {
   375  		return nil, nil
   376  	}
   377  	a := strings.Split(s, ",")
   378  	ps := make([]*Param, len(a))
   379  	for i := range ps {
   380  		s2 := trim(a[i])
   381  		b := strings.Split(s2, " ")
   382  		if len(b) != 2 {
   383  			b = strings.Split(s2, "\t")
   384  			if len(b) != 2 {
   385  				return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"")
   386  			}
   387  		}
   388  		ps[i] = &Param{
   389  			Name:      trim(b[0]),
   390  			Type:      trim(b[1]),
   391  			fn:        f,
   392  			tmpVarIdx: -1,
   393  		}
   394  	}
   395  	return ps, nil
   396  }
   397  
   398  // extractSection extracts text out of string s starting after start
   399  // and ending just before end. found return value will indicate success,
   400  // and prefix, body and suffix will contain correspondent parts of string s.
   401  func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) {
   402  	s = trim(s)
   403  	if strings.HasPrefix(s, string(start)) {
   404  		// no prefix
   405  		body = s[1:]
   406  	} else {
   407  		a := strings.SplitN(s, string(start), 2)
   408  		if len(a) != 2 {
   409  			return "", "", s, false
   410  		}
   411  		prefix = a[0]
   412  		body = a[1]
   413  	}
   414  	a := strings.SplitN(body, string(end), 2)
   415  	if len(a) != 2 {
   416  		return "", "", "", false
   417  	}
   418  	return prefix, a[0], a[1], true
   419  }
   420  
   421  // newFn parses string s and return created function Fn.
   422  func newFn(s string) (*Fn, error) {
   423  	s = trim(s)
   424  	f := &Fn{
   425  		Rets:       &Rets{},
   426  		src:        s,
   427  		PrintTrace: *printTraceFlag,
   428  	}
   429  	// function name and args
   430  	prefix, body, s, found := extractSection(s, '(', ')')
   431  	if !found || prefix == "" {
   432  		return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"")
   433  	}
   434  	f.Name = prefix
   435  	var err error
   436  	f.Params, err = extractParams(body, f)
   437  	if err != nil {
   438  		return nil, err
   439  	}
   440  	// return values
   441  	_, body, s, found = extractSection(s, '(', ')')
   442  	if found {
   443  		r, err := extractParams(body, f)
   444  		if err != nil {
   445  			return nil, err
   446  		}
   447  		switch len(r) {
   448  		case 0:
   449  		case 1:
   450  			if r[0].IsError() {
   451  				f.Rets.ReturnsError = true
   452  			} else {
   453  				f.Rets.Name = r[0].Name
   454  				f.Rets.Type = r[0].Type
   455  			}
   456  		case 2:
   457  			if !r[1].IsError() {
   458  				return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"")
   459  			}
   460  			f.Rets.ReturnsError = true
   461  			f.Rets.Name = r[0].Name
   462  			f.Rets.Type = r[0].Type
   463  		default:
   464  			return nil, errors.New("Too many return values in \"" + f.src + "\"")
   465  		}
   466  	}
   467  	// fail condition
   468  	_, body, s, found = extractSection(s, '[', ']')
   469  	if found {
   470  		f.Rets.FailCond = body
   471  	}
   472  	// dll and dll function names
   473  	s = trim(s)
   474  	if s == "" {
   475  		return f, nil
   476  	}
   477  	if !strings.HasPrefix(s, "=") {
   478  		return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
   479  	}
   480  	s = trim(s[1:])
   481  	a := strings.Split(s, ".")
   482  	switch len(a) {
   483  	case 1:
   484  		f.dllfuncname = a[0]
   485  	case 2:
   486  		f.dllname = a[0]
   487  		f.dllfuncname = a[1]
   488  	default:
   489  		return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
   490  	}
   491  	if n := f.dllfuncname; strings.HasSuffix(n, "?") {
   492  		f.dllfuncname = n[:len(n)-1]
   493  		f.Rets.fnMaybeAbsent = true
   494  	}
   495  	return f, nil
   496  }
   497  
   498  // DLLName returns DLL name for function f.
   499  func (f *Fn) DLLName() string {
   500  	if f.dllname == "" {
   501  		return "kernel32"
   502  	}
   503  	return f.dllname
   504  }
   505  
   506  // DLLName returns DLL function name for function f.
   507  func (f *Fn) DLLFuncName() string {
   508  	if f.dllfuncname == "" {
   509  		return f.Name
   510  	}
   511  	return f.dllfuncname
   512  }
   513  
   514  // ParamList returns source code for function f parameters.
   515  func (f *Fn) ParamList() string {
   516  	return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ")
   517  }
   518  
   519  // HelperParamList returns source code for helper function f parameters.
   520  func (f *Fn) HelperParamList() string {
   521  	return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ")
   522  }
   523  
   524  // ParamPrintList returns source code of trace printing part correspondent
   525  // to syscall input parameters.
   526  func (f *Fn) ParamPrintList() string {
   527  	return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
   528  }
   529  
   530  // ParamCount return number of syscall parameters for function f.
   531  func (f *Fn) ParamCount() int {
   532  	n := 0
   533  	for _, p := range f.Params {
   534  		n += len(p.SyscallArgList())
   535  	}
   536  	return n
   537  }
   538  
   539  // SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/...
   540  // to use. It returns parameter count for correspondent SyscallX function.
   541  func (f *Fn) SyscallParamCount() int {
   542  	n := f.ParamCount()
   543  	switch {
   544  	case n <= 3:
   545  		return 3
   546  	case n <= 6:
   547  		return 6
   548  	case n <= 9:
   549  		return 9
   550  	case n <= 12:
   551  		return 12
   552  	case n <= 15:
   553  		return 15
   554  	default:
   555  		panic("too many arguments to system call")
   556  	}
   557  }
   558  
   559  // Syscall determines which SyscallX function to use for function f.
   560  func (f *Fn) Syscall() string {
   561  	c := f.SyscallParamCount()
   562  	if c == 3 {
   563  		return syscalldot() + "Syscall"
   564  	}
   565  	return syscalldot() + "Syscall" + strconv.Itoa(c)
   566  }
   567  
   568  // SyscallParamList returns source code for SyscallX parameters for function f.
   569  func (f *Fn) SyscallParamList() string {
   570  	a := make([]string, 0)
   571  	for _, p := range f.Params {
   572  		a = append(a, p.SyscallArgList()...)
   573  	}
   574  	for len(a) < f.SyscallParamCount() {
   575  		a = append(a, "0")
   576  	}
   577  	return strings.Join(a, ", ")
   578  }
   579  
   580  // HelperCallParamList returns source code of call into function f helper.
   581  func (f *Fn) HelperCallParamList() string {
   582  	a := make([]string, 0, len(f.Params))
   583  	for _, p := range f.Params {
   584  		s := p.Name
   585  		if p.Type == "string" {
   586  			s = p.tmpVar()
   587  		}
   588  		a = append(a, s)
   589  	}
   590  	return strings.Join(a, ", ")
   591  }
   592  
   593  // MaybeAbsent returns source code for handling functions that are possibly unavailable.
   594  func (p *Fn) MaybeAbsent() string {
   595  	if !p.Rets.fnMaybeAbsent {
   596  		return ""
   597  	}
   598  	const code = `%[1]s = proc%[2]s.Find()
   599  	if %[1]s != nil {
   600  		return
   601  	}`
   602  	errorVar := p.Rets.ErrorVarName()
   603  	if errorVar == "" {
   604  		errorVar = "err"
   605  	}
   606  	return fmt.Sprintf(code, errorVar, p.DLLFuncName())
   607  }
   608  
   609  // IsUTF16 is true, if f is W (utf16) function. It is false
   610  // for all A (ascii) functions.
   611  func (f *Fn) IsUTF16() bool {
   612  	s := f.DLLFuncName()
   613  	return s[len(s)-1] == 'W'
   614  }
   615  
   616  // StrconvFunc returns name of Go string to OS string function for f.
   617  func (f *Fn) StrconvFunc() string {
   618  	if f.IsUTF16() {
   619  		return syscalldot() + "UTF16PtrFromString"
   620  	}
   621  	return syscalldot() + "BytePtrFromString"
   622  }
   623  
   624  // StrconvType returns Go type name used for OS string for f.
   625  func (f *Fn) StrconvType() string {
   626  	if f.IsUTF16() {
   627  		return "*uint16"
   628  	}
   629  	return "*byte"
   630  }
   631  
   632  // HasStringParam is true, if f has at least one string parameter.
   633  // Otherwise it is false.
   634  func (f *Fn) HasStringParam() bool {
   635  	for _, p := range f.Params {
   636  		if p.Type == "string" {
   637  			return true
   638  		}
   639  	}
   640  	return false
   641  }
   642  
   643  // HelperName returns name of function f helper.
   644  func (f *Fn) HelperName() string {
   645  	if !f.HasStringParam() {
   646  		return f.Name
   647  	}
   648  	return "_" + f.Name
   649  }
   650  
   651  // Source files and functions.
   652  type Source struct {
   653  	Funcs           []*Fn
   654  	DLLFuncNames    []*Fn
   655  	Files           []string
   656  	StdLibImports   []string
   657  	ExternalImports []string
   658  }
   659  
   660  func (src *Source) Import(pkg string) {
   661  	src.StdLibImports = append(src.StdLibImports, pkg)
   662  	sort.Strings(src.StdLibImports)
   663  }
   664  
   665  func (src *Source) ExternalImport(pkg string) {
   666  	src.ExternalImports = append(src.ExternalImports, pkg)
   667  	sort.Strings(src.ExternalImports)
   668  }
   669  
   670  // ParseFiles parses files listed in fs and extracts all syscall
   671  // functions listed in sys comments. It returns source files
   672  // and functions collection *Source if successful.
   673  func ParseFiles(fs []string) (*Source, error) {
   674  	src := &Source{
   675  		Funcs: make([]*Fn, 0),
   676  		Files: make([]string, 0),
   677  		StdLibImports: []string{
   678  			"unsafe",
   679  		},
   680  		ExternalImports: make([]string, 0),
   681  	}
   682  	for _, file := range fs {
   683  		if err := src.ParseFile(file); err != nil {
   684  			return nil, err
   685  		}
   686  	}
   687  	src.DLLFuncNames = make([]*Fn, 0, len(src.Funcs))
   688  	uniq := make(map[string]bool, len(src.Funcs))
   689  	for _, fn := range src.Funcs {
   690  		name := fn.DLLFuncName()
   691  		if !uniq[name] {
   692  			src.DLLFuncNames = append(src.DLLFuncNames, fn)
   693  			uniq[name] = true
   694  		}
   695  	}
   696  	return src, nil
   697  }
   698  
   699  // DLLs return dll names for a source set src.
   700  func (src *Source) DLLs() []string {
   701  	uniq := make(map[string]bool)
   702  	r := make([]string, 0)
   703  	for _, f := range src.Funcs {
   704  		name := f.DLLName()
   705  		if _, found := uniq[name]; !found {
   706  			uniq[name] = true
   707  			r = append(r, name)
   708  		}
   709  	}
   710  	sort.Strings(r)
   711  	return r
   712  }
   713  
   714  // ParseFile adds additional file path to a source set src.
   715  func (src *Source) ParseFile(path string) error {
   716  	file, err := os.Open(path)
   717  	if err != nil {
   718  		return err
   719  	}
   720  	defer file.Close()
   721  
   722  	s := bufio.NewScanner(file)
   723  	for s.Scan() {
   724  		t := trim(s.Text())
   725  		if len(t) < 7 {
   726  			continue
   727  		}
   728  		if !strings.HasPrefix(t, "//sys") {
   729  			continue
   730  		}
   731  		t = t[5:]
   732  		if !(t[0] == ' ' || t[0] == '\t') {
   733  			continue
   734  		}
   735  		f, err := newFn(t[1:])
   736  		if err != nil {
   737  			return err
   738  		}
   739  		src.Funcs = append(src.Funcs, f)
   740  	}
   741  	if err := s.Err(); err != nil {
   742  		return err
   743  	}
   744  	src.Files = append(src.Files, path)
   745  	sort.Slice(src.Funcs, func(i, j int) bool {
   746  		fi, fj := src.Funcs[i], src.Funcs[j]
   747  		if fi.DLLName() == fj.DLLName() {
   748  			return fi.DLLFuncName() < fj.DLLFuncName()
   749  		}
   750  		return fi.DLLName() < fj.DLLName()
   751  	})
   752  
   753  	// get package name
   754  	fset := token.NewFileSet()
   755  	_, err = file.Seek(0, 0)
   756  	if err != nil {
   757  		return err
   758  	}
   759  	pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly)
   760  	if err != nil {
   761  		return err
   762  	}
   763  	packageName = pkg.Name.Name
   764  
   765  	return nil
   766  }
   767  
   768  // IsStdRepo reports whether src is part of standard library.
   769  func (src *Source) IsStdRepo() (bool, error) {
   770  	if len(src.Files) == 0 {
   771  		return false, errors.New("no input files provided")
   772  	}
   773  	abspath, err := filepath.Abs(src.Files[0])
   774  	if err != nil {
   775  		return false, err
   776  	}
   777  	goroot := runtime.GOROOT()
   778  	if runtime.GOOS == "windows" {
   779  		abspath = strings.ToLower(abspath)
   780  		goroot = strings.ToLower(goroot)
   781  	}
   782  	sep := string(os.PathSeparator)
   783  	if !strings.HasSuffix(goroot, sep) {
   784  		goroot += sep
   785  	}
   786  	return strings.HasPrefix(abspath, goroot), nil
   787  }
   788  
   789  // Generate output source file from a source set src.
   790  func (src *Source) Generate(w io.Writer) error {
   791  	const (
   792  		pkgStd         = iota // any package in std library
   793  		pkgXSysWindows        // x/sys/windows package
   794  		pkgOther
   795  	)
   796  	isStdRepo, err := src.IsStdRepo()
   797  	if err != nil {
   798  		return err
   799  	}
   800  	var pkgtype int
   801  	switch {
   802  	case isStdRepo:
   803  		pkgtype = pkgStd
   804  	case packageName == "windows":
   805  		// TODO: this needs better logic than just using package name
   806  		pkgtype = pkgXSysWindows
   807  	default:
   808  		pkgtype = pkgOther
   809  	}
   810  	if *systemDLL {
   811  		switch pkgtype {
   812  		case pkgStd:
   813  			src.Import("internal/syscall/windows/sysdll")
   814  		case pkgXSysWindows:
   815  		default:
   816  			src.ExternalImport("golang.org/x/sys/windows")
   817  		}
   818  	}
   819  	if packageName != "syscall" {
   820  		src.Import("syscall")
   821  	}
   822  	funcMap := template.FuncMap{
   823  		"packagename": packagename,
   824  		"syscalldot":  syscalldot,
   825  		"newlazydll": func(dll string) string {
   826  			arg := "\"" + dll + ".dll\""
   827  			if !*systemDLL {
   828  				return syscalldot() + "NewLazyDLL(" + arg + ")"
   829  			}
   830  			switch pkgtype {
   831  			case pkgStd:
   832  				return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))"
   833  			case pkgXSysWindows:
   834  				return "NewLazySystemDLL(" + arg + ")"
   835  			default:
   836  				return "windows.NewLazySystemDLL(" + arg + ")"
   837  			}
   838  		},
   839  	}
   840  	t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
   841  	err = t.Execute(w, src)
   842  	if err != nil {
   843  		return errors.New("Failed to execute template: " + err.Error())
   844  	}
   845  	return nil
   846  }
   847  
   848  func usage() {
   849  	fmt.Fprintf(os.Stderr, "usage: mkwinsyscall [flags] [path ...]\n")
   850  	flag.PrintDefaults()
   851  	os.Exit(1)
   852  }
   853  
   854  func main() {
   855  	flag.Usage = usage
   856  	flag.Parse()
   857  	if len(flag.Args()) <= 0 {
   858  		fmt.Fprintf(os.Stderr, "no files to parse provided\n")
   859  		usage()
   860  	}
   861  
   862  	src, err := ParseFiles(flag.Args())
   863  	if err != nil {
   864  		log.Fatal(err)
   865  	}
   866  
   867  	var buf bytes.Buffer
   868  	if err := src.Generate(&buf); err != nil {
   869  		log.Fatal(err)
   870  	}
   871  
   872  	data, err := format.Source(buf.Bytes())
   873  	if err != nil {
   874  		log.Fatal(err)
   875  	}
   876  	if *filename == "" {
   877  		_, err = os.Stdout.Write(data)
   878  	} else {
   879  		err = ioutil.WriteFile(*filename, data, 0644)
   880  	}
   881  	if err != nil {
   882  		log.Fatal(err)
   883  	}
   884  }
   885  
   886  // TODO: use println instead to print in the following template
   887  const srcTemplate = `
   888  
   889  {{define "main"}}// Code generated by 'go generate'; DO NOT EDIT.
   890  
   891  package {{packagename}}
   892  
   893  import (
   894  {{range .StdLibImports}}"{{.}}"
   895  {{end}}
   896  
   897  {{range .ExternalImports}}"{{.}}"
   898  {{end}}
   899  )
   900  
   901  var _ unsafe.Pointer
   902  
   903  // Do the interface allocations only once for common
   904  // Errno values.
   905  const (
   906  	errnoERROR_IO_PENDING = 997
   907  )
   908  
   909  var (
   910  	errERROR_IO_PENDING error = {{syscalldot}}Errno(errnoERROR_IO_PENDING)
   911  	errERROR_EINVAL error     = {{syscalldot}}EINVAL
   912  )
   913  
   914  // errnoErr returns common boxed Errno values, to prevent
   915  // allocations at runtime.
   916  func errnoErr(e {{syscalldot}}Errno) error {
   917  	switch e {
   918  	case 0:
   919  		return errERROR_EINVAL
   920  	case errnoERROR_IO_PENDING:
   921  		return errERROR_IO_PENDING
   922  	}
   923  	// TODO: add more here, after collecting data on the common
   924  	// error values see on Windows. (perhaps when running
   925  	// all.bat?)
   926  	return e
   927  }
   928  
   929  var (
   930  {{template "dlls" .}}
   931  {{template "funcnames" .}})
   932  {{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
   933  {{end}}
   934  
   935  {{/* help functions */}}
   936  
   937  {{define "dlls"}}{{range .DLLs}}	mod{{.}} = {{newlazydll .}}
   938  {{end}}{{end}}
   939  
   940  {{define "funcnames"}}{{range .DLLFuncNames}}	proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}")
   941  {{end}}{{end}}
   942  
   943  {{define "helperbody"}}
   944  func {{.Name}}({{.ParamList}}) {{template "results" .}}{
   945  {{template "helpertmpvars" .}}	return {{.HelperName}}({{.HelperCallParamList}})
   946  }
   947  {{end}}
   948  
   949  {{define "funcbody"}}
   950  func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
   951  {{template "maybeabsent" .}}	{{template "tmpvars" .}}	{{template "syscall" .}}	{{template "tmpvarsreadback" .}}
   952  {{template "seterror" .}}{{template "printtrace" .}}	return
   953  }
   954  {{end}}
   955  
   956  {{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}}	{{.TmpVarHelperCode}}
   957  {{end}}{{end}}{{end}}
   958  
   959  {{define "maybeabsent"}}{{if .MaybeAbsent}}{{.MaybeAbsent}}
   960  {{end}}{{end}}
   961  
   962  {{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}}	{{.TmpVarCode}}
   963  {{end}}{{end}}{{end}}
   964  
   965  {{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
   966  
   967  {{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}}
   968  
   969  {{define "tmpvarsreadback"}}{{range .Params}}{{if .TmpVarReadbackCode}}
   970  {{.TmpVarReadbackCode}}{{end}}{{end}}{{end}}
   971  
   972  {{define "seterror"}}{{if .Rets.SetErrorCode}}	{{.Rets.SetErrorCode}}
   973  {{end}}{{end}}
   974  
   975  {{define "printtrace"}}{{if .PrintTrace}}	print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
   976  {{end}}{{end}}
   977  
   978  `