Compare commits

..

35 Commits

Author SHA1 Message Date
Dianne Skoll
1910808fd7 Update WHATSNEW 2024-07-10 14:04:06 -04:00
Dianne Skoll
1d8cb9749e Add a test to ensure ONCE works even when taking input from STDIN if $OnceFile is set. 2024-07-10 13:55:41 -04:00
Dianne Skoll
e3f9380fcd Allow ONCE to be used if input is stdin, as long as $OnceFile is set.
Clarify documentation.
2024-07-10 09:32:05 -04:00
Dianne Skoll
326c3f59b0 Make tests pass on any day, not just 2024-07-09. :) 2024-07-10 09:23:21 -04:00
Dianne Skoll
03f1c5a047 Fix typo 2024-07-09 09:36:52 -04:00
Dianne Skoll
02122491c3 Add test to ensure we warn if we try to set $OnceFile to something else after a ONCE has been seen. 2024-07-09 09:33:58 -04:00
Dianne Skoll
735f6f5686 Ignore attempts to set $OnceFile to the value it already has. 2024-07-09 09:33:02 -04:00
Dianne Skoll
ac033d75c0 Add the $OnceFile special variable.
This lets us specify a timestamp file that Remind
uses to track the last run for the purpose of handling
the ONCE keyword, rather than using the last access date
of the reminder file.

Keeping a timestamp file is a more reliable way to track
when Remind was last run.
2024-07-09 09:23:15 -04:00
Dianne Skoll
e2185e773a Add warning if someone attempts to create a multi-page SVG calendar. 2024-07-05 16:57:18 -04:00
Dianne Skoll
d9ae417e01 Fix typo 2024-07-05 14:22:55 -04:00
Dianne Skoll
e1d0948538 Update doc. 2024-07-05 14:22:16 -04:00
Dianne Skoll
357ddf285a Add --svg option to rem2pdf to produce SVG instead of PDF output. 2024-07-05 14:18:29 -04:00
Dianne Skoll
41859fc484 Remove some unused definitions; use symbolic value for sort initializer. 2024-07-04 16:58:41 -04:00
Dianne Skoll
07275e71b0 Remove obsolete definitions. 2024-07-04 13:13:27 -04:00
Dianne Skoll
f68521cb95 Fix spacing. 2024-07-04 13:09:21 -04:00
Dianne Skoll
526610bdd2 Stop parsing user func early if possible. 2024-07-02 09:16:47 -04:00
Dianne Skoll
973e3448ae Fix typo in comment. 2024-07-02 07:58:24 -04:00
Dianne Skoll
9a3f28f6fc Optimize storage of short string constants in expr_node objects. 2024-06-12 10:19:27 -04:00
Dianne Skoll
a3e32d2dc4 Update changelog. 2024-06-08 10:57:38 -04:00
Dianne Skoll
a8b78eff00 Add a few more tests. 2024-06-08 09:20:48 -04:00
Dianne Skoll
460db83298 Bump version to 05.00.01 2024-06-08 09:18:42 -04:00
Dianne Skoll
4560712778 Fix a couple of potential memory leaks. 2024-06-07 21:27:24 -04:00
Dianne Skoll
ce8803dde9 The zero-argument form of weekno() is not constant. 2024-06-07 21:22:32 -04:00
Dianne Skoll
60ca5d45e3 Convert "isany" to short-circuit evaluation. 2024-06-07 15:55:12 -04:00
Dianne Skoll
4454613d00 Fix typo 2024-06-06 12:51:02 -04:00
Dianne Skoll
0704808500 Don't rely on newer C feature that allows anonymous parameters 2024-06-06 12:48:44 -04:00
Dianne Skoll
166b1ac499 Only reset alarm if expression time limit was set 2024-06-06 11:47:39 -04:00
Dianne Skoll
e33bf4e80a Explicitly set sa_flags in sigaction. 2024-06-06 10:42:04 -04:00
Dianne Skoll
b3af44d212 Update docs/WHATSNEW for 05.00.00 release. 2024-06-06 10:28:38 -04:00
Dianne Skoll
1e753d5209 Preserve variables needed across iterations. 2024-06-05 15:08:12 -04:00
Dianne Skoll
4bf31005ea Better error location in a couple more cases. 2024-06-05 09:15:10 -04:00
Dianne Skoll
7c86bc910a Better diagnostics of unmatched IFs 2024-06-05 09:08:04 -04:00
Dianne Skoll
4f146a99a9 Simplify Eprint logic. 2024-06-04 21:10:17 -04:00
Dianne Skoll
a6a638e0e6 Add test for UNMATCHED PUSH 2024-06-04 16:27:23 -04:00
Dianne Skoll
325814f5e1 Print the location of unmatched PUSH 2024-06-04 16:25:14 -04:00
32 changed files with 539 additions and 190 deletions

1
.gitignore vendored
View File

@@ -33,3 +33,4 @@ src/version.h
tests/test.out
www/Makefile
gmon.out
tests/once.timestamp

18
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for remind 05.00.00.
# 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='05.00.00'
PACKAGE_STRING='remind 05.00.00'
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 05.00.00 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 05.00.00:";;
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 05.00.00
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 05.00.00, 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 05.00.00, 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 05.00.00
remind config.status 05.00.01
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View File

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

View File

