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