Repository: benhoyt/goawk (go)

Issue:
Support for %i in format strings.
The [POSIX spec](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html#tag_05) permits using `%i` as an alternative to `%d`. This is supported by most awk implementations:
```
% awk 'BEGIN{printf("%i\n",42)}' 
42
% mawk 'BEGIN{printf("%i\n",42)}'
42
% gawk 'BEGIN{printf("%i\n",42)}'
42
```

GoAWK v1.27.0 does not interpret `%i` correctly:
```
% goawk 'BEGIN{printf("%i\n",42)}'
%!i(int=42)
```

Pull request description:
Add printf %i, %a, %A; Fix %o, %x, %X (they should be unsigned)
Fixes #229

Patch:
diff --git a/interp/functions.go b/interp/functions.go
index ffe91f6..3b36aa2 100644
--- a/interp/functions.go
+++ b/interp/functions.go
@@ -82,15 +82,15 @@ func (p *interp) toNative(v value, typ reflect.Type) reflect.Value {
 	case reflect.Int64:
 		return reflect.ValueOf(int64(v.num()))
 	case reflect.Uint:
-		return reflect.ValueOf(uint(v.num()))
+		return reflect.ValueOf(uint(int64(v.num())))
 	case reflect.Uint8:
-		return reflect.ValueOf(uint8(v.num()))
+		return reflect.ValueOf(uint8(int64(v.num())))
 	case reflect.Uint16:
-		return reflect.ValueOf(uint16(v.num()))
+		return reflect.ValueOf(uint16(int64(v.num())))
 	case reflect.Uint32:
-		return reflect.ValueOf(uint32(v.num()))
+		return reflect.ValueOf(uint32(int64(v.num())))
 	case reflect.Uint64:
-		return reflect.ValueOf(uint64(v.num()))
+		return reflect.ValueOf(uint64(int64(v.num())))
 	case reflect.Float32:
 		return reflect.ValueOf(float32(v.num()))
 	case reflect.Float64:
@@ -363,10 +363,21 @@ func (p *interp) parseFmtTypes(s string) (format string, types []byte, err error
 			switch s[i] {
 			case 's':
 				t = 's'
-			case 'd', 'i', 'o', 'x', 'X':
+			case 'd':
 				t = 'd'
+			case 'o', 'x', 'X':
+				t = 'u'
+			case 'i':
+				t = 'd'
+				out[i] = 'd'
 			case 'f', 'e', 'E', 'g', 'G':
 				t = 'f'
+			case 'a':
+				t = 'f'
+				out[i] = 'x'
+			case 'A':
+				t = 'f'
+				out[i] = 'X'
 			case 'u':
 				t = 'u'
 				out[i] = 'd'
@@ -409,7 +420,7 @@ func (p *interp) sprintf(format string, args []value) (string, error) {
 		case 'f':
 			v = a.num()
 		case 'u':
-			v = uint64(a.num())
+			v = uint64(int64(a.num()))
 		case 'c':
 			var c []byte
 			n, isStr := a.isTrueStr()


Test patch:
diff --git a/interp/interp_test.go b/interp/interp_test.go
index 2083b90..20cb3f1 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -96,6 +96,11 @@ NR==3, NR==5 { print NR }
 	{`BEGIN { printf "%c", "" }  # !awk`, "", "\x00", "", ""},
 	{`BEGIN { printf }  # !awk !posix - doesn't error on this`, "", "", "parse error at 1:16: expected printf args, got none", "printf: no arguments"},
 	{`BEGIN { printf("%%%dd", 4) }`, "", "%4d", "", ""},
+	{`BEGIN { printf "%d %i %o %u %x %X", 42, 42, 42, 42, 42, 42 }`, "", "42 42 52 42 2a 2A", "", ""},
+	{`BEGIN { printf "%d %i %o %u %x %X", -42, -42, -42, -42, -42, -42 }`, "", "-42 -42 1777777777777777777726 18446744073709551574 ffffffffffffffd6 FFFFFFFFFFFFFFD6", "", ""},
+	// Note that Go's fmt %a/%A always has a two-digit exponent with a leading zero, but who cares -- it's rarely used.
+	// Also, the output is normalised differently on Gawk under Windows as 0x9.a51eb851eb85p+7, so skip there.
+	{`BEGIN { printf "%a %A\n", 1234.56, 1234.56 }  # !windows-gawk`, "", "0x1.34a3d70a3d70ap+10 0X1.34A3D70A3D70AP+10\n", "", ""},
 
 	// if and loop statements
 	{`BEGIN { if (1) print "t"; }`, "", "t\n", "", ""},