@@ -1,6 +1,45 @@
CHANGES TO REMIND
* VERSION 5.0 Patch 0 - 2024-??-??
* VERSION 5.0 Patch 2 - 2024-07-??
* IMPROVEMENT: Remind: Revamp how ONCE works. You can now set a
special variable $OnceFile to be the path to a timestamp file. The
ONCE directive uses this timestamp file to track when it was last
run rather than the access date of the main reminder script. This
is more reliable because it doesn't rely on the atime of a file
(which might not be maintained accurately) and is not affected if
you edit your reminder script.
* CHANGE: Taking input from stdin no longer implies the "-o" option; ONCE can
work if you set $OnceFile
* CHANGE: Any of the -c, -n, -p and -s options implicitly enable the
-o option. As before, specifying a repeat factor *N or a date that
is not today on the command-line also implies -o.
* MINOR NEW FEATURE: Rem2PDF: Add the "--svg" command-line option to produce
SVG output rather than PDF.
* MINOR IMPROVEMENT: Remind: Improve the storage efficiency of short
string constants in compiled expression trees.
* MINOR IMPROVEMENT: Remind: Remove some obsolete macro definitions
* 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.
@@ -33,6 +72,12 @@ CHANGES TO REMIND
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.

View File

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

View File

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

View File

@@ -11,8 +11,8 @@ reminder or alarm can consist of a message sent to standard output, or
a program to be executed.
.PP
If \fIfilename\fR is specified as a single dash '-', then \fBRemind\fR
takes its input from standard input. This also implicitly enables
the \fB\-o\fR option, described below.
takes its input from standard input.
.PP
If \fIfilename\fR happens to
be a directory rather than a plain file, then \fBRemind\fR reads all of
@@ -32,7 +32,8 @@ ignore them for now and skip to the section "REMINDER FILES".
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
date by piping the output through \fBsort(1)\fR. Note that the \fB\-n\fR
option causes any \fB\-g\fR option to be \fIignored\fR.
option causes any \fB\-g\fR option to be \fIignored\fR and also implicitly
enables the \fB\-o\fR option.
.TP
.B \-j\fR[\fIn\fR]
Runs \fBRemind\fR in "purge" mode to get rid of expired reminders.
@@ -47,7 +48,7 @@ The \fB\-c\fR option causes \fBRemind\fR to produce a calendar that is
sent to standard output. If you supply a number \fIn\fR, then a
calendar will be generated for \fIn\fR months, starting with the
current month. By default, a calendar for only the current month is
produced.
produced. This option implicitly enables the \fB\-o\fR option.
.RS
.PP
You can precede \fIn\fR (if any) with a set of flags. The flags
@@ -173,7 +174,7 @@ The \fB\-s\fR option is very similar to the \fB\-c\fR option, except
that the output calendar is not formatted. It is listed in a "simple
format" that can be used as input for more sophisticated calendar-drawing
programs. If \fIn\fR starts with "+", then it is interpreted as a number
of weeks.
of weeks. This option also implicitly enables the \fB\-o\fR option.
If you immediately follow the \fBs\fR with the letter
\fBa\fR, then \fBRemind\fR displays reminders on the calendar on the
@@ -200,6 +201,9 @@ letter with this option, then the normal calendar-mode substitution filter
is disabled and the %"...%" sequences are preserved in the output.
.RS
.PP
The \fB\-p\fR, \fB\-pp\fR and \fB\-ppp\fR options implicitly enable
the \fB\-o\fR option.
.PP
Note that the \fB\-pp\fR or \fB\-ppp\fR options also enable the \fB\-l\fR
option.
.RE
@@ -223,7 +227,11 @@ error, and to print a security message if a script tests the
$RunOff system variable.
.TP
.B \-o
The \fB\-o\fR option causes \fBRemind\fR to ignore all \fBONCE\fR directives.
The \fB\-o\fR option causes \fBRemind\fR to ignore all \fBONCE\fR
directives. Note that \fBONCE\fR is also ignored if any of the
\fB\-c\fR, \fB\-n\fR, \fB\-p\fR, or \fB\-s\fR options is used, if a
repetition factor \fB*n\fR is used, or if a date other than today's
date is specified on the command-line.
.TP
.B \-t
The \fB\-t\fR option causes \fBRemind\fR to trigger all non-expired reminders,
@@ -1019,26 +1027,35 @@ overrides that.
.B THE ONCE KEYWORD
.PP
Sometimes, it is necessary to ensure that reminders are run only once
on a given day. For example, if you have a reminder that makes a backup
of your files every Friday:
on a given day. For example, compare the following two reminders:
.PP
.nf
REM Fri RUN do_backup
REM Fri RUN do_backup
REM Fri ONCE RUN do_backup
.fi
.PP
(Here, \fIdo_backup\fR is assumed to be a program or shell script that
does the work.) If you run \fBRemind\fR from your .login script, for
example, and log in several times per day, the \fIdo_backup\fR program
will be run each time you log in. If, however, you use the \fBONCE\fR
keyword in the reminder, the \fBRemind\fR checks the last access date of
the reminder script. If it is the same as the current date, \fBRemind\fR
assumes that it has already been run, and will not issue reminders containing
the \fBONCE\fR keyword.
The first will be run every time you invoke \fBRemind\fR on a Friday,
whereas the second will be run only the first time you invoke
\fBRemind\fR on a given Friday.
.PP
If you run \fBRemind\fR from your .login script, for example, and log
in several times per day, the \fIdo_backup\fR program in the first
reminder will be run each time you log in. If, however, you use the
\fBONCE\fR keyword in the reminder, the \fBRemind\fR checks the last
access date of the reminder script. If it is the same as the current
date, \fBRemind\fR assumes that it has already been run, and will not
issue reminders containing the \fBONCE\fR keyword.
.PP
Note that if you view or edit your reminder script, the last access date
will be updated, and the \fBONCE\fR keyword will not operate properly.
You can fix this by setting a timestamp file for \fBRemind\fR to track
the last-run date; see the documentation of \fB$OnceFile\fR in the
\fBSYSTEM VARIABLES\fR section. If you use standard input as your
\fBRemind\fR input file, then you \fImust\fR use \B$OnceFile\fR for the
\fBONCE\fR keyword to work properly.
.PP
If you start \fBRemind\fR with the \fB\-o\fR option, then the \fBONCE\fR
keyword will be ignored.
keyword will be ignored and any \fB$OnceFile\fR will be ignored.
.PP
.B LOCALLY OMITTING WEEKDAYS
.PP
@@ -2532,9 +2549,9 @@ truncated - the width limit will be ignored.
If non-zero, then the \fB\-h\fR option was supplied on the command line.
.TP
.B $IgnoreOnce (read-only)
If non-zero, then the \fB\-o\fR option was supplied on the command line,
or a date different from today's true date was supplied. If non-zero,
then \fBONCE\fR directives will be ignored.
If non-zero, then the \fB\-o\fR option was supplied on the command
line, or implicitly enabled for some other reason. In this case,
\fBONCE\fR directives will be ignored.
.TP
.B $InfDelta (read-only)
If non-zero, then the \fB\-t\fR option was supplied on the command line,
@@ -2691,6 +2708,20 @@ by \fBREM\fR commands; triggers in \fBIFTRIG\fR commands do
not affect it.
.RE
.TP
.B $OnceFile (STRING type)
If you set this variable to a non-empty string, then rather than using
the file access date to determine whether or not to run a ONCE-type
reminder, \fBRemind\fR will maintain a timestamp in the file \fB$OnceFile\fR.
This is more reliable than using the access date of the reminder file.
.RS
.PP
If \fB$OnceFile\fR does not exist, then it will be created the first time
a \fBONCE\fR keyword is processed. The file must be writable by the
current user. If you try to set \fB$OnceFile\fR \fIafter\fR a \fBONCE\fR
reminder has already been processed, \fBRemind\fR will issue a warning
and ignore the attempt to set \fB$OnceFile\fR.
.RE
.TP
.B $ParseUntriggered
A flag indicating whether or not \fBRemind\fR should fully parse \fBREM\fR
statements that are not triggered. 0 means to skip parsing them and 1

