unix/linux: replace "mksysnum.pl" script with a Go program

Port mksysnum.pl Perl script to mksysnum.go.
mkall scripts are modified to run mksysnum.go.
Running ./mkall.sh does not generate any git diff besides
the command name in comments of generated files.

Updates golang/go#27779

Change-Id: Id62df9d819f00729ab19b6bc4d32dddcf8d9832b
Reviewed-on: https://go-review.googlesource.com/c/151377
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
diff --git a/unix/linux/mkall.go b/unix/linux/mkall.go
index 9a30a41..a8a03fd 100644
--- a/unix/linux/mkall.go
+++ b/unix/linux/mkall.go
@@ -222,6 +222,10 @@
 		args = append([]string{"run", "mksyscall.go"}, args...)
 		mainCmd = makeCommand("go", args...)
 		t.setTargetBuildArch(mainCmd)
+	} else if name == "mksysnum" {
+		args = append([]string{"run", "linux/mksysnum.go"}, args...)
+		mainCmd = makeCommand("go", args...)
+		t.setTargetBuildArch(mainCmd)
 	}
 
 	fmtCmd := makeCommand(formatter)
@@ -467,7 +471,7 @@
 	unistdFile := filepath.Join(IncludeDir, "asm/unistd.h")
 
 	args := append(t.cFlags(), unistdFile)
-	return t.commandFormatOutput("gofmt", zsysnumFile, "linux/mksysnum.pl", args...)
+	return t.commandFormatOutput("gofmt", zsysnumFile, "mksysnum", args...)
 }
 
 // makes the zsyscall_linux_$GOARCH.go file
