Compare commits

..

19 Commits

Author SHA1 Message Date
Dianne Skoll
eb1998c888 Update version 2021-03-30 16:34:24 -04:00
Dianne Skoll
543252cbaf Update docs. 2021-03-30 16:28:54 -04:00
Dianne Skoll
6df7c59876 Don't change locale to en_US.utf-8 if it is already a UTF-8 locale. 2021-03-25 09:43:42 -04:00
Dianne Skoll
f780e0afc3 Fix typo. 2021-02-28 10:03:05 -05:00
Dianne Skoll
310e8d3287 Clarify comment. 2021-02-15 16:37:20 -05:00
Dianne Skoll
ba51bdf258 Add tests for arithmetic overflow. 2021-02-15 16:35:23 -05:00
Dianne Skoll
ef88b844fb Catch integer overflow with "/". 2021-02-15 10:50:56 -05:00
Dianne Skoll
67b96b0a26 Send error messages to stderr 2021-02-09 12:13:21 -05:00
Dianne Skoll
562da30fb5 Update TkRemind requirements. 2021-02-09 12:08:43 -05:00
Dianne Skoll
21175e8cf6 Add $IntMax and $IntMin special variables. 2021-02-02 17:10:17 -05:00
Dianne Skoll
80d01f7158 Check for overflow in abs() 2021-02-02 17:04:14 -05:00
Dianne Skoll
90cac447e4 Document how Remind handles overflow. 2021-02-01 19:54:31 -05:00
Dianne Skoll
04bf5b0a8b Add explanatory comments about overflow-checking functions 2021-01-30 21:04:44 -05:00
Dianne Skoll
d667c15b25 Add overflow checks for unary minus. 2021-01-30 21:02:56 -05:00
Dianne Skoll
2123bf4b18 Check all Subtract implementations for overflow. 2021-01-30 15:15:02 -05:00
Dianne Skoll
429a64f29e Make all + implementations consistent 2021-01-30 15:12:46 -05:00
Dianne Skoll
f39381dd6c Check for overflow on addition of all integer types. 2021-01-30 15:07:55 -05:00
Dianne Skoll
0a9eb07f6f Check for overflow on addition, subtraction, multiplication of integers 2021-01-30 12:56:37 -05:00
Dianne Skoll
9c287e3fd7 Check for overflow when parsing integer constant. 2021-01-29 18:09:35 -05:00
15 changed files with 243 additions and 25 deletions

2
configure vendored
View File

@@ -3991,7 +3991,7 @@ _ACEOF
fi
done
VERSION=03.03.05
VERSION=03.03.06
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h"

View File

@@ -75,6 +75,6 @@ if test "$GCC" = yes; then
fi
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale)
VERSION=03.03.05
VERSION=03.03.06
AC_SUBST(VERSION)
AC_OUTPUT(src/Makefile www/Makefile src/version.h)

View File

@@ -1,5 +1,18 @@
CHANGES TO REMIND
* VERSION 3.3 Patch 6 - 2021-03-30
- test/test.rem: Change local to en_US.utf-8 only if current locale
is not a UTF-8 locale.
- MINOR CHANGE: Remind's arithemtic operators (+, -, *, /) give errors
on overflow rather than silently giving the wrong answer.
- MINOR CHANGE: Add $IntMin and $IntMax system variables.
- DOCUMENTATION FIX: Document that TkRemind now requires Tcl/Tk version
8.5 or newer.
* VERSION 3.3 Patch 5 - 2021-01-21
- NEW FEATURE: tkremind: Add ability to change fonts and colors from

View File

@@ -1868,6 +1868,11 @@ otherwise.
.PP
.B NOTES
.PP
If the result of an addition, subtraction or multiplication operation
would not fit in a C "int" type, \fBRemind\fR issues a "Number too
high" error. Unlike C, integer operations will not simply give the
wrong answer in case of overflow.
.PP
Operators of equal precedence are \fIalways\fR evaluated from left
to right, except where parentheses dictate otherwise. This is important,
because the enhanced "+" operator is not necessarily associative.
@@ -2060,6 +2065,14 @@ then \fBONCE\fR directives will be ignored.
.B $InfDelta (read-only)
If non-zero, then the \fB\-t\fR option was supplied on the command line.
.TP
.B $IntMax (read-only)
The largest representable \fBINT\fR. On a machine with 32-bit signed integers
using twos-complement representation, this will be 2147483647.
.TP
.B $IntMin (read-only)
The smallest representable \fBINT\fR. On a machine with 32-bit signed integers
using twos-complement representation, this will be -2147483648.
.TP
.B $LatDeg, $LatMin, $LatSec
These specify the latitude of your location. \fB$LatDeg\fR can
range from \-90 to 90, and the others from \-59 to 59. Northern latitudes

