Compare commits

...

57 Commits

Author SHA1 Message Date
Dianne Skoll b9dea59206 Update changelog.
Remind unit tests / tests (push) Successful in 39s
2024-09-02 14:46:58 -04:00
Dianne Skoll 46aa144b65 Bump version to 05.00.05 2024-09-02 10:15:56 -04:00
Dianne Skoll 914971308d Better error message.
Remind unit tests / tests (push) Successful in 30s
2024-09-01 23:42:29 -04:00
Dianne Skoll a22e81040f Check error return in a couple of spots. 2024-09-01 23:22:00 -04:00
Dianne Skoll 72f74f03cf Check for errors. 2024-09-01 23:18:09 -04:00
Dianne Skoll d0e45e727e Don't print closing ] in error indicator.
Remind unit tests / tests (push) Successful in 31s
2024-09-01 22:39:49 -04:00
Dianne Skoll ce2b2e80da Remove unnecessary _h() function definition.
Remind unit tests / tests (push) Successful in 28s
2024-09-01 11:31:28 -04:00
Dianne Skoll 11771b7d3d Better formatting of ^-- here message for expressions with embedded newlines 2024-09-01 11:27:34 -04:00
Dianne Skoll 01cb028532 Fix up tests to pass in September 2024. 2024-09-01 11:09:32 -04:00
Dianne Skoll 58b6f43b9c Better diagnosis of wrong #args for builtin functions. 2024-09-01 09:03:26 -04:00
Dianne Skoll 1dedb667e8 Prevent a couple of file descriptor leaks.
Remind unit tests / tests (push) Successful in 28s
2024-08-31 09:06:05 -04:00
Dianne Skoll 8a96236788 Avoid warnings if REM_USE_WCHAR is undefined.
Remind unit tests / tests (push) Successful in 28s
2024-08-30 20:44:08 -04:00
Dianne Skoll 2a13163659 Fix some weird edge cases in text-mode calendar formatting.
Remind unit tests / tests (push) Successful in 28s
2024-08-29 13:25:36 -04:00
Dianne Skoll 336a9684d4 In calendar mode, SHADE the entire calendar box including day number.
Remind unit tests / tests (push) Successful in 29s
2024-08-29 08:50:57 -04:00
Dianne Skoll 86945c6e18 Remove incorrect test obsoleted by changes in 05.00.04. 2024-08-29 08:39:55 -04:00
Dianne Skoll 684280db5e Remove "SCHED _sfun" and hard-code "+15" instead. 2024-08-29 08:15:10 -04:00
Dianne Skoll d801408933 Update WHATSNEW. 2024-08-29 08:01:50 -04:00
Dianne Skoll 79b3da3820 Update man page.
Remind unit tests / tests (push) Successful in 27s
2024-08-28 20:43:27 -04:00
Dianne Skoll ed021d3f46 Let IIF's boolean arg be any type... we use truthy(). 2024-08-28 20:41:21 -04:00
Dianne Skoll 27d0fda280 Factor out truthy code from Fiif 2024-08-28 20:38:04 -04:00
Dianne Skoll ef12da4ec6 Refactor out "truthy". 2024-08-28 20:36:30 -04:00
Dianne Skoll 7b098e95ad Make error messages more consistent. 2024-08-28 18:21:35 -04:00
Dianne Skoll 7d13f4b09e Better diagnostics when printing "Too many/few arguments" errors 2024-08-28 18:14:43 -04:00
Dianne Skoll cecdfe6ade Bump version to 05.00.04. 2024-08-28 18:00:50 -04:00
Dianne Skoll 216bbd6378 Fix the logic that recurses into user-defined functions. 2024-08-28 17:56:47 -04:00
Dianne Skoll 30e2e9c633 When looking at SATISFY expressions to make sure they use the trigger date, look recursively into user-defined functions in the expression also. 2024-08-28 17:49:12 -04:00
Dianne Skoll bf2aabd610 Update release notes. 2024-08-28 15:42:23 -04:00
Dianne Skoll c019221d15 Add diagnostics for missing/bad subst_XXX functions. 2024-08-28 15:15:34 -04:00
Dianne Skoll b7bd6faf07 Warn if SCHED/WARN/OMITFUNC functions take wrong number of args. 2024-08-28 15:02:22 -04:00
Dianne Skoll 80d58220fe In warnings, give file/lineno where function is defined. 2024-08-28 14:59:13 -04:00
Dianne Skoll cd8624e176 Tweak diagnostic messages. 2024-08-28 14:35:49 -04:00
Dianne Skoll 8515fb7ddd Update / clarify manual. 2024-08-28 14:28:34 -04:00
Dianne Skoll 84f9f4ae0a Update tests. 2024-08-28 14:23:14 -04:00
Dianne Skoll e201ebcfa4 Document that logical operators can take any type; add tests. 2024-08-28 14:22:07 -04:00
Dianne Skoll 4e15c3ec35 Remove an optimization that was interfering with expression debugging. 2024-08-28 14:21:44 -04:00
Dianne Skoll 1adccf9b1f Add tests for SCHED/WARN/OMITFUNC functions that don't use their argument. 2024-08-28 13:54:47 -04:00
Dianne Skoll a1aa5c2ad9 Change wording of warning slightly. 2024-08-28 13:54:40 -04:00
Dianne Skoll 1e0d650737 Fix tests. 2024-08-28 13:49:34 -04:00
Dianne Skoll 553d092ca8 Diagnost if WARN/SCHED/OMITFUNC functions don't reference their argument. 2024-08-28 13:49:17 -04:00
Dianne Skoll dc62841517 Fix accidental broken commit. :( 2024-08-28 13:45:42 -04:00
Dianne Skoll 326e7bfc53 Document calendar back-end handling of %_ 2024-08-28 13:23:54 -04:00
Dianne Skoll b9dc7c16ad Use /s modifier on regex. 2024-08-28 13:09:18 -04:00
Dianne Skoll abd54b016b Add tests for newlines in calendars. 2024-08-28 12:56:53 -04:00
Dianne Skoll db02b54067 Collapse multiple whitespace; convert newlines to "<br />" in HTML output. 2024-08-28 12:52:39 -04:00
Dianne Skoll 40a78dfbbb Collapse multiple newlines after all other processing. 2024-08-28 12:52:29 -04:00
Dianne Skoll c860b46baa Collapse multiple runs of whitespace 2024-08-28 12:52:07 -04:00
Dianne Skoll 1458ba8856 Clean up whitespace runs in rem2pdf.
Remind unit tests / tests (push) Successful in 45s
2024-08-28 10:44:41 -04:00
Dianne Skoll 7b9b6ebc96 Preserve %_ newlines in "-C" mode. 2024-08-28 10:14:06 -04:00
Dianne Skoll 7ee4073c7a Document warning on function redefinitiion.
Remind unit tests / tests (push) Successful in 26s
2024-08-27 21:43:46 -04:00
Dianne Skoll 8c072cd9b6 Add a warning if a function is redefined. 2024-08-27 21:41:45 -04:00
Dianne Skoll b794a45c3f Add more SATISFY diagnostics.
Remind unit tests / tests (push) Successful in 34s
2024-08-27 15:02:11 -04:00
Dianne Skoll 98e491ed1d Remove some left-over debugging code. 2024-08-27 15:01:59 -04:00
Dianne Skoll c397cc06da Rewrite code to avoid need to pass address of "mentioned" variable.
Remind unit tests / tests (push) Successful in 30s
2024-08-27 11:33:41 -04:00
Dianne Skoll 8616236b3c Document that SATISFY clauses that don't mention trigdate are diagnosed. 2024-08-27 11:26:13 -04:00
Dianne Skoll 702704af1a Make sure SATISFY expressions are either constant or mention trigdate. 2024-08-27 11:22:42 -04:00
Dianne Skoll a0d1b19050 Fix typo in comment.
Remind unit tests / tests (push) Successful in 39s
2024-08-23 15:08:35 -04:00
Dianne Skoll a5c8ae491c Add test for diagnosing function definitions with too many arguments. 2024-08-23 15:05:24 -04:00
23 changed files with 1394 additions and 208 deletions
Vendored
+9 -9
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for remind 05.00.03.
# Generated by GNU Autoconf 2.71 for remind 05.00.05.
#
#
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -608,8 +608,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='remind'
PACKAGE_TARNAME='remind'
PACKAGE_VERSION='05.00.03'
PACKAGE_STRING='remind 05.00.03'
PACKAGE_VERSION='05.00.05'
PACKAGE_STRING='remind 05.00.05'
PACKAGE_BUGREPORT=''
PACKAGE_URL='https://dianne.skoll.ca/projects/remind/'
@@ -1264,7 +1264,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures remind 05.00.03 to adapt to many kinds of systems.
\`configure' configures remind 05.00.05 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1326,7 +1326,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of remind 05.00.03:";;
short | recursive ) echo "Configuration of remind 05.00.05:";;
esac
cat <<\_ACEOF
@@ -1414,7 +1414,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
remind configure 05.00.03
remind configure 05.00.05
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1864,7 +1864,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by remind $as_me 05.00.03, which was
It was created by remind $as_me 05.00.05, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -4703,7 +4703,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by remind $as_me 05.00.03, which was
This file was extended by remind $as_me 05.00.05, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -4768,7 +4768,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
remind config.status 05.00.03
remind config.status 05.00.05
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
+1 -1
View File
@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(remind, 05.00.03, , , https://dianne.skoll.ca/projects/remind/)
AC_INIT(remind, 05.00.05, , , https://dianne.skoll.ca/projects/remind/)
AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF'
+1 -1
View File
@@ -264,7 +264,7 @@ foreach $yearkey (sort keys %{$events} ) {
if ($start->hour > 0) {
print " AT ";
print $start->strftime("%H:%M");
print " SCHED _sfun MSG %a %2 ";
print " +15 MSG %a %2 ";
} else {
print " MSG %a ";
}
+81
View File
@@ -1,5 +1,86 @@
CHANGES TO REMIND
* VERSION 5.0 Patch 5 - 2024-09-02
* CHANGE: remind: When using the "-c" option and with the SHADE special
enabled, shade the entire calendar box including the line containing the
day number.
* IMPROVEMENT: remind: Better error messages when diagnosing certain
errors in expressions.
* IMPROVEMENT: include/holidays/jewish.rem: Remove unnecessary _h()
function definition.
* BUG FIX: remind: In a couple of spots when we parsed a character, we did
not check for an error return. This has been fixed.
* BUG FIX: remind: Fix edge-case bugs in "remind -c" output formatting.
* BUG FIX: make test: Fix a test that was broken for all of September 2024.
* BUG FIX: remind: Fix a couple of potential file-descriptor leaks.
* BUG FIX: contrib/ical2rem.pl: Replace "SCHED _sfun" with "+15" to
hard-code 15-minutes advance warning rather than using an undefined
scheduling function. If this is not what you want, you should edit
ical2rem.pl to suit your taste; it's not officially part of Remind and
is meant more as a starting point for you to customize than a finished
product.
* DOCUMENTATION FIX: Remove obsolete info from Remind man page.
* VERSION 5.0 Patch 4 - 2024-08-29
* IMPROVEMENT: remind: When checking if a SATISFY expression refers to the
trigger date, look recursively at any user-defined functions it calls.
This reduces spurious warnings.
* CHANGE: remind: Allow any type to be used as the test argument for IIF.
* IMPROVEMENT: remind man page: Clarify how various types are treated
in boolean context.
* VERSION 5.0 Patch 3 - 2024-08-28
* IMPROVEMENT: remind: Preserve newlines ("%_" sequences) in calendar
mode in most cases. See the remind man page for details.
* IMPROVEMENT: rem2pdf: rem2pdf can now produce PostScript and
Encapsulated PostScript output in addition to PDF and SVG.
* IMPROVEMENT: remind: Emit warnings if a subst_XXX function takes the
wrong number of arguments, or for custom sequences, if the function
is not defined.
* IMPROVEMENT: remind: Emit warnings if WARN/SCHED/OMITFUNC functions
do not reference their argument.
* IMPROVEMENT: remind: Allow strings to be used with logical
operators. The empty string "" is considered false and all other
strings are considered true.
* IMPROVEMENT: remind: Emit warnings for lines that are implicitly
treated as REM commands; add warnings for REM commands that are
implicitly treated as MSG-type reminders.
* IMPROVEMENT: remind: Add an optional fourth argument to the built-in
ampm() function that specifies not to suppress a leading zero in the
hour component.
* IMPROVEMENT: remind: If a SATISFY expression is not constant and
doesn't reference the trigger date somehow, issue a warning.
* IMPROVEMENT: remind: Add a warning if a user-defined function is
redefined. If you do have a use-case that requires you to redefine
a function, simply FUNSET it first before FSETting it for the second
time.
* DOCUMENTATION FIX: Clarify man pages and remove some information that
has become incorrect as Remind has evolved.
* BUG FIX: Fix typos in man pages
* VERSION 5.0 Patch 2 - 2024-07-26
* IMPROVEMENT: Remind: Revamp how ONCE works. You can now set a
+22 -23
View File
@@ -10,7 +10,6 @@ SET InIsrael value("InIsrael", 0)
SET Reform value("Reform", 0)
# Convenient function definition to save typing
FSET _h(x, y) HEBDATE(x,y)
FSET _h2(x, y) HEBDATE(x, y, $U-7)
FSET _PastSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)+1)
FSET _BackTwoFri(x, y) IIF(WKDAYNUM(_h2(x,y))!=5, _h2(x,y), _h2(x,y)-2)
@@ -19,28 +18,28 @@ FSET _BackTwoSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)-2)
SET InIsrael VALUE("InIsrael", 0)
SET Reform VALUE("Reform", 0)
REM [_h(1, "Tishrey")] ++4 MSG %"Rosh Hashana 1%" is %b.
REM [hebdate(1, "Tishrey")] ++4 MSG %"Rosh Hashana 1%" is %b.
# No RH-2 or Tzom Gedalia in Reform
IF !Reform
REM [_h(2, "Tishrey")] ++4 MSG %"Rosh Hashana 2%" is %b.
REM [hebdate(2, "Tishrey")] ++4 MSG %"Rosh Hashana 2%" is %b.
REM [_PastSat(3, "Tishrey")] ++4 MSG %"Tzom Gedalia%" is %b.
ENDIF
REM [_h(10, "Tishrey")] ++4 MSG %"Yom Kippur%" is %b.
REM [_h(15, "Tishrey")] ++4 MSG %"Sukkot 1%" is %b.
REM [hebdate(10, "Tishrey")] ++4 MSG %"Yom Kippur%" is %b.
REM [hebdate(15, "Tishrey")] ++4 MSG %"Sukkot 1%" is %b.
IF !InIsrael
REM [_h(16, "Tishrey")] MSG %"Sukkot 2%"
REM [hebdate(16, "Tishrey")] MSG %"Sukkot 2%"
ENDIF
REM [_h(21, "Tishrey")] ++4 MSG %"Hoshana Rabba%" is %b.
REM [_h(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b.
REM [hebdate(21, "Tishrey")] ++4 MSG %"Hoshana Rabba%" is %b.
REM [hebdate(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b.
IF InIsrael
REM [_h(22, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
REM [hebdate(22, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
ELSE
REM [_h(23, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
REM [hebdate(23, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
ENDIF
# Because Kislev can change length, we must be more careful about Chanukah
@@ -58,11 +57,11 @@ REM [_chan(8)] MSG %"Chanukah 8%"
IF !Reform
# 10 Tevet will never be a Saturday, so whether or not to
# move it is moot. (Thanks to Art Werschulz.)
REM [_h(10, "Tevet")] MSG %"Tzom Tevet%" is %b.
REM [hebdate(10, "Tevet")] MSG %"Tzom Tevet%" is %b.
ENDIF
REM [_h(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b.
REM [_h(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b.
REM [hebdate(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b.
REM [hebdate(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b.
# If Purim is on Sunday, then Fast of Esther is 11 Adar.
IF WKDAYNUM(_h2(13, "Adar")) != 6
@@ -70,33 +69,33 @@ IF WKDAYNUM(_h2(13, "Adar")) != 6
ELSE
REM [_h2(11, "Adar")] ++4 MSG %"Fast of Esther%" is %b.
ENDIF
REM [_h(14, "Adar")] ++4 MSG %"Purim%" is %b.
REM [_h(15, "Nisan")] ++4 MSG %"Pesach%" is %b.
REM [hebdate(14, "Adar")] ++4 MSG %"Purim%" is %b.
REM [hebdate(15, "Nisan")] ++4 MSG %"Pesach%" is %b.
IF !InIsrael
REM [_h(16, "Nisan")] MSG %"Pesach 2%"
REM [hebdate(16, "Nisan")] MSG %"Pesach 2%"
ENDIF
REM [_h(21, "Nisan")] MSG %"Pesach 7%"
REM [hebdate(21, "Nisan")] MSG %"Pesach 7%"
IF !InIsrael && !Reform
REM [_h(22, "Nisan")] MSG %"Pesach 8%"
REM [hebdate(22, "Nisan")] MSG %"Pesach 8%"
ENDIF
REM [_h(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b.
REM [hebdate(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b.
REM [_BackTwoFri(4, "Iyar")] ++4 MSG %"Yom HaZikaron%" is %b.
REM [_BackTwoSat(5, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b.
# Not sure about Reform's position on Lag B'Omer
IF !Reform
REM [_h(18, "Iyar")] ++4 MSG %"Lag B'Omer%" is %b.
REM [hebdate(18, "Iyar")] ++4 MSG %"Lag B'Omer%" is %b.
ENDIF
REM [_h(28, "Iyar")] ++4 MSG %"Yom Yerushalayim%" is %b.
REM [_h(6, "Sivan")] ++4 MSG %"Shavuot%" is %b.
REM [hebdate(28, "Iyar")] ++4 MSG %"Yom Yerushalayim%" is %b.
REM [hebdate(6, "Sivan")] ++4 MSG %"Shavuot%" is %b.
IF !InIsrael && !Reform
REM [_h(7, "Sivan")] MSG %"Shavuot 2%"
REM [hebdate(7, "Sivan")] MSG %"Shavuot 2%"
ENDIF
# Fairly sure Reform Jews don't observe the next two
+64 -26
View File
@@ -1570,7 +1570,35 @@ is replaced with "\fIyy\fR", the last two digits of the year.
.TP
.B %_
(percent-underscore) is replaced with a newline. You can use this to
achieve multi-line reminders.
achieve multi-line reminders. Note that calendar back-ends vary in
how they handle multi-line reminders:
.RS
.TP
.B o
Running \fBremind -c\fR preserves newlines in the terminal calendar output.
.TP
.B o
\fBrem2pdf\fR preserves newlines if \fBremind\fR is invoked with the \fB\-pp\fR
or \fB\-ppp\fR option.
.TP
.B o
\fBrem2html\fR preserves newlines if \fBremind\fR is invoked with the
\fB\-pp\fR option.
.TP
.B o
\fBtkremind\fR preserves newlines.\fR
.TP
.B o
\fBrem2ps\fR converts newlines to spaces. But \fBrem2ps\fR is deprecated;
use \fBrem2pdf\fR instead.
.TP
.B o
The "simple calendar" formats (ie, \fBremind\fR's \fB\-s\fR, \fB\-n\fR and
\fB\-p\fR options) convert newlines to spaces.
.PP
All calendar back-ends collapse multiple spaces to a single space and
multiple newlines to a single newline.
.RE
.TP
.B %1
is replaced with "now", "\fIm\fR minutes from now", "\fIm\fR minutes ago",
@@ -2187,11 +2215,11 @@ and time separator characters for \fBDATE\fR and \fBTIME\fR constants apply
also to \fBDATETIME\fR constants.
.RE
.PP
.B ZERO VALUES
.B ZERO VALUES AND TRUE/FALSE
.PP
The non-string types all have an associated \fIzero\fR value, which is
treated as "false" by the IF command and the logical operators. The
zero values are:
All types have an associated \fIzero value\fR, which is treated as
\fIfalse\fR by the IF command, the IIF function, and the logical
operators. The zero values are:
.PP
.RS
.PP
@@ -2202,10 +2230,11 @@ zero values are:
\fBTIME\fR - 00:00
.PP
\fBDATETIME\fR - '1990-01-01@00:00'
.PP
\fBSTRING\fR - "" (the empty string)
.RE
.PP
Additionally, for the purpose of the IF command (but \fInot\fR the
logical operators) the empty string "" is considered a false value.
Any value other than the \fIzero value\fR is treated as \fItrue\fR.
.PP
.B OPERATORS
.PP
@@ -2228,8 +2257,8 @@ C operators.
.PP
.TP
.B !
Logical negation. Can be applied to an \fBINT\fR type. If the operand
is non-zero, returns zero. Otherwise, returns 1.
Logical negation. Can be applied to any type. If the operand
is non-zero, returns 0. Otherwise, returns 1.
.TP
.B \-
Unary minus. Can be applied to an \fBINT\fR. Returns the negative
@@ -2333,15 +2362,16 @@ If the operands are not of the same type, == returns 0 and != returns
.RE
.TP
.B &&
This is the logical AND operator. Neither of its operands can be
\fBSTRING\fR type. Returns the second operand if both operands are
non-zero. Otherwise, returns whichever operand is zero.
This is the logical AND operator. Returns the second operand if both
operands are non-zero. Otherwise, returns whichever operand is zero.
Operands can be any type and "zero" is interpreted as appropriate for
each operand's type.
.TP
.B ||
This is the logical OR operator. Neither of its operands can be
\fBSTRING\fR type. It returns the first operand that is non-zero; if
both operands are zero, then returns the second operand.
This is the logical OR operator. It returns the first operand that is
non-zero; if both operands are zero, then returns the second operand.
Operands can be any type and "zero" is interpreted as appropriate for
each operand's type.
.PP
.B NOTES
.PP
@@ -3287,14 +3317,14 @@ out. The stripping algorithm is fairly naive; the function starts
stripping characters when it encounters a "<" and it stops stripping
when it encounters a ">".
.TP
.B iif(si_test1, x_arg1, [si_test2, x_arg2,...], x_default)
If \fItest1\fR is not zero or the null string, returns \fIarg1\fR.
Otherwise, if \fItest2\fR is not zero or the null string, returns
\fIarg2\fR, and so on. If all of the \fItest\fR arguments are false,
returns \fIdefault\fR. Note that all arguments are \fIalways\fR evaluated.
This function accepts an odd number of arguments - note that prior to version
03.00.05 of \fBRemind\fR, it accepted 3 arguments only. The 3-argument
version of \fBiif()\fR is compatible with previous versions of \fBRemind\fR.
.B iif(x_test1, x_arg1, [x_test2, x_arg2,...], x_default)
If \fItest1\fR is true, returns \fIarg1\fR. Otherwise, if \fItest2\fR
is true, returns \fIarg2\fR, and so on. If all of the \fItest\fR
arguments are false, returns \fIdefault\fR. Note that all arguments
are \fIalways\fR evaluated. This function accepts an odd number of
arguments - note that prior to version 03.00.05 of \fBRemind\fR, it
accepted 3 arguments only. The 3-argument version of \fBiif()\fR is
compatible with previous versions of \fBRemind\fR.
.TP
.B index(s_search, s_target [,i_start)
Returns an \fBINT\fR that is the location of \fItarget\fR in the
@@ -4534,6 +4564,11 @@ or thirdfunc will exist. \fBRemind\fR does not issue an error if you
try to \fBFUNSET\fR a nonexistent user-defined function; it simply
does nothing in that case.
.PP
If you define a user-defined function and then later on redefine it,
\fBRemind\fR will issue a warning. If you do not want this warning,
then use \fBFUNSET\fR to remove the existing definition before you
redefine the function.
.PP
.SH PRECISE SCHEDULING
.PP
The \fBWARN\fR keyword allows precise control over advance warning in
@@ -4667,8 +4702,11 @@ the error message "Can't compute trigger" is issued. Otherwise,
\fBtrigvalid()\fR is set to 1.
.PP
This is really useful only if \fIexpr\fR involves a call to the
\fBtrigdate()\fR or related functions; otherwise, \fIexpr\fR will not change as
\fBRemind\fR iterates.
\fBtrigdate()\fR or related functions or system variables; otherwise,
\fIexpr\fR will not change as \fBRemind\fR iterates. In fact, if
\fIexpr\fR is not a constant and does not call \fBtrigdate()\fR or
related functions or system variables, then \fBRemind\fR will issue a
warning.
.PP
An example of the usefulness of \fBSATISFY\fR: Suppose you wish to
be warned of every Friday the 13th. Your first attempt may be:
+21 -3
View File
@@ -374,19 +374,37 @@ sub parse_input
($1 % 256), ($1 % 256), ($1 % 256));
}
} elsif ($special eq 'COLOR' || $special eq 'COLOUR') {
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/) {
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/s) {
my($r, $g, $b, $text) = ($1, $2, $3, $4);
my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
$r % 256, $g % 256, $b % 256);
push(@{$days->[$d]}, "<p$class $color>" . escape_html($text) . '</p>');
push(@{$days->[$d]}, "<p$class $color>" . fix_whitespace(escape_html($text)) . '</p>');
}
} elsif ($special eq '*') {
push(@{$days->[$d]}, "<p$class>" . escape_html($body) . '</p>');
push(@{$days->[$d]}, "<p$class>" . fix_whitespace(escape_html($body)) . '</p>');
}
}
return $found_data;
}
sub fix_whitespace
{
my ($text) = @_;
# Collapse multiple spaces/tabs to a single space
$text =~ s/[ \t]+/ /gs;
# Remove whitespace before/after newlines
$text =~ s/\s+\n/\n/gs;
$text =~ s/\n\s+/\n/gs;
# Collapse multiple newlines to a single newline
$text =~ s/\n+/\n/gs;
# Convert newlines to <br />
$text =~ s|\n|<br />|g;
return $text;
}
sub small_calendar
{
my($month, $monlen, $url, $first_col) = @_;
+12
View File
@@ -85,6 +85,18 @@ sub render
} else {
$body = $self->{body};
}
# Clean up the body:
# Collapse multiple spaces/tabs to a single space
$body =~ s/[ \t]+/ /gs;
# Remove whitespace before/after newlines
$body =~ s/\s+\n/\n/gs;
$body =~ s/\n\s+/\n/gs;
# Collapse multiple newlines to a single newline
$body =~ s/\n+/\n/gs;
$layout->set_text(Encode::decode('UTF-8', $body));
my $desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . $settings->{entry_size} . 'px');
$layout->set_font_description($desc);
+7
View File
@@ -1217,6 +1217,13 @@ proc FillCalWindow {} {
continue
}
.cal.t$n configure -state normal
# Canonicalize spaces and newlines
set stuff [regsub -all {[ \t]+} $stuff " "]
set stuff [regsub -all {[ \t]+\n} $stuff "\n"]
set stuff [regsub -all {\n[ \t]} $stuff "\n"]
set stuff [regsub -all {\n+} $stuff "\n"]
if {[regexp {TKTAG([0-9]+)} $tag all tagno] && "$fntag" != "x"} {
.cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag]
.cal.t$n tag bind "TKTAG$tagno" <Enter> "TaggedEnter .cal.t$n"
+44 -24
View File
@@ -338,6 +338,7 @@ UnBackgroundize(int d)
printf("%s", Decolorize());
}
#ifdef REM_USE_WCHAR
static void
send_lrm(void)
{
@@ -352,6 +353,7 @@ send_lrm(void)
printf("\xE2\x80\x8E");
}
}
#endif
static char const *
despace(char const *s)
@@ -526,9 +528,9 @@ get_month_abbrev(char const *mon)
#endif
}
#ifdef REM_USE_WCHAR
static int make_wchar_versions(CalEntry *e)
{
#ifdef REM_USE_WCHAR
size_t len;
wchar_t *buf;
len = mbstowcs(NULL, e->text, 0);
@@ -542,10 +544,8 @@ static int make_wchar_versions(CalEntry *e)
e->wc_text = buf;
e->wc_pos = buf;
return 1;
#else
return 1;
#endif
}
#endif
static void gon(void)
{
@@ -758,13 +758,11 @@ SetMoonEntry(int dse, char const *moon)
if (sscanf(moon, "%d %*d %*d %27[^\x01]", &phase, msg) < 4) {
if (sscanf(moon, "%d", &phase) != 1) {
/* Malformed MOON special; ignore */
fprintf(stderr, "Oops 1\n");
return;
}
}
if (phase < 0 || phase > 3) {
/* Bad phase */
fprintf(stderr, "Oops 2\n");
return;
}
FromDSE(dse, &y, &m, &d);
@@ -902,13 +900,17 @@ static void DoCalendarOneWeek(int nleft)
if (UseVTColors) {
printf("\x1B[1m"); /* Bold */
}
Backgroundize(d);
PrintLeft(buf, ColSpaces-1, '*');
putchar(' ');
UnBackgroundize(d);
if (UseVTColors) {
printf("\x1B[0m"); /* Normal */
}
putchar(' ');
} else {
Backgroundize(d);
PrintLeft(buf, ColSpaces, ' ');
UnBackgroundize(d);
}
gon();
DRAW(tb);
@@ -1124,13 +1126,17 @@ static int WriteCalendarRow(void)
if (UseVTColors) {
printf("\x1B[1m"); /* Bold */
}
Backgroundize(d+i-wd);
PrintLeft(buf, ColSpaces-1, '*');
putchar(' ');
if (UseVTColors) {
printf("\x1B[0m"); /* Normal */
}
putchar(' ');
UnBackgroundize(d+i-wd);
} else {
Backgroundize(d+i-wd);
PrintLeft(buf, ColSpaces, ' ');
UnBackgroundize(d+i-wd);
}
}
gon();
@@ -1198,15 +1204,17 @@ static void PrintLeft(char const *s, int width, char pad)
{
#ifndef REM_USE_WCHAR
int len = strlen(s);
printf("%s", s);
while (len++ < width) putchar(pad);
int i;
for (i=0; i<len && i<width; i++) {
fputc(*(s+i), stdout);
}
while (i++ < width) putchar(pad);
#else
size_t len = mbstowcs(NULL, s, 0);
int i;
wchar_t static_buf[128];
wchar_t *buf;
wchar_t *ws;
int display_len;
if (!len) {
for (i=0; i<width; i++) {
@@ -1225,13 +1233,16 @@ static void PrintLeft(char const *s, int width, char pad)
}
}
(void) mbstowcs(buf, s, len+1);
display_len = wcswidth(buf, len+1);
ws = buf;
for (i=0; i<width;) {
i=0;
while (i<width) {
if (*ws) {
if (i + wcwidth(*ws) > width) {
break;
}
i += wcwidth(*ws);
PutWideChar(*ws++, NULL);
i+= wcwidth(*ws);
} else {
break;
}
@@ -1244,7 +1255,10 @@ static void PrintLeft(char const *s, int width, char pad)
/* Possibly send lrm control sequence */
send_lrm();
for (i=display_len; i<width; i++) fputc(pad, stdout);
while (i<width) {
fputc(pad, stdout);
i++;
}
if (buf != static_buf) free(buf);
#endif
@@ -1265,7 +1279,7 @@ static void PrintCentered(char const *s, int width, char *pad)
int i;
for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) {
for (i=0; i<width-d; i++) {
if (*s) {
if (isspace(*s)) {
putchar(' ');
@@ -1309,13 +1323,14 @@ static void PrintCentered(char const *s, int width, char *pad)
if (d < 0) d = 0;
ws = buf;
for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) {
i=0;
while (i+d < width) {
if (*ws) {
PutWideChar(*ws++, NULL);
if (wcwidth(*ws) == 0) {
/* Don't count this character... it's zero-width */
i--;
if (i+d + wcwidth(*ws) > width) {
break;
}
i += wcwidth(*ws);
PutWideChar(*ws++, NULL);
} else {
break;
}
@@ -1327,7 +1342,10 @@ static void PrintCentered(char const *s, int width, char *pad)
/* Possibly send lrm control sequence */
send_lrm();
for (i=d+display_len; i<width; i++) fputs(pad, stdout);
while (i+d<width) {
fputs(pad, stdout);
i++;
}
if (buf != static_buf) free(buf);
#endif
}
@@ -1414,7 +1432,7 @@ static int WriteOneColLine(int col)
/* Find the last space char within the column. */
width = 0;
while (width <= ColSpaces) {
if (!*ws) {
if (!*ws || *ws == '\n') {
wspace = ws;
break;
}
@@ -1520,7 +1538,7 @@ static int WriteOneColLine(int col)
/* Find the last space char within the column. */
while (s - e->pos <= ColSpaces) {
if (!*s) {space = s; break;}
if (!*s || *s == '\n') {space = s; break;}
if (isspace(*s)) space = s;
s++;
}
@@ -2145,7 +2163,9 @@ static int DoCalRem(ParsePtr p, int col)
FreeTrig(&trig);
return E_NO_MEM;
}
#ifdef REM_USE_WCHAR
make_wchar_versions(e);
#endif
DBufInit(&(e->tags));
DBufPuts(&(e->tags), DBufValue(&(trig.tags)));
if (SynthesizeTags) {
+155
View File
@@ -32,6 +32,152 @@ static int ParseUntil (ParsePtr s, Trigger *t, int type);
static int ShouldTriggerBasedOnWarn (Trigger *t, int dse, int *err);
static int ComputeTrigDuration(TimeTrig *t);
static int
ensure_expr_references_first_local_arg(expr_node *node)
{
expr_node *other;
if (!node) {
return 0;
}
if (node->type == N_LOCAL_VAR && node->u.arg == 0) {
return 1;
}
if (ensure_expr_references_first_local_arg(node->child)) {
return 1;
}
other = node->sibling;
while (other) {
if (ensure_expr_references_first_local_arg(other)) {
return 1;
}
other = other->sibling;
}
return 0;
}
static void
check_trigger_function(char const *fname, char const *type)
{
UserFunc *f;
if (!*fname) {
return;
}
f = FindUserFunc(fname);
if (!f) {
if (strcmp(type, "WARN")) {
/* Undefined WARN functions are diagnosed elsewhere... */
Wprint("Undefined %s function: `%s'", type, fname);
}
return;
}
if (f->nargs != 1) {
Wprint("%s function `%s' defined at %s:%d should take 1 argument but actually takes %d", type, fname, f->filename, f->lineno, f->nargs);
return;
}
if (ensure_expr_references_first_local_arg(f->node)) {
return;
}
Wprint("%s function `%s' defined at %s:%d does not use its argument", type, fname, f->filename, f->lineno);
}
static void
ensure_satnode_mentions_trigdate_aux(expr_node *node, int *mentioned)
{
char const *name;
expr_node *other;
UserFunc *f;
if (!node) {
return;
}
if (*mentioned) {
return;
}
if (node->type == N_BUILTIN_FUNC) {
name = node->u.builtin_func->name;
if (!strcmp(name, "trigdate") ||
!strcmp(name, "trigdatetime")) {
*mentioned = 1;
return;
}
} else if (node->type == N_SHORT_SYSVAR || node->type == N_SYSVAR) {
if (node->type == N_SHORT_SYSVAR) {
name = node->u.name;
} else {
name = node->u.value.v.str;
}
if (!StrCmpi(name, "T") ||
!StrCmpi(name, "Td") ||
!StrCmpi(name, "Tm") ||
!StrCmpi(name, "Tw") ||
!StrCmpi(name, "Ty")) {
*mentioned = 1;
return;
}
} else if (node->type == N_SHORT_USER_FUNC || node->type == N_USER_FUNC) {
if (node->type == N_SHORT_USER_FUNC) {
name = node->u.name;
} else {
name = node->u.value.v.str;
}
f = FindUserFunc(name);
if (f && !f->recurse_flag) {
f->recurse_flag = 1;
ensure_satnode_mentions_trigdate_aux(f->node, mentioned);
f->recurse_flag = 0;
if (*mentioned) {
return;
}
}
}
ensure_satnode_mentions_trigdate_aux(node->child, mentioned);
if (*mentioned) {
return;
}
other = node->sibling;
while (other) {
ensure_satnode_mentions_trigdate_aux(other, mentioned);
if (*mentioned) {
return;
}
other = other->sibling;
}
}
static void ensure_satnode_mentions_trigdate(expr_node *node)
{
int mentioned = 0;
char const *str;
if (node->type == N_CONSTANT || node->type == N_SHORT_STR) {
if (node->type == N_CONSTANT) {
if (node->u.value.type == INT_TYPE) {
if (node->u.value.v.val == 0) {
Wprint("SATISFY: constant 0 will never be true");
}
return;
}
if (node->u.value.type != STR_TYPE) {
return;
}
str = node->u.value.v.str;
} else {
str = node->u.name;
}
if (!*str) {
Wprint("SATISFY: constant \"\" will never be true");
}
return;
}
ensure_satnode_mentions_trigdate_aux(node, &mentioned);
if (!mentioned) {
Wprint("SATISFY: expression has no reference to trigdate() or $T...");
}
}
static int
ComputeTrigDuration(TimeTrig *t)
{
@@ -595,6 +741,11 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
trig->scanfrom = DSEToday;
}
/* Check that any SCHED / WARN / OMITFUNC functions refer to
their arguments */
check_trigger_function(trig->sched, "SCHED");
check_trigger_function(trig->warn, "WARN");
check_trigger_function(trig->omitfunc, "OMITFUNC");
return OK;
}
@@ -1287,6 +1438,10 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
if (!sat_node) {
return E_SWERR;
}
/* Diagnose if SAT_NODE does not reference trigdate */
ensure_satnode_mentions_trigdate(sat_node);
iter = 0;
start = trig->scanfrom;
while (iter++ < MaxSatIter) {
+32 -7
View File
@@ -31,6 +31,19 @@
#define SHIP_OUT(s) if(DBufPuts(dbuf, s) != OK) return E_NO_MEM
static int
check_subst_args(UserFunc *f, int n)
{
if (!f) {
return 0;
}
if (f->nargs == n) {
return 1;
}
Wprint("Function `%s' defined at %s:%d should take %d argument%s, but actually takes %d",
f->name, f->filename, f->lineno, n, (n == 1 ? "" : "s"), f->nargs);
return 0;
}
/***************************************************************/
/* */
/* DoSubst */
@@ -67,6 +80,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int altmode;
int r;
Value v;
UserFunc *func;
FromDSE(dse, &y, &m, &d);
@@ -100,7 +114,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
L_AMPM_OVERRIDE (pm, h)
#else
r = -1;
if (UserFuncExists("subst_ampm") == 1) {
func = FindUserFunc("subst_ampm");
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ampm(%d)", h);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
@@ -129,7 +144,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
L_AMPM_OVERRIDE (cpm, ch)
#else
r = -1;
if (UserFuncExists("subst_ampm") == 1) {
func = FindUserFunc("subst_ampm");
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ampm(%d)", ch);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
@@ -154,7 +170,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
#ifdef L_ORDINAL_OVERRIDE
L_ORDINAL_OVERRIDE;
#else
if (UserFuncExists("subst_ordinal") == 1) {
func = FindUserFunc("subst_ordinal");
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ordinal(%d)", d);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
@@ -250,7 +267,13 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
if (!c) {
Wprint("Warning: Unterminated %%{...} substitution sequence");
}
if (UserFuncExists(s) != 3) {
func = FindUserFunc(s);
if (!func) {
Wprint("No substition function `%s' defined", s);
continue;
}
if (!check_subst_args(func, 3)) {
continue;
}
snprintf(ss, sizeof(s) - (ss-s), "(%d,'%04d-%02d-%02d',%02d:%02d)",
@@ -270,7 +293,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
}
done = 0;
snprintf(uf, sizeof(uf), "subst_%c", tolower(c));
if (UserFuncExists(uf) == 3) {
func = FindUserFunc(uf);
if (func && check_subst_args(func, 3)) {
snprintf(s, sizeof(s), "subst_%c(%d,'%04d-%02d-%02d',%02d:%02d)",
tolower(c), altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s;
@@ -345,7 +369,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
if (!done) {
snprintf(uf, sizeof(uf), "subst_%cx", tolower(c));
if (UserFuncExists(uf) == 3) {
func = FindUserFunc(uf);
if (func && check_subst_args(func, 3)) {
snprintf(s, sizeof(s), "subst_%cx(%d,'%04d-%02d-%02d',%02d:%02d)",
tolower(c), altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s;
@@ -797,7 +822,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
break;
case '_':
if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != CAL_MODE && mode != ADVANCE_MODE && !(MsgCommand && *MsgCommand))) {
if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || DoCalendar || (mode != CAL_MODE && mode != ADVANCE_MODE && !(MsgCommand && *MsgCommand))) {
snprintf(s, sizeof(s), "%s", NL);
} else {
snprintf(s, sizeof(s), " ");
+2 -2
View File
@@ -204,7 +204,7 @@ EXTERN char *ErrMsg[]
/* E_ERR_READING */ "Error reading",
/* E_EXPECTING_EOL */ "Expecting end-of-line",
/* E_BAD_HEBDATE */ "Invalid Hebrew date",
/* E_IIF_ODD */ "IIF needs odd number of arguments",
/* E_IIF_ODD */ "iif(): odd number of arguments required",
/* E_MISS_ENDIF */ "Warning: Missing ENDIF",
/* E_EXPECT_COMMA */ "Expecting comma",
/* E_WD_TWICE */ "Weekday specified twice",
@@ -241,7 +241,7 @@ EXTERN char *ErrMsg[]
/* E_NOREMINDERS */ "No reminders.",
/* M_QUEUED */ "%d reminder(s) queued for later today.\n",
/* E_EXPECTING_NUMBER */ "Expecting number",
/* M_BAD_WARN_FUNC */ "Bad function in WARN clause",
/* M_BAD_WARN_FUNC */ "Undefined WARN function",
/* E_CANT_CONVERT_TZ */ "Can't convert between time zones",
/* E_NO_MATCHING_REMS */ "No files matching *.rem",
/* E_STRING_TOO_LONG */ "String too long",
+54 -45
View File
@@ -425,8 +425,14 @@ eval_builtin(expr_node *node, Value *locals, Value *ans, int *nonconst)
Value stack_args[STACK_ARGS_MAX];
/* Check that we have the right number of argumens */
if (node->num_kids < f->minargs) return E_2FEW_ARGS;
if (node->num_kids > f->maxargs && f->maxargs != NO_MAX) return E_2MANY_ARGS;
if (node->num_kids < f->minargs) {
Eprint("%s(): %s", f->name, ErrMsg[E_2FEW_ARGS]);
return E_2FEW_ARGS;
}
if (node->num_kids > f->maxargs && f->maxargs != NO_MAX) {
Eprint("%s(): %s", f->name, ErrMsg[E_2MANY_ARGS]);
return E_2MANY_ARGS;
}
/* If this is a new-style function that knows about expr_nodes,
let it evaluate itself */
@@ -622,10 +628,12 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
/* Make sure we have the right number of arguments */
if (node->num_kids < f->nargs) {
DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, ErrMsg[E_2FEW_ARGS]));
Eprint("%s(): %s", f->name, ErrMsg[E_2FEW_ARGS]);
return E_2FEW_ARGS;
}
if (node->num_kids > f->nargs) {
DBG(fprintf(ErrFp, "%s(...) => %s\n", fname, ErrMsg[E_2MANY_ARGS]));
Eprint("%s(): %s", f->name, ErrMsg[E_2MANY_ARGS]);
return E_2MANY_ARGS;
}
@@ -1367,7 +1375,7 @@ static int divide_or_mod(expr_node *node, Value *locals, Value *ans, int *noncon
/***************************************************************/
/* */
/* domod - evaluate the "%" operator */
/* do_mod - evaluate the "%" operator */
/* */
/***************************************************************/
static int do_mod(expr_node *node, Value *locals, Value *ans, int *nonconst)
@@ -1397,14 +1405,10 @@ static int logical_not(expr_node *node, Value *locals, Value *ans, int *nonconst
r = evaluate_expr_node(node->child, locals, &v1, nonconst);
if (r != OK) return r;
if (v1.type != INT_TYPE) {
DBG(debug_evaluation_unop(ans, E_BAD_TYPE, &v1, "!"));
DestroyValue(v1);
return E_BAD_TYPE;
}
ans->type = INT_TYPE;
ans->v.val = !(v1.v.val);
ans->v.val = !truthy(&v1);
DBG(debug_evaluation_unop(ans, OK, &v1, "!"));
DestroyValue(v1);
return OK;
}
@@ -1452,22 +1456,16 @@ static int logical_binop(expr_node *node, Value *locals, Value *ans, int *noncon
/* Bail on error */
if (r != OK) return r;
if (v.type == STR_TYPE) {
DBG(debug_evaluation_binop(ans, E_BAD_TYPE, &v, NULL, opname));
DestroyValue(v);
return E_BAD_TYPE;
}
if (is_and) {
/* If first arg is false, return it */
if (!v.v.val) {
if (!truthy(&v)) {
*ans = v;
DBG(debug_evaluation_binop(ans, OK, &v, NULL, opname));
return OK;
}
} else {
/* If first arg is true, return it */
if (v.v.val) {
if (truthy(&v)) {
*ans = v;
DBG(debug_evaluation_binop(ans, OK, &v, NULL, opname));
return OK;
@@ -1476,12 +1474,8 @@ static int logical_binop(expr_node *node, Value *locals, Value *ans, int *noncon
/* Otherwise, evaluate and return second arg */
r = evaluate_expr_node(node->child->sibling, locals, ans, nonconst);
if (r == OK && ans->type == STR_TYPE) {
DBG(debug_evaluation_binop(ans, E_BAD_TYPE, &v, ans, opname));
DestroyValue(*ans);
return E_BAD_TYPE;
}
DBG(debug_evaluation_binop(ans, r, &v, ans, opname));
DestroyValue(v);
return r;
}
@@ -1561,9 +1555,10 @@ static int parse_expr_token_aux(DynamicBuffer *buf, char const **in)
return E_NO_MEM;
}
(*in)++;
}
return OK;
} else {
return E_PARSE_ERR;
}
return OK;
case '!':
case '>':
case '<':
@@ -1784,6 +1779,7 @@ static expr_node * parse_function_call(char const **e, int *r, Var *locals, int
expr_node *node;
expr_node *arg;
char *s;
char const *ptr;
CHECK_PARSE_LEVEL();
node = alloc_expr_node(r);
@@ -1856,6 +1852,7 @@ static expr_node * parse_function_call(char const **e, int *r, Var *locals, int
}
}
if (TOKEN_IS(")")) {
ptr = *e;
*r = GET_TOKEN();
if (*r != OK) {
return free_expr_tree(node);
@@ -1864,8 +1861,14 @@ static expr_node * parse_function_call(char const **e, int *r, Var *locals, int
/* Check args for builtin funcs */
if (node->type == N_BUILTIN_FUNC) {
f = node->u.builtin_func;
if (node->num_kids < f->minargs) *r = E_2FEW_ARGS;
if (node->num_kids > f->maxargs && f->maxargs != NO_MAX) *r = E_2MANY_ARGS;
if (node->num_kids < f->minargs) {
*e = ptr;
*r = E_2FEW_ARGS;
}
if (node->num_kids > f->maxargs && f->maxargs != NO_MAX) {
*e = ptr;
*r = E_2MANY_ARGS;
}
}
if (*r != OK) {
if (node->type == N_BUILTIN_FUNC) {
@@ -2167,22 +2170,6 @@ static expr_node *parse_factor(char const **e, int *r, Var *locals, int level)
return NULL;
}
/* If the child is a constant int, optimize! */
if (node->type == N_CONSTANT &&
node->u.value.type == INT_TYPE) {
if (op == '-') {
if (node->u.value.v.val == INT_MIN) {
*r = E_2LOW;
return free_expr_tree(node);
}
node->u.value.v.val = -node->u.value.v.val;
} else {
node->u.value.v.val = !node->u.value.v.val;
}
return node;
}
/* Not a constant int; we need to add a node */
factor_node = alloc_expr_node(r);
if (!factor_node) {
free_expr_tree(node);
@@ -2524,15 +2511,23 @@ expr_node *parse_expression(char const **e, int *r, Var *locals)
}
}
if (*r == E_EXPECT_COMMA ||
*r == E_PARSE_ERR ||
*r == E_MISS_RIGHT_PAREN ||
*r == E_EXPECTING_EOL ||
*r == E_2MANY_ARGS ||
*r == E_2FEW_ARGS ||
*r == E_PARSE_ERR ||
*r == E_EOLN ||
*r == E_ILLEGAL_CHAR) {
orig = o2;
while (*orig) {
fprintf(ErrFp, "%c", *orig++);
if (*orig == '\n') {
fprintf(ErrFp, " ");
orig++;
} else if (*orig == ']' && ! *(orig+1)) {
break;
} else {
fprintf(ErrFp, "%c", *orig++);
}
}
fprintf(ErrFp, "\n");
orig = o2;
@@ -3076,3 +3071,17 @@ void print_expr_nodes_stats(void)
fprintf(stderr, " Expression nodes leaked: %d\n", ExprNodesUsed);
fprintf(stderr, " Parse level high-water: %d\n", parse_level_high_water);
}
/* Return 1 if a value is "true" for its type, 0 if "false" */
int truthy(Value const *v)
{
if (v->type == STR_TYPE) {
if (v->v.str && *(v->v.str)) {
return 1;
} else {
return 0;
}
}
return (v->v.val != 0);
}
+2 -20
View File
@@ -1284,6 +1284,7 @@ static int FChoose(expr_node *node, Value *locals, Value *ans, int *nonconst)
PUT(ErrMsg[E_BAD_TYPE]);
OUT();
}
Eprint("choose(): %s", ErrMsg[E_BAD_TYPE]);
return E_BAD_TYPE;
}
n = v.v.val;
@@ -1974,7 +1975,6 @@ static int FIndex(func_info *info)
/***************************************************************/
static int FIif(expr_node *node, Value *locals, Value *ans, int *nonconst)
{
int istrue;
int r;
int done;
Value v;
@@ -2014,26 +2014,8 @@ static int FIif(expr_node *node, Value *locals, Value *ans, int *nonconst)
done = 1;
PUT(PrintValue(&v, NULL));
}
if (v.type != STR_TYPE && v.type != INT_TYPE) {
if (DebugFlag & DB_PRTEXPR) {
cur = cur->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(ErrMsg[E_BAD_TYPE]);
OUT();
}
return E_BAD_TYPE;
}
if (v.type == INT_TYPE) {
istrue = v.v.val;
} else {
istrue = *(v.v.str);
}
if (istrue) {
if (truthy(&v)) {
r = evaluate_expr_node(cur->sibling, locals, ans, nonconst);
if (r == OK && (DebugFlag & DB_PRTEXPR)) {
PUT(", ");
+3
View File
@@ -1119,6 +1119,8 @@ guess_terminal_background(int *r, int *g, int *b)
if (n != 8) {
/* write failed... WTF? Not much we can do */
tty_reset(ttyfd);
close(ttyfd);
return;
}
@@ -1142,6 +1144,7 @@ guess_terminal_background(int *r, int *g, int *b)
return;
}
tty_reset(ttyfd);
close(ttyfd);
buf[n+1] = 0;
if (n < 25) {
/* Too short */
+2
View File
@@ -61,6 +61,8 @@ expr_node *parse_expression(char const **e, int *r, Var *locals);
int evaluate_expression(expr_node *node, Value *locals, Value *ans, int *nonconst);
int evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst);
int truthy(Value const *v);
void print_expr_tree(expr_node *node, FILE *fp);
void unlimit_execution_time(void);
expr_node *free_expr_tree(expr_node *node);
+1
View File
@@ -317,5 +317,6 @@ typedef struct udf_struct {
int nargs;
char const *filename;
int lineno;
int recurse_flag;
} UserFunc;
+15 -1
View File
@@ -129,6 +129,9 @@ int DoFset(ParsePtr p)
/* We already have it! Our work here is done. */
return OK;
}
/* Warn about redefinition */
Wprint("Function %s redefined (previously defined at %s:%d)",
existing->name, existing->filename, existing->lineno);
}
/* Should be followed by '(' */
@@ -157,6 +160,7 @@ int DoFset(ParsePtr p)
return E_NO_MEM;
}
func->lineno = LineNo;
func->recurse_flag = 0;
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
if (!Hush) {
@@ -203,6 +207,11 @@ int DoFset(ParsePtr p)
local_array[i+1].next = NULL;
func->nargs++;
c = ParseNonSpaceChar(p, &r, 0);
if (r) {
DBufFree(&buf);
DestroyUserFunc(func);
return r;
}
if (c == ')') break;
else if (c != ',') {
DestroyUserFunc(func);
@@ -213,6 +222,10 @@ int DoFset(ParsePtr p)
/* Allow an optional = sign: FSET f(x) = x*x */
c = ParseNonSpaceChar(p, &r, 1);
if (r) {
DestroyUserFunc(func);
return r;
}
if (c == '=') {
(void) ParseNonSpaceChar(p, &r, 0);
}
@@ -237,8 +250,9 @@ int DoFset(ParsePtr p)
}
c = ParseNonSpaceChar(p, &r, 1);
if (c != 0) {
if (c != 0 || r != 0) {
DestroyUserFunc(func);
if (r != 0) return r;
return E_EXPECTING_EOL;
}
+5 -2
View File
@@ -587,7 +587,7 @@ int DoSet (Parser *p)
{
Value v;
int r;
int ch;
DynamicBuffer buf;
DynamicBuffer buf2;
DBufInit(&buf);
@@ -597,8 +597,11 @@ int DoSet (Parser *p)
if (r) return r;
/* Allow optional equals-sign: SET var = value */
if (ParseNonSpaceChar(p, &r, 1) == '=') {
ch = ParseNonSpaceChar(p, &r, 1);
if (r) return r;
if (ch == '=') {
ParseNonSpaceChar(p, &r, 0);
if (r) return r;
}
if (p->isnested) {
+3
View File
@@ -583,6 +583,9 @@ rm -f ../tests/once.timestamp
tail +2 ../tests/once.timestamp >> ../tests/test.out 2>&1
rm -f ../tests/once.timestamp
# Newlines in calendar output
(echo 'REM 16 MSG foo%_bar%_baz wookie quux apple %_ %_ %_ blech'; echo "REM 16 MSG ANOTHER") | ../src/remind -c -w80 - 1 sep 1990 >> ../tests/test.out 2>&1
# Remove references to SysInclude, which is build-specific
grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out
+737 -44
View File
File diff suppressed because it is too large Load Diff
+121
View File
@@ -922,6 +922,16 @@ REM MSG Here: %{custom}
REM MSG There: %*{custom}
REM MSG Bad: %{custom
REM MSG Undefined: %{nopity_nope_nope}
# Bad substitution functions
FSET subst_bad() "foo"
REM MSG %{bad}
FSET subst_ampm(a, b, c, d, e, f, g) "wookie"
REM AT 11:00 MSG %2
# Test FUNSET
FSET square(x) x*x
SET a square(5)
@@ -998,6 +1008,117 @@ msg [shellescape("😆")]
This should be diagnosed as implicitly being REM
REM This should be diganosed as implicitly being MSG-type
# Check that user-defined functions with too many arguments are
# correctly diagnosed.
# This should be OK
FSET f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63) 3
# This should give an error
FSET f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64) 3
# Check that SATISFY expressions that don't reference trigdate are diagnosed
# These should all NOT be diagnosed
set x 3
REM SATISFY 1
REM SATISFY trigdate() > '1990-01-01'
REM AT 23:59 SATISFY trigdatetime() > '1990-01-01@12:00'
REM SATISFY $T > '1990-01-01'
REM SATISFY $Ty > 1990
REM SATISFY $Tm > 0
REM SATISFY $Td > 0
REM SATISFY $Tw > -1
REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * max(5, $Td)]
FSET references_t(x) $T != x
REM SATISFY references_t($U)
FSET recursive_t(x) iif(x==0, recursive_t(1), references_t($U))
REM SATISFY recursive_t(0)
REM SATISFY recursive_t(2)
# These should be diagnosed
REM SATISFY 0
REM SATSIFY ""
REM SATISFY [version() > "01.00.00"]
REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * 5]
FSET gg(x) 0
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
FSET gg(x,y,z) 0
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
FSET gg() 0
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
FSET gg(x) x-x
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
REM WARN not_defined MSG Wookie
REM AT 11:00 SCHED not_defined MSG blork
REM OMITFUNC not_defined MSG hehe
### Strings in logical operators
SET logstr "" && 7
SET logstr "foo" && 7
SET logstr "" && ""
SET logstr "foo" && ""
SET logstr "" && "bar"
SET logstr "foo" && "bar"
SET logstr "" && '2023-02-01'
SET logstr "foo" && '2023-02-01'
SET logstr "" || 7
SET logstr "foo" || 7
SET logstr "" || ""
SET logstr "foo" || ""
SET logstr "" || "bar"
SET logstr "foo" || "bar"
SET logstr "" || '2023-02-01'
SET logstr "foo" || '2023-02-01'
set xyz ! 0
set xyz ! 1
set xyz ! 2
set xyz ! date(baseyr(), 1, 1)
set xyz ! date(baseyr(), 1, 2)
set xyz ! '2024-01-01'
set xyz ! datetime(baseyr(), 1, 1, 00:00)
set xyz ! datetime(baseyr(), 1, 1, 00:01)
set xyz ! datetime(baseyr(), 1, 2, 12:30)
set xyz ! '2024-01-01@11:47'
set xyz ! 00:00
set xyz ! 00:01
set xyz ! 23:59
set xyz ! ""
set xyz ! "foo"
set xyz ! "0"
# Test error messages for function calls with too many / too few args
set zxk version(1)
set zxk max()
fset dooby(x) 1
set zxk dooby()
set zxk dooby(1, 2)
set zxk dooby(1)
# Don't want Remind to queue reminders
EXIT