| # Copyright 2011 The Go Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style | 
 | # license that can be found in the LICENSE file. | 
 |  | 
 | # This AWK script reads a Go file with comments describing syscall | 
 | # functions and the C routines they map to.  It generates the Go code | 
 | # which calls the C routines. | 
 |  | 
 | # The syscall functins are marked by lines beginning with "//sys" and | 
 | # read like func declarations if //sys is replaced by func, but: | 
 | #	* The parameter lists must give a name for each argument. | 
 | #	  This includes return parameters. | 
 | #	* The parameter lists must give a type for each argument: | 
 | #	   the (x, y, z int) shorthand is not allowed. | 
 | #	* If the return parameter is an error, it must be named err. | 
 |  | 
 | # A line beginning with //sysnb is like //sys, except that the | 
 | # goroutine will not be suspended during the execution of the library | 
 | # call.  This must only be used for library calls which can never | 
 | # block, as otherwise the library call could cause all goroutines to | 
 | # hang. | 
 |  | 
 | # After the //sys or //sysnb line comes a second line which describes | 
 | # the C function.  The name must be the name of the function in the C | 
 | # library, and may be the same as the Go function.  The limitations on | 
 | # the argument list are the same as for the //sys line, but there must | 
 | # be at most one result parameter, and it must be given as just a | 
 | # type, without a name. | 
 |  | 
 | BEGIN { | 
 |     print "// Code generated by mksyscall.awk. DO NOT EDIT." | 
 |     print "" | 
 |     print "package syscall" | 
 |     print "" | 
 |     print "import \"unsafe\"" | 
 |     print "" | 
 |     status = 0 | 
 | } | 
 |  | 
 | /^\/\/sys/ { | 
 |     if ($1 == "//sysnb") { | 
 | 	blocking = 0 | 
 |     } else { | 
 | 	blocking = 1 | 
 |     } | 
 |  | 
 |     line = $0 | 
 |  | 
 |     if (match(line, "//sys(nb)?[ 	]*[a-zA-Z0-9_]+\\([^()]*\\) *(\\(([^()]+)\\))?") == 0) { | 
 | 	print "unmatched line:", $0 | "cat 1>&2" | 
 | 	status = 1 | 
 | 	next | 
 |     } | 
 |  | 
 |     # Sets a[1] = //sysnb, a[2] == function name. | 
 |     split(line, a, "[ 	(]+") | 
 |     gofnname = a[2] | 
 |  | 
 |     off = match(line, "\\([^()]*\\)") | 
 |     end = index(substr(line, off, length(line) - off + 1), ")") | 
 |     gofnparams = substr(line, off + 1, end - 2) | 
 |  | 
 |     line = substr(line, off + end, length(line) - (off + end) + 1) | 
 |     off = match(line, "\\([^()]*\\)") | 
 |     if (off == 0) { | 
 | 	gofnresults = "" | 
 |     } else { | 
 | 	end = index(substr(line, off, length(line) - off + 1), ")") | 
 | 	gofnresults = substr(line, off + 1, end - 2) | 
 |     } | 
 |  | 
 |     getline | 
 |     line = $0 | 
 |  | 
 |     if (match(line, "//[a-zA-Z0-9_]+\\([^()]*\\)") == 0) { | 
 | 	print "unmatched C line", $0, "after", gofnname | "cat 1>&2" | 
 | 	status = 1 | 
 | 	next | 
 |     } | 
 |  | 
 |     split(line, a, "[ 	(]+") | 
 |     cfnname = substr(a[1], 3, length(a[1]) - 2) | 
 |  | 
 |     off = match(line, "\\([^()]*\\)") | 
 |     end = index(substr(line, off, length(line) - off + 1), ")") | 
 |     cfnparams = substr(line, off + 1, end - 2) | 
 |  | 
 |     line = substr(line, off + end + 1, length(line) - (off + end) + 1) | 
 |     while (substr(line, 1, 1) == " ") { | 
 | 	line = substr(line, 2, length(line) - 1) | 
 |     } | 
 |     end = index(line, " ") | 
 |     if (end != 0) { | 
 | 	line = substr(line, 1, end) | 
 |     } | 
 |     cfnresult = line | 
 |  | 
 |     printf("// Automatically generated wrapper for %s/%s\n", gofnname, cfnname) | 
 |     if (!(cfnname in cfns)) { | 
 |         cfns[cfnname] = 1 | 
 |         printf("//go:noescape\n") | 
 |         printf("//extern-sysinfo %s\n", cfnname) | 
 |         printf("func c_%s(%s) %s\n", cfnname, cfnparams, cfnresult) | 
 |     } | 
 |     printf("func %s(%s) %s%s%s%s{\n", | 
 | 	   gofnname, gofnparams, gofnresults == "" ? "" : "(", gofnresults, | 
 | 	   gofnresults == "" ? "" : ")", gofnresults == "" ? "" : " ") | 
 |  | 
 |     loc = gofnname "/" cfnname ":" | 
 |  | 
 |     haserr = 0 | 
 |     if (gofnresults != "") { | 
 | 	fields = split(gofnresults, goresults, ", *") | 
 | 	for (goresult = 1; goresults[goresult] != ""; goresult++) { | 
 | 	    if (split(goresults[goresult], goparam) == 2) { | 
 | 		if (goparam[1] == "err") { | 
 | 		    haserr = 1 | 
 | 		    break | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |     split(gofnparams, goargs, ", *") | 
 |     split(cfnparams, cargs, ", *") | 
 |     args = "" | 
 |     carg = 1 | 
 |     for (goarg = 1; goargs[goarg] != ""; goarg++) { | 
 | 	if (cargs[carg] == "") { | 
 | 	    print loc, "not enough C parameters" | 
 | 	} | 
 |  | 
 | 	if (args != "") { | 
 | 	    args = args ", " | 
 | 	} | 
 |  | 
 | 	if (split(goargs[goarg], a) != 2) { | 
 | 	    print loc, "bad parameter:", goargs[goarg] | "cat 1>&2" | 
 | 	    status = 1 | 
 | 	    next | 
 | 	} | 
 |  | 
 | 	goname = a[1] | 
 | 	gotype = a[2] | 
 |  | 
 | 	if (split(cargs[carg], a) != 2) { | 
 | 	    print loc, "bad C parameter:", cargs[carg] | "cat 1>&2" | 
 | 	    status = 1 | 
 | 	    next | 
 | 	} | 
 |  | 
 | 	ctype = a[2] | 
 |  | 
 | 	if (gotype ~ /^\*/) { | 
 | 	    if (gotype != ctype) { | 
 | 		print loc, "Go/C pointer type mismatch:", gotype, ctype | "cat 1>&2" | 
 | 		status = 1 | 
 | 		next | 
 | 	    } | 
 | 	    args = args goname | 
 | 	} else if (gotype == "string") { | 
 | 	    if (ctype != "*byte") { | 
 | 		print loc, "Go string not matched to C *byte:", gotype, ctype | "cat 1>&2" | 
 | 		status = 1 | 
 | 		next | 
 | 	    } | 
 | 	    printf("\tvar _p%d *byte\n", goarg) | 
 | 	    if (haserr) { | 
 | 		printf("\t_p%d, err = BytePtrFromString(%s)\n", goarg, goname) | 
 | 		printf("\tif err != nil {\n\t\treturn\n\t}\n") | 
 | 	    } else { | 
 | 		print loc, "uses string arguments but has no error return" | "cat 1>&2" | 
 | 		printf("\t_p%d, _ = BytePtrFromString(%s)\n", goarg, goname) | 
 | 	    } | 
 | 	    args = sprintf("%s_p%d", args, goarg) | 
 | 	} else if (gotype ~ /^\[\](.*)/) { | 
 | 	    if (ctype !~ /^\*/ || cargs[carg + 1] == "") { | 
 | 		print loc, "bad C type for slice:", gotype, ctype | "cat 1>&2" | 
 | 		status = 1 | 
 | 		next | 
 | 	    } | 
 |  | 
 | 	    # Convert a slice into a pair of pointer, length. | 
 | 	    # Don't try to take the address of the zeroth element of a | 
 | 	    # nil slice. | 
 | 	    printf("\tvar _p%d %s\n", goarg, ctype) | 
 | 	    printf("\tif len(%s) > 0 {\n", goname) | 
 | 	    printf("\t\t_p%d = (%s)(unsafe.Pointer(&%s[0]))\n", goarg, ctype, goname) | 
 | 	    printf("\t} else {\n") | 
 | 	    printf("\t\t_p%d = (%s)(unsafe.Pointer(&_zero))\n", goarg, ctype) | 
 | 	    printf("\t}\n") | 
 |  | 
 | 	    ++carg | 
 | 	    if (split(cargs[carg], cparam) != 2) { | 
 | 		print loc, "bad C parameter:", cargs[carg] | "cat 1>&2" | 
 | 		status = 1 | 
 | 		next | 
 | 	    } | 
 |  | 
 | 	    args = sprintf("%s_p%d, %s(len(%s))", args, goarg, cparam[2], goname) | 
 | 	} else if (gotype == "uintptr" && ctype ~ /^\*/) { | 
 | 	    args = sprintf("%s(%s)(unsafe.Pointer(%s))", args, ctype, goname) | 
 | 	} else if (gotype == "unsafe.Pointer" && ctype ~ /^\*/) { | 
 | 	    args = sprintf("%s(%s)(%s)", args, ctype, goname) | 
 | 	} else { | 
 | 	    args = sprintf("%s%s(%s)", args, ctype, goname) | 
 | 	} | 
 |  | 
 | 	carg++ | 
 |     } | 
 |  | 
 |     if (cargs[carg] != "") { | 
 | 	print loc, "too many C parameters" | "cat 1>&2" | 
 | 	status = 1 | 
 | 	next | 
 |     } | 
 |  | 
 |     if (blocking) { | 
 | 	print "\tEntersyscall()" | 
 |     } | 
 |  | 
 |     printf("\t") | 
 |     if (gofnresults != "") { | 
 | 	printf("_r := ") | 
 |     } | 
 |     printf("c_%s(%s)\n", cfnname, args) | 
 |  | 
 |     seterr = 0 | 
 |     if (gofnresults != "") { | 
 | 	fields = split(gofnresults, goresults, ", *") | 
 | 	if (fields > 2) { | 
 | 	    print loc, "too many Go results" | "cat 1>&2" | 
 | 	    status = 1 | 
 | 	    next | 
 | 	} | 
 | 	usedr = 0 | 
 | 	for (goresult = 1; goresults[goresult] != ""; goresult++) { | 
 | 	    if (split(goresults[goresult], goparam) != 2) { | 
 | 		print loc, "bad result:", goresults[goresult] | "cat 1>&2" | 
 | 		status = 1 | 
 | 		next | 
 | 	    } | 
 |  | 
 | 	    goname = goparam[1] | 
 | 	    gotype = goparam[2] | 
 |  | 
 | 	    if (goname == "err") { | 
 | 		print "\tvar errno Errno" | 
 | 		print "\tsetErrno := false" | 
 | 		if (cfnresult ~ /^\*/) { | 
 | 		    print "\tif _r == nil {" | 
 | 		} else { | 
 | 		    print "\tif _r < 0 {" | 
 | 		} | 
 | 		print "\t\terrno = GetErrno()" | 
 | 		print "\t\tsetErrno = true" | 
 | 		print "\t}" | 
 | 		seterr = 1 | 
 | 	    } else if (gotype == "uintptr" && cfnresult ~ /^\*/) { | 
 | 		printf("\t%s = (%s)(unsafe.Pointer(_r))\n", goname, gotype) | 
 | 	    } else { | 
 | 		if (usedr) { | 
 | 		    print loc, "two parameters but no errno parameter" | "cat 1>&2" | 
 | 		    status = 1 | 
 | 		    next | 
 | 		} | 
 | 		printf("\t%s = (%s)(_r)\n", goname, gotype) | 
 | 		usedr = 1 | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |     if (blocking) { | 
 | 	print "\tExitsyscall()" | 
 |     } | 
 |  | 
 |     if (seterr) { | 
 | 	print "\tif setErrno {" | 
 | 	print "\t\terr = errno" | 
 | 	print "\t}" | 
 |     } | 
 |  | 
 |     if (gofnresults != "") { | 
 | 	print "\treturn" | 
 |     } | 
 |  | 
 |     print "}" | 
 |  | 
 |     print "" | 
 |  | 
 |     next | 
 | } | 
 |  | 
 | { next } | 
 |  | 
 | END { | 
 |     if (status != 0) { | 
 | 	print "*** mksyscall.awk failed" | "cat 1>&2" | 
 | 	exit status | 
 |     } | 
 | } |