/bin/sh and BIG NUMBERS

Alex Semenyaka alexs at ratmir.ru
Fri Apr 4 19:06:33 PST 2003


I found that /bin/sh cannot handle numbers those do not fit to integer type.
That is not too bad. Too bad that it just silently warps them in arithmetical
operations:

alexs at snark> /bin/sh -c 'echo $((10000000000-1))'
2147483646

That was not a problem 5 years ago... But now we have a lot of 64-bits values.
So those old scripts which perfectly worked for a long time now can give wrong
results, and you will not be able even to notice it, there is no any diagnostics
or such. The simplest way to fix it is to switch internal /bin/sh arithmetics
from 32 to 64-bits (you know, approach "640K ought to be enough for anybody").
I've did the patch for this (below), please, look at it. Any comments or
suggestions?

diff -u -U1 -r ../sh.old/arith.h ./arith.h
--- ../sh.old/arith.h	Fri Jul 19 08:38:51 2002
+++ ./arith.h	Sat Apr  5 06:26:48 2003
@@ -36,3 +36,3 @@
 
-int arith(char *);
+long long arith(char *);
 int expcmd(int , char **);
diff -u -U1 -r ../sh.old/arith.y ./arith.y
--- ../sh.old/arith.y	Fri Jul 19 08:38:51 2002
+++ ./arith.y	Sat Apr  5 06:23:58 2003
@@ -1 +1,7 @@
+%{
+#define YYSTYPE long long
+
+static long long arith_res;
+%}
+
 %token ARITH_NUM ARITH_LPAREN ARITH_RPAREN
@@ -16,3 +22,4 @@
 exp:	expr = {
-			return ($1);
+			arith_res = $1;
+			return (0);
 		}
@@ -110,7 +117,5 @@
 
-int
+long long
 arith(char *s)
 {
-	long result;
-
 	arith_buf = arith_startbuf = s;
@@ -118,3 +123,3 @@
 	INTOFF;
-	result = yyparse();
+	yyparse();
 	arith_lex_reset();	/* reprime lex */
@@ -122,3 +127,3 @@
 
-	return (result);
+	return (arith_res);
 }
@@ -144,3 +149,3 @@
 	char **ap;
-	long i;
+	long long i;
 
@@ -169,3 +174,3 @@
 
-	out1fmt("%ld\n", i);
+	out1fmt("%qd\n", i);
 	return (! i);
diff -u -U1 -r ../sh.old/arith_lex.l ./arith_lex.l
--- ../sh.old/arith_lex.l	Fri Jul 19 08:38:51 2002
+++ ./arith_lex.l	Sat Apr  5 06:24:30 2003
@@ -48,3 +48,3 @@
 
-extern int yylval;
+extern long long yylval;
 extern char *arith_buf, *arith_startbuf;
@@ -58,3 +58,3 @@
 [ \t\n]	{ ; }
-[0-9]+	{ yylval = atol(yytext); return(ARITH_NUM); }
+[0-9]+	{ yylval = strtoll(yytext, NULL, 10); return(ARITH_NUM); }
 "("	{ return(ARITH_LPAREN); }
diff -u -U1 -r ../sh.old/expand.c ./expand.c
--- ../sh.old/expand.c	Fri Jan 17 14:37:03 2003
+++ ./expand.c	Sat Apr  5 06:35:15 2003
@@ -368,3 +368,3 @@
 	char *p, *start;
-	int result;
+	long long result;
 	int begoff;
@@ -384,6 +384,6 @@
 	 */
-#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
-#error "integers with more than 10 digits are not supported"
-#endif
-	CHECKSTRSPACE(12 - 2, expdest);
+//#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
+//#error "integers with more than 10 digits are not supported"
+//#endif
+	CHECKSTRSPACE(21 - 2, expdest);
 	USTPUTC('\0', expdest);
@@ -409,3 +409,3 @@
 	result = arith(p+2);
-	fmtstr(p, 12, "%d", result);
+	fmtstr(p, 21, "%qd", result);
 	while (*p++)


/Alexs


More information about the freebsd-hackers mailing list