From 116767901f9fc22f837f2830dbd42ad57d70fcb1 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sun, 28 Nov 2021 09:50:10 -0500 Subject: [PATCH] Convert word, wordlist, and intcmp functions to use long long Modify make functions that parse integer values to use long long values instead of long: on Windows long is the same as int (4 bytes) and we don't want behavior to differ between different platforms. * bootstrap.conf: Change strtol module to strtoll module. * src/function.c (parse_numeric): Use strtoll() and return long long. (func_word): Use long long. (func_wordlist): Use long long. Verify second argument is >= 0. (func_intcmp): Use long long. * src/config.ami.template: Don't define HAVE_STRTOLL. * src/config-vms.template: Define HAVE_STRTOLL. * src/config.W32.template: Define HAVE_STRTOLL. * tests/run_make_tests.pl: Set $ERR_out_of_range to the proper string. * tests/scripts/functions/word: Rework to use the new style and avoid TAB characters. Verify trailing whitespace is ignored. Add a test for a negative second argument to wordlist. Add tests for max signed integer values. Use $ERR_out_of_range for the error string. * tests/scripts/functions/intcmp: Add tests for min and max signed integer values. Use $ERR_out_of_range for the error string. --- bootstrap.conf | 2 +- src/config.ami.template | 3 ++ src/config.h-vms.template | 3 ++ src/config.h.W32.template | 3 ++ src/function.c | 22 ++++++---- src/makeint.h | 5 ++- tests/run_make_tests.pl | 11 +++++ tests/scripts/functions/intcmp | 10 ++++- tests/scripts/functions/word | 76 +++++++++++++++++----------------- 9 files changed, 84 insertions(+), 51 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index 69058aa0..13ded359 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -51,5 +51,5 @@ getloadavg host-cpu-c-abi mempcpy strerror -strtol +strtoll make-glob" diff --git a/src/config.ami.template b/src/config.ami.template index 0d44fc64..52d8f51f 100644 --- a/src/config.ami.template +++ b/src/config.ami.template @@ -261,6 +261,9 @@ this program. If not, see . */ /* Define if you have the strsignal function. */ /* #undef HAVE_STRSIGNAL */ +/* Define if you have the strtoll function. */ +/* #undef HAVE_STRTOLL */ + /* Define if you have the wait3 function. */ /* #undef HAVE_WAIT3 */ diff --git a/src/config.h-vms.template b/src/config.h-vms.template index 62daac2d..f3f6d30f 100644 --- a/src/config.h-vms.template +++ b/src/config.h-vms.template @@ -301,6 +301,9 @@ this program. If not, see . */ /* Define to 1 if you have the strsignal function. */ /* #undef HAVE_STRSIGNAL */ +/* Define to 1 if you have the `strtoll' function. */ +#define HAVE_STRTOLL 1 + /* Define to 1 if you have the wait3 function. */ /* #undef HAVE_WAIT3 */ diff --git a/src/config.h.W32.template b/src/config.h.W32.template index bd3b4763..3a92b205 100644 --- a/src/config.h.W32.template +++ b/src/config.h.W32.template @@ -293,6 +293,9 @@ this program. If not, see . */ /* Define to 1 if you have the 'strsignal' function. */ /* #undef HAVE_STRSIGNAL */ +/* Define to 1 if you have the `strtoll' function. */ +#define HAVE_STRTOLL 1 + /* Define to 1 if `d_type' is a member of `struct dirent'. */ /* SV 57152: MinGW64 version of dirent doesn't support d_type. */ #ifndef __MINGW64__ diff --git a/src/function.c b/src/function.c index 63fd238c..75aa1ca6 100644 --- a/src/function.c +++ b/src/function.c @@ -765,17 +765,17 @@ strip_whitespace (const char **begpp, const char **endpp) return (char *)*begpp; } -static long +static long long parse_numeric (const char *s, const char *msg) { const char *beg = s; const char *end = s + strlen (s) - 1; char *endp; - long num; + long long num; strip_whitespace (&beg, &end); errno = 0; - num = strtol (beg, &endp, 10); + num = strtoll (beg, &endp, 10); if (errno == ERANGE) OSS (fatal, *expanding_var, "%s: '%s'", strerror (errno), s); else if (endp == beg || endp <= end) @@ -790,11 +790,11 @@ func_word (char *o, char **argv, const char *funcname UNUSED) { const char *end_p; const char *p; - long i; + long long i; i = parse_numeric (argv[0], _("non-numeric first argument to 'word' function")); - if (i <= 0) + if (i < 1) O (fatal, *expanding_var, _("first argument to 'word' function must be greater than 0")); @@ -812,7 +812,7 @@ func_word (char *o, char **argv, const char *funcname UNUSED) static char * func_wordlist (char *o, char **argv, const char *funcname UNUSED) { - long start, stop, count; + long long start, stop, count; start = parse_numeric (argv[0], _("non-numeric first argument to 'wordlist' function")); @@ -821,7 +821,11 @@ func_wordlist (char *o, char **argv, const char *funcname UNUSED) if (start < 1) ON (fatal, *expanding_var, - "invalid first argument to 'wordlist' function: '%ld'", start); + "invalid first argument to 'wordlist' function: '%lld'", start); + + if (stop < 0) + ON (fatal, *expanding_var, + "invalid second argument to 'wordlist' function: '%lld'", stop); count = stop - start + 1; @@ -1288,7 +1292,7 @@ func_intcmp (char *o, char **argv, const char *funcname UNUSED) { char *lhs_str = expand_argument (argv[0], NULL); char *rhs_str = expand_argument (argv[1], NULL); - long lhs, rhs; + long long lhs, rhs; lhs = parse_numeric (lhs_str, _("non-numeric first argument to 'intcmp' function")); @@ -1304,7 +1308,7 @@ func_intcmp (char *o, char **argv, const char *funcname UNUSED) if (lhs == rhs) { char buf[INTSTR_LENGTH+1]; - sprintf (buf, "%ld", lhs); + sprintf (buf, "%lld", lhs); o = variable_buffer_output(o, buf, strlen (buf)); } return o; diff --git a/src/makeint.h b/src/makeint.h index bf9dab73..9c80535c 100644 --- a/src/makeint.h +++ b/src/makeint.h @@ -482,8 +482,9 @@ extern struct rlimit stack_limit; #define CSTRLEN(_s) (sizeof (_s)-1) #define STRING_SIZE_TUPLE(_s) (_s), CSTRLEN(_s) -/* The number of bytes needed to represent the largest integer as a string. */ -#define INTSTR_LENGTH CSTRLEN ("18446744073709551616") +/* The number of bytes needed to represent the largest integer as a string. + Large enough for both the largest signed and unsigned long long. */ +#define INTSTR_LENGTH CSTRLEN ("18446744073709551615") #define DEFAULT_TTYNAME "true" #ifdef HAVE_TTYNAME diff --git a/tests/run_make_tests.pl b/tests/run_make_tests.pl index cc46af1f..ebf202a2 100644 --- a/tests/run_make_tests.pl +++ b/tests/run_make_tests.pl @@ -108,6 +108,7 @@ $ERR_read_only_file = undef; $ERR_unreadable_file = undef; $ERR_nonexe_file = undef; $ERR_exe_dir = undef; +$ERR_out_of_range = undef; { use locale; @@ -118,6 +119,12 @@ $ERR_exe_dir = undef; # Windows has POSIX locale, but only LC_ALL not LC_MESSAGES $loc = POSIX::setlocale(&POSIX::LC_ALL); POSIX::setlocale(&POSIX::LC_ALL, 'C'); + + # See set_defaults() as this doesn't work right on Windows :( + $! = &POSIX::ERANGE; + $ERR_out_of_range = "$!"; + } else { + $ERR_out_of_range = 'Numerical result out of range'; } if (open(my $F, '<', 'file.none')) { @@ -457,6 +464,10 @@ sub set_defaults $scriptsuffix = '.com'; } else { $scriptsuffix = '.bat'; + + # Frustratingly, Perl reports the wrong strerror string for ERANGE. + # It's weird because Python gets it right. Not sure what's going on. + $ERR_out_of_range = 'Result too large'; } } diff --git a/tests/scripts/functions/intcmp b/tests/scripts/functions/intcmp index 1abb205f..eb58ae30 100644 --- a/tests/scripts/functions/intcmp +++ b/tests/scripts/functions/intcmp @@ -10,6 +10,8 @@ n = -10 z = 0 # Positive p = 1000000000 +min = -9223372036854775808 +max = 9223372036854775807 .RECIPEPREFIX = > all: > @echo 0_1 $(intcmp $n,$n) @@ -25,7 +27,11 @@ all: > @echo 3_1 $(intcmp $z,$p,lt,eq,gt) > @echo 3_2 $(intcmp $p,$z,lt,eq,gt) > @echo 3_3 $(intcmp $p,$p,lt,eq,gt) -', '', "0_1 -10\n0_2\n0_3\n1_1\n1_2 lt\n1_3\n2_1 lt\n2_2 ge\n2_3 ge\n3_0\n3_1 lt\n3_2 gt\n3_3 eq\n"); +> @echo 4_0 $(intcmp $(min),$(max),lt,eq,gt) +> @echo 4_1 $(intcmp $(max),$(min),lt,eq,gt) +> @echo 4_2 $(intcmp $(min),$(min),lt,eq,gt) +> @echo 4_3 $(intcmp $(max),$(max),lt,eq,gt) +', '', "0_1 -10\n0_2\n0_3\n1_1\n1_2 lt\n1_3\n2_1 lt\n2_2 ge\n2_3 ge\n3_0\n3_1 lt\n3_2 gt\n3_3 eq\n4_0 lt\n4_1 gt\n4_2 eq\n4_3 eq\n"); # Test error conditions @@ -46,7 +52,7 @@ run_make_test(undef, run_make_test(undef, 'intcmp-e3', - "#MAKEFILE#:4: *** Numerical result out of range: '9999999999999999999'. Stop.", + "#MAKEFILE#:4: *** $ERR_out_of_range: '9999999999999999999'. Stop.", 512); diff --git a/tests/scripts/functions/word b/tests/scripts/functions/word index 044bc947..1c87c4a1 100644 --- a/tests/scripts/functions/word +++ b/tests/scripts/functions/word @@ -6,43 +6,42 @@ $details = "\ Produce a variable with a large number of words in it, determine the number of words, and then read each one back.\n"; -open(MAKEFILE,"> $makefile"); -print MAKEFILE <<'EOF'; +run_make_test(' string := word.pl general_test2.pl FORCE.pl word.pl generic_test.perl MAKEFILES_variable.pl string2 := $(string) $(string) $(string) $(string) $(string) $(string) $(string) string3 := $(string2) $(string2) $(string2) $(string2) $(string2) $(string2) $(string2) string4 := $(string3) $(string3) $(string3) $(string3) $(string3) $(string3) $(string3) +.RECIPEPREFIX = > all: - @echo $(words $(string)) - @echo $(words $(string4)) - @echo $(word 1, $(string)) - @echo $(word 100, $(string)) - @echo $(word 1, $(string)) - @echo $(word 1000, $(string3)) - @echo $(wordlist 3, 4, $(string)) - @echo $(wordlist 4, 3, $(string)) - @echo $(wordlist 1, 6, $(string)) - @echo $(wordlist 5, 7, $(string)) - @echo $(wordlist 100, 110, $(string)) - @echo $(wordlist 7, 10, $(string2)) -EOF -close(MAKEFILE); - -&run_make_with_options($makefile, "", &get_logfile); -$answer = "6\n" - ."2058\n" - ."word.pl\n" - ."\n" - ."word.pl\n" - ."\n" - ."FORCE.pl word.pl\n" - ."\n" - ."word.pl general_test2.pl FORCE.pl word.pl generic_test.perl MAKEFILES_variable.pl\n" - ."generic_test.perl MAKEFILES_variable.pl\n" - ."\n" - ."word.pl general_test2.pl FORCE.pl word.pl\n"; -&compare_output($answer, &get_logfile(1)); - +> @echo $(words $(string)) +> @echo $(words $(string4)) +> @echo $(word 1, $(string)) +> @echo $(word 100, $(string)) +> @echo $(word 1 , $(string)) +> @echo $(word 1000, $(string3)) +> @echo $(word 9223372036854775807, $(string2)) +> @echo $(wordlist 3, 4, $(string)) +> @echo $(wordlist 4, 3, $(string)) +> @echo $(wordlist 1 , 6 , $(string)) +> @echo $(wordlist 5,7, $(string)) +> @echo $(wordlist 100, 110, $(string)) +> @echo $(wordlist 7, 10, $(string2)) +> @echo $(wordlist 9223372036854775807, 0, $(string2)) +', '', "6\n" + ."2058\n" + ."word.pl\n" + ."\n" + ."word.pl\n" + ."\n" + ."\n" + ."FORCE.pl word.pl\n" + ."\n" + ."word.pl general_test2.pl FORCE.pl word.pl generic_test.perl MAKEFILES_variable.pl\n" + ."generic_test.perl MAKEFILES_variable.pl\n" + ."\n" + ."word.pl general_test2.pl FORCE.pl word.pl\n" + ."\n" +); # Test error conditions @@ -72,7 +71,7 @@ run_make_test(undef, run_make_test(undef, 'word-e4', - "#MAKEFILE#:6: *** Numerical result out of range: '9999999999999999999'. Stop.", + "#MAKEFILE#:6: *** $ERR_out_of_range: '9999999999999999999'. Stop.", 512); run_make_test(undef, @@ -133,6 +132,11 @@ run_make_test(undef, "#MAKEFILE#:4: *** invalid first argument to 'wordlist' function: '0'. Stop.", 512); +run_make_test(undef, + 'wordlist-e s=1 e=-1', + "#MAKEFILE#:4: *** invalid second argument to 'wordlist' function: '-1'. Stop.", + 512); + # TEST #8 -- test $(firstword ) # @@ -145,8 +149,7 @@ b := $(firstword $(list)) .PHONY: all -all: - @test "$a" = "$b" && echo $a +all: ; @test "$a" = "$b" && echo $a ', '', 'foo'); @@ -163,8 +166,7 @@ b := $(lastword $(list)) .PHONY: all -all: - @test "$a" = "$b" && echo $a +all: ; @test "$a" = "$b" && echo $a ', '', 'baz');