Compare commits

...

82 Commits

Author SHA1 Message Date
Dianne Skoll
a3e32d2dc4 Update changelog. 2024-06-08 10:57:38 -04:00
Dianne Skoll
a8b78eff00 Add a few more tests. 2024-06-08 09:20:48 -04:00
Dianne Skoll
460db83298 Bump version to 05.00.01 2024-06-08 09:18:42 -04:00
Dianne Skoll
4560712778 Fix a couple of potential memory leaks. 2024-06-07 21:27:24 -04:00
Dianne Skoll
ce8803dde9 The zero-argument form of weekno() is not constant. 2024-06-07 21:22:32 -04:00
Dianne Skoll
60ca5d45e3 Convert "isany" to short-circuit evaluation. 2024-06-07 15:55:12 -04:00
Dianne Skoll
4454613d00 Fix typo 2024-06-06 12:51:02 -04:00
Dianne Skoll
0704808500 Don't rely on newer C feature that allows anonymous parameters 2024-06-06 12:48:44 -04:00
Dianne Skoll
166b1ac499 Only reset alarm if expression time limit was set 2024-06-06 11:47:39 -04:00
Dianne Skoll
e33bf4e80a Explicitly set sa_flags in sigaction. 2024-06-06 10:42:04 -04:00
Dianne Skoll
b3af44d212 Update docs/WHATSNEW for 05.00.00 release. 2024-06-06 10:28:38 -04:00
Dianne Skoll
1e753d5209 Preserve variables needed across iterations. 2024-06-05 15:08:12 -04:00
Dianne Skoll
4bf31005ea Better error location in a couple more cases. 2024-06-05 09:15:10 -04:00
Dianne Skoll
7c86bc910a Better diagnostics of unmatched IFs 2024-06-05 09:08:04 -04:00
Dianne Skoll
4f146a99a9 Simplify Eprint logic. 2024-06-04 21:10:17 -04:00
Dianne Skoll
a6a638e0e6 Add test for UNMATCHED PUSH 2024-06-04 16:27:23 -04:00
Dianne Skoll
325814f5e1 Print the location of unmatched PUSH 2024-06-04 16:25:14 -04:00
Dianne Skoll
5c4ea7d09e Allow "configure" to work, haha. 2024-06-04 12:02:54 -04:00
Dianne Skoll
2bf73987ac Don't need to flush unless we're about to print. 2024-06-04 11:56:05 -04:00
Dianne Skoll
3e9eeea8dc Update docs; kill time-limiter when we queue. 2024-06-04 11:51:27 -04:00
Dianne Skoll
d164d72c1c Update test file 2024-06-04 09:02:50 -04:00
Dianne Skoll
632cee62d9 Don't permit unary-minus overflow. 2024-06-04 09:02:26 -04:00
Dianne Skoll
7a40260f0d Add some expression evaluation tests. 2024-06-04 08:37:19 -04:00
Dianne Skoll
09f043b3de Don't print bytes allocated --- makes test output differ on different architectures. 2024-06-04 07:32:53 -04:00
Dianne Skoll
c0341c8ba3 Fix typo. 2024-06-03 21:54:12 -04:00
Dianne Skoll
850c717803 Handle unary '+' properly. 2024-06-03 20:20:54 -04:00
Dianne Skoll
f13f9e18bd Somewhat better error reporting. 2024-06-03 20:14:11 -04:00
Dianne Skoll
8bdca0d684 More code refactoring. 2024-06-03 19:49:36 -04:00
Dianne Skoll
129bf5612e Refactor some code. 2024-06-03 19:43:28 -04:00
Dianne Skoll
829962fae1 We don't need to check for excessive parse depth if we're not at the high water mark. 2024-06-03 19:35:42 -04:00
Dianne Skoll
c5f9ed8541 Fix logic error 2024-06-03 18:04:10 -04:00
Dianne Skoll
4a7cef4644 Better error diagnostics. 2024-06-03 18:02:19 -04:00
Dianne Skoll
0e010b56ec Fix some error messages. 2024-06-03 17:59:11 -04:00
Dianne Skoll
ee179ee2f5 Suppress duplicate call frames. 2024-06-03 17:47:38 -04:00
Dianne Skoll
e28712cef3 Add "s" to list of debug flags. 2024-06-03 16:16:44 -04:00
Dianne Skoll
8f0a2a7e79 Limit parse high-water to 2000 2024-06-03 15:15:58 -04:00
Dianne Skoll
ef23bba77f Track parse level high-water mark. 2024-06-03 15:13:32 -04:00
Dianne Skoll
602086ae2d Make "-ds" show built-in functions with ucfirst and user-defined ones all lower-case. 2024-06-03 14:56:32 -04:00
Dianne Skoll
f5a170acbd Update man page. 2024-06-03 11:58:37 -04:00
Dianne Skoll
8125b96f0b Add --max-excution-time cmdline option. 2024-06-03 10:40:58 -04:00
Dianne Skoll
0bb7d89bb9 Update man page. 2024-06-03 10:14:36 -04:00
Dianne Skoll
eb109bbbc0 Finish commenting expr.c 2024-06-03 10:01:38 -04:00
Dianne Skoll
1a0809fd31 Start updating man page. 2024-06-02 23:23:06 -04:00
Dianne Skoll
09625b9d68 More docs 2024-06-02 23:07:49 -04:00
Dianne Skoll
4e164c4268 More comments. 2024-06-02 21:08:04 -04:00
Dianne Skoll
691185f22c Update test file so tests pass. 2024-06-02 20:50:34 -04:00
Dianne Skoll
a8bfb41a9e Add $ExpressionTimeLimit system variable. 2024-06-02 14:01:17 -04:00
Dianne Skoll
fafb30db05 Add --max-execution-time option 2024-06-02 12:56:46 -04:00
Dianne Skoll
243e816523 Add translated error messages 2024-06-02 12:43:32 -04:00
Dianne Skoll
b49c0f52bd Implement EXPR ON and EXPR OFF 2024-06-02 12:39:00 -04:00
Dianne Skoll
07fca94a7f Properly handle purging of SCANFROM reminders. 2024-06-02 12:07:12 -04:00
Dianne Skoll
73917ee537 Only set nonconst_expr in purge mode in a couple of cases. 2024-06-02 12:02:29 -04:00
Dianne Skoll
76f9edecf6 Don't purge reminders with a SCANFROM clause. 2024-06-02 10:36:21 -04:00
Dianne Skoll
d77d9854d2 Remove src/expr.h - everything in it is now in types.h or protos.h 2024-06-02 10:26:41 -04:00
Dianne Skoll
c2b53f95a4 Remove dead code 2024-06-02 10:23:24 -04:00
Dianne Skoll
caef8b80d6 Make it easier to match error messages to error constants. 2024-06-02 10:00:04 -04:00
Dianne Skoll
5e016768af More comments. 2024-06-02 09:46:53 -04:00
Dianne Skoll
ee08ce98d7 More comments. 2024-06-01 23:33:33 -04:00
Dianne Skoll
581bd95838 Allow placing a literal [ in a reminder by using [[ 2024-06-01 18:46:17 -04:00
Dianne Skoll
bb92dab1ab More docs. 2024-06-01 16:44:37 -04:00
Dianne Skoll
83b5c52c76 Make parse_expr_token much any following whitespace to replicate prior behavior.
There may be other parts of the code that rely on this.
2024-06-01 16:35:20 -04:00
Dianne Skoll
93eca25141 Fix spurious "Missing ']'" error reported by Jochen Sprickerhof 2024-06-01 16:30:48 -04:00
Dianne Skoll
34421cb10e Don't funset the Jewish holiday functions. 2024-06-01 14:08:48 -04:00
Dianne Skoll
29b87898aa Add some ASCII art 2024-06-01 14:00:39 -04:00
Dianne Skoll
e9e4db94bd More comments 2024-06-01 13:44:21 -04:00
Dianne Skoll
c95ad0261a Start adding comments; do not distinguish between N_BINARY_OPERATOR and N_UNARY_OPERATOR 2024-06-01 13:37:58 -04:00
Dianne Skoll
7fef456483 Don't allocate - nodes if we don't need to 2024-06-01 13:23:52 -04:00
Dianne Skoll
386131e74d Show bytes used for expression notes. 2024-06-01 09:45:37 -04:00
Dianne Skoll
824d3c88f1 Store at most 5 function args on stack. Any more and we malloc. 2024-06-01 09:41:26 -04:00
Dianne Skoll
18a206abd2 Store args on stack if possible. 2024-06-01 09:30:42 -04:00
Dianne Skoll
8dbae776c9 Fix SEGV. 2024-06-01 09:27:27 -04:00
Dianne Skoll
b78702cc53 Assume all function names are lower-case. 2024-06-01 08:46:27 -04:00
Dianne Skoll
d2b43605ad Completely revamp expression engine. 2024-06-01 08:19:12 -04:00
Dianne Skoll
7728e09337 Don't allow duplicate arg names in function definitions. 2024-06-01 08:19:09 -04:00
Dianne Skoll
2666353ce6 Put an include guard around auto.rem 2024-06-01 08:19:08 -04:00
Dianne Skoll
0b8a306483 Disable CI for "wip" branches. 2024-06-01 08:19:08 -04:00
Dianne Skoll
b51a0b2d08 Document that -ivar is the same as -ivar=0 2024-05-25 10:26:00 -04:00
Dianne Skoll
959355b19c Stricter parsing of '-i' option. 2024-05-25 09:36:33 -04:00
Dianne Skoll
9c3f0f1994 Allow "-ifoo" on the command-line, which is the same as "-ifoo=0" 2024-05-25 09:22:41 -04:00
Dianne Skoll
dac337a65b Convert all leading spaces to tabs. Per Tim Chase. 2024-05-01 09:48:59 -04:00
Dianne Skoll
98739dfdbc Replace spaces with tab (per Emanuele Torre)
Because different invisible things are different.
2024-05-01 09:11:19 -04:00
Dianne Skoll
17b7a1ea84 Fix typo 2024-04-29 17:13:52 -04:00
48 changed files with 4791 additions and 2254 deletions

View File

@@ -1,3 +1,8 @@
workflow:
rules:
- if: $CI_COMMIT_BRANCH =~ /wip/
when: never
tests:
image: 'debian:stable-slim'
before_script:

18
configure vendored
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 04.03.07.
# Generated by GNU Autoconf 2.71 for remind 05.00.01.
#
#
# 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='04.03.07'
PACKAGE_STRING='remind 04.03.07'
PACKAGE_VERSION='05.00.01'
PACKAGE_STRING='remind 05.00.01'
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 04.03.07 to adapt to many kinds of systems.
\`configure' configures remind 05.00.01 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 04.03.07:";;
short | recursive ) echo "Configuration of remind 05.00.01:";;
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 04.03.07
remind configure 05.00.01
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 04.03.07, which was
It was created by remind $as_me 05.00.01, 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 04.03.07, which was
This file was extended by remind $as_me 05.00.01, 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 04.03.07
remind config.status 05.00.01
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View File

@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(remind, 04.03.07, , , https://dianne.skoll.ca/projects/remind/)
AC_INIT(remind, 05.00.01, , , https://dianne.skoll.ca/projects/remind/)
AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF'

View File

@@ -1,5 +1,82 @@
CHANGES TO REMIND
* VERSION 5.0 Patch 1 - 2024-06-08
* MINOR IMPROVEMENT: Add short-circuit evaluation to the isany() function,
which now only evaluates those arguments absolutely necessary to determine
the result.
* BUG FIX: Mark weekno() as a non-constant function (the zero-argument form
depends on external conditions.)
* BUG FIX: Fix a couple of memory leaks.
* BUG FIX: Don't rely on support for unnamed function parameters; this caused
compilation failures with older versions of gcc.
* VERSION 5.0 Patch 0 - 2024-06-06
* MAJOR CHANGE: The expression evaluation engine has been completely replaced
with a new one that splits parsing and evaluating into two separate steps.
It also features short-circuit evaluation of &&, ||, iif() and choose().
This should speed up expression-heavy reminder files.
NOTE INCOMPATIBILITY: In expressions with side-effects, the short-circuit
evaluation might change the result you get. For example, consider
running the following file through: remind file.rem 2024-06-04
SET a trig("Mon +7") || trig("Thu +7")
MSG trig = [trig()]
Older Remind versions will output:
trig = 2024-06-06
whereas this version outputs:
trig = 2024-06-10
because the second part of the "||" expression is not evaluated. The vast
majority of Remind expressions do not have side-effects and should yield
the same results as before.
The newer expression engine also permits recursive functions, but
these are not recommended. Still, if you want to, you can do:
fset factorial(n) iif(n <= 1, 1, n*factorial(n-1))
and it will work for values of n that don't cause integer overflow.
* IMPROVEMENT: If there's an unmatched PUSH-OMIT-CONTEXT, print the
filename and line number containing it.
* IMPROVEMENT: If there's an IF with a missing ENDIF, print the filename
and line number of the IF statement.
* NEW FEATURE: Add EXPR OFF command to completely disable expression
evaluation. Useful if you INCLUDE files that you don't expect to
contain expressions and may come from slightly untrustworthy sources.
* NEW FEATURE: Add $ExpressionTimeLimit system variable to enforce a
maximum limit on how long evaluating an expression is allowed to take.
* NEW FEATURE: Add --max-execution-time=n command-line option to terminate
Remind if it runs for more than n seconds.
* CHANGE: Make the command-line option "-ifoo" equivalent to "-ifoo=0"
* CHANGE: Permit a literal [ in a reminder by using the sequence [[
The old ["["] still works.
* BUG FIX: In "purge" mode, Remind would sometimes purge reminders with
a relative "SCANFROM" which haven't actually expired. This has been fixed.
* BUG FIX: Disallow something like: FSET func(x, x) expr
which shouldn't have been allowed in the first place.
* BUG FIX: Replace leading spaces with tabs in Makefiles (per Emanuele Torre
and Tim Chase)
* VERSION 4.3 Patch 7 - 2024-04-29
* IMPROVEMENT: build.tk: Add a note if build.tk obtains default settings
@@ -8,7 +85,7 @@ CHANGES TO REMIND
* IMPROVEMENT: configure: Pass all args to AC_INIT including the Remind
home page. Remove some unused autoconf cruft.
* IMPROVEMENT: Use standard C library versions of strdup, strcasemp and
* IMPROVEMENT: Use standard C library versions of strdup, strcasecmp and
strncasecmp where available, rather than using our own versions.
* MINOR FEATURE: remind: Make $Tt a synonym for trigtime().

View File

@@ -106,7 +106,3 @@ IF !Reform
[_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b.
[_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b.
ENDIF
# Clean up
FUNSET _h _h2 _PastSat _BackTwoFri _BackTwoSat _chan

View File

@@ -1,24 +1,28 @@
# SPDX-License-Identifier: GPL-2.0-only
SET autolang getenv("REMIND_LANG")
if !defined("__autolang__")
SET __autolang__ 1
PRESERVE __autolang__
SET autolang getenv("REMIND_LANG")
IF autolang == ""
SET autolang getenv("LC_ALL")
ENDIF
IF autolang == ""
SET autolang getenv("LANGUAGE")
ENDIF
IF autolang == ""
SET autolang getenv("LANG")
ENDIF
IF autolang == ""
SET autolang getenv("LC_ALL")
ENDIF
IF autolang == ""
SET autolang getenv("LANGUAGE")
ENDIF
IF autolang == ""
SET autolang getenv("LANG")
ENDIF
IF autolang != ""
IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 5)) + ".rem", "r") == 0
INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 5))].rem
ELSE
IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 2)) + ".rem", "r") == 0
INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 2))].rem
IF autolang != ""
IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 5)) + ".rem", "r") == 0
INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 5))].rem
ELSE
IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 2)) + ".rem", "r") == 0
INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 2))].rem
ENDIF
ENDIF
ENDIF
UNSET autolang
ENDIF
UNSET autolang

View File

@@ -87,3 +87,5 @@ SET daylightST_starts_str "Beginn Sommerzeit"
# Daylight saving time ends
SET daylightST_ends_str "Ende Sommerzeit"
PRESERVE earthseasons_Perihelion_str earthseasons_EquinoxMar_str earthseasons_SolsticeJun_str earthseasons_Aphelion_str earthseasons_EquinoxSep_str earthseasons_SolsticeDec_str daylightST_starts_str daylightST_ends_str

View File

@@ -81,3 +81,5 @@ SET daylightST_starts_str "Έναρξη θέρους"
# Daylight saving time ends
SET daylightST_ends_str "Τέλος θέρους"
PRESERVE earthseasons_Perihelion_str earthseasons_EquinoxMar_str earthseasons_SolsticeJun_str earthseasons_Aphelion_str earthseasons_EquinoxSep_str earthseasons_SolsticeDec_str daylightST_starts_str daylightST_ends_str

View File

@@ -28,10 +28,6 @@ Anything after the __EOF__ marker is completely ignored.
\fBRemind\fR has a slew of options. If you're new to the program,
ignore them for now and skip to the section "REMINDER FILES".
.TP
.B \-\-version
The \fB\-\-version\fR option causes \fBRemind\fR to print its version number
to standard output and then exit.
.TP
.B \-n
The \fB\-n\fR option causes \fBRemind\fR to print the \fBnext\fR occurrence
of each reminder in a simple calendar format. You can sort this by
@@ -302,9 +298,9 @@ Echo lines when displaying error messages
Trace the reading of reminder files
.TP
.B s
Upon exit, print the high-water mark of the operator and value stacks
used for expression-parsing. This is unlikely to be useful unless
you're intimately familiar with Remind's source code.
Trace expression parsing and display the internal expression node
tree. This is unlikely to be useful unless you are working on
\fBRemind\fR's expression evaluation engine.
.RE
.TP
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
@@ -420,7 +416,9 @@ TAG clause.
\fB\-i\fR\fIvar\fR\fB=\fR\fIexpr\fR
Sets the value of the specified \fIvar\fR to \fIexpr\fR, and \fBpreserves\fR
\fIvar\fR. \fIExpr\fR can be any valid \fBRemind\fR expression. See the
section "INITIALIZING VARIABLES ON THE COMMAND LINE" for more details.
section "INITIALIZING VARIABLES ON THE COMMAND LINE" for more details. If
you omit the \fB=\fR\fIexpr\fR part, then \fIvar\fR is initialized to 0.
In other words, \fB\-i\fIvar\fR is exactly the same as \fB\-i\fIvar\fR\fB=\fR0.
.TP
\fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR
Allows you to define a function on the command line.
@@ -451,6 +449,30 @@ with the date incrementing on each iteration. You may have to enclose
the parameter in quotes to avoid shell expansion. See the subsection
"Repeated Execution" in the section "CALENDAR MODE" for more
information.
.SH LONG OPTIONS
\fBRemind\fR supports the following long options, which \fIare\fR
case-sensitive:
.RP
.B \-\-version
The \fB\-\-version\fR option causes \fBRemind\fR to print its version number
to standard output and then exit.
.TP
.B \-\-max-execution-time\fR=\fIn\fR
Limit the total execution time (as measured by the wall clock) to
\fIn\fR seconds. This is useful if \fBRemind\fR is invoked on
potentially-untrustworthy files that could attempt to use a lot of
resources. Note that the limit \fIn\fR is approximate and
\fBRemind\fR might execute for one or two more seconds before it is
killed. If \fIn\fR is specified as zero, then no limit is applied, just
as if the option had not been used at all.
.PP
If a limit is applied, it applies only to the foreground run of \fBRemind\fR.
If \fBRemind\fR finishes processing the script and then starts handling
queued reminders, the time limit is reset to no limit.
.PP
.SH REMINDER FILES
.PP
\fBRemind\fR uses scripts to control its operation. You can use any
@@ -1880,24 +1902,6 @@ If you run \fBRemind\fR with the \fB\-r\fR command-line option,
regardless of any \fBRUN\fR commands in the reminder script. However,
any command supplied with the \fB\-k\fR option will still be executed.
.PP
One use of the \fBRUN\fR command is to provide a secure interface
between \fBRemind\fR and the \fBElm\fR mail system. The \fBElm\fR
system can automatically scan incoming mail for reminder or calendar
entries, and place them in your calendar file. To use this feature,
you should set the calendar filename option under \fBElm\fR to be something
like "~/.reminders.in", \fInot\fR your main reminder file! This is
so that any \fBRUN ON\fR commands mailed to you can never be activated.
.PP
Then, you can use the \fBElm\fR \fIscan message for calendar entries\fR
command to place reminders prefaced by "->" into .reminders.in. In
your main .reminders file, include the following lines:
.PP
.nf
RUN OFF # Disable RUN
INCLUDE .reminders.in
RUN ON # Re-enable RUN
.fi
.PP
In addition, \fBRemind\fR contains a few other security
features. It will not read a file that is group- or world-writable.
It will not run set-uid. If it reads a file you don't own, it will
@@ -1909,6 +1913,30 @@ attempt to check the ownership of standard input, even if it is
coming from a file, and hence does \fInot\fR disable RUN and shell()
in this situation.
.PP
.SH THE EXPR COMMAND
.PP
\fBRemind\fR lets you completely disable expression evaluation. This
could be useful if you are running Remind on a somewhat-untrustworthy
file that is not expected to contain expressions. To disable
expression evaluation, use:
.PP
.nf
EXPR OFF
.fi
.PP
If \fBRemind\fR encounters an expression while EXPR OFF is in effect, it
returns an error
.PP
To re-enable expression evaluation, use:
.PP
.nf
EXPR ON
.fi
.PP
As with \fBRUN ON\fB, \fBEXPR ON\fR can be used only in the top-level
script, not in an included file.
.PP
.SH THE INCLUDECMD COMMAND
.PP
\fBRemind\fR allows you to execute a shell command and evaluate the
@@ -2161,13 +2189,13 @@ than those on higher lines. The operators approximately correspond to
C operators.
.PP
.nf
! - (unary logical negation and arithmetic negation)
* / %
+ -
< <= > >=
== !=
&&
||
! - (unary logical negation and arithmetic negation)
* / % (multiplication, division, modulus)
+ - (addition/concatenation, subtraction)
< <= > >= (comparisons)
== != (equality and inequality tests)
&& (logical AND)
|| (logical OR)
.fi
.PP
.B DESCRIPTION OF OPERATORS
@@ -2279,16 +2307,15 @@ If the operands are not of the same type, == returns 0 and != returns
.RE
.TP
.B &&
This is the logical AND operator. Both of its operands must be of the
same type and must not be \fBSTRING\fR type. Returns the second
operand if both operands are non-zero. Otherwise, returns a zero
of the same type as the operands.
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.
.TP
.B ||
This is the logical OR operator. Both of its operands must be of
the same type and must not be of \fBSTRING\fR type. It returns
the first operand that is non-zero; if both operands are zero, then
returns a zero of the same type as the operands.
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.
.PP
.B NOTES
.PP
@@ -2309,15 +2336,10 @@ For example:
12:59 + (1 + "test") yields "12:591test"
.fi
.PP
The logical operators are \fInot\fR so-called short-circuit operators, as
they are in C. Both operands are always evaluated. Thus, an expression
such as:
.PP
.nf
(f!=0) && (100/f <= 3)
.fi
.PP
will cause an error if f is zero.
The logical operators are so-called short-circuit operators, as
they are in C. This means that if the first operand of || is true,
then the second operand is \fInot\fR evaluated. Similarly, if the first
operand of && is false, then the second operand is \fInot\fR evaluated.
.PP
.B VARIABLES
.PP
@@ -2474,6 +2496,14 @@ because the final parenthesis and quote are ignored (for the purposes
of spacing) when they follow a period.
.RE
.TP
.B $ExpressionTimeLimit
If set to a non-zero value \fIn\fR, than any expression that takes longer than
\fIn\fR seconds to evaluate will be aborted and an error returned. This is
to prevent maliciously-crafted expressions for creating a denial-of-service.
In an included file, $ExpressionTimeLimit can only be lowered from its
current value. In the top-level file, it can be set to any value, including
zero to disable the time limit.
.TP
.B $FirstIndent
The number of spaces by which to indent the first line of a \fBMSF\fR-type
reminder. The default is 0.
@@ -3838,7 +3868,7 @@ function for advance warning to work properly. This is because
\fBtrig\fR returns a date constant (the trigger date) and the
REM command does not know the details of \fBtrig\fR's arguments.
.PP
Note that because \fBRemind\fR does not have short-circuit logical
Note that because \fBRemind\fR has short-circuit logical
operators, something like:
.PP
.nf
@@ -3846,58 +3876,9 @@ operators, something like:
.fi
would set the value of trig() to the date of the following
Thursday. Even though trig("Mon +7") always returns true,
the logical-OR operator still evaluates trig("Fri +7") which
\fIalso\fR returns true and sets \fBtrig()\fR.
.PP
You can work around the lack of a short-circuit logical-OR as follows:
If \fBtrig\fR returns a true value, the specific value it returns
can be coerced to a DATE which is the trigger date. So the following code:
.PP
.nf
SET a trig("Mon +4") || trig("Fri +4")
IF a
REM [a] +4 MSG [wkday($T)] %b.
ENDIF
.fi
.PP
would operate as follows:
.PP
.nf
On Monday: Monday today.
On Tuesday: Friday in 3 days' time.
On Wednesday: Friday in 2 days' time.
On Thursday: Monday in 4 days' time.
On Friday: Monday in 3 days' time.
On Saturday: Monday in 2 days' time.
On Sunday: Monday tomorrow.
.fi
.PP
Compare with the following:
.PP
.nf
SET a trig("Mon +4") || trig("Fri +4")
IF a
REM [trig()] +4 MSG [wkday($T)] %b.
ENDIF
.fi
.PP
which yields:
.PP
.nf
On Monday: Friday in 4 days' time.
On Tuesday: Friday in 3 days' time.
On Wednesday: Friday in 2 days' time.
On Thursday: Friday tomorrow.
On Friday: Friday today.
On Saturday: Monday in 2 days' time.
On Sunday: Monday tomorrow.
.fi
.PP
That is because \fBtrig()\fR returns the trigger date of
the \fIlast\fR trig function that returns true,
whereas the value of \fBa\fR is the trigger date of the \fIfirst\fR
trig function that returns true.
Monday. Because trig("Mon +7") always returns true,
the logical-OR operator does not bother evaluating trig("Fri +7") which
therefore does not set \fBtrig()\fR.
.PP
\fBImportant Note\fR: Because \fBtrig()\fR always returns an absolute
date, it will \fBnot\fR work properly with a \fBSATISFY\fR clause.
@@ -4237,6 +4218,14 @@ with square brackets. For example:
This evaluates the expression "mydate", where "mydate" is
presumably some pre-computed variable, and then "pastes" the result
into the command-line for the parser to process.
.PP
If you want a literal "[" character for some reason, simply use "[[". For
example:
.PP
.nf
REM MSG Here are [[square] brackets!
.fi
.PP
A formal description of this is: When \fBRemind\fR encounters a
"pasted-in" expression, it evaluates the expression, and coerces the
@@ -4784,7 +4773,7 @@ under program control. The format is:
.PP
\fBDEBUG\fR [+\fIflagson\fR] [\-\fIflagsoff\fR]
.PP
\fIFlagson\fR and \fIflagsoff\fR consist of strings of the characters "extvlf"
\fIFlagson\fR and \fIflagsoff\fR consist of strings of the characters "extvlfs"
that correspond to the debugging options discussed in the command-line
options section. If preceded with a "+", the corresponding group of
debugging options is switched on. Otherwise, they are switched off.

View File

@@ -15,7 +15,7 @@ install:
@if test "$(PERL)" = "" ; then \
echo "Not installing rem2html; Perl is required"; exit 0; fi; \
for m in $(PERLMODS_NEEDED) ; \
do \
do \
$(PERL) -M$$m -e 1 > /dev/null 2>&1; \
if test $$? != 0 ; then echo "Not installing rem2html; missing $$m"; exit 0; fi; \
done; \

View File

@@ -30,7 +30,7 @@ REMINDSRCS= calendar.c dynbuf.c dorem.c dosubst.c expr.c files.c funcs.c \
globals.c hbcal.c init.c main.c md5.c moon.c omit.c queue.c \
sort.c token.c trigger.c userfns.c utils.c var.c
REMINDHDRS=config.h custom.h dynbuf.h err.h expr.h globals.h lang.h \
REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h lang.h \
md5.h protos.h rem2ps.h types.h version.h
REMINDOBJS= $(REMINDSRCS:.c=.o)
@@ -72,7 +72,7 @@ install: all
update-desktop-database < /dev/null > /dev/null 2>&1 ; \
xdg-icon-resource install --novendor --size 64 $(DESTDIR)$(prefix)/share/pixmaps/tkremind.png < /dev/null > /dev/null 2>&1 || true; \
xdg-desktop-menu install --novendor $(DESTDIR)$(prefix)/share/applications/tkremind.desktop < /dev/null > /dev/null 2>&1 || true; \
fi
fi
install-stripped: install
strip $(DESTDIR)$(bindir)/remind || true

View File

@@ -35,7 +35,6 @@
#include "lang.h"
#include "types.h"
#include "protos.h"
#include "expr.h"
#include "globals.h"
#include "err.h"
#include "md5.h"
@@ -1670,6 +1669,7 @@ static void GenerateCalEntries(int col)
case T_Pop: r=PopOmitContext(&p); break;
case T_Push: r=PushOmitContext(&p); break;
case T_Preserve: r=DoPreserve(&p); break;
case T_Expr: r = DoExpr(&p); break;
case T_RemType: if (tok.val == RUN_TYPE) {
r=DoRun(&p);
break;

View File

@@ -166,6 +166,10 @@
#define PASSTHRU_LEN 32
#define MAX_RECURSION_LEVEL 1000
#define MAX_FUNC_ARGS 64
#define PSBEGIN "# rem2ps begin"
#define PSEND "# rem2ps end"

View File

@@ -166,6 +166,10 @@
#define PASSTHRU_LEN 32
#define MAX_RECURSION_LEVEL 1000
#define MAX_FUNC_ARGS 64
#define PSBEGIN "# rem2ps begin"
#define PSEND "# rem2ps end"

View File

@@ -23,7 +23,6 @@
#include "globals.h"
#include "err.h"
#include "protos.h"
#include "expr.h"
static int ParseTimeTrig (ParsePtr s, TimeTrig *tim);
static int ParseLocalOmit (ParsePtr s, Trigger *t);
@@ -163,6 +162,7 @@ int DoRem(ParsePtr p)
if (p->expr_happened) {
if (p->nonconst_expr) {
PurgeEchoLine("%s\n", "#!P: Next line may have expired, but contains non-constant expression");
PurgeEchoLine("%s\n", "#!P: or a relative SCANFROM clause");
PurgeEchoLine("%s\n", CurLine);
} else {
PurgeEchoLine("%s\n", "#!P: Next line has expired, but contains expression... please verify");
@@ -473,10 +473,10 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
r=ParseToken(s, &buf);
if (r) return r;
StrnCpy(trig->omitfunc, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->omitfunc);
/* An OMITFUNC counts as a nonconst_expr! */
s->expr_happened = 1;
s->nonconst_expr = 1;
s->expr_happened = 1;
s->nonconst_expr = 1;
DBufFree(&buf);
break;
@@ -484,6 +484,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
r=ParseToken(s, &buf);
if(r) return r;
StrnCpy(trig->warn, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->warn);
DBufFree(&buf);
break;
@@ -525,6 +526,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
r=ParseToken(s, &buf);
if(r) return r;
StrnCpy(trig->sched, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->sched);
DBufFree(&buf);
break;
@@ -860,6 +862,9 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
tok.val = -tok.val;
}
FromDSE(DSEToday - tok.val, &y, &m, &d);
/* Don't purge reminders with a relative scanfrom */
s->expr_happened = 1;
s->nonconst_expr = 1;
break;
default:
@@ -1268,20 +1273,28 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
{
int iter, dse, r, start;
Value v;
char const *s;
char const *t;
expr_node *sat_node;
int nonconst = 0;
t = p->pos;
sat_node = ParseExpr(p, &r);
if (r != OK) {
return r;
}
if (!sat_node) {
return E_SWERR;
}
iter = 0;
start = trig->scanfrom;
while (iter++ < MaxSatIter) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0);
if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r;
}
if (dse != start && trig->duration_days) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days);
if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r;
}
} else if (dse == start) {
@@ -1294,13 +1307,18 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
SaveAllTriggerInfo(trig, tt, dse, tt->ttime, 1);
}
if (dse == -1) {
free_expr_tree(sat_node);
return E_EXPIRED;
}
s = p->pos;
r = EvaluateExpr(p, &v);
t = p->pos;
if (r) return r;
if (v.type != INT_TYPE && v.type != STR_TYPE) return E_BAD_TYPE;
r = evaluate_expression(sat_node, NULL, &v, &nonconst);
if (r) {
free_expr_tree(sat_node);
return r;
}
if (v.type != INT_TYPE && v.type != STR_TYPE) {
free_expr_tree(sat_node);
return E_BAD_TYPE;
}
if ((v.type == INT_TYPE && v.v.val) ||
(v.type == STR_TYPE && *v.v.str)) {
AdjustTriggerForDuration(trig->scanfrom, dse, trig, tt, 1);
@@ -1325,17 +1343,17 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
}
fprintf(ErrFp, "\n");
}
free_expr_tree(sat_node);
return OK;
}
p->pos = s;
if (dse+trig->duration_days < start) {
start++;
} else {
start = dse+trig->duration_days+1;
}
}
p->pos = t;
LastTrigValid = 0;
free_expr_tree(sat_node);
return E_CANT_TRIG;
}

View File

@@ -12,7 +12,7 @@
/***************************************************************/
#include "config.h"
#include "expr.h"
#include "types.h"
#define L_IN_DOSUBST
#include <stdio.h>
#include <string.h>
@@ -20,7 +20,6 @@
#include <stdlib.h>
#include "types.h"
#include "globals.h"
#include "err.h"
#include "protos.h"
@@ -142,6 +141,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else {
r = -1;
}
DestroyValue(v);
} else {
Eprint("%s", ErrMsg[r]);
}
@@ -166,6 +166,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else {
r = -1;
}
DestroyValue(v);
} else {
Eprint("%s", ErrMsg[r]);
}
@@ -242,7 +243,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
break;
}
if (i < 64) {
*ss++ = c;
*ss++ = tolower(c);
*ss = 0;
i++;
}
@@ -269,10 +270,10 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
continue;
}
done = 0;
snprintf(uf, sizeof(uf), "subst_%c", c);
snprintf(uf, sizeof(uf), "subst_%c", tolower(c));
if (UserFuncExists(uf) == 3) {
snprintf(s, sizeof(s), "subst_%c(%d,'%04d-%02d-%02d',%02d:%02d)",
c, altmode ? 1 : 0, y, m+1, d, h, min);
tolower(c), altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
if (r == OK) {
@@ -344,10 +345,10 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
if (!done) {
snprintf(uf, sizeof(uf), "subst_%cx", c);
snprintf(uf, sizeof(uf), "subst_%cx", tolower(c));
if (UserFuncExists(uf) == 3) {
snprintf(s, sizeof(s), "subst_%cx(%d,'%04d-%02d-%02d',%02d:%02d)",
c, altmode ? 1 : 0, y, m+1, d, h, min);
tolower(c), altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
if (r == OK) {

View File

@@ -124,7 +124,9 @@ int DBufPuts(DynamicBuffer *dbuf, char const *str)
**********************************************************************/
void DBufFree(DynamicBuffer *dbuf)
{
if (dbuf->buffer != dbuf->staticBuf) free(dbuf->buffer);
if (dbuf->buffer != NULL && dbuf->buffer != dbuf->staticBuf) {
free(dbuf->buffer);
}
DBufInit(dbuf);
}

216
src/err.h
View File

@@ -122,6 +122,9 @@
#define E_TIME_TWICE 102
#define E_DURATION_NO_AT 103
#define E_EXPECTING_WEEKDAY 104
#define E_REPEATED_ARG 105
#define E_EXPR_DISABLED 106
#define E_TIME_EXCEEDED 107
#ifdef MK_GLOBALS
#undef EXTERN
@@ -140,111 +143,114 @@ EXTERN char *ErrMsg[]
#ifdef MK_GLOBALS
= {
"Ok",
"Missing ']'",
"Missing quote",
"Expression too complex - too many operators",
"Expression too complex - too many operands",
"Missing ')'",
"Undefined function",
"Illegal character",
"Expecting binary operator",
"Out of memory",
"Ill-formed number",
"Op stack underflow - internal error",
"Va stack underflow - internal error",
"Can't coerce",
"Type mismatch",
"Date overflow",
"Stack error - internal error",
"Division by zero",
"Undefined variable",
"Unexpected end of line",
"Unexpected end of file",
"I/O error",
"Line too long",
"Internal error",
"Bad date specification",
"Not enough arguments",
"Too many arguments",
"Ill-formed time",
"Number too high",
"Number too low",
"Can't open file",
"INCLUDE nested too deeply (max. " STR(INCLUDE_NEST) ")",
"Parse error",
"Can't compute trigger",
"Too many nested IFs",
"ELSE with no matching IF",
"ENDIF with no matching IF",
"Can't OMIT every weekday",
"Extraneous token(s) on line",
"POP-OMIT-CONTEXT without matching PUSH-OMIT-CONTEXT",
"RUN disabled",
"Domain error",
"Invalid identifier",
"Recursive function call detected",
"",
"Cannot modify system variable",
"C library function can't represent date/time",
"Attempt to redefine built-in function",
"Can't nest function definition in expression",
"Must fully specify date to use repeat factor",
"Year specified twice",
"Month specified twice",
"Day specified twice",
"Unknown token",
"Must specify month in OMIT command",
"Too many partial OMITs (max. " STR(MAX_PARTIAL_OMITS) ")",
"Too many full OMITs (max. " STR(MAX_FULL_OMITS) ")",
"Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT",
"Error reading",
"Expecting end-of-line",
"Invalid Hebrew date",
"IIF needs odd number of arguments",
"Warning: Missing ENDIF",
"Expecting comma",
"Weekday specified twice",
"Only use one of BEFORE, AFTER or SKIP",
"Can't nest MSG, MSF, RUN, etc. in expression",
"Repeat value specified twice",
"Delta value specified twice",
"Back value specified twice",
"ONCE keyword used twice. (Hah.)",
"Expecting time after AT",
"THROUGH/UNTIL keyword used twice",
"Incomplete date specification",
"FROM/SCANFROM keyword used twice",
"Variable",
"Value",
"*UNDEFINED*",
"Entering UserFN",
"Leaving UserFN",
"Expired",
"fork() failed - can't do queued reminders",
"Can't access file",
"Illegal system date: Year is less than %d\n",
"Unknown debug flag '%c'\n",
"Unknown option '%c'\n",
"Unknown user '%s'\n",
"Could not change gid to %d\n",
"Could not change uid to %d\n",
"Out of memory for environment\n",
"Missing '=' sign",
"Missing variable name",
"Missing expression",
"Can't reset access date of %s\n",
"Remind: '-i' option: %s\n",
"No reminders.",
"%d reminder(s) queued for later today.\n",
"Expecting number",
"Bad function in WARN clause",
"Can't convert between time zones",
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Expecting weekday name"
/* OK */ "Ok",
/* E_MISS_END */ "Missing ']'",
/* E_MISS_QUOTE */ "Missing quote",
/* E_OP_STK_OVER */ "Expression too complex",
/* E_VA_STK_OVER */ "Expression too complex - too many operands",
/* E_MISS_RIGHT_PAREN */ "Missing ')'",
/* E_UNDEF_FUNC */ "Undefined function",
/* E_ILLEGAL_CHAR */ "Illegal character",
/* E_EXPECTING_BINOP */ "Expecting binary operator",
/* E_NO_MEM */ "Out of memory",
/* E_BAD_NUMBER */ "Ill-formed number",
/* E_OP_STK_UNDER */ "Op stack underflow - internal error",
/* E_VA_STK_UNDER */ "Va stack underflow - internal error",
/* E_CANT_COERCE */ "Can't coerce",
/* E_BAD_TYPE */ "Type mismatch",
/* E_DATE_OVER */ "Date overflow",
/* E_STACK_ERR */ "Stack error - internal error",
/* E_DIV_ZERO */ "Division by zero",
/* E_NOSUCH_VAR */ "Undefined variable",
/* E_EOLN */ "Unexpected end of line",
/* E_EOF */ "Unexpected end of file",
/* E_IO_ERR */ "I/O error",
/* E_LINE_2_LONG */ "Line too long",
/* E_SWERR */ "Internal error",
/* E_BAD_DATE */ "Bad date specification",
/* E_2FEW_ARGS */ "Not enough arguments",
/* E_2MANY_ARGS */ "Too many arguments",
/* E_BAD_TIME */ "Ill-formed time",
/* E_2HIGH */ "Number too high",
/* E_2LOW */ "Number too low",
/* E_CANT_OPEN */ "Can't open file",
/* E_NESTED_INCLUDE */ "INCLUDE nested too deeply (max. " STR(INCLUDE_NEST) ")",
/* E_PARSE_ERR */ "Parse error",
/* E_CANT_TRIG */ "Can't compute trigger",
/* E_NESTED_IF */ "Too many nested IFs",
/* E_ELSE_NO_IF */ "ELSE with no matching IF",
/* E_ENDIF_NO_IF */ "ENDIF with no matching IF",
/* E_2MANY_LOCALOMIT */ "Can't OMIT every weekday",
/* E_EXTRANEOUS_TOKEN */ "Extraneous token(s) on line",
/* E_POP_NO_PUSH */ "POP-OMIT-CONTEXT without matching PUSH-OMIT-CONTEXT",
/* E_RUN_DISABLED */ "RUN disabled",
/* E_DOMAIN_ERR */ "Domain error",
/* E_BAD_ID */ "Invalid identifier",
/* E_RECURSIVE */ "Too many recursive function calls",
/* E_PARSE_AS_REM */ "",
/* E_CANT_MODIFY */ "Cannot modify system variable",
/* E_MKTIME_PROBLEM */ "C library function can't represent date/time",
/* E_REDEF_FUNC */ "Attempt to redefine built-in function",
/* E_CANTNEST_FDEF */ "Can't nest function definition in expression",
/* E_REP_FULSPEC */ "Must fully specify date to use repeat factor",
/* E_YR_TWICE */ "Year specified twice",
/* E_MON_TWICE */ "Month specified twice",
/* E_DAY_TWICE */ "Day specified twice",
/* E_UNKNOWN_TOKEN */ "Unknown token",
/* E_SPEC_MON */ "Must specify month in OMIT command",
/* E_2MANY_PART */ "Too many partial OMITs (max. " STR(MAX_PARTIAL_OMITS) ")",
/* E_2MANY_FULL */ "Too many full OMITs (max. " STR(MAX_FULL_OMITS) ")",
/* E_PUSH_NOPOP */ "Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT",
/* 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_MISS_ENDIF */ "Warning: Missing ENDIF",
/* E_EXPECT_COMMA */ "Expecting comma",
/* E_WD_TWICE */ "Weekday specified twice",
/* E_SKIP_ERR */ "Only use one of BEFORE, AFTER or SKIP",
/* E_CANT_NEST_RTYPE */ "Can't nest MSG, MSF, RUN, etc. in expression",
/* E_REP_TWICE */ "Repeat value specified twice",
/* E_DELTA_TWICE */ "Delta value specified twice",
/* E_BACK_TWICE */ "Back value specified twice",
/* E_ONCE_TWICE */ "ONCE keyword used twice. (Hah.)",
/* E_EXPECT_TIME */ "Expecting time after AT",
/* E_UNTIL_TWICE */ "THROUGH/UNTIL keyword used twice",
/* E_INCOMPLETE */ "Incomplete date specification",
/* E_SCAN_TWICE */ "FROM/SCANFROM keyword used twice",
/* E_VAR */ "Variable",
/* E_VAL */ "Value",
/* E_UNDEF */ "*UNDEFINED*",
/* E_ENTER_FUN */ "Entering UserFN",
/* E_LEAVE_FUN */ "Leaving UserFN",
/* E_EXPIRED */ "Expired",
/* E_CANTFORK */ "fork() failed - can't do queued reminders",
/* E_CANTACCESS */ "Can't access file",
/* M_BAD_SYS_DATE */ "Illegal system date: Year is less than %d\n",
/* M_BAD_DB_FLAG */ "Unknown debug flag '%c'\n",
/* M_BAD_OPTION */ "Unknown option '%c'\n",
/* M_BAD_USER */ "Unknown user '%s'\n",
/* M_NO_CHG_GID */ "Could not change gid to %d\n",
/* M_NO_CHG_UID */ "Could not change uid to %d\n",
/* M_NOMEM_ENV */ "Out of memory for environment\n",
/* E_MISS_EQ */ "Missing '=' sign",
/* E_MISS_VAR */ "Missing variable name",
/* E_MISS_EXPR */ "Missing expression",
/* M_CANTSET_ACCESS */ "Can't reset access date of %s\n",
/* M_I_OPTION */ "Remind: '-i' option: %s\n",
/* 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",
/* 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",
/* E_TIME_TWICE */ "Time specified twice",
/* E_DURATION_NO_AT */ "Cannot specify DURATION without specifying AT",
/* E_EXPECTING_WEEKDAY */ "Expecting weekday name",
/* E_REPEATED_ARG */ "Duplicate argument name",
/* E_EXPR_DISABLED */ "Expression evaluation is disabled",
/* E_TIME_EXCEEDED */ "Time limit for expression evaluation exceeded",
}
#endif /* MK_GLOBALS */
;

3686
src/expr.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,67 +0,0 @@
/***************************************************************/
/* */
/* EXPR.H */
/* */
/* Contains a few definitions used by expression evaluator. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */
/***************************************************************/
/* Define the types of values */
#define ERR_TYPE 0
#define INT_TYPE 1
#define TIME_TYPE 2
#define DATE_TYPE 3
#define STR_TYPE 4
#define DATETIME_TYPE 5
#define SPECIAL_TYPE 6 /* Only for system variables */
#define CONST_INT_TYPE 7 /* Only for system variables */
/* Define stuff for parsing expressions */
#define BEG_OF_EXPR '['
#define END_OF_EXPR ']'
#define COMMA ','
#define UN_OP 0 /* Unary operator */
#define BIN_OP 1 /* Binary Operator */
#define FUNC 2 /* Function */
/* Make the pushing and popping of values and operators in-line code
for speed. BEWARE: These macros invoke return if an error happens ! */
#define PushOpStack(op) \
do { if (OpStackPtr >= OP_STACK_SIZE) return E_OP_STK_OVER; \
else { OpStack[OpStackPtr++] = (op); if (OpStackPtr > OpStackHiWater) OpStackHiWater = OpStackPtr; } } while(0)
#define PopOpStack(op) \
if (OpStackPtr <= 0) \
return E_OP_STK_UNDER; \
else \
(op) = OpStack[--OpStackPtr]
#define PushValStack(val) \
do { if (ValStackPtr >= VAL_STACK_SIZE) { \
DestroyValue(val); \
return E_VA_STK_OVER; \
} else { \
ValStack[ValStackPtr++] = (val); \
if (ValStackPtr > ValStackHiWater) ValStackHiWater = ValStackPtr; \
} } while (0);
#define PopValStack(val) \
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_mul_overflow(int a, int b);
extern int _private_add_overflow(int a, int b);
extern int _private_sub_overflow(int a, int b);

View File

@@ -79,6 +79,7 @@ typedef struct {
int LineNo;
unsigned int IfFlags;
int NumIfs;
int IfLinenos[IF_NEST];
long offset;
CachedLine *CLine;
int ownedByMe;
@@ -526,8 +527,14 @@ static int NextChainedFile(IncludeStruct *i)
static int PopFile(void)
{
IncludeStruct *i;
int j;
if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]);
if (!Hush && NumIfs) {
Eprint("%s", ErrMsg[E_MISS_ENDIF]);
for (j=NumIfs-1; j >=0; j--) {
fprintf(ErrFp, "%s(%d): IF without ENDIF\n", FileName, IfLinenos[j]);
}
}
if (!IStackPtr) return E_EOF;
i = &IStack[IStackPtr-1];
@@ -547,6 +554,7 @@ static int PopFile(void)
LineNo = i->LineNo;
IfFlags = i->IfFlags;
memcpy(IfLinenos, i->IfLinenos, IF_NEST);
NumIfs = i->NumIfs;
CLine = i->CLine;
fp = NULL;
@@ -871,6 +879,7 @@ static int IncludeCmd(char const *cmd)
i->LineNo = LineNo;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
i->CLine = CLine;
i->offset = -1L;
i->chain = NULL;
@@ -973,6 +982,7 @@ int IncludeFile(char const *fname)
i->LineNo = LineNo;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
i->CLine = CLine;
i->offset = -1L;
i->chain = NULL;

View File

@@ -54,12 +54,16 @@
#include "globals.h"
#include "protos.h"
#include "err.h"
#include "expr.h"
/* Defines that used to be static variables */
#define Nargs (info->nargs)
#define RetVal (info->retval)
#define DBG(x) do { if (DebugFlag & DB_PRTEXPR) { x; } } while(0)
/* Debugging helpers for "choose()", "iif(), etc. */
#define PUT(x) DBufPuts(&DebugBuf, x)
#define OUT() do { fprintf(ErrFp, "%s\n", DBufValue(&DebugBuf)); DBufFree(&DebugBuf); } while(0)
static int
solstice_equinox_for_year(int y, int which);
@@ -74,7 +78,7 @@ static int FArgs (func_info *);
static int FAsc (func_info *);
static int FBaseyr (func_info *);
static int FChar (func_info *);
static int FChoose (func_info *);
static int FChoose (expr_node *, Value *, Value *, int *);
static int FCoerce (func_info *);
static int FColumns (func_info *);
static int FCurrent (func_info *);
@@ -101,9 +105,9 @@ static int FHebyear (func_info *);
static int FHour (func_info *);
static int FHtmlEscape (func_info *);
static int FHtmlStriptags (func_info *);
static int FIif (func_info *);
static int FIif (expr_node *, Value *, Value *, int *);
static int FIndex (func_info *);
static int FIsAny (func_info *);
static int FIsAny (expr_node *, Value *, Value *, int *);
static int FIsdst (func_info *);
static int FIsleap (func_info *);
static int FIsomitted (func_info *);
@@ -180,14 +184,9 @@ static int FWkday (func_info *);
static int FWkdaynum (func_info *);
static int FYear (func_info *);
static int CleanUpAfterFunc (func_info *);
static int CheckArgs (BuiltinFunc *f, int nargs);
static int SunStuff (int rise, double cosz, int dse);
static int tz_set_tz (char const *tz);
/* "Overload" the struct Operator definition */
#define NO_MAX 127
/* Caches for extracting months, days, years from dates - may
improve performance slightly. */
static int CacheDse = -1;
@@ -196,14 +195,9 @@ static int CacheYear, CacheMon, CacheDay;
static int CacheHebDse = -1;
static int CacheHebYear, CacheHebMon, CacheHebDay;
/* We need access to the value stack */
extern Value ValStack[];
extern int ValStackPtr;
extern int ValStackHiWater;
/* Macro for accessing arguments from the value stack - args are numbered
from 0 to (Nargs - 1) */
#define ARG(x) (ValStack[ValStackPtr - Nargs + (x)])
#define ARG(x) (info->args[x])
#define ARGV(x) ARG(x).v.val
#define ARGSTR(x) ARG(x).v.str
@@ -231,215 +225,127 @@ extern int ValStackHiWater;
/* The array holding the built-in functions. */
BuiltinFunc Func[] = {
/* Name minargs maxargs is_constant func */
/* Name minargs maxargs is_constant func newfunc*/
{ "abs", 1, 1, 1, FAbs },
{ "access", 2, 2, 0, FAccess },
{ "adawn", 0, 1, 0, FADawn},
{ "adusk", 0, 1, 0, FADusk},
{ "ampm", 1, 3, 1, FAmpm },
{ "ansicolor", 1, 5, 1, FAnsicolor },
{ "args", 1, 1, 0, FArgs },
{ "asc", 1, 1, 1, FAsc },
{ "baseyr", 0, 0, 1, FBaseyr },
{ "char", 1, NO_MAX, 1, FChar },
{ "choose", 2, NO_MAX, 1, FChoose },
{ "coerce", 2, 2, 1, FCoerce },
{ "columns", 0, 1, 0, FColumns },
{ "current", 0, 0, 0, FCurrent },
{ "date", 3, 3, 1, FDate },
{ "datepart", 1, 1, 1, FDatepart },
{ "datetime", 2, 5, 1, FDateTime },
{ "dawn", 0, 1, 0, FDawn},
{ "day", 1, 1, 1, FDay },
{ "daysinmon", 2, 2, 1, FDaysinmon },
{ "defined", 1, 1, 0, FDefined },
{ "dosubst", 1, 3, 0, FDosubst },
{ "dusk", 0, 1, 0, FDusk },
{ "easterdate", 0, 1, 0, FEasterdate },
{ "evaltrig", 1, 2, 0, FEvalTrig },
{ "filedate", 1, 1, 0, FFiledate },
{ "filedatetime", 1, 1, 0, FFiledatetime },
{ "filedir", 0, 0, 0, FFiledir },
{ "filename", 0, 0, 0, FFilename },
{ "getenv", 1, 1, 0, FGetenv },
{ "hebdate", 2, 5, 0, FHebdate },
{ "hebday", 1, 1, 0, FHebday },
{ "hebmon", 1, 1, 0, FHebmon },
{ "hebyear", 1, 1, 0, FHebyear },
{ "hour", 1, 1, 1, FHour },
{ "htmlescape", 1, 1, 1, FHtmlEscape },
{ "htmlstriptags",1, 1, 1, FHtmlStriptags },
{ "iif", 1, NO_MAX, 1, FIif },
{ "index", 2, 3, 1, FIndex },
{ "isany", 1, NO_MAX, 1, FIsAny },
{ "isdst", 0, 2, 0, FIsdst },
{ "isleap", 1, 1, 1, FIsleap },
{ "isomitted", 1, 1, 0, FIsomitted },
{ "language", 0, 0, 1, FLanguage },
{ "localtoutc", 1, 1, 1, FLocalToUTC },
{ "lower", 1, 1, 1, FLower },
{ "max", 1, NO_MAX, 1, FMax },
{ "min", 1, NO_MAX, 1, FMin },
{ "minsfromutc", 0, 2, 0, FMinsfromutc },
{ "minute", 1, 1, 1, FMinute },
{ "mon", 1, 1, 1, FMon },
{ "monnum", 1, 1, 1, FMonnum },
{ "moondate", 1, 3, 0, FMoondate },
{ "moondatetime", 1, 3, 0, FMoondatetime },
{ "moonphase", 0, 2, 0, FMoonphase },
{ "moontime", 1, 3, 0, FMoontime },
{ "multitrig", 1, NO_MAX, 0, FMultiTrig },
{ "ndawn", 0, 1, 0, FNDawn},
{ "ndusk", 0, 1, 0, FNDusk},
{ "nonomitted", 2, NO_MAX, 0, FNonomitted },
{ "now", 0, 0, 0, FNow },
{ "ord", 1, 1, 1, FOrd },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster },
{ "ostype", 0, 0, 1, FOstype },
{ "pad", 3, 4, 1, FPad },
{ "plural", 1, 3, 1, FPlural },
{ "psmoon", 1, 4, 1, FPsmoon},
{ "psshade", 1, 3, 1, FPsshade},
{ "realcurrent", 0, 0, 0, FRealCurrent},
{ "realnow", 0, 0, 0, FRealnow},
{ "realtoday", 0, 0, 0, FRealtoday },
{ "rows", 0, 0, 0, FRows },
{ "sgn", 1, 1, 1, FSgn },
{ "shell", 1, 2, 0, FShell },
{ "shellescape", 1, 1, 1, FShellescape },
{ "slide", 2, NO_MAX, 0, FSlide },
{ "soleq", 1, 2, 0, FSoleq },
{ "stdout", 0, 0, 1, FStdout },
{ "strlen", 1, 1, 1, FStrlen },
{ "substr", 2, 3, 1, FSubstr },
{ "sunrise", 0, 1, 0, FSunrise},
{ "sunset", 0, 1, 0, FSunset },
{ "time", 2, 2, 1, FTime },
{ "timepart", 1, 1, 1, FTimepart },
{ "timezone", 0, 1, 1, FTimezone },
{ "today", 0, 0, 0, FToday },
{ "trig", 0, NO_MAX, 0, FTrig },
{ "trigback", 0, 0, 0, FTrigback },
{ "trigdate", 0, 0, 0, FTrigdate },
{ "trigdatetime", 0, 0, 0, FTrigdatetime },
{ "trigdelta", 0, 0, 0, FTrigdelta },
{ "trigduration", 0, 0, 0, FTrigduration },
{ "trigeventduration", 0, 0, 0, FTrigeventduration },
{ "trigeventstart", 0, 0, 0, FTrigeventstart },
{ "trigfrom", 0, 0, 0, FTrigfrom },
{ "trigger", 1, 3, 0, FTrigger },
{ "trigpriority", 0, 0, 0, FTrigpriority },
{ "trigrep", 0, 0, 0, FTrigrep },
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom },
{ "trigtags", 0, 0, 0, FTrigtags },
{ "trigtime", 0, 0, 0, FTrigtime },
{ "trigtimedelta",0, 0, 0, FTrigtimedelta },
{ "trigtimerep", 0, 0, 0, FTrigtimerep },
{ "triguntil", 0, 0, 0, FTriguntil },
{ "trigvalid", 0, 0, 0, FTrigvalid },
{ "typeof", 1, 1, 1, FTypeof },
{ "tzconvert", 2, 3, 0, FTzconvert },
{ "upper", 1, 1, 1, FUpper },
{ "utctolocal", 1, 1, 1, FUTCToLocal },
{ "value", 1, 2, 0, FValue },
{ "version", 0, 0, 1, FVersion },
{ "weekno", 0, 3, 1, FWeekno },
{ "wkday", 1, 1, 1, FWkday },
{ "wkdaynum", 1, 1, 1, FWkdaynum },
{ "year", 1, 1, 1, FYear }
{ "abs", 1, 1, 1, FAbs, NULL },
{ "access", 2, 2, 0, FAccess, NULL },
{ "adawn", 0, 1, 0, FADawn, NULL},
{ "adusk", 0, 1, 0, FADusk, NULL},
{ "ampm", 1, 3, 1, FAmpm, NULL },
{ "ansicolor", 1, 5, 1, FAnsicolor, NULL },
{ "args", 1, 1, 0, FArgs, NULL },
{ "asc", 1, 1, 1, FAsc, NULL },
{ "baseyr", 0, 0, 1, FBaseyr, NULL },
{ "char", 1, NO_MAX, 1, FChar, NULL },
{ "choose", 2, NO_MAX, 1, NULL, FChoose }, /*NEW-STYLE*/
{ "coerce", 2, 2, 1, FCoerce, NULL },
{ "columns", 0, 1, 0, FColumns, NULL },
{ "current", 0, 0, 0, FCurrent, NULL },
{ "date", 3, 3, 1, FDate, NULL },
{ "datepart", 1, 1, 1, FDatepart, NULL },
{ "datetime", 2, 5, 1, FDateTime, NULL },
{ "dawn", 0, 1, 0, FDawn, NULL },
{ "day", 1, 1, 1, FDay, NULL },
{ "daysinmon", 2, 2, 1, FDaysinmon, NULL },
{ "defined", 1, 1, 0, FDefined, NULL },
{ "dosubst", 1, 3, 0, FDosubst, NULL },
{ "dusk", 0, 1, 0, FDusk, NULL },
{ "easterdate", 0, 1, 0, FEasterdate, NULL },
{ "evaltrig", 1, 2, 0, FEvalTrig, NULL },
{ "filedate", 1, 1, 0, FFiledate, NULL },
{ "filedatetime", 1, 1, 0, FFiledatetime, NULL },
{ "filedir", 0, 0, 0, FFiledir, NULL },
{ "filename", 0, 0, 0, FFilename, NULL },
{ "getenv", 1, 1, 0, FGetenv, NULL },
{ "hebdate", 2, 5, 0, FHebdate, NULL },
{ "hebday", 1, 1, 0, FHebday, NULL },
{ "hebmon", 1, 1, 0, FHebmon, NULL },
{ "hebyear", 1, 1, 0, FHebyear, NULL },
{ "hour", 1, 1, 1, FHour, NULL },
{ "htmlescape", 1, 1, 1, FHtmlEscape, NULL },
{ "htmlstriptags",1, 1, 1, FHtmlStriptags, NULL },
{ "iif", 1, NO_MAX, 1, NULL, FIif }, /*NEW-STYLE*/
{ "index", 2, 3, 1, FIndex, NULL },
{ "isany", 1, NO_MAX, 1, NULL, FIsAny }, /*NEW-STYLE*/
{ "isdst", 0, 2, 0, FIsdst, NULL },
{ "isleap", 1, 1, 1, FIsleap, NULL },
{ "isomitted", 1, 1, 0, FIsomitted, NULL },
{ "language", 0, 0, 1, FLanguage, NULL },
{ "localtoutc", 1, 1, 1, FLocalToUTC, NULL },
{ "lower", 1, 1, 1, FLower, NULL },
{ "max", 1, NO_MAX, 1, FMax, NULL },
{ "min", 1, NO_MAX, 1, FMin, NULL },
{ "minsfromutc", 0, 2, 0, FMinsfromutc, NULL },
{ "minute", 1, 1, 1, FMinute, NULL },
{ "mon", 1, 1, 1, FMon, NULL },
{ "monnum", 1, 1, 1, FMonnum, NULL },
{ "moondate", 1, 3, 0, FMoondate, NULL },
{ "moondatetime", 1, 3, 0, FMoondatetime, NULL },
{ "moonphase", 0, 2, 0, FMoonphase, NULL },
{ "moontime", 1, 3, 0, FMoontime, NULL },
{ "multitrig", 1, NO_MAX, 0, FMultiTrig, NULL },
{ "ndawn", 0, 1, 0, FNDawn, NULL },
{ "ndusk", 0, 1, 0, FNDusk, NULL },
{ "nonomitted", 2, NO_MAX, 0, FNonomitted, NULL },
{ "now", 0, 0, 0, FNow, NULL },
{ "ord", 1, 1, 1, FOrd, NULL },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster, NULL },
{ "ostype", 0, 0, 1, FOstype, NULL },
{ "pad", 3, 4, 1, FPad, NULL },
{ "plural", 1, 3, 1, FPlural, NULL },
{ "psmoon", 1, 4, 1, FPsmoon, NULL },
{ "psshade", 1, 3, 1, FPsshade, NULL },
{ "realcurrent", 0, 0, 0, FRealCurrent, NULL },
{ "realnow", 0, 0, 0, FRealnow, NULL },
{ "realtoday", 0, 0, 0, FRealtoday, NULL },
{ "rows", 0, 0, 0, FRows, NULL },
{ "sgn", 1, 1, 1, FSgn, NULL },
{ "shell", 1, 2, 0, FShell, NULL },
{ "shellescape", 1, 1, 1, FShellescape, NULL },
{ "slide", 2, NO_MAX, 0, FSlide, NULL },
{ "soleq", 1, 2, 0, FSoleq, NULL },
{ "stdout", 0, 0, 1, FStdout, NULL },
{ "strlen", 1, 1, 1, FStrlen, NULL },
{ "substr", 2, 3, 1, FSubstr, NULL },
{ "sunrise", 0, 1, 0, FSunrise, NULL },
{ "sunset", 0, 1, 0, FSunset, NULL },
{ "time", 2, 2, 1, FTime, NULL },
{ "timepart", 1, 1, 1, FTimepart, NULL },
{ "timezone", 0, 1, 1, FTimezone, NULL },
{ "today", 0, 0, 0, FToday, NULL },
{ "trig", 0, NO_MAX, 0, FTrig, NULL },
{ "trigback", 0, 0, 0, FTrigback, NULL },
{ "trigdate", 0, 0, 0, FTrigdate, NULL },
{ "trigdatetime", 0, 0, 0, FTrigdatetime, NULL },
{ "trigdelta", 0, 0, 0, FTrigdelta, NULL },
{ "trigduration", 0, 0, 0, FTrigduration, NULL },
{ "trigeventduration", 0, 0, 0, FTrigeventduration, NULL },
{ "trigeventstart", 0, 0, 0, FTrigeventstart, NULL },
{ "trigfrom", 0, 0, 0, FTrigfrom, NULL },
{ "trigger", 1, 3, 0, FTrigger, NULL },
{ "trigpriority", 0, 0, 0, FTrigpriority, NULL },
{ "trigrep", 0, 0, 0, FTrigrep, NULL },
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom, NULL },
{ "trigtags", 0, 0, 0, FTrigtags, NULL },
{ "trigtime", 0, 0, 0, FTrigtime, NULL },
{ "trigtimedelta",0, 0, 0, FTrigtimedelta, NULL },
{ "trigtimerep", 0, 0, 0, FTrigtimerep, NULL },
{ "triguntil", 0, 0, 0, FTriguntil, NULL },
{ "trigvalid", 0, 0, 0, FTrigvalid, NULL },
{ "typeof", 1, 1, 1, FTypeof, NULL },
{ "tzconvert", 2, 3, 0, FTzconvert, NULL },
{ "upper", 1, 1, 1, FUpper, NULL },
{ "utctolocal", 1, 1, 1, FUTCToLocal, NULL },
{ "value", 1, 2, 0, FValue, NULL },
{ "version", 0, 0, 1, FVersion, NULL },
{ "weekno", 0, 3, 0, FWeekno, NULL },
{ "wkday", 1, 1, 1, FWkday, NULL },
{ "wkdaynum", 1, 1, 1, FWkdaynum, NULL },
{ "year", 1, 1, 1, FYear, NULL }
};
/* Need a variable here - Func[] array not really visible to outside. */
int NumFuncs = sizeof(Func) / sizeof(Operator) ;
/***************************************************************/
/* */
/* CallFunc */
/* */
/* Call a function given a pointer to it, and the number */
/* of arguments supplied. */
/* */
/***************************************************************/
int CallFunc(BuiltinFunc *f, int nargs)
{
register int r = CheckArgs(f, nargs);
int i;
func_info info_obj;
func_info *info = &info_obj;
Nargs = nargs;
RetVal.type = ERR_TYPE;
if (DebugFlag & DB_PRTEXPR) {
fprintf(ErrFp, "%s(", f->name);
for (i=0; i<nargs; i++) {
PrintValue(&ARG(i), ErrFp);
if (i<nargs-1) fprintf(ErrFp, ", ");
}
fprintf(ErrFp, ") => ");
if (r) {
fprintf(ErrFp, "%s\n", ErrMsg[r]);
return r;
}
}
if (r) {
Eprint("%s(): %s", f->name, ErrMsg[r]);
return r;
}
r = (*(f->func))(info);
if (r) {
DestroyValue(RetVal);
if (DebugFlag & DB_PRTEXPR)
fprintf(ErrFp, "%s\n", ErrMsg[r]);
else
Eprint("%s(): %s", f->name, ErrMsg[r]);
return r;
}
if (DebugFlag & DB_PRTEXPR) {
PrintValue(&RetVal, ErrFp);
fprintf(ErrFp, "\n");
}
r = CleanUpAfterFunc(info);
return r;
}
/***************************************************************/
/* */
/* CheckArgs */
/* */
/* Check that the right number of args have been supplied */
/* for a function. */
/* */
/***************************************************************/
static int CheckArgs(BuiltinFunc *f, int nargs)
{
if (nargs < f->minargs) return E_2FEW_ARGS;
if (nargs > f->maxargs && f->maxargs != NO_MAX) return E_2MANY_ARGS;
return OK;
}
/***************************************************************/
/* */
/* CleanUpAfterFunc */
/* */
/* Clean up the stack after a function call - remove */
/* args and push the new value. */
/* */
/***************************************************************/
static int CleanUpAfterFunc(func_info *info)
{
Value v;
int i;
for (i=0; i<Nargs; i++) {
PopValStack(v);
DestroyValue(v);
}
PushValStack(RetVal);
return OK;
}
int NumFuncs = sizeof(Func) / sizeof(BuiltinFunc) ;
/***************************************************************/
/* */
@@ -473,10 +379,9 @@ static int RetStrVal(char const *s, func_info *info)
/***************************************************************/
static int FStrlen(func_info *info)
{
Value *v = &ARG(0);
if (v->type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
RetVal.type = INT_TYPE;
size_t l = strlen(v->v.str);
size_t l = strlen(ARGSTR(0));
if (l > INT_MAX) return E_2HIGH;
RETVAL = (int) l;
return OK;
@@ -1131,8 +1036,8 @@ static int FOrd(func_info *info)
/* */
/* FPad - Pad a string to min length */
/* */
/* pad("1", "0", 4) --> "0004" */
/* pad("1", "0", 4, 1) --> "4000" */
/* pad("1", "0", 4) --> "0001" */
/* pad("1", "0", 4, 1) --> "1000" */
/* pad("foo", "bar", 7) -> "barbfoo" */
/* */
/***************************************************************/
@@ -1256,25 +1161,66 @@ static int FPlural(func_info *info)
/* otherwise. */
/* */
/***************************************************************/
static int FIsAny(func_info *info)
static int FIsAny(expr_node *node, Value *locals, Value *ans, int *nonconst)
{
int i;
RetVal.type = INT_TYPE;
RETVAL = 0;
for (i=1; i<Nargs; i++) {
if (ARG(0).type == ARG(i).type) {
if (ARG(0).type == STR_TYPE) {
if (!strcmp(ARGSTR(0), ARGSTR(i))) {
RETVAL = 1;
return OK;
}
} else {
if (ARGV(0) == ARGV(i)) {
RETVAL = 1;
return OK;
}
DynamicBuffer DebugBuf;
expr_node *cur;
int r;
Value v;
Value candidate;
ans->type = INT_TYPE;
ans->v.val = 0;
DBG(DBufInit(&DebugBuf));
DBG(PUT("isany("));
cur = node->child;
r = evaluate_expr_node(cur, locals, &v, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(PrintValue(&v, NULL)));
while(cur->sibling) {
cur = cur->sibling;
r = evaluate_expr_node(cur, locals, &candidate, nonconst);
if (r != OK) {
DestroyValue(v);
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(", "));
DBG(PUT(PrintValue(&candidate, NULL)));
if (candidate.type != v.type) {
DestroyValue(candidate);
continue;
}
if (v.type == STR_TYPE) {
if (strcmp(v.v.str, candidate.v.str)) {
DestroyValue(candidate);
continue;
}
} else {
if (v.v.val != candidate.v.val) {
DestroyValue(candidate);
continue;
}
}
DestroyValue(candidate);
ans->v.val = 1;
break;
}
DestroyValue(v);
if (DebugFlag & DB_PRTEXPR) {
while(cur->sibling) {
cur = cur->sibling;
PUT(", ?");
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return OK;
}
@@ -1287,15 +1233,63 @@ static int FIsAny(func_info *info)
/* from 1. */
/* */
/***************************************************************/
static int FChoose(func_info *info)
static int FChoose(expr_node *node, Value *locals, Value *ans, int *nonconst)
{
int v;
DynamicBuffer DebugBuf;
expr_node *cur;
int r;
int n;
int nargs = node->num_kids;
Value v;
DBG(DBufInit(&DebugBuf));
DBG(PUT("choose("));
ASSERT_TYPE(0, INT_TYPE);
v = ARGV(0);
if (v < 1) v = 1;
if (v > Nargs-1) v = Nargs-1;
DCOPYVAL(RetVal, ARG(v));
cur = node->child;
r = evaluate_expr_node(cur, locals, &v, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(PrintValue(&v, NULL)));
if (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;
}
n = v.v.val;
if (n < 1) n = 1;
if (n > nargs-1) n = nargs-1;
while(n--) {
cur = cur->sibling;
DBG(if (n) { PUT(", ?"); });
if (!cur) return E_SWERR; /* Should not happen! */
}
r = evaluate_expr_node(cur, locals, ans, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
if (DebugFlag & DB_PRTEXPR) {
PUT(", ");
PUT(PrintValue(ans, NULL));
cur = cur->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return OK;
}
@@ -1481,7 +1475,7 @@ static int FValue(func_info *info)
ASSERT_TYPE(0, STR_TYPE);
switch(Nargs) {
case 1:
return GetVarValue(ARGSTR(0), &RetVal, NULL, NULL);
return GetVarValue(ARGSTR(0), &RetVal);
case 2:
v = FindVar(ARGSTR(0), 0);
@@ -1953,33 +1947,101 @@ static int FIndex(func_info *info)
/* */
/* FIif */
/* */
/* The IIF function. */
/* The IIF function. Uses new-style evaluation */
/* */
/***************************************************************/
static int FIif(func_info *info)
static int FIif(expr_node *node, Value *locals, Value *ans, int *nonconst)
{
int istrue;
int arg;
int r;
int done;
Value v;
expr_node *cur;
DynamicBuffer DebugBuf;
if (!(Nargs % 2)) return E_IIF_ODD;
DBG(DBufInit(&DebugBuf));
DBG(PUT("iif("));
cur = node->child;
for (arg=0; arg<Nargs-1; arg += 2) {
if (ARG(arg).type != STR_TYPE && ARG(arg).type != INT_TYPE)
return E_BAD_TYPE;
if (ARG(arg).type == INT_TYPE)
istrue = ARG(arg).v.val;
else
istrue = *(ARG(arg).v.str);
if (istrue) {
DCOPYVAL(RetVal, ARG(arg+1));
return OK;
}
if (!(node->num_kids % 2)) {
if (DebugFlag & DB_PRTEXPR) {
r = 0;
while(cur) {
if (r) PUT(", ");
r=1;
PUT("?");
cur = cur->sibling;
}
PUT(") => ");
PUT(ErrMsg[E_IIF_ODD]);
OUT();
}
return E_IIF_ODD;
}
DCOPYVAL(RetVal, ARG(Nargs-1));
return OK;
done = 0;
while(cur->sibling) {
r = evaluate_expr_node(cur, locals, &v, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
if (DebugFlag & DB_PRTEXPR) {
if (done) PUT(", ");
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) {
r = evaluate_expr_node(cur->sibling, locals, ans, nonconst);
if (r == OK && (DebugFlag & DB_PRTEXPR)) {
PUT(", ");
PUT(PrintValue(ans, NULL));
cur = cur->sibling->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(", ?"));
cur = cur->sibling->sibling;
}
/* Return the last arg */
r = evaluate_expr_node(cur, locals, ans, nonconst);
if (DebugFlag & DB_PRTEXPR) {
if (done) PUT(", ");
PUT(PrintValue(ans, NULL));
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return r;
}
/***************************************************************/
@@ -2101,6 +2163,7 @@ static int FArgs(func_info *info)
{
ASSERT_TYPE(0, STR_TYPE);
RetVal.type = INT_TYPE;
strtolower(ARGSTR(0));
RETVAL = UserFuncExists(ARGSTR(0));
return OK;
}
@@ -2373,14 +2436,18 @@ static int FEasterdate(func_info *info)
{
int y, m, d;
int g, c, x, z, e, n;
int base;
if (Nargs == 0) {
base = DSEToday;
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
base = -1;
y = ARGV(0);
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
base = DATEPART(ARG(0));
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
@@ -2406,7 +2473,7 @@ static int FEasterdate(func_info *info)
RetVal.type = DATE_TYPE;
RETVAL = DSE(y, m, d);
y++; } while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
y++; } while (base > -1 && RETVAL < base);
return OK;
}
@@ -2422,7 +2489,9 @@ static int FOrthodoxeaster(func_info *info)
{
int y, m, d;
int a, b, c, dd, e, f, dse;
int base = -1;
if (Nargs == 0) {
base = DSEToday;
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
@@ -2430,6 +2499,7 @@ static int FOrthodoxeaster(func_info *info)
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
base = DATEPART(ARG(0));
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
@@ -2449,7 +2519,7 @@ static int FOrthodoxeaster(func_info *info)
RetVal.type = DATE_TYPE;
RETVAL = dse;
y++;
} while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
} while (base > -1 && RETVAL < base);
return OK;
}
@@ -3896,3 +3966,38 @@ FSoleq(func_info *info)
RETVAL = ret;
return OK;
}
/* Compare two strings case-insensitively, where we KNOW
that the second string is definitely lower-case */
static int strcmp_lcfirst(char const *s1, char const *s2)
{
int r;
while (*s1 && *s2) {
r = tolower(*s1) - *s2;
if (r) return r;
s1++;
s2++;
}
return tolower(*s1) - *s2;
}
/***************************************************************/
/* */
/* FindBuiltinFunc */
/* */
/* Find a built-in function. */
/* */
/***************************************************************/
BuiltinFunc *FindBuiltinFunc(char const *name)
{
int top=NumFuncs-1, bot=0;
int mid, r;
while (top >= bot) {
mid = (top + bot) / 2;
r = strcmp_lcfirst(name, Func[mid].name);
if (!r) return &Func[mid];
else if (r > 0) bot = mid+1;
else top = mid-1;
}
return NULL;
}

View File

@@ -23,6 +23,7 @@
#define INIT(var, val) var
#endif
#include <signal.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
@@ -73,6 +74,9 @@ EXTERN INIT( int InfiniteDelta, 0);
EXTERN INIT( int DefaultTDelta, 0);
EXTERN INIT( int DeltaOverride, 0);
EXTERN INIT( int RunDisabled, 0);
EXTERN INIT( int ExpressionEvaluationDisabled, 0);
EXTERN INIT( int ExpressionEvaluationTimeLimit, 0);
EXTERN INIT( volatile sig_atomic_t ExpressionTimeLimitExceeded, 0);
EXTERN INIT( int IgnoreOnce, 0);
EXTERN INIT( int SortByTime, 0);
EXTERN INIT( int SortByDate, 0);
@@ -110,6 +114,7 @@ EXTERN INIT( int PurgeIncludeDepth, 0);
EXTERN INIT( FILE *PurgeFP, NULL);
EXTERN INIT( int NumIfs, 0);
EXTERN INIT( unsigned int IfFlags, 0);
EXTERN INIT( int IfLinenos[IF_NEST], {0});
EXTERN INIT( int LastTrigValid, 0);
EXTERN Trigger LastTrigger;
EXTERN TimeTrig LastTimeTrig;
@@ -159,6 +164,9 @@ EXTERN DynamicBuffer Banner;
EXTERN DynamicBuffer LineBuffer;
EXTERN DynamicBuffer ExprBuf;
/* User-func recursion level */
EXTERN INIT( unsigned int FuncRecursionLevel, 0);
extern int NumFullOmits, NumPartialOmits;
/* List of months */

View File

@@ -19,7 +19,7 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -38,7 +38,6 @@
#include "types.h"
#include "globals.h"
#include "protos.h"
#include "expr.h"
#include "err.h"
static int should_guess_terminal_background = 1;
@@ -605,7 +604,7 @@ void InitRemind(int argc, char const *argv[])
case 'D':
while (*arg) {
switch(*arg++) {
case 's': case 'S': DebugFlag |= DB_EXPR_STACKS; break;
case 's': case 'S': DebugFlag |= DB_PARSE_EXPR; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
@@ -926,7 +925,12 @@ static void InitializeVar(char const *str)
r = 0;
while (*str && *str != '=') {
if (r < VAR_NAME_LEN) {
varname[r++] = *str;
if (isalpha(*str) || *str == '_' || (r > 0 && *str == '(') || (r == 0 && *str == '$') || (r > 0 && isdigit(*str))) {
varname[r++] = *str;
} else {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_ILLEGAL_CHAR]);
return;
}
}
if (*str == '(') {
/* Do a function definition if we see a paren */
@@ -936,10 +940,28 @@ static void InitializeVar(char const *str)
str++;
}
varname[r] = 0;
if (!*str) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]);
if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return;
}
if (!*str) {
/* Setting a system var does require =expr on the commandline */
if (*varname == '$') {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]);
return;
}
val.type = INT_TYPE;
val.v.val = 0;
r = SetVar(varname, &val);
if (!r) {
r = PreserveVar(varname);
}
if (r) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]);
}
return;
}
if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return;
@@ -992,13 +1014,65 @@ AddTrustedUser(char const *username)
NumTrustedUsers++;
}
static pid_t LimiterPid = (pid_t) -1;
void unlimit_execution_time(void)
{
if (LimiterPid != (pid_t) -1) {
kill(LimiterPid, SIGTERM);
LimiterPid = (pid_t) -1;
}
}
static void limit_execution_time(int t)
{
pid_t parent = getpid();
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid > 0) {
LimiterPid = pid;
/* In the parent */
return;
}
/* In the child */
time_t start = time(NULL);
while(1) {
sleep(1);
if (kill(parent, 0) < 0) {
/* Parent has probably exited */
exit(0);
}
if (time(NULL) - start > t) {
kill(parent, SIGXCPU);
exit(0);
}
}
}
static void
ProcessLongOption(char const *arg)
{
int t;
if (!strcmp(arg, "version")) {
printf("%s\n", VERSION);
exit(EXIT_SUCCESS);
}
if (sscanf(arg, "max-execution-time=%d", &t) == 1) {
if (t < 0) {
fprintf(ErrFp, "%s: --max-execution-time must be non-negative\n", ArgV[0]);
return;
}
if (t > 0) {
limit_execution_time(t);
}
return;
}
fprintf(ErrFp, "%s: Unknown long option --%s\n", ArgV[0], arg);
}

View File

@@ -145,7 +145,7 @@ EXTERN char *ErrMsg[] =
"Ok",
"Puuttuva ']'",
"Puuttuva lainausmerkki",
"Liian monimutkainen lauseke - liikaa operaattoreita",
"Liian monimutkainen lauseke",
"Liian monimutkainen lauseke - liikaa operandeja",
"Puuttuva ')'",
"Määrittelemätön funktio",
@@ -246,7 +246,10 @@ EXTERN char *ErrMsg[] =
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Odotettu viikonpäivän nimi"
"Odotettu viikonpäivän nimi",
"Päällekkäinen argumentin nimi",
"Lausekkeiden arviointi on poistettu käytöstä",
};
#endif /* MK_GLOBALS */

View File

@@ -119,7 +119,7 @@ EXTERN char *ErrMsg[] =
"Ok",
"']' manquant",
"Apostrophe manquant",
"Expression trop complexe - trop d'opérateurs",
"Expression trop complexe",
"Expression trop complexe - trop d'opérandes",
"')' manquante",
"Fonction non-définie",
@@ -221,6 +221,9 @@ EXTERN char *ErrMsg[] =
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Nom du jour de la semaine attendu",
"Nom de l'argument en double",
"L'évaluation de l'expression est désactivée",
};
#endif /* MK_GLOBALS */

View File

@@ -135,7 +135,7 @@ EXTERN char *ErrMsg[] =
"OK",
"Brakujący ']'",
"Brakujący nawias",
"Zbyt skomplikowane wyrażenie - za dużo operatorów",
"Zbyt skomplikowane wyrażenie",
"Zbyt skomplikowane wyrażenie - za dużo argumentów",
"Brakujący ')'",
"Nie zdefiniowana funkcja",
@@ -236,7 +236,9 @@ EXTERN char *ErrMsg[] =
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Oczekiwana nazwa dnia tygodnia"
"Oczekiwana nazwa dnia tygodnia",
"Zduplikowana nazwa argumentu",
"Ocena wyrażeń jest wyłączona",
};
#endif /* MK_GLOBALS */

View File

@@ -144,7 +144,7 @@ EXTERN char *ErrMsg[] =
"Ok",
"Falta um ']'",
"Falta uma aspa",
"Expressao muito complexa - muitos operadores",
"Expressao muito complexa",
"Expressao muito complexa - muitos operandos",
"Falta um ')'",
"Funcao nao definida",
@@ -246,6 +246,8 @@ EXTERN char *ErrMsg[] =
"Time specified twice",
"Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana",
"Nome de argumento duplicado",
"A avaliação da expressão está desabilitada",
};
#endif /* MK_GLOBALS */

View File

@@ -46,7 +46,6 @@
#include "types.h"
#include "protos.h"
#include "expr.h"
#include "globals.h"
#include "err.h"
@@ -55,6 +54,38 @@ static void DoReminders(void);
/* Macro for simplifying common block so as not to litter code */
#define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0)
void
exitfunc(void)
{
/* Kill any execution-time-limiter process */
unlimit_execution_time();
if (DebugFlag & DB_PARSE_EXPR) {
fflush(stdout);
fflush(stderr);
UnsetAllUserFuncs();
print_expr_nodes_stats();
}
}
static void sigalrm(int sig)
{
UNUSED(sig);
if (ExpressionEvaluationTimeLimit) {
ExpressionTimeLimitExceeded = 1;
}
}
static void sigxcpu(int sig)
{
UNUSED(sig);
int r = write(STDERR_FILENO, "\n\nmax-execution-time exceeded.\n\n", 32);
/* Pretend to use r to avoid compiler warning */
_exit(1 + (r-r));
}
/***************************************************************/
/***************************************************************/
/** **/
@@ -66,6 +97,8 @@ int main(int argc, char *argv[])
{
int pid;
struct sigaction act;
#ifdef HAVE_SETLOCALE
setlocale(LC_ALL, "");
#endif
@@ -78,10 +111,29 @@ int main(int argc, char *argv[])
ArgV = (char const **) argv;
InitRemind(argc, (char const **) argv);
act.sa_handler = sigalrm;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
if (sigaction(SIGALRM, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno));
exit(1);
}
act.sa_handler = sigxcpu;
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
if (sigaction(SIGXCPU, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno));
exit(1);
}
DBufInit(&(LastTrigger.tags));
ClearLastTriggers();
atexit(DebugExitFunc);
atexit(exitfunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar();
@@ -106,7 +158,7 @@ int main(int argc, char *argv[])
}
if (!Hush) {
if (DestroyOmitContexts())
if (DestroyOmitContexts(1))
Eprint("%s", ErrMsg[E_PUSH_NOPOP]);
if (!Daemon && !NextMode && !NumTriggered && !NumQueued) {
printf("%s\n", ErrMsg[E_NOREMINDERS]);
@@ -159,7 +211,7 @@ void
PerIterationInit(void)
{
ClearGlobalOmits();
DestroyOmitContexts();
DestroyOmitContexts(1);
DestroyVars(0);
DefaultColorR = -1;
DefaultColorG = -1;
@@ -286,9 +338,11 @@ static void DoReminders(void)
case T_Pop: r=PopOmitContext(&p); break;
case T_Preserve: r=DoPreserve(&p); break;
case T_Push: r=PushOmitContext(&p); break;
case T_Expr: r = DoExpr(&p); break;
case T_RemType: if (tok.val == RUN_TYPE) {
r=DoRun(&p);
} else {
DestroyParser(&p);
CreateParser(CurLine, &p);
r=DoRem(&p);
purge_handled = 1;
@@ -297,10 +351,8 @@ static void DoReminders(void)
/* If we don't recognize the command, do a REM by default */
/* Note: Since the parser hasn't been used yet, we don't */
/* need to destroy it here. */
default: CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
default: DestroyParser(&p); CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
}
if (r && (!Hush || r != E_RUN_DISABLED)) {
@@ -458,6 +510,16 @@ int ParseChar(ParsePtr p, int *err, int peek)
return *(p->pos++);
}
}
/* Convert [[ to just a literal [ */
if (*p->pos == BEG_OF_EXPR && *(p->pos+1) == BEG_OF_EXPR) {
if (peek) {
return *(p->pos+1);
} else {
p->pos++;
return *(p->pos++);
}
}
p->expr_happened = 1;
p->pos++;
r = EvalExpr(&(p->pos), &val, p);
@@ -466,8 +528,15 @@ int ParseChar(ParsePtr p, int *err, int peek)
DestroyParser(p);
return 0;
}
while(*p->pos && (isempty(*p->pos))) {
p->pos++;
}
if (*p->pos != END_OF_EXPR) {
*err = E_MISS_END;
if (*p->pos) {
*err = E_PARSE_ERR;
} else {
*err = E_MISS_END;
}
DestroyParser(p);
DestroyValue(val);
return 0;
@@ -582,6 +651,57 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
}
}
/***************************************************************/
/* */
/* ParseExpr */
/* */
/* We are expecting an expression here. Parse it and return */
/* the value node tree. */
/* */
/***************************************************************/
expr_node * ParseExpr(ParsePtr p, int *r)
{
int bracketed = 0;
expr_node *node;
if (p->isnested) {
*r = E_PARSE_ERR; /* Can't nest expressions */
return NULL;
}
if (!p->pos) {
*r = E_PARSE_ERR; /* Missing expression */
return NULL;
}
while (isempty(*p->pos)) (p->pos)++;
if (!*(p->pos)) {
*r = E_EOLN;
return NULL;
}
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
}
node = parse_expression(&(p->pos), r, NULL);
if (*r) {
return free_expr_tree(node);
}
if (bracketed) {
if (*p->pos != END_OF_EXPR) {
if (*p->pos) {
*r = E_PARSE_ERR;
} else {
*r = E_MISS_END;
}
return free_expr_tree(node);
}
(p->pos)++;
}
return node;
}
/***************************************************************/
/* */
/* EvaluateExpr */
@@ -593,21 +713,22 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
int EvaluateExpr(ParsePtr p, Value *v)
{
int bracketed = 0;
int r;
int nonconst = 0;
expr_node *node = ParseExpr(p, &r);
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
while (isempty(*p->pos)) (p->pos)++;
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
if (r != OK) {
return r;
}
r = EvalExpr(&(p->pos), v, p);
if (!node) {
return E_SWERR;
}
r = evaluate_expression(node, NULL, v, &nonconst);
free_expr_tree(node);
if (r) return r;
if (bracketed) {
if (*p->pos != END_OF_EXPR) return E_MISS_END;
(p->pos)++;
if (nonconst) {
p->nonconst_expr = 1;
}
return OK;
}
@@ -643,36 +764,36 @@ void Wprint(char const *fmt, ...)
void Eprint(char const *fmt, ...)
{
va_list argptr;
char const *fname;
/* Check if more than one error msg. from this line */
if (!FreshLine && !ShowAllErrors) return;
if (FreshLine && FileName) {
FreshLine = 0;
if (strcmp(FileName, "-")) {
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
}
} else {
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
}
}
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
} else if (FileName) {
fprintf(ErrFp, " ");
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
}
if (!FileName) {
return;
}
if (strcmp(FileName, "-")) {
fname = FileName;
} else {
fname = "-stdin-";
}
if (FreshLine) {
(void) fprintf(ErrFp, "%s(%d): ", fname, LineNo);
} else {
fprintf(ErrFp, " ");
}
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
return;
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, "\n");
}
if (FreshLine) {
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
}
FreshLine = 0;
}
/***************************************************************/
@@ -841,6 +962,7 @@ int DoIf(ParsePtr p)
}
}
IfLinenos[NumIfs] = LineNo;
NumIfs++;
IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
IfFlags |= syndrome << (2 * NumIfs - 2);
@@ -1025,8 +1147,8 @@ int DoDebug(ParsePtr p)
case 's':
case 'S':
if (val) DebugFlag |= DB_EXPR_STACKS;
else DebugFlag &= ~DB_EXPR_STACKS;
if (val) DebugFlag |= DB_PARSE_EXPR;
else DebugFlag &= ~DB_PARSE_EXPR;
break;
case 'x':
@@ -1094,7 +1216,7 @@ int DoBanner(ParsePtr p)
}
}
DBufFree(&Banner);
err = DBufPuts(&Banner, DBufValue(&buf));
DBufFree(&buf);
return err;
@@ -1106,7 +1228,6 @@ int DoBanner(ParsePtr p)
/* */
/* Enable or disable the RUN command under program control */
/* */
/* */
/***************************************************************/
int DoRun(ParsePtr p)
{
@@ -1133,6 +1254,38 @@ int DoRun(ParsePtr p)
return VerifyEoln(p);
}
/***************************************************************/
/* */
/* DoExpr */
/* */
/* Enable or disable expression evaluation */
/* */
/***************************************************************/
int DoExpr(ParsePtr p)
{
int r;
DynamicBuffer buf;
DBufInit(&buf);
if ( (r=ParseToken(p, &buf)) ) return r;
/* Only allow EXPR ON in top-level script */
if (! StrCmpi(DBufValue(&buf), "ON")) {
if (TopLevel()) ExpressionEvaluationDisabled = 0;
}
/* But allow EXPR OFF anywhere */
else if (! StrCmpi(DBufValue(&buf), "OFF"))
ExpressionEvaluationDisabled = 1;
else {
DBufFree(&buf);
return E_PARSE_ERR;
}
DBufFree(&buf);
return VerifyEoln(p);
}
/***************************************************************/
/* */
/* DoFlush */

View File

@@ -66,7 +66,6 @@
#include <time.h>
#include "types.h"
#include "protos.h"
#include "expr.h"
#include "globals.h"
#include "err.h"

View File

@@ -14,13 +14,12 @@
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "types.h"
#include "protos.h"
#include "globals.h"
#include "err.h"
#include "expr.h"
static int BexistsIntArray (int const array[], int num, int key);
static void InsertIntoSortedArray (int *array, int num, int key);
@@ -37,6 +36,8 @@ int NumFullOmits, NumPartialOmits;
/* The structure for saving and restoring OMIT contexts */
typedef struct omitcontext {
struct omitcontext *next;
char *filename;
int lineno;
int numfull, numpart;
int *fullsave;
int *partsave;
@@ -79,19 +80,25 @@ int DoClear(ParsePtr p)
/* */
/* Free all the memory used by saved OMIT contexts. */
/* As a side effect, return the number of OMIT contexts */
/* destroyed. */
/* destroyed. If print_unmatched is true, print an error for */
/* each undestroyed OMIT contect */
/* */
/***************************************************************/
int DestroyOmitContexts(void)
int DestroyOmitContexts(int print_unmatched)
{
OmitContext *c = SavedOmitContexts;
OmitContext *d;
int num = 0;
while (c) {
if (print_unmatched) {
Wprint("Unmatched PUSH-OMIT-CONTEXT at %s(%d)",
c->filename, c->lineno);
}
num++;
if (c->fullsave) free(c->fullsave);
if (c->partsave) free(c->partsave);
if (c->filename) free(c->filename);
d = c->next;
free(c);
c = d;
@@ -116,16 +123,28 @@ int PushOmitContext(ParsePtr p)
context = NEW(OmitContext);
if (!context) return E_NO_MEM;
if (FileName) {
context->filename = StrDup(FileName);
} else {
context->filename = StrDup("");
}
if (!context->filename) {
free(context);
return E_NO_MEM;
}
context->lineno = LineNo;
context->numfull = NumFullOmits;
context->numpart = NumPartialOmits;
context->weekdaysave = WeekdayOmits;
context->fullsave = malloc(NumFullOmits * sizeof(int));
if (NumFullOmits && !context->fullsave) {
free(context->filename);
free(context);
return E_NO_MEM;
}
context->partsave = malloc(NumPartialOmits * sizeof(int));
if (NumPartialOmits && !context->partsave) {
free(context->filename);
free(context->fullsave);
free(context);
return E_NO_MEM;
@@ -175,6 +194,7 @@ int PopOmitContext(ParsePtr p)
/* Free memory used by the saved context */
if (c->partsave) free(c->partsave);
if (c->fullsave) free(c->fullsave);
if (c->filename) free(c->filename);
free(c);
return VerifyEoln(p);

View File

@@ -43,6 +43,7 @@
int CallUserFunc (char const *name, int nargs, ParsePtr p);
int DoFset (ParsePtr p);
int DoFunset (ParsePtr p);
void UnsetAllUserFuncs(void);
void ProduceCalendar (void);
char const *SimpleTime (int tim);
char const *CalendarTime (int tim, int duration);
@@ -56,9 +57,16 @@ int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim);
int ParseLiteralDate (char const **s, int *dse, int *tim);
int ParseLiteralTime (char const **s, int *tim);
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);
void print_expr_tree(expr_node *node, FILE *fp);
void unlimit_execution_time(void);
expr_node *free_expr_tree(expr_node *node);
int EvalExpr (char const **e, Value *v, ParsePtr p);
int DoCoerce (char type, Value *v);
void PrintValue (Value *v, FILE *fp);
char const *PrintValue (Value *v, FILE *fp);
int CopyValue (Value *dest, const Value *src);
int ReadLine (void);
int OpenFile (char const *fname);
@@ -77,8 +85,9 @@ int JulianToGregorianOffset(int y, int m);
int ParseChar (ParsePtr p, int *err, int peek);
int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
expr_node * ParseExpr(ParsePtr p, int *r);
void print_expr_nodes_stats(void);
int EvaluateExpr (ParsePtr p, Value *v);
int Evaluate (char const **s, Var *locals, ParsePtr p);
int FnPopValStack (Value *val);
void Eprint (char const *fmt, ...);
void Wprint (char const *fmt, ...);
@@ -98,10 +107,11 @@ int VerifyEoln (ParsePtr p);
int DoDebug (ParsePtr p);
int DoBanner (ParsePtr p);
int DoRun (ParsePtr p);
int DoExpr (ParsePtr p);
int DoErrMsg (ParsePtr p);
int ClearGlobalOmits (void);
int DoClear (ParsePtr p);
int DestroyOmitContexts (void);
int DestroyOmitContexts (int print_unmatched);
int PushOmitContext (ParsePtr p);
int PopOmitContext (ParsePtr p);
int IsOmitted (int dse, int localomit, char const *omitfunc, int *omit);
@@ -128,10 +138,13 @@ char *StrDup (char const *s);
int StrCmpi (char const *s1, char const *s2);
#endif
void strtolower(char *s);
Var *FindVar (char const *str, int create);
SysVar *FindSysVar (char const *name);
int DeleteVar (char const *str);
int SetVar (char const *str, Value const *val);
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p);
int GetVarValue (char const *str, Value *val);
int DoSet (Parser *p);
int DoUnset (Parser *p);
int DoDump (ParsePtr p);
@@ -144,10 +157,10 @@ int DoMsgCommand (char const *cmd, char const *msg, int is_queued);
int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
unsigned int HashVal (char const *str);
int DateOK (int y, int m, int d);
Operator *FindOperator (char const *name, Operator where[], int num);
BuiltinFunc *FindFunc (char const *name, BuiltinFunc where[], int num);
BuiltinFunc *FindBuiltinFunc (char const *name);
int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio);
void IssueSortedReminders (void);
UserFunc *FindUserFunc(char const *name);
int UserFuncExists (char const *fn);
void DSEToHeb (int dse, int *hy, int *hm, int *hd);
int HebNameToNum (char const *mname);
@@ -207,6 +220,7 @@ void set_cloexec(FILE *fp);
int push_call(char const *filename, char const *func, int lineno);
void clear_callstack(void);
int print_callstack(FILE *fp);
int have_callstack(void);
void pop_call(void);
void FixSpecialType(Trigger *trig);
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);
@@ -217,3 +231,11 @@ void WriteJSONTimeTrigger(TimeTrig const *tt);
#include <wchar.h>
void PutWideChar(wchar_t const wc, DynamicBuffer *output);
#endif
/* 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_mul_overflow(int a, int b);
extern int _private_add_overflow(int a, int b);
extern int _private_sub_overflow(int a, int b);

View File

@@ -34,7 +34,6 @@
#include "globals.h"
#include "err.h"
#include "protos.h"
#include "expr.h"
#undef USE_INOTIFY
#if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1)
@@ -311,6 +310,12 @@ void HandleQueuedReminders(void)
struct sigaction sa;
char qid[64];
/* Disable any potential pending SIGALRMs */
alarm(0);
/* Un-limit execution time */
unlimit_execution_time();
/* Turn off sorting -- otherwise, TriggerReminder has no effect! */
SortByDate = 0;
@@ -354,7 +359,8 @@ void HandleQueuedReminders(void)
if (ShouldFork || Daemon) {
sa.sa_handler = SigIntHandler;
sa.sa_flags = 0;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
(void) sigaction(SIGINT, &sa, NULL);
sa.sa_handler = SigContHandler;
(void) sigaction(SIGCONT, &sa, NULL);

View File

@@ -18,7 +18,6 @@
#include <stdlib.h>
#include "types.h"
#include "protos.h"
#include "expr.h"
#include "globals.h"
#include "err.h"

View File

@@ -56,6 +56,7 @@ Token TokArray[] = {
{ "endif", 5, T_EndIf, 0 },
{ "errmsg", 6, T_ErrMsg, 0 },
{ "exit", 4, T_Exit, 0 },
{ "expr", 4, T_Expr, 0 },
{ "february", 3, T_Month, 1 },
{ "first", 5, T_Ordinal, 0 },
{ "flush", 5, T_Flush, 0 },

View File

@@ -15,7 +15,6 @@
#include <stdlib.h>
#include "types.h"
#include "expr.h"
#include "protos.h"
#include "globals.h"
#include "err.h"

View File

@@ -13,6 +13,22 @@
#include <limits.h>
#include "dynbuf.h"
typedef struct udf_struct UserFunc;
/* Define the types of values */
#define ERR_TYPE 0
#define INT_TYPE 1
#define TIME_TYPE 2
#define DATE_TYPE 3
#define STR_TYPE 4
#define DATETIME_TYPE 5
#define SPECIAL_TYPE 6 /* Only for system variables */
#define CONST_INT_TYPE 7 /* Only for system variables */
#define BEG_OF_EXPR '['
#define END_OF_EXPR ']'
#define COMMA ','
/* Values */
typedef struct {
char type;
@@ -22,29 +38,61 @@ typedef struct {
} v;
} Value;
/* Define the type of operators */
typedef struct {
char const *name;
char prec;
char type;
int (*func)(void);
} Operator;
/* New-style expr_node structure and constants */
enum expr_node_type
{
N_FREE,
N_ERROR,
N_CONSTANT,
N_LOCAL_VAR,
N_SHORT_VAR,
N_VARIABLE,
N_SHORT_SYSVAR,
N_SYSVAR,
N_BUILTIN_FUNC,
N_SHORT_USER_FUNC,
N_USER_FUNC,
N_OPERATOR,
};
/* Structure for passing in Nargs and out RetVal from functions */
typedef struct {
int nargs;
Value *args;
Value retval;
} func_info;
/* Forward reference */
typedef struct expr_node_struct expr_node;
/* Define the type of user-functions */
typedef struct {
char const *name;
char minargs;
char maxargs;
char is_constant;
/* Old-style function calling convention */
int (*func)(func_info *);
/* New-style function calling convention */
int (*newfunc)(expr_node *node, Value *locals, Value *ans, int *nonconst);
} BuiltinFunc;
#define SHORT_NAME_BUF 16
typedef struct expr_node_struct {
struct expr_node_struct *child;
struct expr_node_struct *sibling;
enum expr_node_type type;
int num_kids;
union {
Value value;
int arg;
BuiltinFunc *builtin_func;
char name[SHORT_NAME_BUF];
int (*operator_func) (struct expr_node_struct *node, Value *locals, Value *ans, int *nonconst);
} u;
} expr_node;
/* Define the structure of a variable */
typedef struct var {
struct var *next;
@@ -142,6 +190,8 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define MSF_TYPE 7
#define PASSTHRU_TYPE 8
/* For function arguments */
#define NO_MAX 127
/* DEFINES for debugging flags */
#define DB_PRTLINE 1
@@ -150,7 +200,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32
#define DB_EXPR_STACKS 64
#define DB_PARSE_EXPR 64
/* Enumeration of the tokens */
enum TokTypes
@@ -184,7 +234,8 @@ enum TokTypes
T_MaybeUncomputable,
T_Ordinal,
T_In,
T_LastBack
T_LastBack,
T_Expr
};
/* The structure of a token */
@@ -244,3 +295,26 @@ typedef struct {
#define TERMINAL_BACKGROUND_UNKNOWN -1
#define TERMINAL_BACKGROUND_DARK 0
#define TERMINAL_BACKGROUND_LIGHT 1
typedef int (*SysVarFunc)(int, Value *);
/* The structure of a system variable */
typedef struct {
char const *name;
char modifiable;
int type;
void *value;
int min; /* Or const-value */
int max;
} SysVar;
/* Define the data structure used to hold a user-defined function */
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
expr_node *node;
char **args;
int nargs;
char const *filename;
int lineno;
} UserFunc;

View File

@@ -26,38 +26,36 @@
#include "globals.h"
#include "protos.h"
#include "err.h"
#include "expr.h"
#define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */
/* Define the data structure used to hold a user-defined function */
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
char const *text;
Var *locals;
char IsActive;
int nargs;
char const *filename;
int lineno;
} UserFunc;
/* The hash table */
static UserFunc *FuncHash[FUNC_HASH_SIZE];
/* Access to built-in functions */
extern int NumFuncs;
extern BuiltinFunc Func[];
/* We need access to the expression evaluation stack */
extern Value ValStack[];
extern int ValStackPtr;
static void DestroyUserFunc (UserFunc *f);
static void FUnset (char const *name);
static void FSet (UserFunc *f);
static int SetUpLocalVars (UserFunc *f);
static void DestroyLocalVals (UserFunc *f);
/***************************************************************/
/* */
/* HashVal */
/* Given a string, compute the hash value. */
/* */
/***************************************************************/
unsigned int HashVal_nocase(char const *str)
{
register unsigned int i=0;
register unsigned int j=1;
register unsigned int len=0;
while(*str && len < VAR_NAME_LEN) {
i += j * (*str);
str++;
len++;
j = 3-j;
}
return i;
}
/***************************************************************/
/* */
@@ -80,6 +78,7 @@ int DoFunset(ParsePtr p)
break;
}
seen_one = 1;
strtolower(DBufValue(&buf));
FUnset(DBufValue(&buf));
DBufFree(&buf);
}
@@ -98,8 +97,11 @@ int DoFset(ParsePtr p)
{
int r;
int c;
int i;
UserFunc *func;
Var *v;
UserFunc *existing;
Var *locals = NULL;
Var local_array[MAX_FUNC_ARGS];
int orig_namelen;
DynamicBuffer buf;
@@ -124,6 +126,21 @@ int DoFset(ParsePtr p)
return E_PARSE_ERR;
}
/* Convert to lower-case */
strtolower(DBufValue(&buf));
/* If the function exists and was defined at the same line of the same
file, do nothing */
existing = FindUserFunc(DBufValue(&buf));
if (existing) {
if (!strcmp(existing->filename, FileName) &&
strcmp(existing->filename, "[cmdline]") &&
existing->lineno == LineNo) {
DBufFree(&buf);
/* We already have it! Our work here is done. */
return OK;
}
}
func = NEW(UserFunc);
if (!func) {
DBufFree(&buf);
@@ -142,25 +159,22 @@ int DoFset(ParsePtr p)
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
if (!Hush) {
if (FindFunc(func->name, Func, NumFuncs)) {
if (FindBuiltinFunc(func->name)) {
Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name);
}
}
func->locals = NULL;
func->text = NULL;
func->IsActive = 0;
func->node = NULL;
func->nargs = 0;
func->args = NULL;
/* Get the local variables - we insert the local variables in REVERSE
order, but that's OK, because we pop them off the stack in reverse
order, too, so everything works out just fine. */
/* Get the local variables */
c=ParseNonSpaceChar(p, &r, 1);
if (r) return r;
if (c == ')') {
(void) ParseNonSpaceChar(p, &r, 0);
}
else {
} else {
locals = local_array;
while(1) {
if ( (r=ParseIdentifier(p, &buf)) ) return r;
if (*DBufValue(&buf) == '$') {
@@ -168,18 +182,25 @@ int DoFset(ParsePtr p)
DestroyUserFunc(func);
return E_BAD_ID;
}
v = NEW(Var);
if (!v) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_NO_MEM;
}
/* If we've already seen this local variable, error */
for (i=0; i<func->nargs; i++) {
if (!StrinCmp(DBufValue(&buf), local_array[i].name, VAR_NAME_LEN)) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_REPEATED_ARG;
}
}
i = func->nargs;
if (i >= MAX_FUNC_ARGS-1) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_2MANY_ARGS;
}
local_array[i].v.type = ERR_TYPE;
StrnCpy(local_array[i].name, DBufValue(&buf), VAR_NAME_LEN);
local_array[i].next = &(local_array[i+1]);
local_array[i+1].next = NULL;
func->nargs++;
v->v.type = ERR_TYPE;
StrnCpy(v->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
v->next = func->locals;
func->locals = v;
c = ParseNonSpaceChar(p, &r, 0);
if (c == ')') break;
else if (c != ',') {
@@ -194,17 +215,42 @@ int DoFset(ParsePtr p)
if (c == '=') {
(void) ParseNonSpaceChar(p, &r, 0);
}
/* Copy the text over */
if (p->isnested) {
Eprint("%s", ErrMsg[E_CANTNEST_FDEF]);
DestroyUserFunc(func);
return E_PARSE_ERR;
}
func->text = StrDup(p->pos);
if (!func->text) {
DestroyUserFunc(func);
return E_NO_MEM;
while(*(p->pos) && isspace(*(p->pos))) {
p->pos++;
}
if (!*(p->pos)) {
DestroyUserFunc(func);
return E_EOLN;
}
/* Parse the expression */
func->node = parse_expression(&(p->pos), &r, locals);
if (!func->node) {
DestroyUserFunc(func);
return r;
}
c = ParseNonSpaceChar(p, &r, 1);
if (c != 0) {
DestroyUserFunc(func);
return E_EXPECTING_EOL;
}
/* Save the argument names */
if (func->nargs) {
func->args = calloc(sizeof(char *), func->nargs);
for (i=0; i<func->nargs; i++) {
func->args[i] = StrDup(local_array[i].name);
if (!func->args[i]) {
DestroyUserFunc(func);
return E_NO_MEM;
}
}
}
/* If an old definition of this function exists, destroy it */
@@ -228,23 +274,22 @@ int DoFset(ParsePtr p)
/***************************************************************/
static void DestroyUserFunc(UserFunc *f)
{
Var *v, *prev;
/* Free the local variables first */
v = f->locals;
while(v) {
DestroyValue(v->v);
prev = v;
v = v->next;
free(prev);
}
int i;
/* Free the function definition */
if (f->text) free( (char *) f->text);
if (f->node) free_expr_tree(f->node);
/* Free the filename */
if (f->filename) free( (char *) f->filename);
/* Free arg names */
if (f->args) {
for (i=0; i<f->nargs; i++) {
if (f->args[i]) free(f->args[i]);
}
free(f->args);
}
/* Free the data structure itself */
free(f);
}
@@ -262,12 +307,12 @@ static void FUnset(char const *name)
UserFunc *cur, *prev;
int h;
h = HashVal(name) % FUNC_HASH_SIZE;
h = HashVal_nocase(name) % FUNC_HASH_SIZE;
cur = FuncHash[h];
prev = NULL;
while(cur) {
if (! StrinCmp(name, cur->name, VAR_NAME_LEN)) break;
if (! strncmp(name, cur->name, VAR_NAME_LEN)) break;
prev = cur;
cur = cur->next;
}
@@ -285,134 +330,22 @@ static void FUnset(char const *name)
/***************************************************************/
static void FSet(UserFunc *f)
{
int h = HashVal(f->name) % FUNC_HASH_SIZE;
int h = HashVal_nocase(f->name) % FUNC_HASH_SIZE;
f->next = FuncHash[h];
FuncHash[h] = f;
}
/***************************************************************/
/* */
/* CallUserFunc */
/* */
/* Call a user-defined function. */
/* */
/***************************************************************/
int CallUserFunc(char const *name, int nargs, ParsePtr p)
UserFunc *FindUserFunc(char const *name)
{
UserFunc *f;
int h = HashVal(name) % FUNC_HASH_SIZE;
int i;
char const *s;
UserFunc *f;
int h = HashVal_nocase(name) % FUNC_HASH_SIZE;
/* Search for the function */
f = FuncHash[h];
while (f && StrinCmp(name, f->name, VAR_NAME_LEN)) f = f->next;
if (!f) {
Eprint("%s: `%s'", ErrMsg[E_UNDEF_FUNC], name);
return E_UNDEF_FUNC;
}
/* Debugging stuff */
if (DebugFlag & DB_PRTEXPR) {
fprintf(ErrFp, "%s %s(", ErrMsg[E_ENTER_FUN], f->name);
for (i=0; i<nargs; i++) {
PrintValue(&ValStack[ValStackPtr - nargs + i], ErrFp);
if (i<nargs-1) fprintf(ErrFp, ", ");
}
fprintf(ErrFp, ")\n");
}
/* Detect illegal recursive call */
if (f->IsActive) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n", ErrMsg[E_RECURSIVE]);
}
return E_RECURSIVE;
}
/* Check number of args */
if (nargs != f->nargs) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n",
ErrMsg[(nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS]);
}
return (nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS;
}
/* Found the function - set up a local variable frame */
h = SetUpLocalVars(f);
if (h) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n", ErrMsg[h]);
}
return h;
}
/* Evaluate the expression */
f->IsActive = 1;
s = f->text;
/* Skip the opening bracket, if there's one */
while (isempty(*s)) s++;
if (*s == BEG_OF_EXPR) {
s++;
}
push_call(f->filename, f->name, f->lineno);
h = Evaluate(&s, f->locals, p);
if (h == OK) {
pop_call();
}
f->IsActive = 0;
DestroyLocalVals(f);
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
if (h) fprintf(ErrFp, "%s\n", ErrMsg[h]);
else {
PrintValue(&ValStack[ValStackPtr-1], ErrFp);
fprintf(ErrFp, "\n");
}
}
return h;
/* Search for the function */
f = FuncHash[h];
while (f && strncmp(name, f->name, VAR_NAME_LEN)) f = f->next;
return f;
}
/***************************************************************/
/* */
/* SetUpLocalVars */
/* */
/* Set up the local variables from the stack frame. */
/* */
/***************************************************************/
static int SetUpLocalVars(UserFunc *f)
{
int i, r;
Var *var;
for (i=0, var=f->locals; var && i<f->nargs; var=var->next, i++) {
if ( (r=FnPopValStack(&(var->v))) ) {
DestroyLocalVals(f);
return r;
}
}
return OK;
}
/***************************************************************/
/* */
/* DestroyLocalVals */
/* */
/* Destroy the values of all local variables after evaluating */
/* the function. */
/* */
/***************************************************************/
static void DestroyLocalVals(UserFunc *f)
{
Var *v = f->locals;
while(v) {
DestroyValue(v->v);
v = v->next;
}
}
/***************************************************************/
/* */
/* UserFuncExists */
@@ -423,12 +356,33 @@ static void DestroyLocalVals(UserFunc *f)
/***************************************************************/
int UserFuncExists(char const *fn)
{
UserFunc *f;
int h = HashVal(fn) % FUNC_HASH_SIZE;
UserFunc *f = FindUserFunc(fn);
f = FuncHash[h];
while (f && StrinCmp(fn, f->name, VAR_NAME_LEN)) f = f->next;
if (!f) return -1;
else return f->nargs;
}
/***************************************************************/
/* */
/* UnsetAllUserFuncs */
/* */
/* Call FUNSET on all user funcs. Used with -ds flag to */
/* ensure no expr_node memory leaks. */
/* */
/***************************************************************/
void
UnsetAllUserFuncs(void)
{
UserFunc *f;
UserFunc *next;
int i;
for (i=0; i<FUNC_HASH_SIZE; i++) {
f = FuncHash[i];
while(f) {
next = f->next;
DestroyUserFunc(f);
f = next;
}
FuncHash[i] = NULL;
}
}

