| # 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 "// This file was automatically generated by mksyscall.awk" |
| 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 %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 |
| } |
| } |