View File

@@ -60,7 +60,7 @@ my $settings = {
margin_bottom => 36,
margin_left => 36,
margin_right => 36,
svg => 0,
verbose => 0,
};
@@ -80,6 +80,7 @@ Options:
--landscape, -l Print in landscape orientation
--small-calendars=N Choose location for small calendars
--svg Output SVG instead of PDF
-cN Synonym for --small-calendars=N
--left-numbers, -x Print day numbers on the left
--fill-page, -e Fill the entire page
@@ -112,6 +113,7 @@ Getopt::Long::Configure('bundling_values');
my $ret = GetOptions('landscape|l' => \$settings->{landscape},
'small-calendars|c=i' => \$settings->{small_calendars},
'left-numbers|x' => \$settings->{numbers_on_left},
'svg' => \$settings->{svg},
'fill-page|e' => \$settings->{fill_entire_page},
'media|m=s' => \$settings->{media},
'width|w=i' => \$settings->{width},
@@ -187,8 +189,14 @@ my $done_one = 0;
my $errored_out = 0;
my $surface = Cairo::PdfSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height});
my $surface;
if ($settings->{svg}) {
$surface = Cairo::SvgSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height});
} else {
$surface = Cairo::PdfSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height});
}
# set_metadata not available in older versions of Cairo
eval { $surface->set_metadata('title', 'Calendar'); };
@@ -199,6 +207,7 @@ eval { $surface->set_metadata('subject', 'Calendar'); };
my $cr = Cairo::Context->create($surface);
$cr->set_line_width($settings->{line_thickness});
my $warned = 0;
while(1) {
my ($obj, $err) = Remind::PDF->create_from_stream(*STDIN,
{color => 1,
@@ -215,8 +224,15 @@ while(1) {
}
last;
}
$done_one = 1;
if ($settings->{svg} && $done_one) {
if (!$warned) {
print STDERR "WARNING: --svg can only output one page; ignoring subsequent\nmonths in a multi-month calendar.\n";
$warned = 1;
}
next;
}
$obj->render($cr, $settings);
$done_one = 1;
}
$surface->finish();
@@ -273,17 +289,18 @@ __END__
=head1 NAME
rem2pdf - draw a PDF calendar from Remind output
rem2pdf - draw a PDF or SVG calendar from Remind output
=head1 SYNOPSIS
remind -pp [options] file | rem2pdf [options] > output.pdf
remind -pp [options] file | rem2pdf --svg [options] > output.svg
=head1 DESCRIPTION
B<rem2pdf> reads the standard input, which should be the results of
running B<remind> with the B<-p>, B<-pp> or B<-ppp> options. It emits
PDF code that draws a calendar to standard output.
PDF or SVG code that draws a calendar to standard output.
B<rem2pdf> uses the Pango text formatting library (L<https://pango.gnome.org/>)
and the Cairo graphics library (L<https://www.cairographics.org/>) to produce
@@ -298,6 +315,12 @@ output at all.
=over
=item --svg
Output SVG instead of PDF. In this case, you should feed C<rem2pdf>
only one month's worth of calendar data, because it cannot create
a multi-page SVG file.
=item --landscape, -l
Print the calendar in landscape orientation. Essentially, this swaps

View File

@@ -1008,7 +1008,17 @@ as were read from the C<remind -ppp> stream
sub render
{
my ($self, $cr, $settings) = @_;
my $done = 0;
my $warned = 0;
foreach my $e (@{$self->{entries}}) {
if ($settings->{svg} && $done) {
if (!$warned) {
print STDERR "WARNING: --svg can only output one page; ignoring subsequent\nmonths in a multi-month calendar.\n";
$warned = 1;
}
next;
}
$done = 1;
$e->render($cr, $settings);
}
}

View File

@@ -20,7 +20,7 @@
/* western hemisphere. */
/* */
/* The default values are initially set to the city hall in Ottawa, */
/* Ontario, Canada. */
/* Ontario, Canada. */
/*---------------------------------------------------------------------*/
#define DEFAULT_LATITUDE 45.420556
#define DEFAULT_LONGITUDE -75.689722
@@ -68,12 +68,6 @@
/**********************************************************************/
/**********************************************************************/
/*---------------------------------------------------------------------*/
/* WANT_SHELL_ESCAPING: Define this if you want special shell */
/* characters to be escaped with a backslash for the -k option. */
/*---------------------------------------------------------------------*/
#define WANT_SHELL_ESCAPING 1
/*---------------------------------------------------------------------*/
/* BASE: The base year for date calculation. NOTE! January 1 of the */
/* base year MUST be a Monday, else Remind will not work! */
@@ -109,16 +103,6 @@
/*---------------------------------------------------------------------*/
#define MAX_STR_LEN 65535
/*---------------------------------------------------------------------*/
/* OP_STACK_SIZE: The size of the operator stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define OP_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */
/*---------------------------------------------------------------------*/

View File

@@ -20,7 +20,7 @@
/* western hemisphere. */
/* */
/* The default values are initially set to the city hall in Ottawa, */
/* Ontario, Canada. */
/* Ontario, Canada. */
/*---------------------------------------------------------------------*/
#define DEFAULT_LATITUDE 45.420556
#define DEFAULT_LONGITUDE -75.689722
@@ -68,12 +68,6 @@
/**********************************************************************/
/**********************************************************************/
/*---------------------------------------------------------------------*/
/* WANT_SHELL_ESCAPING: Define this if you want special shell */
/* characters to be escaped with a backslash for the -k option. */
/*---------------------------------------------------------------------*/
#define WANT_SHELL_ESCAPING 1
/*---------------------------------------------------------------------*/
/* BASE: The base year for date calculation. NOTE! January 1 of the */
/* base year MUST be a Monday, else Remind will not work! */
@@ -109,16 +103,6 @@
/*---------------------------------------------------------------------*/
#define MAX_STR_LEN 65535
/*---------------------------------------------------------------------*/
/* OP_STACK_SIZE: The size of the operator stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define OP_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */
/*---------------------------------------------------------------------*/

View File

@@ -182,7 +182,7 @@ int DoRem(ParsePtr p)
if (dse == DSEToday &&
!(!IgnoreOnce &&
trig.once != NO_ONCE &&
FileAccessDate == DSEToday))
GetOnceDate() == DSEToday))
QueueReminder(p, &trig, &tim, trig.sched);
/* If we're in daemon mode, do nothing over here */
if (Daemon) {
@@ -1191,7 +1191,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
*err = 0;
/* Handle the ONCE modifier in the reminder. */
if (!IgnoreOnce && t->once !=NO_ONCE && FileAccessDate == DSEToday)
if (!IgnoreOnce && t->once !=NO_ONCE && GetOnceDate() == DSEToday)
return 0;
if (dse < DSEToday) return 0;

View File

@@ -13,7 +13,6 @@
#include "config.h"
#include "types.h"
#define L_IN_DOSUBST
#include <stdio.h>
#include <string.h>
#include <ctype.h>
@@ -141,6 +140,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else {
r = -1;
}
DestroyValue(v);
} else {
Eprint("%s", ErrMsg[r]);
}
@@ -165,6 +165,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else {
r = -1;
}
DestroyValue(v);
} else {
Eprint("%s", ErrMsg[r]);
}