diff --git a/unix/linux/mksysnum.go b/unix/linux/mksysnum.go
new file mode 100755
index 0000000..e1ad597
--- /dev/null
+++ b/unix/linux/mksysnum.go
@@ -0,0 +1,143 @@
+// Copyright 2018 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.
+
+// +build ignore
+
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+	"os/exec"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+var (
+	goos, goarch string
+)
+
+// cmdLine returns this programs's commandline arguments
+func cmdLine() string {
+	return "go run linux/mksysnum.go " + strings.Join(os.Args[1:], " ")
+}
+
+// buildTags returns build tags
+func buildTags() string {
+	return fmt.Sprintf("%s,%s", goarch, goos)
+}
+
+func format(name string, num int, offset int) string {
+	if num > 999 {
+		// ignore deprecated syscalls that are no longer implemented
+		// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
+		return ""
+	}
+	name = strings.ToUpper(name)
+	num = num + offset
+	return fmt.Sprintf("	SYS_%s = %d;\n", name, num)
+}
+
+func checkErr(err error) {
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "%v\n", err)
+		os.Exit(1)
+	}
+}
+
+// source string and substring slice for regexp
+type re struct {
+	str string   // source string
+	sub []string // matched sub-string
+}
+
+// Match performs regular expression match
+func (r *re) Match(exp string) bool {
+	r.sub = regexp.MustCompile(exp).FindStringSubmatch(r.str)
+	if r.sub != nil {
+		return true
+	}
+	return false
+}
+
+func main() {
+	// Get the OS and architecture (using GOARCH_TARGET if it exists)
+	goos = os.Getenv("GOOS")
+	goarch = os.Getenv("GOARCH_TARGET")
+	if goarch == "" {
+		goarch = os.Getenv("GOARCH")
+	}
+	// Check if GOOS and GOARCH environment variables are defined
+	if goarch == "" || goos == "" {
+		fmt.Fprintf(os.Stderr, "GOARCH or GOOS not defined in environment\n")
+		os.Exit(1)
+	}
+	// Check that we are using the new build system if we should
+	if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
+		fmt.Fprintf(os.Stderr, "In the new build system, mksysnum should not be called directly.\n")
+		fmt.Fprintf(os.Stderr, "See README.md\n")
+		os.Exit(1)
+	}
+
+	cc := os.Getenv("CC")
+	if cc == "" {
+		fmt.Fprintf(os.Stderr, "CC is not defined in environment\n")
+		os.Exit(1)
+	}
+	args := os.Args[1:]
+	args = append([]string{"-E", "-dD"}, args...)
+	cmd, err := exec.Command(cc, args...).Output() // execute command and capture output
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "can't run %s", cc)
+		os.Exit(1)
+	}
+	text := ""
+	s := bufio.NewScanner(strings.NewReader(string(cmd)))
+	var offset, prev int
+	for s.Scan() {
+		t := re{str: s.Text()}
+		if t.Match(`^#define __NR_Linux\s+([0-9]+)`) {
+			// mips/mips64: extract offset
+			offset, _ = strconv.Atoi(t.sub[1]) // Make offset=0 if empty or non-numeric
+		} else if t.Match(`^#define __NR(\w*)_SYSCALL_BASE\s+([0-9]+)`) {
+			// arm: extract offset
+			offset, _ = strconv.Atoi(t.sub[1]) // Make offset=0 if empty or non-numeric
+		} else if t.Match(`^#define __NR_syscalls\s+`) {
+			// ignore redefinitions of __NR_syscalls
+		} else if t.Match(`^#define __NR_(\w*)Linux_syscalls\s+`) {
+			// mips/mips64: ignore definitions about the number of syscalls
+		} else if t.Match(`^#define __NR_(\w+)\s+([0-9]+)`) {
+			prev, err = strconv.Atoi(t.sub[2])
+			checkErr(err)
+			text += format(t.sub[1], prev, offset)
+		} else if t.Match(`^#define __NR3264_(\w+)\s+([0-9]+)`) {
+			prev, err = strconv.Atoi(t.sub[2])
+			checkErr(err)
+			text += format(t.sub[1], prev, offset)
+		} else if t.Match(`^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)`) {
+			r2, err := strconv.Atoi(t.sub[2])
+			checkErr(err)
+			text += format(t.sub[1], prev+r2, offset)
+		} else if t.Match(`^#define __NR_(\w+)\s+\(__NR_(?:SYSCALL_BASE|Linux) \+ ([0-9]+)`) {
+			r2, err := strconv.Atoi(t.sub[2])
+			checkErr(err)
+			text += format(t.sub[1], r2, offset)
+		}
+	}
+	err = s.Err()
+	checkErr(err)
+	fmt.Printf(template, cmdLine(), buildTags(), text)
+}
+
+const template = `// %s
+// Code generated by the command above; see README.md. DO NOT EDIT.
+
+// +build %s
+
+package unix
+
+const(
+%s)`
diff --git a/unix/linux/mksysnum.pl b/unix/linux/mksysnum.pl
deleted file mode 100755
index 63fd800..0000000
--- a/unix/linux/mksysnum.pl
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env perl
-# Copyright 2009 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.
-
-use strict;
-
-if($ENV{'GOARCH'} eq "" || $ENV{'GOOS'} eq "") {
-	print STDERR "GOARCH or GOOS not defined in environment\n";
-	exit 1;
-}
-
-# Check that we are using the new build system if we should
-if($ENV{'GOLANG_SYS_BUILD'} ne "docker") {
-	print STDERR "In the new build system, mksysnum should not be called directly.\n";
-	print STDERR "See README.md\n";
-	exit 1;
-}
-
-my $command = "$0 ". join(' ', @ARGV);
-
-print <<EOF;
-// $command
-// Code generated by the command above; see README.md. DO NOT EDIT.
-
-// +build $ENV{'GOARCH'},$ENV{'GOOS'}
-
-package unix
-
-const(
-EOF
-
-my $offset = 0;
-
-sub fmt {
-	my ($name, $num) = @_;
-	if($num > 999){
-		# ignore deprecated syscalls that are no longer implemented
-		# https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
-		return;
-	}
-	$name =~ y/a-z/A-Z/;
-	$num = $num + $offset;
-	print "	SYS_$name = $num;\n";
-}
-
-my $prev;
-open(CC, "$ENV{'CC'} -E -dD @ARGV |") || die "can't run $ENV{'CC'}";
-while(<CC>){
-	if(/^#define __NR_Linux\s+([0-9]+)/){
-		# mips/mips64: extract offset
-		$offset = $1;
-	}
-	elsif(/^#define __NR(\w*)_SYSCALL_BASE\s+([0-9]+)/){
-		# arm: extract offset
-		$offset = $1;
-	}
-	elsif(/^#define __NR_syscalls\s+/) {
-		# ignore redefinitions of __NR_syscalls
-	}
-	elsif(/^#define __NR_(\w*)Linux_syscalls\s+/) {
-		# mips/mips64: ignore definitions about the number of syscalls
-	}
-	elsif(/^#define __NR_(\w+)\s+([0-9]+)/){
-		$prev = $2;
-		fmt($1, $2);
-	}
-	elsif(/^#define __NR3264_(\w+)\s+([0-9]+)/){
-		$prev = $2;
-		fmt($1, $2);
-	}
-	elsif(/^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)/){
-		fmt($1, $prev+$2)
-	}
-	elsif(/^#define __NR_(\w+)\s+\(__NR_Linux \+ ([0-9]+)/){
-		fmt($1, $2);
-	}
-	elsif(/^#define __NR_(\w+)\s+\(__NR_SYSCALL_BASE \+ ([0-9]+)/){
-		fmt($1, $2);
-	}
-}
-
-print <<EOF;
-)
-EOF
diff --git a/unix/zsysnum_linux_386.go b/unix/zsysnum_linux_386.go
index 8f33ece..8d17873 100644
--- a/unix/zsysnum_linux_386.go
+++ b/unix/zsysnum_linux_386.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include -m32 /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -m32 /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build 386,linux
diff --git a/unix/zsysnum_linux_amd64.go b/unix/zsysnum_linux_amd64.go
index 70c1a2c..b3d8ad7 100644
--- a/unix/zsysnum_linux_amd64.go
+++ b/unix/zsysnum_linux_amd64.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include -m64 /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -m64 /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build amd64,linux
diff --git a/unix/zsysnum_linux_arm.go b/unix/zsysnum_linux_arm.go
index 6e281d6..e092822 100644
--- a/unix/zsysnum_linux_arm.go
+++ b/unix/zsysnum_linux_arm.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build arm,linux
diff --git a/unix/zsysnum_linux_arm64.go b/unix/zsysnum_linux_arm64.go
index f9157e1..3206967 100644
--- a/unix/zsysnum_linux_arm64.go
+++ b/unix/zsysnum_linux_arm64.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include -fsigned-char /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -fsigned-char /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build arm64,linux
diff --git a/unix/zsysnum_linux_mips.go b/unix/zsysnum_linux_mips.go
index 1671449..6893a5b 100644
--- a/unix/zsysnum_linux_mips.go
+++ b/unix/zsysnum_linux_mips.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build mips,linux
diff --git a/unix/zsysnum_linux_mips64.go b/unix/zsysnum_linux_mips64.go
index 1270a1c..40164ca 100644
--- a/unix/zsysnum_linux_mips64.go
+++ b/unix/zsysnum_linux_mips64.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build mips64,linux
diff --git a/unix/zsysnum_linux_mips64le.go b/unix/zsysnum_linux_mips64le.go
index 93980be..8a90973 100644
--- a/unix/zsysnum_linux_mips64le.go
+++ b/unix/zsysnum_linux_mips64le.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build mips64le,linux
diff --git a/unix/zsysnum_linux_mipsle.go b/unix/zsysnum_linux_mipsle.go
index 0fc7726..8d78184 100644
--- a/unix/zsysnum_linux_mipsle.go
+++ b/unix/zsysnum_linux_mipsle.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build mipsle,linux
diff --git a/unix/zsysnum_linux_ppc64.go b/unix/zsysnum_linux_ppc64.go
index a5c5f3d..ec5bde3 100644
--- a/unix/zsysnum_linux_ppc64.go
+++ b/unix/zsysnum_linux_ppc64.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build ppc64,linux
diff --git a/unix/zsysnum_linux_ppc64le.go b/unix/zsysnum_linux_ppc64le.go
index 5470ead..bdbabdb 100644
--- a/unix/zsysnum_linux_ppc64le.go
+++ b/unix/zsysnum_linux_ppc64le.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build ppc64le,linux
diff --git a/unix/zsysnum_linux_riscv64.go b/unix/zsysnum_linux_riscv64.go
index a5d9919..473c746 100644
--- a/unix/zsysnum_linux_riscv64.go
+++ b/unix/zsysnum_linux_riscv64.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build riscv64,linux
diff --git a/unix/zsysnum_linux_s390x.go b/unix/zsysnum_linux_s390x.go
index de0245a..6eb7c25 100644
--- a/unix/zsysnum_linux_s390x.go
+++ b/unix/zsysnum_linux_s390x.go
@@ -1,4 +1,4 @@
-// linux/mksysnum.pl -Wall -Werror -static -I/tmp/include -fsigned-char /tmp/include/asm/unistd.h
+// go run linux/mksysnum.go -Wall -Werror -static -I/tmp/include -fsigned-char /tmp/include/asm/unistd.h
 // Code generated by the command above; see README.md. DO NOT EDIT.
 
 // +build s390x,linux