mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-16 22:38:37 +02:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3e32d2dc4 | ||
|
|
a8b78eff00 | ||
|
|
460db83298 | ||
|
|
4560712778 | ||
|
|
ce8803dde9 | ||
|
|
60ca5d45e3 | ||
|
|
4454613d00 | ||
|
|
0704808500 | ||
|
|
166b1ac499 | ||
|
|
e33bf4e80a | ||
|
|
b3af44d212 | ||
|
|
1e753d5209 | ||
|
|
4bf31005ea | ||
|
|
7c86bc910a | ||
|
|
4f146a99a9 | ||
|
|
a6a638e0e6 | ||
|
|
325814f5e1 | ||
|
|
5c4ea7d09e | ||
|
|
2bf73987ac | ||
|
|
3e9eeea8dc | ||
|
|
d164d72c1c | ||
|
|
632cee62d9 | ||
|
|
7a40260f0d | ||
|
|
09f043b3de | ||
|
|
c0341c8ba3 | ||
|
|
850c717803 | ||
|
|
f13f9e18bd | ||
|
|
8bdca0d684 | ||
|
|
129bf5612e | ||
|
|
829962fae1 | ||
|
|
c5f9ed8541 | ||
|
|
4a7cef4644 | ||
|
|
0e010b56ec | ||
|
|
ee179ee2f5 | ||
|
|
e28712cef3 | ||
|
|
8f0a2a7e79 | ||
|
|
ef23bba77f | ||
|
|
602086ae2d | ||
|
|
f5a170acbd | ||
|
|
8125b96f0b | ||
|
|
0bb7d89bb9 | ||
|
|
eb109bbbc0 | ||
|
|
1a0809fd31 | ||
|
|
09625b9d68 | ||
|
|
4e164c4268 | ||
|
|
691185f22c | ||
|
|
a8bfb41a9e | ||
|
|
fafb30db05 | ||
|
|
243e816523 | ||
|
|
b49c0f52bd | ||
|
|
07fca94a7f | ||
|
|
73917ee537 | ||
|
|
76f9edecf6 | ||
|
|
d77d9854d2 | ||
|
|
c2b53f95a4 | ||
|
|
caef8b80d6 | ||
|
|
5e016768af | ||
|
|
ee08ce98d7 | ||
|
|
581bd95838 | ||
|
|
bb92dab1ab | ||
|
|
83b5c52c76 | ||
|
|
93eca25141 | ||
|
|
34421cb10e | ||
|
|
29b87898aa | ||
|
|
e9e4db94bd | ||
|
|
c95ad0261a | ||
|
|
7fef456483 | ||
|
|
386131e74d | ||
|
|
824d3c88f1 | ||
|
|
18a206abd2 | ||
|
|
8dbae776c9 | ||
|
|
b78702cc53 | ||
|
|
d2b43605ad | ||
|
|
7728e09337 | ||
|
|
2666353ce6 | ||
|
|
0b8a306483 | ||
|
|
b51a0b2d08 | ||
|
|
959355b19c | ||
|
|
9c3f0f1994 | ||
|
|
dac337a65b | ||
|
|
98739dfdbc | ||
|
|
17b7a1ea84 |
@@ -1,3 +1,8 @@
|
||||
workflow:
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH =~ /wip/
|
||||
when: never
|
||||
|
||||
tests:
|
||||
image: 'debian:stable-slim'
|
||||
before_script:
|
||||
|
||||
18
configure
vendored
18
configure
vendored
@@ -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\\"
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
197
man/remind.1.in
197
man/remind.1.in
@@ -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.
|
||||
|
||||
@@ -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; \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
46
src/dorem.c
46
src/dorem.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
216
src/err.h
@@ -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
3686
src/expr.c
File diff suppressed because it is too large
Load Diff
67
src/expr.h
67
src/expr.h
@@ -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);
|
||||
|
||||
12
src/files.c
12
src/files.c
@@ -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;
|
||||
|
||||
645
src/funcs.c
645
src/funcs.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
86
src/init.c
86
src/init.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
239
src/main.c
239
src/main.c
@@ -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 */
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
#include <time.h>
|
||||
#include "types.h"
|
||||
#include "protos.h"
|
||||
#include "expr.h"
|
||||
#include "globals.h"
|
||||
#include "err.h"
|
||||
|
||||
|
||||
28
src/omit.c
28
src/omit.c
@@ -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);
|
||||
|
||||
34
src/protos.h
34
src/protos.h
@@ -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);
|
||||
|
||||
10
src/queue.c
10
src/queue.c
@@ -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);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "protos.h"
|
||||
#include "expr.h"
|
||||
#include "globals.h"
|
||||
#include "err.h"
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
#include "expr.h"
|
||||
#include "protos.h"
|
||||
#include "globals.h"
|
||||
#include "err.h"
|
||||
|
||||
92
src/types.h
92
src/types.h
@@ -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;
|
||||
|
||||
|
||||
326
src/userfns.c
326
src/userfns.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
66
src/utils.c
66
src/utils.c
@@ -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
|
||||
|
||||
59
src/var.c
59
src/var.c
@@ -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;
|
||||
|
||||
@@ -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
55
tests/expr.rem
Normal 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
4
tests/if1.rem
Normal file
@@ -0,0 +1,4 @@
|
||||
BANNER %
|
||||
set $AddBlankLines 0
|
||||
IF 1
|
||||
INCLUDE [filedir()]/if2.rem
|
||||
5
tests/if2.rem
Normal file
5
tests/if2.rem
Normal file
@@ -0,0 +1,5 @@
|
||||
# Another unmatched IF
|
||||
IF 0
|
||||
ELSE
|
||||
IF 1
|
||||
|
||||
@@ -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
|
||||
|
||||
906
tests/test.cmp
906
tests/test.cmp
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user