View File

@@ -39,6 +39,7 @@
6) N_USER_FUNC: A reference to a user-defined function
7) N_OPERATOR: A reference to an operator such as "+" or "&&"
8) N_ERROR: A node resulting from a parse error
9) N_SHORT_STR: A string constant short enough to store in u.name
Additional types are N_SHORT_VAR, N_SHORT_SYSVAR, and N_SHORT_USER_FUNC
which behave identically to N_VARIABLE, N_SYSVAR and N_USER_FUNC
@@ -167,8 +168,8 @@ static expr_node *expr_node_free_list = NULL;
#define STACK_ARGS_MAX 5
/* Maximum parse level before we bail (to avoid SEGV from filling stack)*/
#define MAX_PARSE_LEVEL 2000
static int parse_level_high_water = 0;
#define CHECK_PARSE_LEVEL() do { if (level > parse_level_high_water) { parse_level_high_water = level; if (level > MAX_PARSE_LEVEL) { *r = E_OP_STK_OVER; return NULL; } } } while(0)
@@ -198,7 +199,6 @@ static UserFunc *CurrentUserFunc = NULL;
/* How many expr_node objects to allocate at a time */
#define ALLOC_CHUNK 64
/***************************************************************/
/* */
/* alloc_expr_node - allocate an expr_node object */
@@ -585,7 +585,7 @@ debug_exit_userfunc(expr_node *node, Value *ans, int r, Value *locals, int nargs
/* eval_userfunc - evaluate a user-defined function */
/* */
/* This function sets up a local value array by evaluating */
/* all of its children, and then evaluated the expr_node */
/* all of its children, and then evaluates the expr_node */
/* tree associated with the user-defined function. */
/* */
/***************************************************************/
@@ -729,10 +729,26 @@ evaluate_expression(expr_node *node, Value *locals, Value *ans, int *nonconst)
alarm(ExpressionEvaluationTimeLimit);
}
r = evaluate_expr_node(node, locals, ans, nonconst);
alarm(0);
if (ExpressionEvaluationTimeLimit > 0) {
alarm(0);
ExpressionTimeLimitExceeded = 0;
}
return r;
}
static int CopyShortStr(Value *ans, expr_node *node)
{
size_t len = strlen(node->u.name);
ans->v.str = malloc(len+1);
if (!ans->v.str) {
ans->type = ERR_TYPE;
return E_NO_MEM;
}
strcpy(ans->v.str, node->u.name);
ans->type = STR_TYPE;
return OK;
}
/***************************************************************/
/* */
/* evaluate_expr_node - evaluate an expression */
@@ -773,6 +789,9 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
ans->type = ERR_TYPE;
return E_SWERR;
case N_SHORT_STR:
return CopyShortStr(ans, node);
case N_CONSTANT:
/* Constant node? Just return a copy of the constant */
return CopyValue(ans, &(node->u.value));
@@ -1870,6 +1889,7 @@ static int set_constant_value(expr_node *atom)
size_t len;
char const *s = DBufValue(&ExprBuf);
atom->u.value.type = ERR_TYPE;
atom->type = N_CONSTANT;
if (!*s) {
Eprint("%s", ErrMsg[E_EOLN]);
@@ -1878,6 +1898,12 @@ static int set_constant_value(expr_node *atom)
ampm = 0;
if (*s == '\"') { /* It's a literal string "*/
len = strlen(s)-1;
if (len <= SHORT_NAME_BUF) {
atom->type = N_SHORT_STR;
strncpy(atom->u.name, s+1, len-1);
atom->u.name[len-1] = 0;
return OK;
}
atom->u.value.type = STR_TYPE;
atom->u.value.v.str = malloc(len);
if (! atom->u.value.v.str) {
@@ -2013,9 +2039,7 @@ static int make_atom(expr_node *atom, Var *locals)
/* Constant */
r = set_constant_value(atom);
if (r == OK) {
atom->type = N_CONSTANT;
} else {
if (r != OK) {
atom->type = N_ERROR;
}
return r;
@@ -2503,6 +2527,8 @@ expr_node *parse_expression(char const **e, int *r, Var *locals)
*r == E_PARSE_ERR ||
*r == E_MISS_RIGHT_PAREN ||
*r == E_EXPECTING_EOL ||
*r == E_PARSE_ERR ||
*r == E_EOLN ||
*r == E_ILLEGAL_CHAR) {
orig = o2;
while (*orig) {
@@ -2555,6 +2581,9 @@ void print_expr_tree(expr_node *node, FILE *fp)
case N_CONSTANT:
PrintValue(&(node->u.value), fp);
return;
case N_SHORT_STR:
fprintf(fp, "\"%s\"", node->u.name);
return;
case N_SHORT_VAR:
fprintf(fp, "%s", node->u.name);
return;

View File

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

View File

@@ -60,6 +60,9 @@
#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);
@@ -104,7 +107,7 @@ static int FHtmlEscape (func_info *);
static int FHtmlStriptags (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 *);
@@ -263,7 +266,7 @@ BuiltinFunc Func[] = {
{ "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, FIsAny, 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 },
@@ -335,7 +338,7 @@ BuiltinFunc Func[] = {
{ "utctolocal", 1, 1, 1, FUTCToLocal, NULL },
{ "value", 1, 2, 0, FValue, NULL },
{ "version", 0, 0, 1, FVersion, NULL },
{ "weekno", 0, 3, 1, FWeekno, 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 }
@@ -376,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;
@@ -1034,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" */
/* */
/***************************************************************/
@@ -1159,32 +1161,70 @@ 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;
}
/* Debugging helpers for "choose()" and "iif() */
#define PUT(x) DBufPuts(&DebugBuf, x)
#define OUT() do { fprintf(ErrFp, "%s\n", DBufValue(&DebugBuf)); DBufFree(&DebugBuf); } while(0)
/***************************************************************/
/* */
/* FChoose */
@@ -1200,7 +1240,7 @@ static int FChoose(expr_node *node, Value *locals, Value *ans, int *nonconst)
int r;
int n;
int nargs = node->num_kids;
Value(v);
Value v;
DBG(DBufInit(&DebugBuf));
DBG(PUT("choose("));

View File

@@ -78,9 +78,12 @@ 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);
EXTERN INIT( int SortByPrio, 0);
EXTERN INIT( char const *OnceFile, NULL);
EXTERN INIT( int OnceDate, -1);
EXTERN INIT( int ProcessedOnce, 0);
EXTERN INIT( int SortByTime, SORT_NONE);
EXTERN INIT( int SortByDate, SORT_NONE);
EXTERN INIT( int SortByPrio, SORT_NONE);
EXTERN INIT( int UntimedBeforeTimed, 0);
EXTERN INIT( int DefaultPrio, NO_PRIORITY);
EXTERN INIT( int SysTime, -1);
@@ -114,6 +117,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;

View File

@@ -241,7 +241,6 @@ void InitRemind(int argc, char const *argv[])
arg++;
if (!*arg) {
UseStdin = 1;
IgnoreOnce = 1;
i--;
break;
}
@@ -324,6 +323,7 @@ void InitRemind(int argc, char const *argv[])
NextMode = 1;
DontQueue = 1;
Daemon = 0;
IgnoreOnce = 1;
break;
case 'r':
@@ -457,6 +457,7 @@ void InitRemind(int argc, char const *argv[])
break;
case 'c':
case 'C':
IgnoreOnce = 1;
DoCalendar = 1;
weeks = 0;
/* Parse the flags */
@@ -501,6 +502,7 @@ void InitRemind(int argc, char const *argv[])
case 's':
case 'S':
DoSimpleCalendar = 1;
IgnoreOnce = 1;
weeks = 0;
while(*arg) {
if (*arg == 'a' || *arg == 'A') {
@@ -527,6 +529,7 @@ void InitRemind(int argc, char const *argv[])
case 'p':
case 'P':
DoSimpleCalendar = 1;
IgnoreOnce = 1;
PsCal = PSCAL_LEVEL1;
while (*arg == 'a' || *arg == 'A' ||
*arg == 'q' || *arg == 'Q' ||
@@ -720,6 +723,7 @@ void InitRemind(int argc, char const *argv[])
if (rep > 0) {
Iterations = rep;
IgnoreOnce = 1;
DontQueue = 1;
Daemon = 0;
}

View File

@@ -68,17 +68,22 @@ exitfunc(void)
}
}
static void sigalrm(int)
static void sigalrm(int sig)
{
UNUSED(sig);
if (ExpressionEvaluationTimeLimit) {
ExpressionTimeLimitExceeded = 1;
}
}
static void sigxcpu(int)
static void sigxcpu(int sig)
{
write(STDERR_FILENO, "\n\nmax-execution-time exceeded.\n\n", 32);
_exit(1);
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));
}
/***************************************************************/
@@ -109,6 +114,7 @@ int main(int argc, char *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));
@@ -116,6 +122,7 @@ int main(int argc, char *argv[])
}
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",
@@ -151,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]);
@@ -204,7 +211,7 @@ void
PerIterationInit(void)
{
ClearGlobalOmits();
DestroyOmitContexts();
DestroyOmitContexts(1);
DestroyVars(0);
DefaultColorR = -1;
DefaultColorG = -1;
@@ -233,7 +240,8 @@ static void DoReminders(void)
if (!UseStdin) {
FileAccessDate = GetAccessDate(InitialFile);
} else {
FileAccessDate = DSEToday;
FileAccessDate = DSEToday - 1;
if (FileAccessDate < 0) FileAccessDate = 0;
}
if (FileAccessDate < 0) {
@@ -757,43 +765,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);
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, "\n");
}
} else {
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, "\n");
}
}
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
} else if (FileName) {
fprintf(ErrFp, " ");
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp);
va_end(argptr);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, "\n");
}
if (!FileName) {
return;
}
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);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, "\n");
}
if (FreshLine) {
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
}
FreshLine = 0;
}
/***************************************************************/
@@ -962,6 +963,7 @@ int DoIf(ParsePtr p)
}
}
IfLinenos[NumIfs] = LineNo;
NumIfs++;
IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
IfFlags |= syndrome << (2 * NumIfs - 2);
@@ -1215,7 +1217,7 @@ int DoBanner(ParsePtr p)
}
}
DBufFree(&Banner);
err = DBufPuts(&Banner, DBufValue(&buf));
DBufFree(&buf);
return err;
@@ -1890,3 +1892,40 @@ get_month_name(int mon)
if (DynamicMonthName[mon]) return DynamicMonthName[mon];
return MonthName[mon];
}
static int GetOnceDateFromFile(void)
{
FILE *fp;
int once_date = 0;
fp = fopen(OnceFile, "r");
if (fp) {
if (fscanf(fp, "%d", &once_date) != 1) {
once_date = 0;
}
fclose(fp);
}
/* Save today to file */
fp = fopen(OnceFile, "w");
if (!fp) {
Wprint("Warning: Unable to save ONCE timestamp to %s: %s",
OnceFile, strerror(errno));
return once_date;
}
fprintf(fp, "%d\n# This is a timestamp file used by Remind to track ONCE reminders.\n# Do not edit or delete it.\n", DSEToday);
fclose(fp);
return once_date;
}
int GetOnceDate(void)
{
ProcessedOnce = 1;
if (IgnoreOnce || !OnceFile || !*OnceFile) {
return FileAccessDate;
}
if (OnceDate < 0) {
OnceDate = GetOnceDateFromFile();
}
return OnceDate;
}

