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
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
37Usage:
38	mkwinsyscall [flags] [path ...]
39
40The flags are:
41	-output
42		Specify output file name (outputs to console if blank).
43	-trace
44		Generate print statement after every syscall.
45*/
46package main
47
48import (
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
69var (
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
75func trim(s string) string {
76	return strings.Trim(s, " \t")
77}
78
79var packageName string
80
81func packagename() string {
82	return packageName
83}
84
85func windowsdot() string {
86	if packageName == "windows" {
87		return ""
88	}
89	return "windows."
90}
91
92func syscalldot() string {
93	if packageName == "syscall" {
94		return ""
95	}
96	return "syscall."
97}
98
99// Param is function parameter
100type 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.
108func (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.
117func (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.
126func (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.
135func (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.
145func (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.
165func (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.
179func (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.
189func (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.
199func (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.
221func (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.
226func (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.
236func 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.
248type 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.
257func (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.
268func (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.
280func (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.
292func (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.
297func (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
312func (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.
324func (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.
359type 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.
372func 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.
401func 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.
422func 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.
499func (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.
507func (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.
515func (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.
520func (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.
526func (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.
531func (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.
541func (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.
560func (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.
569func (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.
581func (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.
594func (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.
611func (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.
617func (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.
625func (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.
634func (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.
644func (f *Fn) HelperName() string {
645	if !f.HasStringParam() {
646		return f.Name
647	}
648	return "_" + f.Name
649}
650
651// Source files and functions.
652type Source struct {
653	Funcs           []*Fn
654	DLLFuncNames    []*Fn
655	Files           []string
656	StdLibImports   []string
657	ExternalImports []string
658}
659
660func (src *Source) Import(pkg string) {
661	src.StdLibImports = append(src.StdLibImports, pkg)
662	sort.Strings(src.StdLibImports)
663}
664
665func (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.
673func 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.
700func (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.
715func (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.
769func (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.
790func (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
848func usage() {
849	fmt.Fprintf(os.Stderr, "usage: mkwinsyscall [flags] [path ...]\n")
850	flag.PrintDefaults()
851	os.Exit(1)
852}
853
854func 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
887const srcTemplate = `
888
889{{define "main"}}// Code generated by 'go generate'; DO NOT EDIT.
890
891package {{packagename}}
892
893import (
894{{range .StdLibImports}}"{{.}}"
895{{end}}
896
897{{range .ExternalImports}}"{{.}}"
898{{end}}
899)
900
901var _ unsafe.Pointer
902
903// Do the interface allocations only once for common
904// Errno values.
905const (
906	errnoERROR_IO_PENDING = 997
907)
908
909var (
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.
916func 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
929var (
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"}}
944func {{.Name}}({{.ParamList}}) {{template "results" .}}{
945{{template "helpertmpvars" .}}	return {{.HelperName}}({{.HelperCallParamList}})
946}
947{{end}}
948
949{{define "funcbody"}}
950func {{.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`
979