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