View File

@@ -130,6 +130,14 @@ int DateOK(int y, int m, int d)
return 1;
}
void strtolower(char *s)
{
while (*s) {
*s = tolower(*s);
s++;
}
}
/* Functions designed to defeat gcc optimizer */
int _private_mul_overflow(int a, int b)
@@ -187,31 +195,33 @@ typedef struct cs_s {
} cs;
static cs *callstack = NULL;
static cs *freecs = NULL;
static void
destroy_cs(cs *entry)
{
if (entry->filename) free( (void *) entry->filename);
if (entry->func) free( (void *) entry->func);
free( (void *) entry);
entry->next = freecs;
freecs = entry;
}
int
push_call(char const *filename, char const *func, int lineno)
{
cs *entry = NEW(cs);
if (!entry) {
return E_NO_MEM;
cs *entry;
if (freecs) {
entry = freecs;
freecs = freecs->next;
} else {
entry = NEW(cs);
if (!entry) {
return E_NO_MEM;
}
}
entry->next = NULL;
entry->filename = StrDup(filename);
entry->func = StrDup(func);
entry->filename = filename;
entry->func = func;
entry->lineno = lineno;
if (!entry->filename || !entry->func) {
destroy_cs(entry);
return E_NO_MEM;
}
entry->next = callstack;
callstack = entry;
return OK;
@@ -233,11 +243,35 @@ clear_callstack(void)
static void
print_callstack_aux(FILE *fp, cs *entry)
{
if (entry) {
print_callstack_aux(fp, entry->next);
fprintf(fp, "\n");
(void) fprintf(fp, "%s(%d): In function `%s'", entry->filename, entry->lineno, entry->func);
int i = 0;
char const *in = "In";
cs *prev = NULL;
while(entry) {
if (prev) {
in = "Called from";
}
if (!prev || strcmp(prev->func, entry->func) || strcmp(prev->filename, entry->filename) || prev->lineno != entry->lineno) {
if (prev) {
fprintf(fp, "\n");
}
(void) fprintf(fp, " %s(%d): [#%d] %s function `%s'", entry->filename, entry->lineno, i, in, entry->func);
}
prev = entry;
entry = entry->next;
i++;
if (i > 10) {
break;
}
}
if (entry) {
(void) fprintf(fp, "\n [remaining call frames omitted]");
}
}
int
have_callstack(void)
{
return (callstack != NULL);
}
int

View File

@@ -22,7 +22,6 @@
#include <errno.h>
#include <locale.h>
#include "types.h"
#include "expr.h"
#include "globals.h"
#include "protos.h"
#include "err.h"
@@ -39,8 +38,6 @@ static int IntMax = INT_MAX;
static Var *VHashTbl[VAR_HASH_SIZE];
typedef int (*SysVarFunc)(int, Value *);
static double
strtod_in_c_locale(char const *str, char **endptr)
{
@@ -322,6 +319,29 @@ static int datetime_sep_func(int do_set, Value *val)
return OK;
}
static int expr_time_limit_func(int do_set, Value *val)
{
if (!do_set) {
val->type = INT_TYPE;
val->v.val = ExpressionEvaluationTimeLimit;
return OK;
}
if (val->type != INT_TYPE) return E_BAD_TYPE;
if (val->v.val < 0) return E_2LOW;
if (!TopLevel()) {
/* Ignore attempts to set from non-toplevel unless it's
lower than current value */
if (val->v.val == 0 ||
val->v.val >= ExpressionEvaluationTimeLimit) {
return OK;
}
}
ExpressionEvaluationTimeLimit = val->v.val;
return OK;
}
static int default_color_func(int do_set, Value *val)
{
int col_r, col_g, col_b;
@@ -490,7 +510,7 @@ int DeleteVar(char const *str)
/* */
/* SetVar */
/* */
/* Set the indicate variable to the specified value. */
/* Set the indicated variable to the specified value. */
/* */
/***************************************************************/
int SetVar(char const *str, Value const *val)
@@ -511,20 +531,10 @@ int SetVar(char const *str, Value const *val)
/* Get a copy of the value of the variable. */
/* */
/***************************************************************/
int GetVarValue(char const *str, Value *val, Var *locals, ParsePtr p)
int GetVarValue(char const *str, Value *val)
{
Var *v;
/* Try searching local variables first */
v = locals;
while (v) {
if (! StrinCmp(str, v->name, VAR_NAME_LEN))
return CopyValue(val, &v->v);
v = v->next;
}
/* Global variable... mark expression as non-constant */
if (p) p->nonconst_expr = 1;
v=FindVar(str, 0);
if (!v) {
@@ -557,6 +567,11 @@ int DoSet (Parser *p)
ParseNonSpaceChar(p, &r, 0);
}
if (p->isnested) {
Eprint("%s", "Do not use [] around expression in SET command");
return E_CANTNEST_FDEF;
}
r = EvaluateExpr(p, &v);
if (r) {
DBufFree(&buf);
@@ -785,16 +800,6 @@ int DoPreserve (Parser *p)
/* */
/***************************************************************/
/* The structure of a system variable */
typedef struct {
char const *name;
char modifiable;
int type;
void *value;
int min; /* Or const-value */
int max;
} SysVar;
/* Macro to access "min" but as a constval. Just to make source more
readable */
#define constval min
@@ -831,6 +836,7 @@ static SysVar SysVarArr[] = {
{"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 },
{"EndSent", 1, STR_TYPE, &EndSent, 0, 0 },
{"EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 },
{"ExpressionTimeLimit", 1, SPECIAL_TYPE, expr_time_limit_func, 0, 0 },
{"February", 1, STR_TYPE, &DynamicMonthName[1], 0, 0 },
{"FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 },
{"FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 },
@@ -919,7 +925,6 @@ static SysVar SysVarArr[] = {
};
#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) )
static SysVar *FindSysVar (char const *name);
static void DumpSysVar (char const *name, const SysVar *v);
/***************************************************************/
/* */
@@ -1016,7 +1021,7 @@ int GetSysVar(char const *name, Value *val)
/* Find a system var with specified name. */
/* */
/***************************************************************/
static SysVar *FindSysVar(char const *name)
SysVar *FindSysVar(char const *name)
{
int top=NUMSYSVARS-1, bottom=0;
int mid=(top + bottom) / 2;

View File

@@ -35,8 +35,8 @@ set a ansicolor(-1, 0, 0)
set a ansicolor(42, 42, 256)
set a ansicolor("foo")
set a ansicolor("1 1")
set a ansicolor("-1 -1 0");
set a ansicolor("256 1 1");
set a ansicolor("-1 -1 0")
set a ansicolor("256 1 1")
set a ansicolor(128, 128, 128, 2)
set a ansicolor(128, 128, 128, -1)
set a ansicolor(128, 128, 128, 0, 2)

55
tests/expr.rem Normal file
View File

@@ -0,0 +1,55 @@
debug +sx
set a 1
set a 0&&0
set a 0&&1
set a 1&&0
set a 1&&1
set a 0||0
set a 0||1
set a 1||0
set a 1||1
set a 2, 3
set a iif(0, "foo", 0, "bar", 1, "blech", 0, "quux", 1, "borhy", "wacka")
set a max(2*3, 4+5, min(6*7+8, 7+6*8))
set a max(1,,1)
set a 5%0
set a 5/0
set a -$IntMin
set a $IntMin / -1
set a $IntMin % -1
set a (7+5)*(8+2)/(9-4)
set a "foo" * 5
set a "foo" / 5
set a "foo" * "five"
set a "foo" + "bar"
set a '2024-01-02' + 3
set a 3 + '2024-01-02'
set a 11:33 + 75
set a 75 + 11:33
set a '2024-01-01@11:33' + 1500
set a 1500 + '2024-01-01@11:33'
set a '2024-03-02' - '2024-01-01'
set a 15:00 - 14:44
set a (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
set a (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
set a isany(1)
set a isany(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6)
set a isany("foo", 1 + 1, 2:00 + 1, '2021-01-01' + 1, '2021-01-01@14:00' + 1, "f" + "oo", "fo" + "o")

4
tests/if1.rem Normal file
View File

@@ -0,0 +1,4 @@
BANNER %
set $AddBlankLines 0
IF 1
INCLUDE [filedir()]/if2.rem

5
tests/if2.rem Normal file
View File

@@ -0,0 +1,5 @@
# Another unmatched IF
IF 0
ELSE
IF 1

View File

@@ -65,8 +65,6 @@ echo "Test 1" > ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo 'set a 1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+2*3))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))' | ../src/remind -ds - 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo "Test 2" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
@@ -178,7 +176,7 @@ REM 1 Jan 2012 AT 10:00 MSG 10am: Should show up
MSG [$DontTrigAts]
EOF
# An OMITFUNC should indicate nonconst_expr
# OMITFUNC should indicate nonconst_expr
../src/remind -pp - 1 jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM Mon OMITFUNC foo MSG bar
EOF
@@ -541,6 +539,36 @@ EOF
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1
../src/remind -ifoo - <<'EOF' >> ../tests/test.out 2>&1
BANNER %
DUMP
EOF
../src/remind '-i$AddBlankLines' - <<'EOF' >> ../tests/test.out 2>&1
BANNER %
DUMP
EOF
../src/remind ../tests/expr.rem >> ../tests/test.out 2>&1
../src/remind - <<'EOF' >> ../tests/test.out 2>&1
PUSH
POP
PUSH
PUSH
POP
POP
PUSH
PUSH
POP
PUSH
POP
PUSH
POP
EOF
../src/remind ../tests/if1.rem 2020-03-03 >> ../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
cmp -s ../tests/test.out ../tests/test.cmp

File diff suppressed because one or more lines are too long

View File

@@ -777,12 +777,15 @@ ENDIF
# Trig with a good warnfunc
FSET w(x) choose(x, 5, 3, 1, 0)
# Ugh. This is where short-circuit logical operators
# would really come in handy.
Short-circuit operators
IF trig("sun warn w") || trig("thu warn w")
REM [trig()] +5 MSG Foo %b
ENDIF
IF trig("thu warn w") || trig("sun warn w")
REM [trig()] +5 MSG Foo %b
ENDIF
REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo
REM [trig("Mon", "Tue", "Wed")] MSG bar

View File

@@ -79,7 +79,7 @@ install:
chmod 755 $(DESTDIR)$(SCRIPTDIR)/calpdf $(DESTDIR)$(SCRIPTDIR)/calps $(DESTDIR)$(SCRIPTDIR)/hebdate \
$(DESTDIR)$(SCRIPTDIR)/hebps $(DESTDIR)$(SCRIPTDIR)/hebpdf $(DESTDIR)$(SCRIPTDIR)/moon \
$(DESTDIR)$(SCRIPTDIR)/sunrise $(DESTDIR)$(SCRIPTDIR)/sunset \
$(DESTDIR)$(SCRIPTDIR)/hebhtml \
$(DESTDIR)$(SCRIPTDIR)/hebhtml \
-mkdir -p $(DESTDIR)$(IMAGEDIR)
cp calendar.css rem-default.css *.png $(DESTDIR)$(IMAGEDIR)