nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: benhoyt/goawk
Pull request #119 (2022-05-18 08:08:36)

Wire up ctx through system()


Patch:
diff --git a/interp/io.go b/interp/io.go
index 7ec8511..3356649 100644
--- a/interp/io.go
+++ b/interp/io.go
@@ -187,8 +187,11 @@ func (p *interp) execShell(code string) *exec.Cmd {
 	executable := p.shellCommand[0]
 	args := p.shellCommand[1:]
 	args = append(args, code)
-	cmd := exec.Command(executable, args...)
-	return cmd
+	if p.checkCtx {
+		return exec.CommandContext(p.ctx, executable, args...)
+	} else {
+		return exec.Command(executable, args...)
+	}
 }
 
 // Get input Scanner to use for "getline" based on file name
diff --git a/interp/newexecute.go b/interp/newexecute.go
index fe46bd9..438fe6d 100644
--- a/interp/newexecute.go
+++ b/interp/newexecute.go
@@ -144,7 +144,7 @@ func (p *Interpreter) ResetRand() {
 // system() won't be interrupted.
 func (p *Interpreter) ExecuteContext(ctx context.Context, config *Config) (int, error) {
 	p.interp.resetCore()
-	p.interp.checkCtx = ctx != context.Background()
+	p.interp.checkCtx = ctx != context.Background() && ctx != context.TODO()
 	p.interp.ctx = ctx
 	p.interp.ctxDone = ctx.Done()
 	p.interp.ctxOps = 0
diff --git a/interp/vm.go b/interp/vm.go
index 7199365..49b07f5 100644
--- a/interp/vm.go
+++ b/interp/vm.go
@@ -1071,22 +1071,17 @@ func (p *interp) callBuiltin(builtinOp compiler.BuiltinOp) error {
 		cmd.Stdout = p.output
 		cmd.Stderr = p.errorOutput
 		_ = p.flushAll() // ensure synchronization
-		err := cmd.Start()
-		var ret float64
+		err := cmd.Run()
+		ret := 0.0
 		if err != nil {
-			p.printErrorf("%s\n", err)
-			ret = -1
-		} else {
-			err = cmd.Wait()
-			if err != nil {
-				if exitErr, ok := err.(*exec.ExitError); ok {
-					ret = float64(exitErr.ProcessState.ExitCode())
-				} else {
-					p.printErrorf("unexpected error running command %q: %v\n", cmdline, err)
-					ret = -1
-				}
+			if p.checkCtx && p.ctx.Err() != nil {
+				return p.ctx.Err()
+			}
+			if exitErr, ok := err.(*exec.ExitError); ok {
+				ret = float64(exitErr.ProcessState.ExitCode())
 			} else {
-				ret = 0
+				p.printErrorf("%v\n", err)
+				ret = -1
 			}
 		}
 		p.replaceTop(num(ret))


Test patch:
diff --git a/interp/newexecute_test.go b/interp/newexecute_test.go
index 8fbf6d9..7c74e59 100644
--- a/interp/newexecute_test.go
+++ b/interp/newexecute_test.go
@@ -138,6 +138,16 @@ func TestExecuteContextCancel(t *testing.T) {
 	}
 }
 
+func TestExecuteContextSystemTimeout(t *testing.T) {
+	interpreter := newInterp(t, `BEGIN { print system("sleep 4") }`)
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)
+	defer cancel()
+	_, err := interpreter.ExecuteContext(ctx, nil)
+	if !errors.Is(err, context.DeadlineExceeded) {
+		t.Fatalf("expected DeadlineExceeded error, got: %v", err)
+	}
+}
+
 func newInterp(t *testing.T, src string) *interp.Interpreter {
 	t.Helper()
 	prog, err := parser.ParseProgram([]byte(src), nil)
Repository: benhoyt/goawk
Pull request #126 (2022-05-28 00:05:40)

Ignore UTF-8 BOM at start of CSV files
The UTF-8 BOM was present at the start of some CSV files I found (maybe Excel writes it out?), [for example these ones](https://catalogue.data.govt.nz/dataset/directory-of-educational-institutions/resource/20b7c271-fd5a-4c9e-869b-481a0e2453cd), and it made the first field name `"\ufeff_id"` instead of `"_id"`.


Patch:
diff --git a/interp/io.go b/interp/io.go
index 3356649..dc9aa41 100644
--- a/interp/io.go
+++ b/interp/io.go
@@ -430,6 +430,7 @@ type csvSplitter struct {
 
 	recordBuffer []byte
 	fieldIndexes []int
+	noBOMCheck   bool
 
 	fields        *[]string
 	setFieldNames func(names []string)
@@ -442,6 +443,13 @@ type csvSplitter struct {
 // We don't support all encoding/csv features: FieldsPerRecord is not
 // supported, LazyQuotes is always on, and TrimLeadingSpace is always off.
 func (s *csvSplitter) scan(data []byte, atEOF bool) (advance int, token []byte, err error) {
+	// Some CSV files are saved with a UTF-8 BOM at the start; skip it.
+	if !s.noBOMCheck && len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF {
+		data = data[3:]
+		advance = 3
+		s.noBOMCheck = true
+	}
+
 	origData := data
 	if atEOF && len(data) == 0 {
 		// No more data, tell Scanner to stop.
@@ -587,6 +595,8 @@ parseField:
 		preIdx = idx
 	}
 
+	s.noBOMCheck = true
+
 	if s.rowNum == 0 && s.header {
 		// Set header field names and advance, but don't return a line (token).
 		s.rowNum++


Test patch:
diff --git a/interp/interp_test.go b/interp/interp_test.go
index d1a4ba0..b039a97 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -1556,6 +1556,9 @@ var csvTests = []csvTest{
 	{`BEGIN { OUTPUTMODE="csv separator=,"; printf "%s", OUTPUTMODE }`, "", "csv", "", nil},
 	{`BEGIN { OUTPUTMODE="csv separator=|"; printf "%s", OUTPUTMODE }`, "", "csv separator=|", "", nil},
 
+	// Ignores UTF-8 byte order mark (BOM) at start of CSV file
+	{`BEGIN { INPUTMODE="csv" } { print $1=="foo" }`, "\ufefffoo,bar\n\ufefffoo,bar", "1\n0\n", "", nil},
+
 	// Error handling when parsing INPUTMODE and OUTPUTMODE
 	{`BEGIN { INPUTMODE="xyz" }`, "", "", `invalid input mode "xyz"`, nil},
 	{`BEGIN { INPUTMODE="csv separator=foo" }`, "", "", `invalid CSV/TSV separator "foo"`, nil},
Repository: benhoyt/goawk
Pull request #180 (2023-05-18 01:40:31)

Limit ARGC to a reasonable max value
This is to prevent scripts like `ARGC=100000000` from taking forever. There are still lots of ways to make a script that runs for ages / forever of course, but it seems like we shouldn't allow arbitrarily large `ARGC` values.


Patch:
diff --git a/interp/interp.go b/interp/interp.go
index 97f41a2..c16fa13 100644
--- a/interp/interp.go
+++ b/interp/interp.go
@@ -781,7 +781,11 @@ func (p *interp) setSpecial(index int, v value) error {
 	case ast.V_FNR:
 		p.fileLineNum = int(v.num())
 	case ast.V_ARGC:
-		p.argc = int(v.num())
+		argc := int(v.num())
+		if argc > maxFieldIndex {
+			return newError("ARGC set too large: %d", argc)
+		}
+		p.argc = argc
 	case ast.V_CONVFMT:
 		p.convertFormat = p.toString(v)
 	case ast.V_FILENAME:


Test patch:
diff --git a/interp/interp_test.go b/interp/interp_test.go
index 5bd7912..0cf4727 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -305,6 +305,7 @@ BEGIN {
 
 	// Built-in variables
 	{`BEGIN { print ARGC; ARGC=42; print ARGC }  # !gawk`, "", "1\n42\n", "", ""}, // ARGC is properly tested in goawk_test.go
+	{`BEGIN { ARGC=1234567 }`, "", "", "ARGC set too large: 1234567", ""},
 	{`
 BEGIN {
 	print CONVFMT, 1.2345678 ""
Repository: benhoyt/goawk
Pull request #222 (2024-02-28 00:07:15)

Distinguish between /regexp/ and "regexp" in stringified output
Fixes issue #221



Patch:
diff --git a/internal/ast/ast.go b/internal/ast/ast.go
index 98e1307..303df70 100644
--- a/internal/ast/ast.go
+++ b/internal/ast/ast.go
@@ -292,12 +292,16 @@ func (e *NumExpr) String() string {
 	}
 }
 
-// StrExpr is a literal string like "foo".
+// StrExpr is a literal string like "foo" or a regex constant like /foo/.
 type StrExpr struct {
 	Value string
+	Regex bool
 }
 
 func (e *StrExpr) String() string {
+	if e.Regex {
+		return formatRegex(e.Value)
+	}
 	return strconv.Quote(e.Value)
 }
 
@@ -308,8 +312,7 @@ type RegExpr struct {
 }
 
 func (e *RegExpr) String() string {
-	escaped := strings.Replace(e.Regex, "/", `\/`, -1)
-	return "/" + escaped + "/"
+	return formatRegex(e.Regex)
 }
 
 // VarExpr is a variable reference (special var, global, or local).
@@ -460,6 +463,12 @@ func IsLValue(expr Expr) bool {
 	}
 }
 
+// formatRegex formats the regex string r.
+func formatRegex(r string) string {
+	escaped := strings.Replace(r, "/", `\/`, -1)
+	return "/" + escaped + "/"
+}
+
 // Stmt is the abstract syntax tree for any AWK statement.
 type Stmt interface {
 	Node
diff --git a/parser/parser.go b/parser/parser.go
index c5db54a..ebd31cb 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -726,7 +726,7 @@ func (p *parser) primary() ast.Expr {
 	case STRING:
 		s := p.val
 		p.next()
-		return &ast.StrExpr{s}
+		return &ast.StrExpr{Value: s}
 	case DIV, DIV_ASSIGN:
 		// If we get to DIV or DIV_ASSIGN as a primary expression,
 		// it's actually a regex.
@@ -967,7 +967,7 @@ func (p *parser) optionalLValue() ast.Expr {
 func (p *parser) regexStr(parse func() ast.Expr) ast.Expr {
 	if p.matches(DIV, DIV_ASSIGN) {
 		regex := p.nextRegex()
-		return &ast.StrExpr{regex}
+		return &ast.StrExpr{Value: regex, Regex: true}
 	}
 	return parse()
 }


Test patch:
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 1874fbc..468863d 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -124,6 +124,8 @@ $0 {
     a[x, y, z]
     f()
     set(a, k, v)
+    sub(/regex/, repl, s)
+    sub("regex", repl, s)
     sub(regex, repl)
     sub(regex, repl, s)
     gsub(regex, repl)