shithub: kwa

Download patch

ref: 3923757ccbde3af0f253824bc9cf9a99127178fb
parent: 2af227c88bbcf8d6c52eb5b3b3a677d88a281774
author: qwx <qwx@sciops.net>
date: Mon Sep 29 06:21:01 EDT 2025

to_number: only reject trailing garbage when tp is nil, check return in lex

fixes infinite loops when parsing invalid scripts like '.', '..', '.E.'
also now correctly identifies this as a syntax error (T.misc)

--- a/lex.c
+++ b/lex.c
@@ -125,10 +125,12 @@
 			}
 		}
 		*bp = 0;
-		to_number(buf, fp, &rem);	/* parse the number */
+		if(to_number(buf, fp, &rem))	/* parse the number */
+			c = '0';
+		else
+			c = buf[0];
 		unputstr(rem);		/* put rest back for later */
 		rem[0] = 0;
-		c = '0';
 	}
 	*pbuf = buf;
 	*psz = sz;
--- a/lib.c
+++ b/lib.c
@@ -649,6 +649,8 @@
 		return 0;
 	else if (f == 0.0 && ((q = strchr(s, '0')) == nil || q > p))
 		return 0;
+	else if (tp != nil)
+		return 1;
 	for (; (c = *p) != '\0'; p++) {
 		switch(c) {
 		case ' ':
@@ -670,7 +672,7 @@
 int to_number(char *s, Awkfloat *fp, char **tp)
 {
 	vlong v;
-	char *p, *q;
+	char c, *p, *q;
 
 	v = strtoll(s, &p, 0);
 	*fp = (Awkfloat)v;
@@ -687,15 +689,15 @@
 		if (is_float(s, fp, tp))
 			return NUM;
 		return 0;
-	case '\0':
-		if (p == s)
-			return 0;
-		else if (v != 0 || (q = strchr(s, '0')) != nil && q < p)
-			return NUM;
-		break;
 	}
-	for (;; p++) {
-		switch(*p) {
+	if (p == s)
+		return 0;
+	else if (v == 0 && ((q = strchr(s, '0')) == nil || q > p))
+		return 0;
+	else if (tp != nil)
+		return NUM;
+	for (; (c = *p) != '\0'; p++) {
+		switch(c) {
 		case ' ':
 		case '\t':
 		case '\n':
@@ -709,4 +711,5 @@
 			return 0;
 		}
 	}
+	return NUM;
 }
--- a/test/to_number.c
+++ b/test/to_number.c
@@ -9,12 +9,14 @@
 	FLT = 1<<1,
 };
 
-static int is_float(char *s, Awkfloat *fp)
+static int is_float(char *s, Awkfloat *fp, char **tp)
 {
 	char c, *p, *q;
 	Awkfloat f;
 
 	f = *fp = strtod(s, &p);
+	if (tp != nil)
+		*tp = p;
 	if (p == s)
 		return 0;
 	else if (isInf(f, 1) || isInf(f, -1) || isNaN(f))
@@ -21,6 +23,8 @@
 		return 0;
 	else if (f == 0.0 && ((q = strchr(s, '0')) == nil || q > p))
 		return 0;
+	else if (tp != nil)
+		return 1;
 	for (; (c = *p) != '\0'; p++) {
 		switch(c) {
 		case ' ':
@@ -39,13 +43,15 @@
 	return 1;
 }
 
-int to_number(char *s, Awkfloat *fp)
+int to_number(char *s, Awkfloat *fp, char **tp)
 {
 	vlong v;
-	char *p, *q;
+	char c, *p, *q;
 
 	v = strtoll(s, &p, 0);
 	*fp = (Awkfloat)v;
+	if (tp != nil)
+		*tp = p;
 	switch(*p){
 	case '.':
 	case 'E':
@@ -54,18 +60,18 @@
 	case 'e':
 	case 'i':
 	case 'n':
-		if (is_float(s, fp))
+		if (is_float(s, fp, tp))
 			return NUM | FLT;
 		return 0;
-	case '\0':
-		if (p == s)
-			return 0;
-		else if (v != 0 || (q = strchr(s, '0')) != nil && q < p)
-			return NUM;
-		break;
 	}
-	for (;; p++) {
-		switch(*p) {
+	if (p == s)
+		return 0;
+	else if (v == 0 && ((q = strchr(s, '0')) == nil || q > p))
+		return 0;
+	else if (tp != nil)
+		return NUM;
+	for (; (c = *p) != '\0'; p++) {
+		switch(c) {
 		case ' ':
 		case '\t':
 		case '\n':
@@ -79,6 +85,7 @@
 			return 0;
 		}
 	}
+	return NUM;
 }
 
 void
@@ -87,32 +94,33 @@
 	int r;
 	Awkfloat f;
 
-	r = to_number("3", &f); assert(r == NUM);
-	r = to_number(" 3", &f); assert(r == NUM);
-	r = to_number("3 ", &f); assert(r == NUM);
-	r = to_number(" 3 ", &f); assert(r == NUM);
-	r = to_number("3x", &f); assert(r == 0);
-	r = to_number(" 3x", &f); assert(r == 0);
-	r = to_number("3x ", &f); assert(r == 0);
-	r = to_number("3x tyu", &f); assert(r == 0);
-	r = to_number("3e1", &f); assert(r == (NUM | FLT));
-	r = to_number("3e", &f); assert(r == 0);
-	r = to_number("-3e", &f); assert(r == 0);
-	r = to_number(" -3e ", &f); assert(r == 0);
-	r = to_number("3.1", &f); assert(r == (NUM | FLT));
-	r = to_number(" 3.1", &f); assert(r == (NUM | FLT));
-	r = to_number("3.1 ", &f); assert(r == (NUM | FLT));
-	r = to_number(" 3.1 ", &f); assert(r == (NUM | FLT));
-	r = to_number("x ", &f); assert(r == 0);
-	r = to_number(".1 ", &f); assert(r == (NUM | FLT));
-	r = to_number("-1e4 ", &f); assert(r == (NUM | FLT));
-	r = to_number(".", &f); assert(r == 0);
-	r = to_number("", &f); assert(r == 0);
-	r = to_number("0", &f); assert(r == NUM);
-	r = to_number("0.", &f); assert(r == (NUM | FLT));
-	r = to_number("0.0", &f); assert(r == (NUM | FLT));
-	r = to_number("0e", &f); assert(r == 0);
-	r = to_number("nan", &f); assert(r == 0);
-	r = to_number("inf", &f); assert(r == 0);
+	r = to_number("3", &f, nil); assert(r == NUM);
+	r = to_number(" 3", &f, nil); assert(r == NUM);
+	r = to_number("3 ", &f, nil); assert(r == NUM);
+	r = to_number(" 3 ", &f, nil); assert(r == NUM);
+	r = to_number("3x", &f, nil); assert(r == 0);
+	r = to_number(" 3x", &f, nil); assert(r == 0);
+	r = to_number("3x ", &f, nil); assert(r == 0);
+	r = to_number("3x tyu", &f, nil); assert(r == 0);
+	r = to_number("3e1", &f, nil); assert(r == (NUM | FLT));
+	r = to_number("3e", &f, nil); assert(r == 0);
+	r = to_number("-3e", &f, nil); assert(r == 0);
+	r = to_number(" -3e ", &f, nil); assert(r == 0);
+	r = to_number("3.1", &f, nil); assert(r == (NUM | FLT));
+	r = to_number(" 3.1", &f, nil); assert(r == (NUM | FLT));
+	r = to_number("3.1 ", &f, nil); assert(r == (NUM | FLT));
+	r = to_number(" 3.1 ", &f, nil); assert(r == (NUM | FLT));
+	r = to_number("x ", &f, nil); assert(r == 0);
+	r = to_number(".1 ", &f, nil); assert(r == (NUM | FLT));
+	r = to_number("-1e4 ", &f, nil); assert(r == (NUM | FLT));
+	r = to_number(".", &f, nil); assert(r == 0);
+	r = to_number("", &f, nil); assert(r == 0);
+	r = to_number("0", &f, nil); assert(r == NUM);
+	r = to_number("0.", &f, nil); assert(r == (NUM | FLT));
+	r = to_number("0.0", &f, nil); assert(r == (NUM | FLT));
+	r = to_number("0e", &f, nil); assert(r == 0);
+	r = to_number("nan", &f, nil); assert(r == 0);
+	r = to_number("inf", &f, nil); assert(r == 0);
+	/* FIXME: more tests + testcases with tp != nil */
 	exits(nil);
 }
--