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