View File

@@ -100,21 +100,12 @@ static double phase (double, double *, double *, double *, double *, double *, d
#define mmlong 64.975464 /* Moon's mean lonigitude at the epoch */
#define mmlongp 349.383063 /* Mean longitude of the perigee at the
epoch */
#define mlnode 151.950429 /* Mean longitude of the node at the
epoch */
#define minc 5.145396 /* Inclination of the Moon's orbit */
#define mecc 0.054900 /* Eccentricity of the Moon's orbit */
#define mangsiz 0.5181 /* Moon's angular size at distance a
from Earth */
#define msmax 384401.0 /* Semi-major axis of Moon's orbit in km */
#define mparallax 0.9507 /* Parallax at distance a from Earth */
#define synmonth 29.53058868 /* Synodic month (new Moon to new Moon) */
#define lunatbase 2423436.0 /* Base date for E. W. Brown's numbered
series of lunations (1923 January 16) */
/* Properties of the Earth */
#define earthrad 6378.16 /* Radius of Earth in kilometres */
#ifdef PI
#undef PI
#endif
@@ -123,11 +114,6 @@ static double phase (double, double *, double *, double *, double *, double *, d
/* Handy mathematical functions */
#ifdef sgn
#undef sgn
#endif
#define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0)) /* Extract sign */
#ifdef abs
#undef abs
#endif

View File

@@ -14,7 +14,7 @@
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "types.h"
#include "protos.h"
@@ -36,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;
@@ -78,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;
@@ -115,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;
@@ -174,6 +194,7 @@ int PopOmitContext(ParsePtr p)
/* Free memory used by the saved context */
if (c->partsave) free(c->partsave);
if (c->fullsave) free(c->fullsave);
if (c->filename) free(c->filename);
free(c);
return VerifyEoln(p);

View File

@@ -111,7 +111,7 @@ 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);
@@ -225,6 +225,7 @@ void pop_call(void);
void FixSpecialType(Trigger *trig);
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);
void WriteJSONTimeTrigger(TimeTrig const *tt);
int GetOnceDate(void);
#ifdef REM_USE_WCHAR
#define _XOPEN_SOURCE 600
#include <wctype.h>