View File

@@ -13,8 +13,10 @@ Although not all of \fBRemind\fR's features are available with \fBTkRemind\fR,
it creates. This allows you to learn \fBRemind\fR's syntax and then add
extra features as you become a more sophisticated \fBRemind\fR programmer.
\fBTkRemind\fR is written in Tcl, and requires version 8.0
(or higher). It also requires a \fBwish\fR binary.
\fBTkRemind\fR is written in Tcl, and requires version 8.5 (or higher)
as well as the tcllib extension. It also requires a \fBwish\fR
binary. If you are using Tcl/Tk 8.5, you may also need either the Img
or the tkpng extension to handle PNG images.
.SH OPTIONS
\fBTkRemind\fR itself has no options. However, it passes certain options

View File

@@ -16,11 +16,22 @@ exec wish "$0" "$@"
# We need at least version 8.5 because of {*} list expansion operator
if {[catch {package require Tcl 8.5}]} {
puts "This program requires Tcl 8.5 or higher."
puts "You have version [info tclversion]"
puts stderr "This program requires Tcl 8.5 or higher."
puts stderr "You have version [info tclversion]"
exit 1
}
# If it's 8.5, try using the Img or the TkPNG package to
# get PNG support
if {[info tclversion] == 8.5} {
if {[catch {package require Img}]} {
if {[catch {package require tkpng}]} {
puts stderr "Tcl/Tk version 8.5 might require either the Img or tkpng"
puts stderr "package to handle PNG images correctly. TkRemind may"
puts stderr "crash because neither of these packages was found."
}
}
}
wm withdraw .
set Hostname [exec hostname]
@@ -113,13 +124,6 @@ if {[catch {package require json}]} {
missing_tcllib json
}
# Check that we have the right version of wish
if {$tcl_version < 8.0} {
tk_dialog .error Error "You need wish version 8.0 or higher to run TkRemind; you have $tcl_version" error 0 OK
exit 1
}
if {$tcl_platform(platform) == "windows"} {
tk_dialog .error Error "Please do not port Remind to Windows" error 0 OK
exit 1
@@ -927,7 +931,7 @@ proc LoadOptions {} {
}
foreach {key val} $line {}
if {![info exists Option($key)]} {
puts "Unknown option in $ConfigFile: $key"
puts stderr "Unknown option in $ConfigFile: $key"
continue
}
set Option($key) $val
@@ -2569,7 +2573,7 @@ proc DaemonReadable { file } {
}
}
default {
puts "Unknown message from daemon: $line\n"
puts stderr "Unknown message from daemon: $line\n"
}
}
}

View File

