[PATCH] dc: tweak parsing

Bernhard Reutner-Fischer rep.dot.nop at gmail.com
Sun Feb 15 14:00:59 UTC 2015


https://bugs.debian.org/538685
dc requires whitespace between language elements.

We were requiring
1 2 + p
instead of the abbreviated
1 2+p
(for example).

function                                             old     new   delta
stack_machine                                        101     138     +37
dc_main                                              131     102     -29
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 37/-29)              Total: 8 bytes
   text	   data	    bss	    dec	    hex	filename

Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop at gmail.com>
---
 miscutils/dc.c     | 62 ++++++++++++++++++++++++++++--------------------------
 testsuite/dc.tests | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+), 30 deletions(-)
 create mode 100755 testsuite/dc.tests

diff --git a/miscutils/dc.c b/miscutils/dc.c
index 6bcfbe2..c40d1ef 100644
--- a/miscutils/dc.c
+++ b/miscutils/dc.c
@@ -196,14 +196,6 @@ struct op {
 };
 
 static const struct op operators[] = {
-	{"+",   add},
-	{"add", add},
-	{"-",   sub},
-	{"sub", sub},
-	{"*",   mul},
-	{"mul", mul},
-	{"/",   divide},
-	{"div", divide},
 #if ENABLE_FEATURE_DC_LIBM
 	{"**",  power},
 	{"exp", power},
@@ -216,28 +208,45 @@ static const struct op operators[] = {
 	{"not", not},
 	{"eor", eor},
 	{"xor", eor},
+	{"+",   add},
+	{"add", add},
+	{"-",   sub},
+	{"sub", sub},
+	{"*",   mul},
+	{"mul", mul},
+	{"/",   divide},
+	{"div", divide},
 	{"p", print_no_pop},
 	{"f", print_stack_no_pop},
 	{"o", set_output_base},
 };
 
-static void stack_machine(const char *argument)
+/* Feed the stack machine.
+ * Returns a pointer to the first character not yet matched or NULL when
+ * done.
+ */
+static char* stack_machine(const char *argument)
 {
 	char *end;
-	double d;
+	double number;
 	const struct op *o;
 
-	d = strtod(argument, &end);
-	if (end != argument && *end == '\0') {
-		push(d);
-		return;
+	number = strtod(argument, &end);
+	if (end != argument) {
+		push(number);
+		return end;
 	}
+	if (*end == '\0')
+		return NULL;
+	/* We might have matched a digit, eventually advance the argument */
+	argument = skip_whitespace(end);
 
 	o = operators;
 	do {
-		if (strcmp(o->name, argument) == 0) {
+		const size_t name_len = strlen(o->name);
+		if (strncmp(o->name, argument, name_len) == 0) {
 			o->function();
-			return;
+			return ((char *)argument) + name_len;
 		}
 		o++;
 	} while (o != operators + ARRAY_SIZE(operators));
@@ -248,33 +257,26 @@ static void stack_machine(const char *argument)
 int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int dc_main(int argc UNUSED_PARAM, char **argv)
 {
+	char *cursor;
 	INIT_G();
 
 	argv++;
 	if (!argv[0]) {
 		/* take stuff from stdin if no args are given */
 		char *line;
-		char *cursor;
-		char *token;
 		while ((line = xmalloc_fgetline(stdin)) != NULL) {
 			cursor = line;
-			while (1) {
-				token = skip_whitespace(cursor);
-				if (*token == '\0')
-					break;
-				cursor = skip_non_whitespace(token);
-				if (*cursor != '\0')
-					*cursor++ = '\0';
-				stack_machine(token);
+			while (*cursor != '\0') {
+				cursor = stack_machine(cursor);
 			}
 			free(line);
 		}
 	} else {
-		// why? it breaks "dc -2 2 + p"
-		//if (argv[0][0] == '-')
-		//	bb_show_usage();
 		do {
-			stack_machine(*argv);
+			cursor = *argv;
+			while (*cursor != '\0') {
+				cursor = stack_machine(cursor);
+			}
 		} while (*++argv);
 	}
 	return EXIT_SUCCESS;
diff --git a/testsuite/dc.tests b/testsuite/dc.tests
new file mode 100755
index 0000000..a5da537
--- /dev/null
+++ b/testsuite/dc.tests
@@ -0,0 +1,56 @@
+#!/bin/sh
+# Copyright 2015 by Bernhard Reutner-Fischer
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+testing "dc basic syntax (stdin, multiple args)" \
+	"dc" \
+	"30\n" \
+	"" "10 20+p"
+
+testing "dc basic syntax (argv, single arg)" \
+	"dc '10 20+p'" \
+	"30\n" \
+	"" ""
+
+testing "dc basic syntax (argv, multiple args)" \
+	"dc 10 20+p" \
+	"30\n" \
+	"" ""
+
+testing "dc complex with spaces (single arg)" \
+	"dc '8 8 * 2 2 + / p'" \
+	"16\n" \
+	"" ""
+
+testing "dc complex without spaces (single arg)" \
+	"dc '8 8*2 2+/p'" \
+	"16\n" \
+	"" ""
+
+testing "dc complex with spaces (multiple args)" \
+	"dc 8 8 \* 2 2 + / p" \
+	"16\n" \
+	"" ""
+
+testing "dc complex without spaces (multiple args)" \
+	"dc 8 8\*2 2+/p" \
+	"16\n" \
+	"" ""
+
+exit $FAILCOUNT
+
+# we do not support arguments
+testing "dc -e <exprs>" \
+	"dc -e '10 2+f'" \
+	"12\n" \
+	"" ""
+
+testing "dc -f <exprs-from-given-file>" \
+	"dc -f input" \
+	"12\n" \
+	"10 2+f" ""
+
-- 
2.1.4



More information about the busybox mailing list