View File

@@ -359,7 +359,8 @@ void HandleQueuedReminders(void)
if (ShouldFork || Daemon) {
sa.sa_handler = SigIntHandler;
sa.sa_flags = 0;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
(void) sigaction(SIGINT, &sa, NULL);
sa.sa_handler = SigContHandler;
(void) sigaction(SIGCONT, &sa, NULL);

View File

@@ -44,6 +44,7 @@ enum expr_node_type
N_FREE,
N_ERROR,
N_CONSTANT,
N_SHORT_STR,
N_LOCAL_VAR,
N_SHORT_VAR,
N_VARIABLE,

View File

@@ -115,17 +115,6 @@ int DoFset(ParsePtr p)
}
orig_namelen = buf.len;
/* Should be followed by '(' */
c = ParseNonSpaceChar(p, &r, 0);
if (r) {
DBufFree(&buf);
return r;
}
if (c != '(') {
DBufFree(&buf);
return E_PARSE_ERR;
}
/* Convert to lower-case */
strtolower(DBufValue(&buf));
@@ -141,6 +130,18 @@ int DoFset(ParsePtr p)
return OK;
}
}
/* Should be followed by '(' */
c = ParseNonSpaceChar(p, &r, 0);
if (r) {
DBufFree(&buf);
return r;
}
if (c != '(') {
DBufFree(&buf);
return E_PARSE_ERR;
}
func = NEW(UserFunc);
if (!func) {
DBufFree(&buf);

View File

@@ -164,6 +164,40 @@ static int latitude_func(int do_set, Value *val)
return latitude_longitude_func(do_set, val, &Latitude, -90.0, 90.0);
}
static int oncefile_func(int do_set, Value *val)
{
if (do_set) {
if (val->type != STR_TYPE) return E_BAD_TYPE;
if (! (*val->v.str) && (!OnceFile || !*OnceFile)) {
/* Trying to set already-empty string to empty string */
return OK;
}
if (OnceFile && !strcmp(OnceFile, val->v.str)) {
/* Trying to set to the exact same value */
return OK;
}
if (ProcessedOnce) {
Wprint("Not setting $OnceFile: Already processed a reminder with a ONCE clause");
return OK;
}
if (OnceFile) {
free( (void *) OnceFile);
}
OnceFile = StrDup(val->v.str);
if (!OnceFile) return E_NO_MEM;
return OK;
}
if (!OnceFile) {
val->v.str = StrDup("");
} else {
val->v.str = StrDup(OnceFile);
}
if (!val->v.str) return E_NO_MEM;
val->type = STR_TYPE;
return OK;
}
static int terminal_bg_func(int do_set, Value *val)
{
UNUSED(do_set);
@@ -883,6 +917,7 @@ static SysVar SysVarArr[] = {
{"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 },
{"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0 },
{"On", 1, STR_TYPE, &DynamicOn, 0, 0 },
{"OnceFile", 1, SPECIAL_TYPE, oncefile_func, 0, 0 },
{"ParseUntriggered", 1, INT_TYPE, &ParseUntriggered, 0, 1 },
{"Pm", 1, STR_TYPE, &DynamicPm, 0, 0 },
{"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 },

View File

@@ -49,3 +49,7 @@ set a 15:00 - 14:44
set a (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
set a (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
set a isany(1)
set a isany(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6)
set a isany("foo", 1 + 1, 2:00 + 1, '2021-01-01' + 1, '2021-01-01@14:00' + 1, "f" + "oo", "fo" + "o")

4
tests/if1.rem Normal file
View File

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

5
tests/if2.rem Normal file
View File

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

7
tests/test-once.rem Normal file
View File

@@ -0,0 +1,7 @@
BANNER %
SET $OnceFile "../tests/once.timestamp"
REM ONCE MSG This should only be issued once per day.
SET $OnceFile "../tests/once.timestamp"
SET $OnceFile "../tests/once-again.timestamp"

View File

@@ -551,6 +551,38 @@ 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
# Test ONCE with a timestamp file
rm -f ../tests/once.timestamp
../src/remind ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind ../tests/test-once.rem >> ../tests/test.out 2>&1
tail +2 ../tests/once.timestamp >> ../tests/test.out 2>&1
rm -f ../tests/once.timestamp
../src/remind - < ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind - < ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind - < ../tests/test-once.rem >> ../tests/test.out 2>&1
tail +2 ../tests/once.timestamp >> ../tests/test.out 2>&1
rm -f ../tests/once.timestamp
# 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

View File

@@ -1023,7 +1023,7 @@ set a057 value("a05"+"6")
"a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version()
version() => "05.00.00"
version() => "05.00.01"
set a059 wkday(today())
today() => 1991-02-16
wkday(1991-02-16) => "Saturday"
@@ -2612,7 +2612,7 @@ a086 4
a109 2012-01-01
a128 2018-02-03@16:45
a039 "February"
a058 "05.00.00"
a058 "05.00.01"
a077 "1992 92\n"
a096 -4
a119 -1
@@ -2778,6 +2778,7 @@ $NumPartialOmits 0
$NumTrig 41
$October "October"
$On "on"
$OnceFile ""
$ParseUntriggered 1 [0, 1]
$Pm "pm"
$PrefixLineNo 0
@@ -3779,11 +3780,11 @@ isany("foo", 2) => 0
set a isany(1:00, 2)
isany(01:00, 2) => 0
set a isany(1, 2, 1, 3)
isany(1, 2, 1, 3) => 1
isany(1, 2, 1, ?) => 1
set a isany("foo", 2, 3, "foo")
isany("foo", 2, 3, "foo") => 1
set a isany(1:00, 2, "foo", 2:00, 1:00, 9:00)
isany(01:00, 2, "foo", 02:00, 01:00, 09:00) => 1
isany(01:00, 2, "foo", 02:00, 01:00, ?) => 1
# Shellescape
set a shellescape(" !\"#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
@@ -11806,7 +11807,7 @@ SECURITY: Won't read world-writable file or directory!
Error reading include_dir/ww: Can't open file
SECURITY: Won't read world-writable file or directory!
Error reading include_dir/ww: No files matching *.rem
05.00.00
05.00.01
NOTE JSONQUEUE
[{"priority":2,"eventstart":"VOLATILE","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"VOLATILE","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"VOLATILE","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"VOLATILE","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}]
NOTE ENDJSONQUEUE
@@ -12341,8 +12342,49 @@ Parsed expression: (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(
1 + 247 => 248
1 + 248 => 249
1 + 249 => 250
Parsed expression: isany(1)
=> (Isany 1)
isany(1) => 0
Parsed expression: isany(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6)
=> (Isany 1 2 3 4 5 6 1 2 3 4 5 6)
isany(1, 2, 3, 4, 5, 6, 1, ?, ?, ?, ?, ?) => 1
Parsed expression: isany("foo", 1 + 1, 2:00 + 1, '2021-01-01' + 1, '2021-01-01@14:00' + 1, "f" + "oo", "fo" + "o")
=> (Isany "foo" (+ 1 1) (+ 02:00 1) (+ 2021-01-01 1) (+ 2021-01-01@14:00 1) (+ "f" "oo") (+ "fo" "o"))
1 + 1 => 2
02:00 + 1 => 02:01
2021-01-01 + 1 => 2021-01-02
2021-01-01@14:00 + 1 => 2021-01-01@14:01
"f" + "oo" => "foo"
isany("foo", 2, 02:01, 2021-01-02, 2021-01-01@14:01, "foo", ?) => 1
No reminders.
Expression nodes allocated: 512
Expression nodes high-water: 499
Expression nodes leaked: 0
Parse level high-water: 2001
-stdin-(14): Unmatched PUSH-OMIT-CONTEXT at -(7)
-stdin-(14): Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT
No reminders.
../tests/if2.rem(6): Warning: Missing ENDIF
../tests/if2.rem(4): IF without ENDIF
../tests/if2.rem(2): IF without ENDIF
../tests/if1.rem(5): Warning: Missing ENDIF
../tests/if1.rem(3): IF without ENDIF
No reminders.
../tests/test-once.rem(7): Not setting $OnceFile: Already processed a reminder with a ONCE clause
This should only be issued once per day.
../tests/test-once.rem(7): Not setting $OnceFile: Already processed a reminder with a ONCE clause
No reminders.
../tests/test-once.rem(7): Not setting $OnceFile: Already processed a reminder with a ONCE clause
No reminders.
# This is a timestamp file used by Remind to track ONCE reminders.
# Do not edit or delete it.
-stdin-(7): Not setting $OnceFile: Already processed a reminder with a ONCE clause
This should only be issued once per day.
-stdin-(7): Not setting $OnceFile: Already processed a reminder with a ONCE clause
No reminders.
-stdin-(7): Not setting $OnceFile: Already processed a reminder with a ONCE clause
No reminders.
# This is a timestamp file used by Remind to track ONCE reminders.
# Do not edit or delete it.