@@ -13,6 +13,7 @@
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
@@ -493,6 +494,7 @@ static int MakeValue(char const *s, Value *v, Var *locals, ParsePtr p)
int len;
int h, m, r;
int ampm = 0;
int prev_val;
if (*s == '\"') { /* It's a literal string "*/
len = strlen(s)-1;
@@ -519,9 +521,15 @@ static int MakeValue(char const *s, Value *v, Var *locals, ParsePtr p)
return OK;
} else if (isdigit(*s)) { /* It's a number - use len to hold it.*/
len = 0;
prev_val = 0;
while (*s && isdigit(*s)) {
len *= 10;
len += (*s++ - '0');
if (len < prev_val) {
/* We overflowed */
return E_2HIGH;
}
prev_val = len;
}
if (*s == ':' || *s == '.' || *s == TimeSep) { /* Must be a literal time */
s++;
@@ -758,15 +766,22 @@ static int Add(void)
/* If both are ints, just add 'em */
if (v2.type == INT_TYPE && v1.type == INT_TYPE) {
v2.v.val += v1.v.val;
PushValStack(v2);
int old = v1.v.val;
v1.v.val += v2.v.val;
/* Check for overflow */
if (_private_add_overflow(v1.v.val, v2.v.val, old)) {
return E_2HIGH;
}
PushValStack(v1);
return OK;
}
/* If it's a date plus an int, add 'em */
if ((v1.type == DATE_TYPE && v2.type == INT_TYPE) ||
(v1.type == INT_TYPE && v2.type == DATE_TYPE)) {
int old = v1.v.val;
v1.v.val += v2.v.val;
if (_private_add_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER;
if (v1.v.val < 0) return E_DATE_OVER;
v1.type = DATE_TYPE;
PushValStack(v1);
@@ -776,7 +791,9 @@ static int Add(void)
/* If it's a datetime plus an int or a time, add 'em */
if ((v1.type == DATETIME_TYPE && (v2.type == INT_TYPE || v2.type == TIME_TYPE)) ||
((v1.type == INT_TYPE || v1.type == TIME_TYPE) && v2.type == DATETIME_TYPE)) {
int old = v1.v.val;
v1.v.val += v2.v.val;
if (_private_add_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER;
if (v1.v.val < 0) return E_DATE_OVER;
v1.type = DATETIME_TYPE;
PushValStack(v1);
@@ -788,7 +805,10 @@ static int Add(void)
if ((v1.type == TIME_TYPE && v2.type == INT_TYPE) ||
(v1.type == INT_TYPE && v2.type == TIME_TYPE) ||
(v1.type == TIME_TYPE && v2.type == TIME_TYPE)) {
v1.v.val = (v1.v.val + v2.v.val) % MINUTES_PER_DAY;
int old = v1.v.val;
v1.v.val += v2.v.val;
if (_private_add_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER;
v1.v.val = v1.v.val % MINUTES_PER_DAY;
if (v1.v.val < 0) v1.v.val += MINUTES_PER_DAY;
v1.type = TIME_TYPE;
PushValStack(v1);
@@ -848,14 +868,18 @@ static int Subtract(void)
/* If they're both INTs, do subtraction */
if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
int old = v1.v.val;
v1.v.val -= v2.v.val;
if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_2HIGH;
PushValStack(v1);
return OK;
}
/* If it's a date minus an int, do subtraction, checking for underflow */
if (v1.type == DATE_TYPE && v2.type == INT_TYPE) {
int old = v1.v.val;
v1.v.val -= v2.v.val;
if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER;
if (v1.v.val < 0) return E_DATE_OVER;
PushValStack(v1);
return OK;
@@ -864,7 +888,9 @@ static int Subtract(void)
/* If it's a datetime minus an int or a time, do subtraction,
* checking for underflow */
if (v1.type == DATETIME_TYPE && (v2.type == INT_TYPE || v2.type == TIME_TYPE)) {
int old = v1.v.val;
v1.v.val -= v2.v.val;
if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER;
if (v1.v.val < 0) return E_DATE_OVER;
PushValStack(v1);
return OK;
@@ -882,7 +908,9 @@ static int Subtract(void)
if ((v1.type == TIME_TYPE && v2.type == TIME_TYPE) ||
(v1.type == DATETIME_TYPE && v2.type == DATETIME_TYPE) ||
(v1.type == DATE_TYPE && v2.type == DATE_TYPE)) {
int old = v1.v.val;
v1.v.val -= v2.v.val;
if (_private_sub_overflow(v1.v.val, v2.v.val, old)) return E_DATE_OVER;
v1.type = INT_TYPE;
PushValStack(v1);
return OK;
@@ -912,7 +940,11 @@ static int Multiply(void)
}
if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
int old = v1.v.val;
v1.v.val *= v2.v.val;
if (v2.v.val != 0) {
if (_private_div(v1.v.val, v2.v.val) != old) return E_2HIGH;
}
PushValStack(v1);
return OK;
}
@@ -940,6 +972,10 @@ static int Divide(void)
if (v1.type == INT_TYPE && v2.type == INT_TYPE) {
if (v2.v.val == 0) return E_DIV_ZERO;
/* This is the only way it can overflow */
if (v2.v.val == -1 && v1.v.val == INT_MIN) {
return E_2HIGH;
}
v1.v.val /= v2.v.val;
PushValStack(v1);
return OK;
@@ -1114,7 +1150,9 @@ static int UnMinus(void)
{
Value *v = &ValStack[ValStackPtr-1];
if (v->type != INT_TYPE) return E_BAD_TYPE;
int old = v->v.val;
v->v.val = -v->v.val;
if (_private_unminus_overflow(old, v->v.val)) return E_2HIGH;
return OK;
}

View File

@@ -5,7 +5,7 @@
/* Contains a few definitions used by expression evaluator. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2020 by Dianne Skoll */
/* Copyright (C) 1992-2021 by Dianne Skoll */
/* */
/***************************************************************/
@@ -53,3 +53,12 @@ if (ValStackPtr <= 0) \
return E_VA_STK_UNDER; \
else \
(val) = ValStack[--ValStackPtr]
/* These functions are in utils.c and are used to detect overflow
in various arithmetic operators. They have to be in separate
functions with extern linkage to defeat compiler optimizations
that would otherwise break the overflow checks. */
extern int _private_div(int a, int b);
extern int _private_add_overflow(int result, int b, int old);
extern int _private_sub_overflow(int result, int b, int old);
extern int _private_unminus_overflow(int a, int b);

View File

@@ -866,12 +866,14 @@ static int FTime(func_info *info)
/***************************************************************/
static int FAbs(func_info *info)
{
int v;
volatile int v;
ASSERT_TYPE(0, INT_TYPE);
v = ARGV(0);
RetVal.type = INT_TYPE;
RETVAL = (v < 0) ? (-v) : v;
v = RETVAL;
if (v < 0) return E_2HIGH;
return OK;
}

View File

@@ -123,3 +123,26 @@ int DateOK(int y, int m, int d)
d > DaysInMonth(m, y) ) return 0;
else return 1;
}
/* Functions designed to defeat gcc optimizer */
int _private_div(int a, int b) { return a/b; }
int _private_add_overflow(int result, int b, int old)
{
if (b > 0 && result < old) return 1;
if (b < 0 && result > old) return 1;
return 0;
}
int _private_sub_overflow(int result, int b, int old)
{
if (b < 0 && result < old) return 1;
if (b > 0 && result > old) return 1;
return 0;
}
int _private_unminus_overflow(int a, int b)
{
if (a > 0 && b > 0) return 1;
if (a < 0 && b < 0) return 1;
return 0;
}

View File

@@ -17,6 +17,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <limits.h>
#include "types.h"
#include "expr.h"
#include "globals.h"
@@ -31,6 +32,9 @@
#define VALUE ErrMsg[E_VAL]
#define UNDEF ErrMsg[E_UNDEF]
static int IntMin = INT_MIN;
static int IntMax = INT_MAX;
static Var *VHashTbl[VAR_HASH_SIZE];
typedef int (*SysVarFunc)(int, Value *);
@@ -661,6 +665,8 @@ static SysVar SysVarArr[] = {
{"HushMode", 0, INT_TYPE, &Hush, 0, 0 },
{"IgnoreOnce", 0, INT_TYPE, &IgnoreOnce, 0, 0 },
{"InfDelta", 0, INT_TYPE, &InfiniteDelta, 0, 0 },
{"IntMax", 0, INT_TYPE, &IntMax, 0, 0 },
{"IntMin", 0, INT_TYPE, &IntMin, 0, 0 },
{"LatDeg", 1, INT_TYPE, &LatDeg, -90, 90 },
{"LatMin", 1, INT_TYPE, &LatMin, -59, 59 },
{"LatSec", 1, INT_TYPE, &LatSec, -59, 59 },

View File

@@ -3,6 +3,6 @@ cd /home/dfs/Software/Remind.git || exit 1
rm -f .git/COMMIT_EDITMSG .git/*~
git update-server-info && cd .git && rsync --archive --verbose --progress --delete ./ dianne.skoll.ca:web/projects/remind/git/Remind.git/
git update-server-info && cd .git && rsync --exclude HEADER.html --archive --verbose --progress --delete ./ dianne.skoll.ca:web/projects/remind/git/Remind.git/
exit $?

View File

@@ -289,8 +289,17 @@ rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
export LC_ALL=en_US.utf-8
export LANG=en_US.utf-8
# If we're already in a utf-8 locale, do
# nothing
if ! echo $LC_ALL | grep -i utf-8 > /dev/null 2>&1 ; then
export LC_ALL=en_US.utf-8
fi
if ! echo $LANG | grep -i utf-8 > /dev/null 2>&1 ; then
export LANG=en_US.utf-8
fi
../src/remind -w128 -c ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
cmp -s ../tests/test.out ../tests/test.cmp
if [ "$?" = "0" ]; then

View File

@@ -857,7 +857,7 @@ set a057 value("a05"+"6")
"a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version()
version() => "03.03.05"
version() => "03.03.06"
set a059 wkday(today())
today() => 1991-02-16
wkday(1991-02-16) => "Saturday"
@@ -2426,7 +2426,7 @@ a086 4
a109 2012-01-01
a128 2018-02-03@16:45
a039 "February"
a058 "03.03.05"
a058 "03.03.06"
a077 "1992 92
"
a096 -4
@@ -3043,6 +3043,85 @@ coerce("DATETIME", "2020-05-05@12:45") => 2020-05-05@12:45
set x coerce("DATETIME", "2020-05-05@1:45pm")
coerce("DATETIME", "2020-05-05@1:45pm") => 2020-05-05@13:45
# Overflow - these tests only work on machines with 32-bit
# twos-complement signed integers. You may get test failures on
# machines with different architectures.
set a $IntMin - 1
$IntMin => -2147483648
-2147483648 - 1 => Number too high
../tests/test.rem(539): `-': Number too high
set a $IntMin - $IntMax
$IntMin => -2147483648
$IntMax => 2147483647
-2147483648 - 2147483647 => Number too high
../tests/test.rem(540): `-': Number too high
set a $IntMax - $IntMin
$IntMax => 2147483647
$IntMin => -2147483648
2147483647 - -2147483648 => Number too high
../tests/test.rem(541): `-': Number too high
set a $IntMax - (-1)
$IntMax => 2147483647
- 1 => -1
2147483647 - -1 => Number too high
../tests/test.rem(542): `-': Number too high
set a $IntMax + 1
$IntMax => 2147483647
2147483647 + 1 => Number too high
../tests/test.rem(543): `+': Number too high
set a $IntMax + $IntMax
$IntMax => 2147483647
$IntMax => 2147483647
2147483647 + 2147483647 => Number too high
../tests/test.rem(544): `+': Number too high
set a $IntMin + (-1)
$IntMin => -2147483648
- 1 => -1
-2147483648 + -1 => Number too high
../tests/test.rem(545): `+': Number too high
set a $IntMin + $IntMin
$IntMin => -2147483648
$IntMin => -2147483648
-2147483648 + -2147483648 => Number too high
../tests/test.rem(546): `+': Number too high
set a $IntMax * 2
$IntMax => 2147483647
2147483647 * 2 => Number too high
../tests/test.rem(547): `*': Number too high
set a $IntMax * $IntMax
$IntMax => 2147483647
$IntMax => 2147483647
2147483647 * 2147483647 => Number too high
../tests/test.rem(548): `*': Number too high
set a $IntMax * $IntMin
$IntMax => 2147483647
$IntMin => -2147483648
2147483647 * -2147483648 => Number too high
../tests/test.rem(549): `*': Number too high
set a $IntMin * 2
$IntMin => -2147483648
-2147483648 * 2 => Number too high
../tests/test.rem(550): `*': Number too high
set a $IntMin * $IntMin
$IntMin => -2147483648
$IntMin => -2147483648
-2147483648 * -2147483648 => Number too high
../tests/test.rem(551): `*': Number too high
set a $IntMin * $IntMax
$IntMin => -2147483648
$IntMax => 2147483647
-2147483648 * 2147483647 => Number too high
../tests/test.rem(552): `*': Number too high
set a $IntMin / (-1)
$IntMin => -2147483648
- 1 => -1
-2147483648 / -1 => Number too high
../tests/test.rem(553): `/': Number too high
set a abs($IntMin)
$IntMin => -2147483648
abs(-2147483648) => Number too high
../tests/test.rem(554): Number too high
# Don't want Remind to queue reminders
EXIT

View File

@@ -533,6 +533,26 @@ set x coerce("DATETIME", "2020-05-05@12:45am")
set x coerce("DATETIME", "2020-05-05@12:45")
set x coerce("DATETIME", "2020-05-05@1:45pm")
# Overflow - these tests only work on machines with 32-bit
# twos-complement signed integers. You may get test failures on
# machines with different architectures.
set a $IntMin - 1
set a $IntMin - $IntMax
set a $IntMax - $IntMin
set a $IntMax - (-1)
set a $IntMax + 1
set a $IntMax + $IntMax
set a $IntMin + (-1)
set a $IntMin + $IntMin
set a $IntMax * 2
set a $IntMax * $IntMax
set a $IntMax * $IntMin
set a $IntMin * 2
set a $IntMin * $IntMin
set a $IntMin * $IntMax
set a $IntMin / (-1)
set a abs($IntMin)
# Don't want Remind to queue reminders
EXIT