Compare commits

...

114 Commits

Author SHA1 Message Date
Dianne Skoll
d75587fa7b Update change log. 2025-09-12 11:43:15 -04:00
Dianne Skoll
d9a2b15814 Bump version number to 06.01.01. 2025-09-12 11:33:15 -04:00
Dianne Skoll
aa090bf319 Add tests for PrintValue string truncation. 2025-09-12 11:22:51 -04:00
Dianne Skoll
dc6a229f5f Add some const qualifiers, as recommended by cppcheck. 2025-09-12 11:15:10 -04:00
Dianne Skoll
5a3840759a Make PrintValue truncate long strings on a multibyte-character boundary instead of potentially splitting a UTF-8 sequence. 2025-09-12 11:12:19 -04:00
Dianne Skoll
779174ae32 Proper font formatting. 2025-09-11 17:26:43 -04:00
Dianne Skoll
2f70b37d4c Be stricter about parsing hex constants. 2025-09-11 17:22:24 -04:00
Dianne Skoll
d15c8f106b Support hexadecimal integer constants in expressions; add hex() function. 2025-09-11 17:02:22 -04:00
Dianne Skoll
9d999a0074 Support hexacdecimal integer constants in expressions. 2025-09-11 16:50:13 -04:00
Dianne Skoll
097dda1750 Make asc() always return a number between 0 and 255. 2025-09-11 16:42:32 -04:00
Dianne Skoll
565bca4380 Add statistic for max expr node evaluations per line. 2025-09-11 12:04:32 -04:00
Dianne Skoll
adb38fe82e Fix off-by-one error. 2025-09-11 11:25:20 -04:00
Dianne Skoll
57545ddc3f Add test for --max-expr-complexity 2025-09-11 11:20:29 -04:00
Dianne Skoll
804cf14a78 Implement --max-expr-complexity 2025-09-11 11:11:11 -04:00
Dianne Skoll
641b5cec57 Mode docs on columns() vs mbstrlen() 2025-09-10 23:07:23 -04:00
Dianne Skoll
df53db19c4 Fix typo 2025-09-10 23:02:15 -04:00
Dianne Skoll
d9bf902153 Put #ifdef guards around wide char code. 2025-09-10 17:56:32 -04:00
Dianne Skoll
53a12de2f9 Fix typo 2025-09-10 17:54:26 -04:00
Dianne Skoll
fe2b34da68 Avoid memory leak. 2025-09-10 17:54:06 -04:00
Dianne Skoll
7e70ffe7f5 Fix typo 2025-09-10 17:43:59 -04:00
Dianne Skoll
55975154b1 Add codepoint() and mbchar() functions. 2025-09-10 17:33:57 -04:00
Dianne Skoll
3c6191ba61 Add tests where index() does not find the needle. 2025-09-10 13:48:28 -04:00
Dianne Skoll
b00cf9c5b7 Remove some unnecessary assignments. 2025-09-10 13:48:18 -04:00
Dianne Skoll
a6838802ad Use mbsubstr and mbstrlen. 2025-09-10 13:27:57 -04:00
Dianne Skoll
375576fcc5 Add better support for multi-byte character set.
The functions mbindex, mbstrlen and mbsubstr are just like
index, strlen and substr, but they use and return 1-based
character indexes rather than 1-based byte indexes.
2025-09-10 13:25:27 -04:00
Dianne Skoll
2c9087aa79 Update WHATSNEW and prep for 06.01.00 release. 2025-09-08 12:27:27 -04:00
Dianne Skoll
928f65ae47 Add a few more time zone tests. 2025-09-08 12:26:34 -04:00
Dianne Skoll
ed7b4f5ecc Nicer output. 2025-09-06 21:17:36 -04:00
Dianne Skoll
9aad9401ea Fix man page wording. 2025-09-06 18:15:47 -04:00
Dianne Skoll
4574b552b6 Update WHATSNEW. 2025-09-06 12:05:46 -04:00
Dianne Skoll
21e0940257 Add distro-beta target. 2025-09-06 12:02:20 -04:00
Dianne Skoll
7064893ff2 Reorder man page; add explanation of how omit interacts with time zone. 2025-09-06 11:08:34 -04:00
Dianne Skoll
f8781276e2 Clarify that OMITFUNC also sees dates in the TZ timezone. 2025-09-06 11:00:50 -04:00
Dianne Skoll
b02f61ea05 Make alignment.rem executable. 2025-09-05 18:33:59 -04:00
Dianne Skoll
60447d530d Add a small example. 2025-09-05 18:32:05 -04:00
Dianne Skoll
2835176ad3 Make "make test" run all tests by default. Add individual test-basic and test-tz makefile targets. 2025-09-05 17:46:25 -04:00
Dianne Skoll
b0784feb1a Bump version to 06.01.00. 2025-09-04 15:58:07 -04:00
Dianne Skoll
ef7021972e Add trigimetz and trigeventstarttz functions. 2025-09-04 15:36:27 -04:00
Dianne Skoll
e449fcdfcf Add tests for trigtz 2025-09-04 14:29:00 -04:00
Dianne Skoll
47a4456c20 Explicitly initialize LastTrigger.tz to NULL. 2025-09-04 14:26:35 -04:00
Dianne Skoll
e9ee7492f7 Check for setuid/setgid early. 2025-09-04 14:25:09 -04:00
Dianne Skoll
2baa0405f3 Document new JSON key pairs. 2025-09-04 13:54:34 -04:00
Dianne Skoll
3425530c1c Add Makefile for running tests from tests/ with "make test" 2025-09-04 11:00:03 -04:00
Dianne Skoll
eeae3298ef Nicer formatting. 2025-09-04 10:59:58 -04:00
Dianne Skoll
4237dc4a3f Allow empty string to be used in tzconvert to designate system default time zone. 2025-09-04 10:09:46 -04:00
Dianne Skoll
faf8947dda Properly cache stdin in interactive mode. 2025-09-04 00:15:21 -04:00
Dianne Skoll
41a3db3a37 Fix abort. 2025-09-03 23:14:58 -04:00
Dianne Skoll
f12bcf3fe6 Restructure test targets. 2025-09-03 22:36:46 -04:00
Dianne Skoll
f9261bb24f Better target name 2025-09-03 19:35:28 -04:00
Dianne Skoll
2ff9aedba5 Add test. 2025-09-03 19:34:48 -04:00
Dianne Skoll
1b793eff2e Hard-code datetimes so slight astronomical function variations don't mess up test. 2025-09-03 19:33:18 -04:00
Dianne Skoll
e4c41e0f45 More TZ tests. 2025-09-03 19:13:55 -04:00
Dianne Skoll
9ec1006804 Make an empty TZ (ie, TZ "") the same as no TZ. 2025-09-03 19:10:26 -04:00
Dianne Skoll
dd048c0557 Formatting. 2025-09-03 19:06:23 -04:00
Dianne Skoll
dce1f282f4 Improve Makefile. 2025-09-03 19:05:41 -04:00
Dianne Skoll
7120d07d84 Add testtz and test-all targets. 2025-09-03 19:03:59 -04:00
Dianne Skoll
127358db02 The official IANA name of UTC is "Universal". 2025-09-03 18:55:43 -04:00
Dianne Skoll
a89aaf18b6 Add another distclean target 2025-09-03 18:37:33 -04:00
Dianne Skoll
5734bd7524 Start adding TZ tests. 2025-09-03 18:36:13 -04:00
Dianne Skoll
dfabda7dee add trigtz() function 2025-09-03 14:19:31 -04:00
Dianne Skoll
da146d7989 Always output time_in_tz in JSON when needed. 2025-09-03 14:08:56 -04:00
Dianne Skoll
e147925462 Make tests pass; update remind conf mode. 2025-09-03 13:58:09 -04:00
Dianne Skoll
703907c94d Update JSON output. 2025-09-03 13:57:25 -04:00
Dianne Skoll
bcfee04cae Handle another time zone edge case. 2025-09-03 13:21:16 -04:00
Dianne Skoll
5f9e71f9eb More timezone fixification. 2025-09-03 13:05:54 -04:00
Dianne Skoll
272336226e Add timezone support to evaltrig. 2025-09-03 12:42:59 -04:00
Dianne Skoll
32f4b125b5 Seemingly (?) working time zone support!! 2025-09-03 12:35:51 -04:00
Dianne Skoll
7979a69cb9 First semi-sorta-working time zone adjustment. 2025-09-03 12:22:09 -04:00
Dianne Skoll
651368fedc More work on timezone support. 2025-09-03 11:35:21 -04:00
Dianne Skoll
fe002557cf New approach to handling time zone specs in REM command. 2025-09-03 11:02:01 -04:00
Dianne Skoll
d2785d909c Remember DSEToday and SysTime in Local* variables so we can switch timezones. 2025-09-03 10:36:30 -04:00
Dianne Skoll
c2345a240b Update docs to reflect multi-type system variables. 2025-09-02 09:39:32 -04:00
Dianne Skoll
5b6bf25a20 Document readline support. 2025-09-01 10:14:22 -04:00
Dianne Skoll
44f9f0f0d4 Better quotes. 2025-09-01 10:09:27 -04:00
Dianne Skoll
0416d6da9c Update to version 06.00.02. 2025-09-01 10:02:14 -04:00
Dianne Skoll
5d46df871f Clamp trigger() to "1 January 1990 AT 00:00" if UTC flag is used. 2025-08-28 13:30:21 -04:00
Dianne Skoll
c5d661124c Never return a year < 1990 from trigger() 2025-08-28 12:53:28 -04:00
Dianne Skoll
2f239f77a5 Default $ParseUntriggered to 0 instead of 1 2025-08-27 11:34:10 -04:00
Dianne Skoll
1ef05d3f85 Slight efficiency improvement. 2025-08-21 11:50:45 -04:00
Dianne Skoll
7c56aad791 FIx tests. 2025-08-21 11:49:16 -04:00
Dianne Skoll
5f0f68f508 Allow '-i$foo' to be interpreted as '-i$foo=0' 2025-08-20 10:27:22 -04:00
Dianne Skoll
71bfe31002 Make IS_INTERACTIVE() macro. 2025-08-19 21:39:31 -04:00
Dianne Skoll
4661b454c0 Don't add *everything* to history, LOL. 2025-08-19 21:32:26 -04:00
Dianne Skoll
d41520a04f Better example. 2025-08-19 15:54:03 -04:00
Dianne Skoll
0206e538e0 Avoid -Wunused-result warning. 2025-08-19 15:22:05 -04:00
Dianne Skoll
6689cbfda3 Explicitly set latitude and longitude in test files. 2025-08-19 15:04:03 -04:00
Dianne Skoll
5baf102bfe Update WHATSNEW. 2025-08-19 12:43:00 -04:00
Dianne Skoll
c9002d5b54 Bump version to 06.00.01 2025-08-19 12:38:38 -04:00
Dianne Skoll
ac3ee7e22b Use constant-time DBufLen instead of O(n) strlen() 2025-08-19 10:21:01 -04:00
Dianne Skoll
f366037b8d Truncate over-long variable name in DUMP. 2025-08-19 10:13:09 -04:00
Dianne Skoll
a46488a50d Remove incorrect assignment that could cause a segfault. 2025-08-19 09:58:04 -04:00
Dianne Skoll
f91a1a2d65 If we don't have readline, don't declare unused variable. 2025-08-18 21:05:56 -04:00
Dianne Skoll
2ac8fb50e1 Fix typos. 2025-08-18 21:01:48 -04:00
Dianne Skoll
7df826f635 Add whole line at a time to history buffer. 2025-08-18 20:35:10 -04:00
Dianne Skoll
f8ce7b51da Change prompt depending on whether or not we're ignoring lines. 2025-08-18 16:26:58 -04:00
Dianne Skoll
35ee94ca6b If stdin and stdout are TTYs, use readline() where possible. 2025-08-18 16:20:45 -04:00
Dianne Skoll
c2a3468e04 Prep for 6.0.0 RELEASE. 2025-08-18 10:56:18 -04:00
Dianne Skoll
4ff2064452 Fix typos. 2025-08-18 10:50:52 -04:00
Dianne Skoll
8e00bd5acc Fix typo in comment. 2025-08-16 18:32:08 -04:00
Dianne Skoll
86f65e11bb Use keypress-slash to auto-fill COMPLETE-THROUGH field instead of double-click. 2025-08-16 11:54:19 -04:00
Dianne Skoll
0c9ec11fce Add clarifying comment. 2025-08-16 10:09:44 -04:00
Dianne Skoll
07dcaec176 Fix typos. 2025-08-15 22:57:13 -04:00
Dianne Skoll
e87849256b Make --flush simply set standard I/O streams to unbuffered. 2025-08-15 22:41:18 -04:00
Dianne Skoll
fd8ecd88a8 Add --flush option; use it in tests.
Should help with weird systems whose C libraries have different flushing rules.
2025-08-15 22:36:20 -04:00
Dianne Skoll
5e36a6563a Format improvement. 2025-08-15 22:26:18 -04:00
Dianne Skoll
274a2bf067 Don't unconditionally set RunDIsabled. 2025-08-15 20:53:07 -04:00
Dianne Skoll
4aa737e542 Tweak man page 2025-08-15 20:46:05 -04:00
Dianne Skoll
6474f4e0b6 Note that user-defined functions defined in a RUN-OFF context will have RUN OFF during evaluation. 2025-08-15 20:43:59 -04:00
Dianne Skoll
7b7b861399 Disable RUN in callbacks to ordx and subst_xxx functions. 2025-08-15 20:27:51 -04:00
Dianne Skoll
5cb0e82be2 Remove stray ^G. 2025-08-15 20:10:50 -04:00
Dianne Skoll
d9a4bd19f2 Remove extraneous semicolons. 2025-08-15 19:12:14 -04:00
Dianne Skoll
1004946d26 Add a couple of missing .PPs 2025-08-15 18:15:37 -04:00
Dianne Skoll
d877a6cb48 Fix a couple of typos. 2025-08-15 18:05:48 -04:00
Dianne Skoll
3eea329b32 Add some missing .fi directives. 2025-08-15 18:04:42 -04:00
44 changed files with 4169 additions and 2259 deletions

11
.gitignore vendored
View File

@@ -4,12 +4,12 @@
.gitignore
MYMETA.json
MYMETA.yml
Makefile
TAGS
autom4te.cache
blib/
config.log
config.status
gmon.out
man/rem.1
man/rem2ps.1
man/remind.1
@@ -18,6 +18,7 @@ pm_to_blib
rem2html/Makefile
rem2html/rem2html
rem2html/rem2html.1
rem2pdf/Makefile
rem2pdf/Makefile.PL
rem2pdf/Makefile.old
rem2pdf/Makefile.top
@@ -30,8 +31,8 @@ src/rem2ps
src/remind
src/test-*.out
src/version.h
tests/test.out
www/Makefile
gmon.out
tests/once.timestamp
src/xlat.c
tests/once.timestamp
tests/test.out
tests/tz.out
www/Makefile

View File

@@ -39,14 +39,21 @@ install-stripped:
@$(MAKE) -C rem2html install
@$(MAKE) -C rem2pdf -f Makefile.top install INSTALL_BASE=$(INSTALL_BASE)
test:
@$(MAKE) -C src -s test
test: test-basic test-tz
test-tz:
@$(MAKE) -C src -s all
@$(MAKE) -C src -s test-tz
test-basic:
@$(MAKE) -C src -s all
@$(MAKE) -C src -s test-basic
cppcheck:
@$(MAKE) -C src cppcheck
distclean: clean
-rm -f config.cache config.log config.status src/Makefile src/version.h src/config.h tests/test.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf rem2html/rem2html
-rm -f config.cache config.log config.status src/Makefile src/version.h src/config.h tests/test.out tests/tz.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf rem2html/rem2html
-rm -f man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1 scripts/tkremind
-rm -rf autom4te.cache rem2html/Makefile rem2html/rem2html.1

View File

@@ -9,7 +9,14 @@ the GNU General Public License, Vesion 2.
### Remind and Rem2PS
**remind** and **rem2ps** have no prerequisites beyond the standard C
library and the standard math library.
library and the standard math library. **remind** will make use of
GNU Readline if you have it installed.
- On Debian-like systems, install GNU Readline as follows:
`apt install libreadline-dev`
- On RPM-based systems, you need `readline-devel`
### Rem2HTML and Rem2PDF

79
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.72 for remind 06.00.00.
# Generated by GNU Autoconf 2.72 for remind 06.01.01.
#
#
# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation,
@@ -601,8 +601,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='remind'
PACKAGE_TARNAME='remind'
PACKAGE_VERSION='06.00.00'
PACKAGE_STRING='remind 06.00.00'
PACKAGE_VERSION='06.01.01'
PACKAGE_STRING='remind 06.01.01'
PACKAGE_BUGREPORT=''
PACKAGE_URL='https://dianne.skoll.ca/projects/remind/'
@@ -1258,7 +1258,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 06.00.00 to adapt to many kinds of systems.
'configure' configures remind 06.01.01 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1320,7 +1320,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of remind 06.00.00:";;
short | recursive ) echo "Configuration of remind 06.01.01:";;
esac
cat <<\_ACEOF
@@ -1408,7 +1408,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
remind configure 06.00.00
remind configure 06.01.01
generated by GNU Autoconf 2.72
Copyright (C) 2023 Free Software Foundation, Inc.
@@ -1871,7 +1871,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 06.00.00, which was
It was created by remind $as_me 06.01.01, which was
generated by GNU Autoconf 2.72. Invocation command line was
$ $0$ac_configure_args_raw
@@ -2477,6 +2477,8 @@ as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H"
as_fn_append ac_header_c_list " readline/readline.h readline_readline_h HAVE_READLINE_READLINE_H"
as_fn_append ac_header_c_list " readline/history.h readline_history_h HAVE_READLINE_HISTORY_H"
# Auxiliary files required by this configure script.
ac_aux_files="install-sh"
@@ -3941,6 +3943,57 @@ then :
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for readline in -lreadline" >&5
printf %s "checking for readline in -lreadline... " >&6; }
if test ${ac_cv_lib_readline_readline+y}
then :
printf %s "(cached) " >&6
else case e in #(
e) ac_check_lib_save_LIBS=$LIBS
LIBS="-lreadline $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply.
The 'extern "C"' is for builds by C++ compilers;
although this is not generally supported in C code supporting it here
has little cost and some practical benefit (sr 110532). */
#ifdef __cplusplus
extern "C"
#endif
char readline (void);
int
main (void)
{
return readline ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_readline_readline=yes
else case e in #(
e) ac_cv_lib_readline_readline=no ;;
esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS ;;
esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_readline_readline" >&5
printf "%s\n" "$ac_cv_lib_readline_readline" >&6; }
if test "x$ac_cv_lib_readline_readline" = xyes
then :
printf "%s\n" "#define HAVE_LIBREADLINE 1" >>confdefs.h
LIBS="-lreadline $LIBS"
fi
ac_header= ac_cache=
for ac_item in $ac_header_c_list
do
@@ -3973,6 +4026,8 @@ fi
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'.
@@ -4270,6 +4325,12 @@ then :
printf "%s\n" "#define HAVE_INOTIFY_INIT1 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "readline" "ac_cv_func_readline"
if test "x$ac_cv_func_readline" = xyes
then :
printf "%s\n" "#define HAVE_READLINE 1" >>confdefs.h
fi
VERSION=$PACKAGE_VERSION
@@ -4787,7 +4848,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 06.00.00, which was
This file was extended by remind $as_me 06.01.01, which was
generated by GNU Autoconf 2.72. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -4852,7 +4913,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 06.00.00
remind config.status 06.01.01
configured by $0, generated by GNU Autoconf 2.72,
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, 06.00.00, , , https://dianne.skoll.ca/projects/remind/)
AC_INIT(remind, 06.01.01, , , https://dianne.skoll.ca/projects/remind/)
AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF'
@@ -30,7 +30,8 @@ AC_PATH_PROG([PERL], [perl])
dnl Checks for libraries.
AC_CHECK_LIB(m, sqrt)
AC_CHECK_HEADERS_ONCE([sys/time.h stdint.h])
AC_CHECK_LIB(readline, readline)
AC_CHECK_HEADERS_ONCE([sys/time.h stdint.h readline/readline.h readline/history.h])
dnl Integer sizes
AC_CHECK_SIZEOF(unsigned int)
@@ -84,7 +85,7 @@ if test "$?" != 0 ; then
echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!"
exit 1
fi
AC_CHECK_FUNCS(strdup strcasecmp strncasecmp setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1)
AC_CHECK_FUNCS(strdup strcasecmp strncasecmp setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1 readline)
VERSION=$PACKAGE_VERSION
CONFIG_CMD="$0$ac_configure_args_raw"

View File

@@ -120,7 +120,7 @@
"PSFILE" "PUSH" "PUSH-FUNCS" "PUSH-VARS" "PUSH-OMIT-CONTEXT" "REM" "RETURN"
"RUN" "SATISFY" "SCAN" "SCANFROM" "SCHED" "SECOND" "SET"
"SKIP" "SPECIAL" "SYSINCLUDE" "TAG" "THIRD" "THROUGH" "TODO"
"TRANSLATE" "TRANS" "UNSET" "UNTIL" "WARN")
"TRANSLATE" "TRANS" "TZ" "UNSET" "UNTIL" "WARN")
#'(lambda (a b) (> (length a) (length b)))))
@@ -166,12 +166,13 @@
(defconst remind-builtin-functions
(sort
(list "_" "abs" "access" "adawn" "adusk" "ampm" "ansicolor" "args" "asc"
"baseyr" "catch" "catcherr" "char" "choose" "coerce" "columns" "const" "current" "date"
"baseyr" "catch" "catcherr" "char" "choose" "codepoint" "coerce" "columns" "const" "current" "date"
"datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst"
"dusk" "easterdate" "escape" "eval" "evaltrig" "filedate" "filedatetime"
"filedir" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear"
"filedir" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear" "hex"
"hour" "htmlescape" "htmlstriptags" "iif" "index" "isany" "isconst" "isdst"
"isleap" "isomitted" "language" "localtoutc" "lower" "max" "min"
"isleap" "isomitted" "language" "localtoutc" "lower" "max"
"mbasc" "mbindex" "mbstrlen" "mbsubstr" "min"
"minsfromutc" "minute" "mon" "monnum" "moondate" "moondatetime"
"moonphase" "moonrise" "moonrisedir" "moonset" "moonsetdir" "moontime"
"multitrig" "ndawn" "ndusk" "nonconst" "nonomitted" "now" "ord" "orthodoxeaster"
@@ -179,9 +180,9 @@
"realtoday" "rows" "sgn" "shell" "shellescape" "slide" "soleq"
"stdout" "strlen" "substr" "sunrise" "sunset" "time" "timepart"
"timezone" "today" "trig" "trigback" "trigbase" "trigcompletethrough" "trigdate" "trigdatetime"
"trigdelta" "trigduration" "trigeventduration" "trigeventstart"
"trigdelta" "trigduration" "trigeventduration" "trigeventstart" "trigeventstarttz"
"trigfrom" "trigger" "triginfo" "trigistodo" "trigmaxoverdue" "trigpriority" "trigrep"
"trigscanfrom" "trigtags" "trigtime" "trigtimedelta" "trigtimerep"
"trigscanfrom" "trigtags" "trigtime" "trigtimedelta" "trigtimerep" "trigtimetz" "trigtz"
"triguntil" "trigvalid" "typeof" "tzconvert" "upper" "utctolocal"
"value" "version" "weekno" "wkday" "wkdaynum" "year"
)

View File

@@ -1,6 +1,92 @@
CHANGES TO REMIND
* VERSION 6.0 Patch 0 - 2025-??-??
* VERSION 6.1 Patch 1 - 2025-09-12
- NEW FEATURE: remind: Add the new --max-expr-complexity=n
command-line argument. It is possible to write expressions that use
enormous amounts of CPU time, such as the following:
FSET fib(n) iif(n <= 2, 1, fib(n-1)+fib(n-2))
SET a fib(100)
That will take essentially forever to execute, but will not hit the
built-in recursion limit. Using a command-line argument of
--max-expr-complexity=1000000 will terminate evaluation in a few
dozen milliseconds on modern hardware, and should not affect
realistic reminder scripts. See the man page for details.
- IMPROVEMENT: remind: Add UTF-8-aware functions to complement the
byte-aware functions that could give incorrect results by splitting
a UTF-8 character sequence. The correspondence between old and new
functions is:
NON-UTF-8-AWARE UTF-8-AWARE
=============== ===========
strlen mbstrlen
substr mbsubstr
index mbindex
char mbchar
asc codepoint
See the remind(1) man page for details.
- MINOR NEW FEATURE: remind: You can use hexadecimal integer constants
like 0xFE12 in expressions. This is mostly useful for using
codepoint() since Unicode code points are often expressed in
hexadecimal.
- BUG FIX: remind: When truncating a string when executing DUMPVARS or
during debugging of expression evaluation, Remind could sometimes
cut the string in the middle of a UTF-8 sequence. This has been
fixed.
* VERSION 6.1 Patch 0 - 2025-09-08
- MAJOR NEW FEATURE: remind: The TZ keyword lets you specify a time
zone for a REM command. All trigger calculations are performed in
the named time zone.
- DOCUMENTATION: Update README.md to document prerequisites for readline
support.
- DOCUMENTATION: remind.1: Document system variables such as $T that can
have multiple types.
- BUG FIX: remind: If Remind was run interactively ("remind -c -" with
readline support enabled) it would not properly cache the input
file, but would keep asking for interactive input. This has been
fixed.
* VERSION 6.0 Patch 2 - 2025-09-01
- CHANGE: Default $ParseUntriggered to 0 instead of 1. The default
could cause spurious warnings such as "type mismatch" warnings for
untriggered reminders.
- TEST FIX: Explicitly set latitude and longitude in test files.
- BUG FIX: remind: Don't add lines to "readline" history unless they
are actually being read from standard input.
- BUG FIX: remind: On the command-line, make '-i$foo' behave the same
way as '-i$foo=0' as was documented in the man page.
- BUG FIX: remind: Clamp the output of trigger() to "1 January 1990 AT
00:00" if the UTC flag is used, even if it would actually produce a
result in 1989.
- MINOR FIX: remind: Avoid "unused result" compiler warning.
* VERSION 6.0 Patch 1 - 2025-08-19
- NEW FEATURE: remind: Add readline support if input is taken from stdin
(ie, "remind -"). This gives you full editing support if you run
Remind interactively.
- BUG FIX: remind: Fix buffer overflow in DUMPVARS command.
Thanks to Jochen Sprickerhof for helping me find this bug.
* VERSION 6.0 Patch 0 - 2025-08-18
- MAJOR NEW FEATURE: remind: Introduction of TODOs. These are similar
to normal reminders, but (in Agenda Mode) you keep getting reminded
@@ -20,6 +106,11 @@ CHANGES TO REMIND
subst_colon, subst_bang, subst_question, subst_at and subst_hash,
respectively.
- SAFETY IMPROVEMENT: remind: If a function is defined in a context
where RUN is disabled, disable RUN during the evaluation of the
function. Also disable RUN for all subst_XXX callbacks and the
ordx(n) callback.
- MAJOR IMPROVEMENTS: tkremind: TkRemind has been given an overhaul.
The "Show Queue" and "Show Today's Reminders" windows now respect
the color scheme. TkRemind lets you create TODO reminders and
@@ -49,6 +140,9 @@ CHANGES TO REMIND
- MINOR IMPROVEMENT: tkremind: TkRemind now passes all command-line
options back to Remind.
- TEST IMPROVEMENT: remind: Add --flush option and use it in tests to
have more predictable interleaving of stdout/stderr output.
- BUG FIX: tkremind: In a couple of places, the "eval" command was used
where the intention was to use "catch". I blame Perl...
@@ -56,6 +150,11 @@ CHANGES TO REMIND
in the JSON output, the original FROM or SCANFROM value is
preserved, including relative SCANFROMs.
- BUG FIX: tkremind: Remove some extraneous semicolons.
- DOCUMENTATION FIX: Fix some man-page format directive errors;
tweak wording in several pages.
* VERSION 5.5 Patch 0 - 2025-07-28
- NEW FEATURE: remind: Add the PUSH-VARS / POP-VARS commands and the

1
examples/alignment.rem Normal file → Executable file
View File

@@ -1,3 +1,4 @@
#!/usr/bin/env -S remind -@2
# Demo the columns() function
#
# Run as: remind -@2 alignment.rem

24
examples/tflag.rem Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env -S remind -@2
# This is a little Easter Egg that simply draws
# the Transgender Pride Flag. It's included in
# Remind because (1) the author is trans and (2)
# trans people are under attack in the USA and many
# other places. So this is a little show of support
# and resistance for trans people.
SET $AddBlankLines 0
BANNER %
set c max(columns()-1, 10)
set r max(rows(), 5)
set bar "█" * c
set dups r/5
set bars (bar + "%_") * dups
set bars mbsubstr(bars, 0, mbstrlen(bars)-2)
REM SPECIAL COLOR 91 206 250 [bars]
REM SPECIAL COLOR 245 169 184 [bars]
REM SPECIAL COLOR 255 255 255 [bars]
REM SPECIAL COLOR 245 169 184 [bars]
REM SPECIAL COLOR 91 206 250 [bars]

View File

@@ -544,11 +544,20 @@ will produce the following \fBinfo\fR hash:
"summary" : "None"
},
.fi
.PP
.RE
.TP
.B tz \fIzone\fR
If a non-empty TZ clause was present, this key will contain the time zone
name.
.TP
.B time \fIt\fR
If an AT clause was present, this key will contain the time of the AT clause
in minutes after midnight.
in minutes after midnight \fIin the system default time zone\fR.
.TP
.B time_in_tz \fIt\fR
If a TZ clause was present, this key will contain the time of the AT clause
in minutes after midnight \fIin the time zone specified by TZ\fR.
.TP
.B tdelta \fIn\fR
If a time delta (+n after an AT clause) was present, this key contains the
@@ -569,7 +578,12 @@ discussion of duration vs. event duration.
.TP
.B eventstart \fIdt\fR
If an AT clause was present, this key contains the event start time in
the format \fIYYYY-MM-DDTHH:MM\fR.
the format \fIYYYY-MM-DD\fRT\fIHH:MM\fR in the \fIsystem default time zone\fR.
.TP
.B eventstart_in_tz \fIdt\fR
If a TZ clause was present, this key containes the event start time in
the format \fIYYYY-MM-DD\fRT\fIHH:MM\fR in the \fItime zone specified
by TZ\fR.
.TP
.B back \fIn\fR
If the reminder contained a "back" clause (\-n or \-\-n), this key
@@ -684,8 +698,8 @@ emits the line:
However, back-ends should keep reading until EOF in case more data for
subsequent months is forthcoming.
.PP
.SH REM2PS PURE JSON INPUT FORMAT (-PPP OR -P+ OPTION)
.PP
\fBRemind \-ppp\fR and \fBremind \-p+\fR emit \fIpure JSON\fR output.
The format is as follows:
.PP

View File

@@ -11,8 +11,9 @@ 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.
takes its input from standard input. In this case, if \fBRemind\fR
has been compiled against the GNU Readline library, it will use
Readline to give you an interactive line-editing interface.
.PP
If \fIfilename\fR happens to be a directory rather than a plain file,
then \fBRemind\fR reads all of the files (but not any subdirectories!)
@@ -615,9 +616,23 @@ If \fBRemind\fR finishes processing the script and then starts handling
queued reminders, the time limit is reset to no limit.
.RE
.TP
.B \-\-max-expr-complexity\fR=\fIn\fR
Limit the total complexity of expression valuation for a given line in a script
to \fIn\fR nodes. Roughly speaking, each function call, operator, constant,
variable reference, etc corresponds to one expression node. By default,
there's no limit to how many nodes can be evaluated per line, but if you
set this to a high number like 1000000 (one million) or so, you are unlikely
to cause problems with any real-world scripts while preventing pathological
behavior from badly-written or malicious expressions.
.TP
.B \-\-test
The \fB\-\-test\fR long option is only for use by the acceptance tests
run by "make test". Do not use this option in production.
.TP
.B \-\-flush
The \fB\-\-flush\fR long option makes \fBRemind\fR's standard output
and standard error streams unbuffered. It is mostly used for testing and
should probably not be used in production.
.SH REMINDER FILES
.PP
\fBRemind\fR uses scripts to control its operation. You can use any
@@ -687,6 +702,7 @@ Its syntax is:
[\fBSCANFROM\fR \fIscan_date\fR | \fBFROM\fR \fIstart_date\fR]
[\fBDURATION\fR \fIduration\fR]
[\fBTAG\fR \fItag\fR]
[\fBTZ\fR \fItimezone\fR]
[\fBINFO\fR "\fIinfo_string\fR"]
\fBMSG\fR | \fBMSF\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR |
\fBSPECIAL\fR \fIspecial\fR | \fBPS\fR | \fBPSFILE\fR
@@ -2200,6 +2216,81 @@ the first day of the month. The local \fBOMIT\fR keyword causes the
Finally, the \fBAFTER\fR keyword will keep moving the reminder forward
until it has passed any holidays specified with global \fBOMIT\fR
commands.
.PP
.SH TIMEZONE SUPPORT
.PP
The \fBREM\fR command supports an optional \fBTZ\fR keyword, which should
be followed by the \fIcase-sensitive\fR time zone name in which the
command is to be interpreted. Note that if you use the \fBTZ\fR keyword,
then you \fImust also\fR use an \fBAT\fR clause. Here are some examples:
.PP
.nf
REM Wednesday AT 14:00 TZ America/Toronto MSG 2PM Eastern (%2).
REM Wednesday AT 23:59 TZ America/Los_Angeles SATISFY [$Td == 13] MSG Foo %b %2.
.fi
.PP
Within a \fBSATISFY\fR clause and an \fBOMITFUNC\fR function, all
trigger functions and the trigger date are interpreted \fIin the time
zone specified in the \fBREM\fI command\fR. Outside the \fBREM\fR
command, however, trigger functions are adjusted to the local time
zone. If the local time zone is UTC and we feed \fBRemind\fR the
following file on 2025-09-04 UTC:
.PP
.nf
SET $AddBlankLines 0
BANNER %
REM Wednesday AT 14:00 TZ America/Toronto MSG 2PM Eastern (%2).
set a $T
set b $Tt
REM MSG a = [a], b = [b]
REM Wednesday AT 23:59 TZ America/Los_Angeles SATISFY [$Td == 13] MSG Foo %b %2.
set c $T
set d $Tt
REM MSG c = [c], d = [d]
.fi
.PP
Then the output is as follows:
.PP
.nf
a = 2025-09-10, b = 18:00
c = 2026-05-14, d = 06:59
.fi
.PP
That is because the trigger date of the first (Wednesday, 2025-09-10
at 14:00 Eastern time) is 2025-09-10 at 18:00 UTC. In the second case,
Wednesday, 13 May 2026 is the SATISFied trigger date, which is
adjusted to Thursday, 14 May 2026 at 06:59 UTC because of the
time zone adjustment.
.PP
If you use an invalid time zone name after the \fBTZ\fR keyword,
results are undefined. Unfortunately, \fBRemind\fR cannot diagnose
this error becase the C library \fBtzset()\fR function has no
error return.
.PP
In a reminder with the \fBTZ\fR keyword, OMIT dates are evaluated in
the specified time zone. Here's an example: Suppose the local time zone
is America/Toronto and you have this script:
.PP
.nf
# A Friday
OMIT 2025-09-05
# A Saturday
OMIT 2025-09-13
REM Saturday AT 01:00 TZ Europe/Amsterdam SKIP MSG Early Sat AM
.fi
.PP
On 2025-09-05 in the America/Toronto time zone, the reminder \fIwill\fR
trigger. Even though 2025-09-05 has been OMITted, the SKIP keyword
evaluates the date in the Europe/Amsterdam time zone, and 2025-09-06
(the trigger date) is \fInot\fR omitted.
.PP
Conversely, on 2025-09-12 in the America/Toronto time zone, the
reminder will \fInot\fR trigger. Even though 2025-09-12 is not OMITted,
2025-09-13 is, and that is the trigger date in the Europe/Amsterdam
time zone.
.PP
.SH THE DO, INCLUDE AND SYSINCLUDE COMMANDS
.PP
\fBRemind\fR allows you to include other files in your reminder script,
@@ -2258,7 +2349,7 @@ symbolic links to files.
.PP
The \fBSYSINCLUDE\fR command is similar to \fBDO\fR, but it looks for
relative pathnames under the system directory containing standard reminder
scripts. For this version of \fBRemind\fR, the system directory is
scripts. For this installation of \fBRemind\fR, the system directory is
"@prefix@/share/remind".
.PP
.SH THE RETURN COMMAND
@@ -2458,6 +2549,26 @@ word. The \fBINT\fR data type corresponds to the C "int" type.
The \fBSTRING\fR data type consists of strings of characters. It is
somewhat comparable to a C character array, but more closely resembles
the string type in BASIC.
.RS
.PP
\fBRemind\fR normally expects to be running in a UTF-8 environment.
In this environment, there is a difference between \fIbytes\fR and
\fIcharacters\fR since in UTF-8, a character may be represented by
a sequence of more than one byte. For example, in a UTF-8 environment,
the string "🙂" contains one character but four bytes. And the string
"één" contains three characters but five bytes.
.PP
\fBRemind\fR has a set of functions
that work on \fIbytes\fR, namely \fBindex\fR, \fBstrlen\fR and \fBsubstr\fR.
These are not safe to use on multi-byte strings; instead use
\fBmbindex\fR, \fBmbstrlen\fR and \fBmbsubstr\fR. If you know \fIfor sure\fR
that a string contains only single-byte characters, then the byte-oriented
versions may be used and are faster than the multi-byte versions.
.PP
Some ancient or embedded systems may lack the C library functions needed
to deal with multi-byte strings. In that case, the \fBmb\fIxxx\fR functions
all return an error.
.RE
.TP
.B TIME
The \fBTIME\fR data type is used for two different purposes: To represent
@@ -2494,7 +2605,7 @@ as being the combination of \fBDATE\fR and \fBTIME\fR parts.
The following examples illustrate constants in \fBRemind\fR expressions:
.TP
.B INT constants
12, 36, \-10, 0, 1209
12, 36, \-10, 0, 1209, 0x1F, 0xfe00 (the last two demonstrate the use of hexadecimal constants)
.TP
.B STRING constants
"Hello there", "This is a test", "\\nHello\\tThere", ""
@@ -2875,6 +2986,7 @@ reminders to detect duplicates. Consider the following example:
REM Wednesday MSG Hello
SET $DedupeReminders 1
REM Wednesday MSG Hello
.fi
.PP
Every Wednesday, \fBRemind\fR will issue \fItwo\fR "Hello" reminders.
Because $DedupeReminders was 0 when the first "Hello" was issued, it
@@ -2891,6 +3003,7 @@ have been done. Consider the following:
REM MSG [a]
set a "foo"
REM MSG [a]
.fi
.PP
The first REM will trigger and print "foo". The second will trigger and
print "bar". The third will not trigger because it's a duplicate of the
@@ -3187,25 +3300,26 @@ 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
(the default) means to parse them.
A flag indicating whether or not \fBRemind\fR should fully parse
\fBREM\fR statements that are not triggered. 0 (the default) means to
skip parsing them and 1 means to parse them.
.PP
.RS
For example, if we have the following \fBREM\fR statement:
.PP
.nf
REM 2020-01-01 MSG ["bad_expression" * 2]
REM 2020-01-01 MSG ["bad_expression" / 2]
.fi
.PP
Then by default, \fBRemind\fR will fully parse the line and issue
a "Type mismatch" error even if the reminder is not triggered. However,
if \fB$ParseUntriggered\fR is set to 0, then \fBRemind\fR will not
issue the error except on 2020-01-01, when the reminder is triggered.
Then if \fB$ParseUntriggered\fR is set to 1, \fBRemind\fR will fully
parse the line and issue a "Type mismatch" error even if the reminder
is not triggered. However, if \fB$ParseUntriggered\fR is set to 0,
the default, then \fBRemind\fR will not issue the error except on
2020-01-01, when the reminder is triggered.
.PP
Setting \fB$ParseUntriggered\fR to 0 may in some cases slightly
improve performance, at the risk of not catching errors until a
reminder is triggered.
Keeping \fB$ParseUntriggered\fR at 0 may slightly improve performance,
at the risk of not catching errors until a reminder is triggered. We recommend
leaving it set to 0.
.RE
.TP
.B $PrefixLineNo (read-only)
@@ -3264,10 +3378,10 @@ A directory path containing standard reminder scripts. Currently,
The value of \fB$SysInclude\fR is "@prefix@/share/remind" on
this installation.
.TP
.B $T (read-only, DATE type)
.B $T (read-only, DATE or INT type)
Equivalent to \fBtrigdate()\fR. (See BUILT-IN FUNCTIONS.)
.TP
.B $Tb (read-only, DATE type)
.B $Tb (read-only, DATE or INT type)
Equivalent to \fBtrigbase()\fR.
.TP
.B $Td (read-only)
@@ -3276,7 +3390,7 @@ Equivalent to \fBday(trigdate())\fR.
.B $Tm (read-only)
Equivalent to \fBmonnum(trigdate())\fR.
.TP
.B $Tu (read-only, DATE type)
.B $Tu (read-only, DATE or INT type)
Equivalent to \fBtriguntil()\fR.
.TP
.B $Tw (read-only)
@@ -3285,7 +3399,7 @@ Equivalent to \fBwkdaynum(trigdate())\fR.
.B $Ty (read-only)
Equivalent to \fByear(trigdate())\fR.
.TP
.B $Tt (read-only, TIME type)
.B $Tt (read-only, TIME or INT type)
Equivalent to \fBtrigtime()\fR.
.TP
.B $TimeSep (STRING type)
@@ -3546,11 +3660,15 @@ function has been defined previously. The \fBargs()\fR function is
available only in versions of \fBRemind\fR from 03.00.04 and up.
.TP
.B asc(s_string)
Returns an \fBINT\fR that is the ASCII code of the first character
in \fIstring\fR. As a special case, \fBasc("")\fR returns 0. For UTF-8
strings, this will return the UTF-8 byte with which the string
begins, which is not likely to be very useful (and may indeed be negative
on machines where \fBchar\fR is a signed type.)
Returns an \fBINT\fR that is the ASCII code of the first byte in
\fIstring\fR. As a special case, \fBasc("")\fR returns 0. For UTF-8
strings, this will return the UTF-8 byte with which the string begins,
which is not likely to be very useful.
.TP
.B codepoint(s_string)
Returns an \fBINT\fR that is the code point of the first character
in \fIstring\fR, treating multi-byte characters correctly. As a special case,
\fBcodepoint("")\fR returns 0.
.TP
.B baseyr()
Returns the "base year" that was compiled into \fBRemind\fR (normally
@@ -3607,6 +3725,15 @@ It is easy to create invalid UTF-8 sequences; \fBchar\fR does not check
for this. Note that none of the arguments can be 0, unless there is only one
argument. As a special case, \fBchar(0)\fR returns "".
.TP
.B mbchar(i_i1 [,i_i2...])
This function can take any number of \fBINT\fR arguments. It returns
a \fBSTRING\fR consisting of the characters specified by the
arguments. Any codepoint may be supplied and a correct multi-byte
character string will be returned. Note that none of the arguments
can be 0, unless there is only one argument. As a special case,
\fBmbchar(0)\fR returns "". Additionally, no argument may be a
negative number.
.TP
.B choose(i_index, x_arg1 [,x_arg2...])
\fBChoose\fR must take at least two arguments, the first of which is
an \fBINT\fR. If \fIindex\fR is \fIn\fR, then the \fIn\fRth subsequent
@@ -3678,6 +3805,13 @@ ANSI color-changing sequences occupy zero columns whereas some Unicode
characters occupy two columns. \fBcolumns(str)\fR takes all of that
into account. Note that if \fBRemind\fR was compiled without Unicode support,
\fBcolumns(str)\fR returns a type mismatch error.
.PP
The result of \fBcolumns(str)\fR may be less than, equal to, or
greater than the result of \fBmbstrlen(str)\fR. This is because some
Unicode characters are so-called combining characters that add one to the
character length, but don't occupy any columns on their own. And other
Unicode characters are double-width characters that add one to the
character length, but two to the number of display columns.
.RE
.TP
.B const(x_arg)
@@ -3786,7 +3920,7 @@ for the specified year. If \fIarg\fR is a \fBDATE\fR or
after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS
.P
.PP
Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox
Easter date, see \fBorthodoxeaster\fR.
.RE
@@ -3921,6 +4055,11 @@ Support for Hebrew dates - see the section "THE HEBREW CALENDAR"
.B hebyear(dq_date)
Support for Hebrew dates - see the section "THE HEBREW CALENDAR"
.TP
.B hex(i_n)
Returns a \fBSTRING\fR that is the hexadecimal representation of \fIn\fR.
There is no "0x" prefix and any letters in the returned value
are uppper-case.
.TP
.B hour(tq_time)
Returns the hour component of \fItime\fR.
.TP
@@ -3946,14 +4085,23 @@ compatible with previous versions of \fBRemind\fR.
.TP
.B index(s_search, s_target [,i_start)
Returns an \fBINT\fR that is the location of \fItarget\fR in the
string \fIsearch\fR. The first character of a string is numbered 1.
If \fItarget\fR does not exist in \fIsearch\fR, then 0 is returned.
string \fIsearch\fR. Note that \fBindex\fR uses \fIbyte\fR positions,
not character positions, so should not be used on non-ASCII strings. Use
\fBmbindex\fR for non-ASCII strings.
.PP
The first byte of a string is numbered 1. If \fItarget\fR does not
exist in \fIsearch\fR, then 0 is returned.
.RS
.PP
The optional parameter \fIstart\fR specifies the position in
\fIsearch\fR at which to start looking for \fItarget\fR.
.RE
.TP
.B mbindex(s_search, s_target [,i_start])
Similar to \fBindex()\fR but returns the \fIcharacter\fR position rather
than the \fIbyte\fR position. Also, \fIstart\fR is interpreted as a
1-based character index rather than a byte index.
.TP
.B isany(arg1 [,arg2, ..., argN]);
Returns 1 if the first argument \fIarg1\fR is equal to any of the
subsequent arguments \fIarg2\fR through \fIargN\fR; returns 0 otherwise.
@@ -4306,7 +4454,7 @@ Returns a string that is the ordinal number \fInum\fR. For example,
In order to help with localization, if you define a function called
\fBordx\fR that takes a single parameter, then calling
\fBord\fR(\fIn\fR) invokes \fBordx\fR(\fIn\fR) and returns whatever
it does.
it does. During the callback to \fBordx\fR, \fBRUN\fR will be disabled.
.RE
.TP
.B orthodoxeaster([dqi_arg])
@@ -4316,7 +4464,7 @@ for the specified year. If \fIarg\fR is a \fBDATE\fR or
after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS
.P
.PP
Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western
Easter date, see \fBeasterdate\fR.
.RE
@@ -4557,17 +4705,27 @@ output is not going to a TTY.
.RE
.TP
.B strlen(s_str)
Returns the length of \fIstr\fR. If the length of \fIstr\fR is too large
to represent as an integer, emits a "Number too high" error. Note that
\fBstrlen\fR returns the number of \fIbytes\fR in the string, not the
number of \fIcharacters\fR. These numbers are the same for ASCII strings,
but may be different for UTF-8 strings.
Returns the length of \fIstr\fR in bytes. If the length of \fIstr\fR
is too large to represent as an integer, emits a "Number too high"
error. Note that \fBstrlen\fR returns the number of \fIbytes\fR in
the string, not the number of \fIcharacters\fR. These numbers are the
same for ASCII strings, but may be different for UTF-8 strings.
.TP
.B mbstrlen(str)
Similar to \fBstrlen\fR, but returns the length of the string in
\fIcharacters\fR rather than \fIbytes\fR and is thus safe for use
on multi-byte strings.
.TP
.B substr(s_str, i_start [,i_end])
Returns a \fBSTRING\fR consisting of all characters in \fIstr\fR from
\fIstart\fR up to and including \fIend\fR. Characters are numbered
Returns a \fBSTRING\fR consisting of all bytes in \fIstr\fR from
\fIstart\fR up to and including \fIend\fR. Bytes are numbered
from 1. If \fIend\fR is not supplied, then it defaults to the length
of \fIstr\fR.
of \fIstr\fR. Because \fBsubstr\fR uses \fIbyte\fR indexes rather than
\fIcharacter\fR indexes, it should not be used on multi-byte strings.
.TP
.B mbsubstr(s_str, i_start [,i_end])
Similar to \fBsubstr\fR but uses \fIcharacter\fR indexes rather than
\fIbyte\fR indexes, and is thus safe for use on multi-byte strings.
.TP
.B sunrise([dq_date])
Returns a \fBTIME\fR indicating the time of sunrise on the specified
@@ -4694,6 +4852,11 @@ have an \fBAT\fR clause, returns the integer -1 (and differs from
\fBtrigdatetime()\fR in this respect.) See "MULTI-DAY EVENTS" for more
information.
.TP
.B trigeventstarttz()
Similar to \fBtrigeventstart()\fR but returns the DATETIME in the time
zone specified by a TZ clause, if one was present. If no TZ clause
was present, returns the same value as \fBtrigeventstart()\fR.
.TP
.B trigeventduration()
Returns a \fBTIME\fR representing the duration of the most recent
triggerable \fBREM\fR command that had an \fBAT\fR and a
@@ -4744,6 +4907,11 @@ command. Returns a positive integer N if the "repeat" is of the form
Similar to \fBtrigrep()\fR, but returns the repeat used in the \fBAT\fR clause
of a timed reminder.
.TP
.B trigtz()
If a \fBTZ\fR clause was used in the last \fBREM\fR or \fBIFTRIG\fR
command, returns the time zone name. Otherwise returns the empty
string.
.TP
.B trigduration()
Returns (as a TIME type) the \fBDURATION\fR parameter of a timed
reminder. If there is no \fBDURATION\fR parameter, returns the
@@ -4805,12 +4973,10 @@ Returns a string suitable for use in a \fBREM\fR command or a
dates in advance. Note that in earlier versions of \fBRemind\fR,
\fBtrigger\fR was required to convert a date into something the
\fBREM\fR command could consume. However, in this version of
\fBRemind\fR, you can omit it. Note that \fBtrigger()\fR \fIalways\fR
returns its result in English, even for non-English versions of
\fBRemind\fR. Normally, the \fIdate\fR and \fItime\fR are the local
date and time; however, if \fIutcflag\fR is non-zero, the \fIdate\fR
and \fItime\fR are interpreted as UTC times, and are converted to
local time. Examples:
\fBRemind\fR, you can omit it. Normally, the \fIdate\fR and
\fItime\fR are the local date and time; however, if \fIutcflag\fR is
non-zero, the \fIdate\fR and \fItime\fR are interpreted as UTC times,
and are converted to local time. Examples:
.RS
.PP
trigger('1993/04/01')
@@ -4829,6 +4995,11 @@ Finally:
.PP
returns "30 November 1994 AT 22:00" for EST, which is 5 hours behind UTC.
The value for your time zone may differ.
.PP
\fBtrigger()\fR will \fInever\fR return a date earlier than "1 January 1990"
even if the UTC flag dictates that it should. So do not use it for reminders
before about 2 January 1990 in your local time zone. I do not anticipate this
restriction being a real problem.
.RE
.TP
.B trigtags()
@@ -4838,11 +5009,16 @@ were no TAGs. If there are multiple tags, they are each separated by
a single comma, not a comma and a space.
.TP
.B trigtime()
Returns the time of the last \fBREM\fR command with an \fBAT\fR
clause. If the last \fBREM\fR did not have an \fBAT\fR clause,
returns the integer 0. If a \fBREM\fR command has an \fBAT\fR clause
with a \fBDURATION\fR, then you can compute the end time as
\fBtrigtime() + trigduration()\fR.
Returns the time of the last \fBREM\fR command with an \fBAT\fR clause
\fIin the system default time zone\fR. If the last \fBREM\fR did not
have an \fBAT\fR clause, returns the integer 0. If a \fBREM\fR
command has an \fBAT\fR clause with a \fBDURATION\fR, then you can
compute the end time as \fBtrigtime() + trigduration()\fR.
.TP
.B trigtimetz()
Similar to \fBtrigtime()\fR but returns the time in the time zone specified
by a TZ clause, if one was present. If no TZ clause was present, returns
the same value as \fBtrigtime()\fR.
.TP
.B trigvalid()
Returns 1 if the value returned by \fBtrigdate()\fR is valid for the most
@@ -4859,11 +5035,13 @@ command can never be triggered:
Returns "STRING", "INT", "DATE", "TIME" or "DATETIME", depending on the type of \fIarg\fR.
.TP
.B tzconvert(q_datetime, s_srczone [,s_dstzone])
Converts \fBdatetime\fR from the time zone named by \fBsrczone\fR to the
time zone named by \fBdstzone\fR. If \fBdstzone\fR is omitted, the
default system time zone is used. The return value is a DATETIME. Time
zone names are system-dependent; consult your operating system for legal
values. Here is an example:
Converts \fBdatetime\fR from the time zone named by \fBsrczone\fR to
the time zone named by \fBdstzone\fR. If \fBsrczone\fR is the empty
string, then the default system time zone is used as the source zone.
If \fBdstzone\fR is omitted or is the empty string, the default system
time zone is used as the destination zone. The return value is a
DATETIME. Time zone names are system-dependent; consult your
operating system for legal values. Here is an example:
.PP
.nf
tzconvert('2007-07-08@01:14', "Canada/Eastern", "Canada/Pacific")
@@ -5323,6 +5501,13 @@ it is ignored and the built-in function is used. To prevent conflicts
with future versions of \fBRemind\fR (which may define more built-in
functions), you may wish to name all user-defined functions beginning
with an underscore.
.TP
o
If a user-defined function is defined in a context where \fBRUN\fR is
disabled, then whenever that function is called, \fBRUN\fR will be disabled
during its evaluation, \fIeven if\fR it is called from a context where
\fBRUN\fR is enabled.
.PP
To delete a user-defined function, use \fBFUNSET\fR. This takes a
space-separated list of user-defined functions to delete. For
@@ -6514,6 +6699,9 @@ If you use a \fB%{name}\fR sequence and the function \fBsubst_\fIname\fR is
not defined or returns an error, then \fB%{name}\fR is replaced with the
empty string.
.PP
Note that when \fBRemind\fR invokes any callback function for a
substitution sequence, \fBRUN\fR will be disabled.
.PP
.SH THE TRANSLATION TABLE
.PP
To assist with localizing reminder files, \fBRemind\fR maintains a

View File

@@ -98,12 +98,13 @@ The fifth group of controls associates a time and possible duration
with the reminder. You can also specify advance notice, possibly
repeating.
The sixth control allows you to specify whether the reminder is a TODO,
and if so, its completion date. Double-clicking in the "Complete through:"
field automatically fills in the date of the calendar entry. Otherwise,
enter a possible completion date in the form YYYY-MM-DD. You can also enter
a number in the "Max overdue days:" field to limit how many days past the
due date \fBRemind\fR will keep reminding you of the TODO.
The sixth control allows you to specify whether the reminder is a
TODO, and if so, its completion date. Typing a slash "/" in the
"Complete through:" field automatically fills in the date of the
calendar entry. Otherwise, enter a completion date in the form
YYYY-MM-DD. You can also enter a number in the "Max overdue days:"
field to limit how many days past the due date \fBRemind\fR will keep
reminding you of the TODO.
The seventh control specifies what \fBRemind\fR should do if a reminder
falls on a holiday or weekend.
@@ -296,12 +297,12 @@ IP address of your SMTP server here.
.TP
.B Text Editor
This specifies a text editor to invoke when a reminder is right-clicked.
The characters "%d" are replaced with the lined number of the file
containing the reminder, and "%s" are replaced with the file name.
The sequence "%d" is replaced with the line number of the file
containing the reminder, and "%s" is replaced with the file name.
Useful strings might be "emacs +%d %s" or "gvim +%d %s"
.TP
.B Extra Argument for Remind
.B Extra Arguments for Remind
This specifies any extra arguments that should be passed to Remind
when \fBTkRemind\fR invokes \fBremind\fR. Unless you know what
you are doing, leave this blank.

View File

@@ -1033,7 +1033,7 @@ proc SaveOptions { w } {
set n [expr $i*7]
for {set j 0} {$j < 7} {incr j} {
set f [expr $n+$j]
.cal.l$f configure -anchor $Option(DayAnchor);
.cal.l$f configure -anchor $Option(DayAnchor)
}
}
.b.status configure -foreground $Option(LabelColor) -background $Option(WinBackground)
@@ -1047,8 +1047,8 @@ proc SaveOptions { w } {
.b.queue configure -foreground $Option(LabelColor) -background $Option(WinBackground)
.b.quit configure -foreground $Option(LabelColor) -background $Option(WinBackground)
.b.options configure -foreground $Option(LabelColor) -background $Option(WinBackground)
. configure -background $Option(LineColor);
.h configure -background $Option(LineColor);
. configure -background $Option(LineColor)
.h configure -background $Option(LineColor)
.cal configure -background $Option(LineColor)
.b configure -background $Option(LineColor)
}
@@ -1189,7 +1189,7 @@ proc FillCalWindow {} {
gets $file line
set DayNames {}
foreach day $line {
set day [regsub -all {_} $day " "];
set day [regsub -all {_} $day " "]
lappend DayNames $day
}
@@ -1385,7 +1385,7 @@ proc ThisMonth {} {
# Do nothing if already there
if { $CurMonth == $TodayMonth && $CurYear == $TodayYear } {
return 0;
return 0
}
set CurMonth $TodayMonth
set CurYear $TodayYear
@@ -1733,6 +1733,7 @@ proc complete_through_today { w } {
} else {
$w.complete_through insert end [clock format [clock seconds] -format %Y-%m-%d]
}
return -code break
}
#---------------------------------------------------------------------------
@@ -1970,7 +1971,7 @@ proc CreateModifyDialog {w day firstDay args} {
$w.todobut deselect
label $w.lcomplete -text "Complete through: "
entry $w.complete_through -width 20
bind $w.complete_through <Double-Button-1> [list complete_through_today $w]
bind $w.complete_through <KeyPress-slash> [list complete_through_today $w]
balloon_add_help $w.complete_through "Enter the date of completed TODO in the form YYYY-MM-DD"
label $w.loverdue -text "Max overdue days: "
entry $w.max_overdue -width 6
@@ -3034,10 +3035,10 @@ proc DaemonReadable { file } {
return
}
if {[catch {set obj [::json::json2dict $line]}]} {
return;
return
}
if {![dict exists $obj response]} {
return;
return
}
set response [dict get $obj response]
switch -- $response {
@@ -3838,7 +3839,7 @@ proc details_enter { w } {
lappend lines [list "URL:" "Middle-click to open [dict get $info url]"]
}
if {[llength $lines] < 1} {
return;
return
}
balloon_cancel_timer
@@ -4254,7 +4255,7 @@ proc DisplayTime {} {
# .moon_last
#***********************************************************************
proc CreateMoonWindows {} {
global Option;
global Option
catch { destroy .moon_new }
catch { destroy .moon_first }
catch { destroy .moon_full }
@@ -4381,7 +4382,7 @@ proc ShowTodaysReminders { force date } {
if {$TwentyFourHourMode} {
append cmdline "-b1 "
}
append cmdline $Option(ExtraRemindArgs);
append cmdline $Option(ExtraRemindArgs)
append cmdline " $ReminderFile"
if {"$date" != ""} {
append cmdline " $date"
@@ -4547,10 +4548,10 @@ proc compare_reminders { a b } {
set a_prio [dict get $a priority]
set b_prio [dict get $b priority]
if {$a_prio < $b_prio} {
return -1;
return -1
}
if {$a_prio > $b_prio} {
return 1;
return 1
}
return 0
}

View File

@@ -40,9 +40,14 @@ REMINDOBJS= $(REMINDSRCS:.c=.o) $(XLATSRC:.c=.o)
all: remind rem2ps
test: all
test-basic: all
@sh ../tests/test-rem
test-tz: all
@sh ../tests/test-timezone-support
test: test-basic test-tz
.c.o:
@CC@ -c @CPPFLAGS@ @CFLAGS@ @DEFS@ $(CEXTRA) -DSYSDIR=$(datarootdir)/remind -I. -I$(srcdir) $<
@@ -117,6 +122,8 @@ beta-tgz:
gzip -f -v -9 remind-$(VERSION)-BETA-$(BETA).tar
gpg --detach-sign -u dianne@skoll.ca remind-$(VERSION)-BETA-$(BETA).tar.gz
distro-beta: beta-tgz
#---------------- Stuff after this added by "make depend" -----------------

View File

@@ -848,6 +848,9 @@ void ProduceCalendar(void)
if (CalMonths) {
FromDSE(DSEToday, &y, &m, &d);
DSEToday = DSE(y, m, 1);
LocalDSEToday = DSEToday;
LocalSysTime = 0;
SysTime = 0;
GenerateCalEntries(-1);
DidAMonth = 0;
if (PsCal == PSCAL_LEVEL3) {
@@ -862,8 +865,13 @@ void ProduceCalendar(void)
}
return;
} else {
if (MondayFirst) DSEToday -= (DSEToday%7);
else DSEToday -= ((DSEToday+1)%7);
if (MondayFirst) {
DSEToday -= (DSEToday%7);
LocalDSEToday -= (LocalDSEToday%7);
} else {
DSEToday -= ((DSEToday+1)%7);
LocalDSEToday -= ((LocalDSEToday+1)%7);
}
GenerateCalEntries(-1);
@@ -912,7 +920,7 @@ static void DoCalendarOneWeek(int nleft)
int y, m, d, done, i, l, wd;
char buf[128];
int LinesWritten = 0;
int OrigDse = DSEToday;
int OrigDse = LocalDSEToday;
InitMoonsAndShades();
/* Fill in the column entries */
@@ -920,6 +928,7 @@ static void DoCalendarOneWeek(int nleft)
ColToDay[i] = DayOf(DSEToday);
GenerateCalEntries(i);
DSEToday++;
LocalDSEToday++;
}
/* Figure out weekday of first column */
@@ -1205,6 +1214,7 @@ static int WriteCalendarRow(void)
GenerateCalEntries(i);
ColToDay[i] = DayOf(DSEToday);
DSEToday++;
LocalDSEToday++;
}
/* Output the entries */
@@ -1932,6 +1942,17 @@ static int DoCalRem(ParsePtr p, int col)
return r;
}
if (trig.tz != NULL && tim.ttime == NO_TIME) {
FreeTrig(&trig);
return E_TZ_NO_AT;
}
/* An empty string for time zone is just a missing time zone */
if (trig.tz != NULL && !*trig.tz) {
free( (void *) trig.tz);
trig.tz = NULL;
}
if (trig.typ == MSG_TYPE ||
trig.typ == CAL_TYPE ||
trig.typ == MSF_TYPE) {
@@ -1951,7 +1972,9 @@ static int DoCalRem(ParsePtr p, int col)
return E_EOLN;
}
if (trig.typ == SAT_TYPE) {
EnterTimezone(trig.tz);
r=DoSatRemind(&trig, &tim, p);
ExitTimezone(trig.tz);
if (r) {
if (r == E_CANT_TRIG && trig.maybe_uncomputable) {
r = OK;
@@ -2019,7 +2042,9 @@ static int DoCalRem(ParsePtr p, int col)
}
} else {
/* Calculate the trigger date */
EnterTimezone(trig.tz);
dse = ComputeTrigger(get_scanfrom(&trig), &trig, &tim, &r, 1);
ExitTimezone(trig.tz);
if (r) {
if (r == E_CANT_TRIG && trig.maybe_uncomputable) {
r = OK;
@@ -2029,6 +2054,9 @@ static int DoCalRem(ParsePtr p, int col)
}
}
/* Adjust trigger date/time to time zone */
dse = AdjustTriggerForTimeZone(&trig, dse, &tim);
/* Add to global OMITs if so indicated */
if (trig.addomit) {
r = AddGlobalOmit(dse);
@@ -2299,6 +2327,9 @@ static int DoCalRem(ParsePtr p, int col)
e->nonconst_expr = nonconst_expr;
e->if_depth = get_if_pointer() - get_base_if_pointer();
e->trig = trig;
if (e->trig.tz) {
e->trig.tz = StrDup(e->trig.tz);
}
e->tt = tim;
#ifdef REM_USE_WCHAR
e->wc_pos = NULL;
@@ -2395,6 +2426,9 @@ static void WriteSimpleEntryProtocol1(CalEntry const *e)
void WriteJSONTimeTrigger(TimeTrig const *tt)
{
PrintJSONKeyPairTime("time", tt->ttime);
if (tt->ttime != tt->ttime_orig) {
PrintJSONKeyPairTime("time_in_tz", tt->ttime_orig);
}
PrintJSONKeyPairTime("nexttime", tt->nexttime);
PrintJSONKeyPairInt("tdelta", tt->delta);
PrintJSONKeyPairInt("trep", tt->rep);
@@ -2526,6 +2560,10 @@ void WriteJSONTrigger(Trigger const *t, int include_tags)
PrintJSONKeyPairDate("from", t->from);
PrintJSONKeyPairInt("priority", t->priority);
PrintJSONKeyPairDateTime("eventstart", t->eventstart);
if (t->eventstart_orig != NO_TIME &&
t->eventstart_orig != t->eventstart) {
PrintJSONKeyPairDateTime("eventstart_in_tz", t->eventstart_orig);
}
if (t->eventduration != NO_TIME) {
PrintJSONKeyPairInt("eventduration", t->eventduration);
}
@@ -2547,6 +2585,7 @@ void WriteJSONTrigger(Trigger const *t, int include_tags)
}
PrintJSONKeyPairString("tags", DBufValue(&(t->tags)));
}
PrintJSONKeyPairString("tz", t->tz);
}
static void WriteSimpleEntryProtocol2(CalEntry *e)
@@ -2569,6 +2608,9 @@ static void WriteSimpleEntryProtocol2(CalEntry *e)
}
if (e->time != NO_TIME) {
PrintJSONKeyPairInt("time", e->time);
if (e->tt.ttime_orig != e->tt.ttime) {
PrintJSONKeyPairInt("time_in_tz", e->tt.ttime_orig);
}
if (e->tt.delta) {
PrintJSONKeyPairInt("tdelta", e->tt.delta);
}

View File

@@ -45,6 +45,9 @@
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `readline' function. */
#undef HAVE_READLINE
/* Define to 1 if you have the `strcasecmp' function. */
#undef HAVE_STRCASECMP
@@ -81,6 +84,12 @@
/* Define to 1 if you have the <wctype.h> header file. */
#undef HAVE_WCTYPE_H
/* Define to 1 if you have the <readline/history.h> header file. */
#undef HAVE_READLINE_HISTORY_H
/* Define to 1 if you have the <readline/readline.h> header file. */
#undef HAVE_READLINE_READLINE_H
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT

View File

@@ -159,3 +159,15 @@
#else
#undef REM_USE_WCHAR
#endif
#if defined(HAVE_READLINE) && defined(HAVE_READLINE_READLINE_H)
#define USE_READLINE 1
#else
#undef USE_READLINE
#endif
#if defined(HAVE_READLINE) && defined(HAVE_READLINE_READLINE_H) && defined(HAVE_READLINE_HISTORY_H)
#define USE_READLINE_HISTORY 1
#else
#undef USE_READLINE_HISTORY
#endif

View File

@@ -22,8 +22,8 @@
/* The default values are initially set to the city hall in Ottawa, */
/* Ontario, Canada. */
/*---------------------------------------------------------------------*/
#define DEFAULT_LATITUDE 45.420556
#define DEFAULT_LONGITUDE -75.689722
#define DEFAULT_LATITUDE 45.42055555555555
#define DEFAULT_LONGITUDE -75.68944444444445
#define LOCATION "Ottawa"
/*---------------------------------------------------------------------*/
@@ -159,3 +159,15 @@
#else
#undef REM_USE_WCHAR
#endif
#if defined(HAVE_READLINE) && defined(HAVE_READLINE_READLINE_H)
#define USE_READLINE 1
#else
#undef USE_READLINE
#endif
#if defined(HAVE_READLINE) && defined(HAVE_READLINE_READLINE_H) && defined(HAVE_READLINE_HISTORY_H)
#define USE_READLINE_HISTORY 1
#else
#undef USE_READLINE_HISTORY
#endif

View File

@@ -18,6 +18,7 @@
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "types.h"
#include "globals.h"
@@ -32,6 +33,126 @@ static int ParseUntil (ParsePtr s, Trigger *t, int type);
static int ShouldTriggerBasedOnWarn (Trigger const *t, int dse, int *err);
static int ComputeTrigDuration(TimeTrig const *t);
static int CalledEnterTimezone = 0;
int AdjustTriggerForTimeZone(Trigger *trig, int dse, TimeTrig *tim)
{
int y, m, d, hour, minute;
int r;
struct tm tm;
if (!trig->tz || dse < 0) {
/* Already local time or did not compute trigger date - no adjustments needed */
return dse;
}
FromDSE(dse, &y, &m, &d);
hour = tim->ttime_orig / 60;
minute = tim->ttime_orig % 60;
r = tz_convert(y, m, d, hour, minute, trig->tz, LocalTimeZone, &tm);
if (r != 1) {
Wprint(tr("Error adjusting trigger to local time zone"));
return dse;
}
dse = DSE(tm.tm_year+1900, tm.tm_mon, tm.tm_mday);
tim->ttime = tm.tm_hour * 60 + tm.tm_min;
/* Adjust eventstart also */
trig->eventstart = dse * MINUTES_PER_DAY + tim->ttime;
SaveAllTriggerInfo(trig, tim, dse, tim->ttime, 1);
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%s): Trig(tz_adj %s) = %s, %d %s, %d AT %02d:%02d",
GetCurrentFilename(), line_range(LineNoStart, LineNo), trig->tz,
get_day_name(dse % 7), tm.tm_mday, get_month_name(tm.tm_mon),
1900 + tm.tm_year, tim->ttime / 60, tim->ttime % 60);
if (tim->duration != NO_TIME) {
fprintf(ErrFp, " DURATION %02d:%02d",
(tim->duration / 60),
(tim->duration % 60));
}
fprintf(ErrFp, "\n");
}
return dse;
}
void ExitTimezone(char const *tz)
{
if (!CalledEnterTimezone) {
fprintf(stderr, "ExitTimezone called without EnterTimezone!!!\n");
abort();
}
CalledEnterTimezone = 0;
if (!tz || !*tz) {
/* Nothing to do */
return;
}
/* Revert to our local time zone */
(void) tz_set_tz(LocalTimeZone);
DSEToday = LocalDSEToday;
SysTime = LocalSysTime;
FromDSE(DSEToday, &CurYear, &CurMon, &CurDay);
if (DebugFlag & DB_SWITCH_ZONE) {
fprintf(stderr, "TZ exit %s: %04d-%02d-%02d %02d:%02d\n", tz,
CurYear, CurMon+1, CurDay, SysTime / 3600, (SysTime/60) % 60);
}
return;
}
void EnterTimezone(char const *tz)
{
struct tm tm;
int y, m, d;
time_t t;
if (CalledEnterTimezone) {
fprintf(stderr, "EnterTimezone called twice in a row!!!\n");
abort();
}
CalledEnterTimezone = 1;
if (!tz || !*tz) {
/* Stay in local timezone */
return;
}
FromDSE(LocalDSEToday, &y, &m, &d);
tm.tm_sec = 0;
tm.tm_min = (LocalSysTime/60) % 60;
tm.tm_hour = LocalSysTime / 3600;
tm.tm_mday = d;
tm.tm_mon = m;
tm.tm_year = y - 1900;
tm.tm_wday = 0; /* Ignored by mktime */
tm.tm_yday = 0; /* Ignored by mktime */
tm.tm_isdst = -1; /* Information not available */
t = mktime(&tm); /* Convert local time to seconds */
/* Set target timezone */
(void) tz_set_tz(tz);
/* Update our variables */
(void) localtime_r(&t, &tm);
SysTime = tm.tm_min * 60 + (tm.tm_hour * 3600);
CurDay = tm.tm_mday;
CurMon = tm.tm_mon;
CurYear = tm.tm_year + 1900;
/* Adjust DSEToday back by a day if midnight in our time zone requires it */
if (SysTime < LocalSysTime) {
DSEToday--;
FromDSE(DSEToday, &CurYear, &CurMon, &CurDay);
}
if (DebugFlag & DB_SWITCH_ZONE) {
fprintf(stderr, "TZ enter %s: %04d-%02d-%02d %02d:%02d\n", tz,
CurYear, CurMon+1, CurDay, SysTime / 3600, (SysTime/60) % 60);
}
}
void remove_trailing_newlines(DynamicBuffer *buf)
{
char *s = (char *) DBufValue(buf) + DBufLen(buf) - 1;
@@ -287,6 +408,18 @@ int DoRem(ParsePtr p)
return r;
}
if (trig.tz != NULL && tim.ttime == NO_TIME) {
PurgeEchoLine("%s\n", CurLine);
FreeTrig(&trig);
return E_TZ_NO_AT;
}
/* An empty string for time zone is just a missing time zone */
if (trig.tz != NULL && !*trig.tz) {
free( (void *) trig.tz);
trig.tz = NULL;
}
if (trig.complete_through != NO_DATE && !trig.is_todo) {
PurgeEchoLine("%s\n", CurLine);
FreeTrig(&trig);
@@ -312,7 +445,9 @@ int DoRem(ParsePtr p)
PurgeEchoLine("%s\n", "#!P: Cannot purge SATISFY-type reminders");
}
PurgeEchoLine("%s\n", CurLine);
EnterTimezone(trig.tz);
r=DoSatRemind(&trig, &tim, p);
ExitTimezone(trig.tz);
if (r) {
if (r == E_CANT_TRIG && trig.maybe_uncomputable) {
r = OK;
@@ -372,7 +507,9 @@ int DoRem(ParsePtr p)
}
} else {
/* Calculate the trigger date */
EnterTimezone(trig.tz);
dse = ComputeTrigger(get_scanfrom(&trig), &trig, &tim, &r, 1);
ExitTimezone(trig.tz);
if (r) {
if (PurgeMode) {
if (!Hush) {
@@ -388,6 +525,9 @@ int DoRem(ParsePtr p)
}
}
/* Adjust trigger date/time to time zone */
dse = AdjustTriggerForTimeZone(&trig, dse, &tim);
/* Add to global OMITs if so indicated */
if (trig.addomit) {
r = AddGlobalOmit(dse);
@@ -437,6 +577,7 @@ int DoRem(ParsePtr p)
}
r = OK;
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
/* Filter unwanted events/todos */
if (todo_filtered(&trig)) {
@@ -479,6 +620,9 @@ int DoRem(ParsePtr p)
}
if (tim.ttime != NO_TIME) {
PrintJSONKeyPairInt("time", tim.ttime);
if (tim.ttime_orig != tim.ttime) {
PrintJSONKeyPairInt("time_in_tz", tim.ttime_orig);
}
}
if (p->nonconst_expr) {
PrintJSONKeyPairInt("nonconst_expr", 1);
@@ -664,11 +808,13 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
trig->omitfunc[0] = 0;
trig->duration_days = 0;
trig->eventstart = NO_TIME;
trig->eventstart_orig = NO_TIME;
trig->eventduration = NO_TIME;
trig->maybe_uncomputable = 0;
DBufInit(&(trig->tags));
trig->passthru[0] = 0;
tim->ttime = NO_TIME;
tim->ttime_orig = NO_TIME;
tim->delta = DefaultTDelta;
tim->rep = NO_REP;
tim->duration = NO_TIME;
@@ -678,6 +824,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
trig->complete_through = NO_DATE;
trig->adj_for_last = 0;
trig->infos = NULL;
trig->tz = NULL;
int parsing = 1;
while(parsing) {
@@ -740,6 +887,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
trig->m = m;
trig->d = d;
tim->ttime = (tok.val % MINUTES_PER_DAY);
tim->ttime_orig = tim->ttime;
break;
case T_WkDay:
@@ -795,6 +943,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
DBufFree(&buf);
if (tim->ttime != NO_TIME) return E_TIME_TWICE;
tim->ttime = tok.val;
tim->ttime_orig = tok.val;
r = ParseTimeTrig(s, tim);
if (r) return r;
trig->duration_days = ComputeTrigDuration(tim);
@@ -951,6 +1100,22 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
DBufFree(&buf);
break;
case T_Tz:
if (trig->tz) {
return E_TZ_SPECIFIED_TWICE;
}
r = ParseTokenOrQuotedString(s, &buf);
if (r != OK) {
return r;
}
trig->tz = StrDup(DBufValue(&buf));
if (!trig->tz) {
return E_NO_MEM;
}
DBufFree(&buf);
break;
case T_Info:
r = ParseQuotedString(s, &buf);
if (r != OK) {
@@ -1097,6 +1262,7 @@ static int ParseTimeTrig(ParsePtr s, TimeTrig *tim)
DBufFree(&buf);
if (tim->ttime != NO_TIME) return E_TIME_TWICE;
tim->ttime = tok.val;
tim->ttime_orig = tok.val;
break;
case T_Delta:
@@ -1817,6 +1983,7 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
} else if (dse == start) {
if (tt->ttime != NO_TIME) {
trig->eventstart = MINUTES_PER_DAY * r + tt->ttime;
trig->eventstart_orig = trig->eventstart;
if (tt->duration != NO_TIME) {
trig->eventduration = tt->duration;
}

View File

@@ -145,7 +145,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig const *tt, int
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ampm(%d)", h);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
r = EvalExprRunDisabled(&expr, &v, NULL);
if (r == OK) {
if (!DoCoerce(STR_TYPE, &v)) {
snprintf(mypm, sizeof(mypm), "%s", v.v.str);
@@ -173,7 +173,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig const *tt, int
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ampm(%d)", ch);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
r = EvalExprRunDisabled(&expr, &v, NULL);
if (r == OK) {
if (!DoCoerce(STR_TYPE, &v)) {
snprintf(mycpm, sizeof(mycpm), "%s", v.v.str);
@@ -196,7 +196,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig const *tt, int
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ordinal(%d)", d);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
r = EvalExprRunDisabled(&expr, &v, NULL);
if (r == OK) {
if (!DoCoerce(STR_TYPE, &v)) {
snprintf(myplu, sizeof(myplu), "%s", v.v.str);
@@ -360,7 +360,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig const *tt, int
snprintf(ss, sizeof(s) - (ss-s), "(%d,'%04d-%02d-%02d',%02d:%02d)",
altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
r = EvalExprRunDisabled(&expr, &v, NULL);
if (r == OK) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(dbuf, v.v.str) != OK) {
@@ -383,7 +383,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig const *tt, int
snprintf(s, sizeof(s), "%s(%d,'%04d-%02d-%02d',%02d:%02d)",
substname, altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
r = EvalExprRunDisabled(&expr, &v, NULL);
if (r == OK) {
if (v.type != INT_TYPE || v.v.val != 0) {
if (!DoCoerce(STR_TYPE, &v)) {
@@ -439,7 +439,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig const *tt, int
snprintf(s, sizeof(s), "%s(%d,'%04d-%02d-%02d',%02d:%02d)",
substname, altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL);
r = EvalExprRunDisabled(&expr, &v, NULL);
if (r == OK) {
if (v.type != INT_TYPE || v.v.val != 0) {
if (!DoCoerce(STR_TYPE, &v)) {

View File

@@ -132,7 +132,11 @@
#define E_COMPLETE_WITHOUT_TODO 108
#define E_MAX_OVERDUE_TWICE 109
#define E_MAX_OVERDUE_WITHOUT_TODO 110
#define E_TZ_SPECIFIED_TWICE 111
#define E_TZ_NO_AT 112
#define E_NO_MB 113
#define E_BAD_MB_SEQ 114
#define E_EXPR_NODES_EXCEEDED 115
#ifdef MK_GLOBALS
#undef EXTERN
#define EXTERN
@@ -261,6 +265,11 @@ EXTERN char *ErrMsg[]
/* E_COMPLETE_WITHOUT_TODO */ "COMPLETE-THROUGH specified without TODO",
/* E_MAX_OVERDUE_TWICE */ "MAX-OVERDUE specified twice",
/* E_MAX_OVERDUE_WITHOUT_TODO */ "MAX-OVERDUE specified without TODO",
/* E_TZ_SPECIFIED_TWICE */ "TZ specified twice",
/* E_TZ_NO_AT */ "TZ specified for non-timed reminder",
/* E_NO_MB */ "C library does not support multibyte characters",
/* E_BAD_MB_SEQ */ "Invalid multibyte sequence",
/* E_EXPR_NODES_EXCEEDED */ "Maximum expression complexity exceeded",
}
#endif /* MK_GLOBALS */
;

View File

@@ -316,7 +316,7 @@ add_child(expr_node *parent, expr_node *child)
/* */
/***************************************************************/
static void
debug_evaluation(Value *ans, int r, char const *fmt, ...)
debug_evaluation(Value const *ans, int r, char const *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
@@ -343,7 +343,7 @@ debug_evaluation(Value *ans, int r, char const *fmt, ...)
/* */
/***************************************************************/
static void
debug_evaluation_binop(Value *ans, int r, Value *v1, Value *v2, char const *fmt, ...)
debug_evaluation_binop(Value const *ans, int r, Value const *v1, Value const *v2, char const *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
@@ -381,7 +381,7 @@ debug_evaluation_binop(Value *ans, int r, Value *v1, Value *v2, char const *fmt,
/* */
/***************************************************************/
static void
debug_evaluation_unop(Value *ans, int r, Value *v1, char const *fmt, ...)
debug_evaluation_unop(Value const *ans, int r, Value const *v1, char const *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
@@ -570,7 +570,7 @@ eval_builtin(expr_node *node, Value *locals, Value *ans, int *nonconst)
/* All went well; copy the result destructively */
(*ans) = info.retval;
/* Special case of const cunction */
/* Special case of const function */
if (!strcmp(f->name, "const")) {
if (*nonconst) {
nonconst_debug(0, tr("Non-constant expression converted to constant by `const' built-in function"));
@@ -641,7 +641,7 @@ debug_enter_userfunc(expr_node *node, Value *locals, int nargs)
/* */
/***************************************************************/
static void
debug_exit_userfunc(expr_node *node, Value *ans, int r, Value *locals, int nargs)
debug_exit_userfunc(expr_node *node, Value const *ans, int r, Value *locals, int nargs)
{
char const *fname;
int i;
@@ -694,6 +694,7 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
Value *new_locals = NULL;
expr_node *kid;
int i, r, j, pushed;
int old_rundisabled;
/* If we have <= STACK_ARGS_MAX, store them on the stack here */
Value stack_locals[STACK_ARGS_MAX];
@@ -781,9 +782,15 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
DBG(debug_enter_userfunc(node, new_locals, f->nargs));
old_rundisabled = RunDisabled;
if (f->run_disabled) {
RunDisabled |= RUN_UF;
}
/* Evaluate the function's expr_node tree */
r = evaluate_expr_node(f->node, new_locals, ans, nonconst);
RunDisabled = old_rundisabled;
DBG(debug_exit_userfunc(node, ans, r, new_locals, f->nargs));
if (r != OK) {
@@ -881,6 +888,15 @@ evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst)
if (!node) {
return E_SWERR;
}
if (ExpressionNodeLimitPerLine > 0 &&
ExpressionNodesEvaluatedThisLine >= ExpressionNodeLimitPerLine) {
return E_EXPR_NODES_EXCEEDED;
}
ExpressionNodesEvaluated++;
ExpressionNodesEvaluatedThisLine++;
if (ExpressionNodesEvaluatedThisLine > MaxExprNodesPerLine) {
MaxExprNodesPerLine = ExpressionNodesEvaluatedThisLine;
}
switch(node->type) {
case N_FREE:
case N_ERROR:
@@ -2125,6 +2141,31 @@ static int set_constant_value(expr_node *atom)
atom->u.value.type = INT_TYPE;
val = 0;
prev_val = 0;
if (*s == '0' && (*(s+1) == 'x' || *(s+1) == 'X')) {
/* Hex constant */
s += 2;
if (!*s || !isxdigit(*s)) {
return E_BAD_NUMBER;
}
while (*s && isxdigit(*s)) {
val *= 16;
if (*s >= '0' && *s <= '9') {
val += (*s - '0');
} else {
val += (toupper(*s) - 'A') + 10;
}
s++;
if (val < prev_val) {
return E_2HIGH;
}
prev_val = val;
}
if (*s) {
return E_BAD_NUMBER;
}
atom->u.value.v.val = val;
return OK;
}
while (*s && isdigit(*s)) {
val *= 10;
val += (*s++ - '0');
@@ -2869,9 +2910,29 @@ static char const *get_operator_name(expr_node *node)
else return "UNKNOWN_OPERATOR";
}
/***************************************************************/
/* */
/* EvalExprRunDisabled - parse and evaluate an expression */
/* */
/* Evaluate an expression. Return 0 if OK, non-zero if error */
/* Put the result into value pointed to by v. During */
/* evaluation, RUN will be disabled */
/* */
/***************************************************************/
int EvalExprRunDisabled(char const **e, Value *v, ParsePtr p)
{
int old_run_disabled = RunDisabled;
int r;
RunDisabled |= RUN_CB;
r = EvalExpr(e, v, p);
RunDisabled = old_run_disabled;
return r;
}
/***************************************************************/
/* */
/* EvalExpr - parse and evaluate an expression. */
/* */
/* Evaluate an expression. Return 0 if OK, non-zero if error */
/* Put the result into value pointed to by v. */
/* */
@@ -2903,6 +2964,28 @@ int EvalExpr(char const **e, Value *v, ParsePtr p)
return r;
}
#ifdef REM_USE_WCHAR
/* Truncate a wide-char string to MAX_PRT_LEN characters */
static char const *truncate_string(char const *src)
{
static wchar_t wbuf[MAX_PRT_LEN+1];
static char cbuf[(MAX_PRT_LEN * 8) + 1];
size_t l;
l = mbstowcs(wbuf, src, MAX_PRT_LEN);
if (l == (size_t) -1) {
return src;
}
wbuf[MAX_PRT_LEN] = 0;
l = wcstombs(cbuf, wbuf, MAX_PRT_LEN*8);
if (l == (size_t) -1) {
return src;
}
cbuf[MAX_PRT_LEN*8] = 0;
return cbuf;
}
#endif
/***************************************************************/
/* */
/* PrintValue */
@@ -2913,10 +2996,13 @@ int EvalExpr(char const **e, Value *v, ParsePtr p)
static DynamicBuffer printbuf = {NULL, 0, 0, ""};
#define PV_PUTC(fp, c) do { if (fp) { putc((c), fp); } else { DBufPutc(&printbuf, (c)); } } while(0);
char const *PrintValue (Value *v, FILE *fp)
char const *PrintValue (Value const *v, FILE *fp)
{
int y, m, d;
unsigned char const *s;
int max_str_put = MAX_PRT_LEN;
int truncated = 0;
char pvbuf[512];
if (!fp) {
/* It's OK to DBufFree an uninitialized *BUT STATIC* dynamic buffer */
@@ -2924,9 +3010,19 @@ char const *PrintValue (Value *v, FILE *fp)
}
if (v->type == STR_TYPE) {
#ifdef REM_USE_WCHAR
s = (unsigned char const *) truncate_string(v->v.str);
if (s != (unsigned char const *) v->v.str) {
max_str_put = INT_MAX;
if (strlen((char const *) s) != strlen(v->v.str)) {
truncated = 1;
}
}
#else
s = (unsigned char const *) v->v.str;
#endif
PV_PUTC(fp, '"');
for (y=0; y<MAX_PRT_LEN && *s; y++) {
for (y=0; y<max_str_put && *s; y++) {
switch(*s) {
case '\a': PV_PUTC(fp, '\\'); PV_PUTC(fp, 'a'); break;
case '\b': PV_PUTC(fp, '\\'); PV_PUTC(fp, 'b'); break;
@@ -2952,7 +3048,7 @@ char const *PrintValue (Value *v, FILE *fp)
s++;
}
PV_PUTC(fp, '"');
if (*s) {
if (*s || truncated) {
if (fp) {
fprintf(fp, "...");
} else {

View File

@@ -41,6 +41,13 @@
#include "globals.h"
#include "err.h"
#ifdef USE_READLINE
#include <readline/readline.h>
#endif
#ifdef USE_READLINE_HISTORY
#include <readline/history.h>
#endif
/* Convenient macros for closing files */
#define FCLOSE(fp) ((((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
@@ -134,6 +141,9 @@ void InitFiles(void)
fprintf(ErrFp, "Unable to initialize filename hash table: Out of memory. Exiting.\n");
exit(1);
}
#ifdef USE_READLINE_HISTORY
using_history();
#endif
}
void SetCurrentFilename(char const *fname)
@@ -177,6 +187,7 @@ got_a_fresh_line(void)
{
FreshLine = 1;
WarnedAboutImplicit = 0;
ExpressionNodesEvaluatedThisLine = 0;
}
void set_cloexec(int fd)
@@ -268,6 +279,8 @@ int ReadLine(void)
return ReadLineFromFile(0);
}
#define IS_INTERACTIVE() (fileno(fp) == STDIN_FILENO && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
/***************************************************************/
/* */
/* ReadLineFromFile */
@@ -280,6 +293,11 @@ static int ReadLineFromFile(int use_pclose)
int l;
char copy_buffer[4096];
size_t n;
int force_eof = 0;
#ifdef USE_READLINE
int read_some = 0;
#endif
DynamicBuffer buf;
@@ -287,18 +305,56 @@ static int ReadLineFromFile(int use_pclose)
DBufFree(&LineBuffer);
LineNoStart = LineNo+1;
if (!fp) {
return E_EOF;
}
while(fp) {
#ifdef USE_READLINE
if (IS_INTERACTIVE()) {
char *lin;
if (should_ignore_line()) {
if (read_some) {
lin = readline("Rem...? ");
} else {
lin = readline("Remind? ");
}
} else {
if (read_some) {
lin = readline("Rem...> ");
} else {
lin = readline("Remind> ");
}
}
if (lin) {
read_some = 1;
DBufFree(&buf);
if (DBufPuts(&buf, lin) != OK) {
free(lin);
DBufFree(&buf);
return E_NO_MEM;
}
free(lin);
force_eof = 0;
} else {
force_eof = 1;
}
} else {
#endif
if (DBufGets(&buf, fp) != OK) {
DBufFree(&LineBuffer);
return E_NO_MEM;
}
#ifdef USE_READLINE
}
#endif
LineNo++;
if (ferror(fp)) {
DBufFree(&buf);
DBufFree(&LineBuffer);
return E_IO_ERR;
}
if (feof(fp)) {
if (feof(fp) || force_eof) {
if (use_pclose) {
PCLOSE(fp);
} else {
@@ -360,6 +416,11 @@ static int ReadLineFromFile(int use_pclose)
CurLine = DBufValue(&LineBuffer);
}
#ifdef USE_READLINE_HISTORY
if (fp && IS_INTERACTIVE()) {
add_history(CurLine);
}
#endif
got_a_fresh_line();
clear_callstack();
if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp);
@@ -398,6 +459,7 @@ static int OpenFile(char const *fname)
fprintf(ErrFp, tr("Reading `%s': Found in cache"), fname);
fprintf(ErrFp, "\n");
}
fp = NULL;
CLine = h->cache;
SetCurrentFilename(fname);
LineNo = 0;
@@ -615,6 +677,7 @@ static int PopFile(void)
IncludeStruct *i;
pop_excess_ifs(FileName);
fp = NULL;
if (!IStackPtr) return E_EOF;
i = &IStack[IStackPtr-1];
@@ -627,17 +690,16 @@ static int PopFile(void)
RunDisabled = oldRunDisabled;
}
if (IStackPtr <= 1) {
if (TopLevel()) {
IStackPtr = 0;
return E_EOF;
}
IStackPtr--;
LineNo = i->LineNo;
LineNoStart = i->LineNoStart;
set_base_if_pointer(i->base_if_pointer);
CLine = i->CLine;
fp = NULL;
SetCurrentFilename(i->filename);
if (!i->ownedByMe) {
RunDisabled |= RUN_NOTOWNER;

View File

@@ -87,6 +87,7 @@ static int FCatch (expr_node *, Value *, Value *, int *);
static int FCatchErr (func_info *);
static int FChar (func_info *);
static int FChoose (expr_node *, Value *, Value *, int *);
static int FCodepoint (func_info *);
static int FCoerce (func_info *);
static int FColumns (func_info *);
static int FCurrent (func_info *);
@@ -112,6 +113,7 @@ static int FHebdate (func_info *);
static int FHebday (func_info *);
static int FHebmon (func_info *);
static int FHebyear (func_info *);
static int FHex (func_info *);
static int FHour (func_info *);
static int FHtmlEscape (func_info *);
static int FHtmlStriptags (func_info *);
@@ -126,6 +128,10 @@ static int FLanguage (func_info *);
static int FLocalToUTC (func_info *);
static int FLower (func_info *);
static int FMax (func_info *);
static int FMbchar (func_info *);
static int FMbindex (func_info *);
static int FMbstrlen (func_info *);
static int FMbsubstr (func_info *);
static int FMin (func_info *);
static int FMinsfromutc (func_info *);
static int FMinute (func_info *);
@@ -181,6 +187,7 @@ static int FTrigduration (func_info *);
static int FTriginfo (func_info *);
static int FTrigeventduration(func_info *);
static int FTrigeventstart (func_info *);
static int FTrigeventstarttz (func_info *);
static int FTrigfrom (func_info *);
static int FTrigger (func_info *);
static int FTrigistodo (func_info *);
@@ -190,8 +197,10 @@ static int FTrigrep (func_info *);
static int FTrigscanfrom (func_info *);
static int FTrigtags (func_info *);
static int FTrigtime (func_info *);
static int FTrigtimetz (func_info *);
static int FTrigtimedelta (func_info *);
static int FTrigtimerep (func_info *);
static int FTrigtz (func_info *);
static int FTriguntil (func_info *);
static int FTrigvalid (func_info *);
static int FTypeof (func_info *);
@@ -206,7 +215,6 @@ static int FWkdaynum (func_info *);
static int FYear (func_info *);
static int SunStuff (int rise, double cosz, int dse);
static int tz_set_tz (char const *tz);
/* Caches for extracting months, days, years from dates - may
improve performance slightly. */
@@ -263,6 +271,7 @@ BuiltinFunc Func[] = {
{ "catcherr", 0, 0, 0, FCatchErr, NULL },
{ "char", 1, NO_MAX, 1, FChar, NULL },
{ "choose", 2, NO_MAX, 1, NULL, FChoose }, /*NEW-STYLE*/
{ "codepoint", 1, 1, 1, FCodepoint, NULL },
{ "coerce", 2, 2, 1, FCoerce, NULL },
{ "columns", 0, 1, 0, FColumns, NULL },
{ "const", 1, 1, 1, FNonconst, NULL },
@@ -289,6 +298,7 @@ BuiltinFunc Func[] = {
{ "hebday", 1, 1, 0, FHebday, NULL },
{ "hebmon", 1, 1, 0, FHebmon, NULL },
{ "hebyear", 1, 1, 0, FHebyear, NULL },
{ "hex", 1, 1, 1, FHex, NULL },
{ "hour", 1, 1, 1, FHour, NULL },
{ "htmlescape", 1, 1, 1, FHtmlEscape, NULL },
{ "htmlstriptags",1, 1, 1, FHtmlStriptags, NULL },
@@ -303,6 +313,10 @@ BuiltinFunc Func[] = {
{ "localtoutc", 1, 1, 1, FLocalToUTC, NULL },
{ "lower", 1, 1, 1, FLower, NULL },
{ "max", 1, NO_MAX, 1, FMax, NULL },
{ "mbchar", 1, NO_MAX, 1, FMbchar, NULL },
{ "mbindex", 2, 3, 1, FMbindex, NULL },
{ "mbstrlen", 1, 1, 1, FMbstrlen, NULL },
{ "mbsubstr", 2, 3, 1, FMbsubstr, NULL },
{ "min", 1, NO_MAX, 1, FMin, NULL },
{ "minsfromutc", 0, 2, 0, FMinsfromutc, NULL },
{ "minute", 1, 1, 1, FMinute, NULL },
@@ -357,6 +371,7 @@ BuiltinFunc Func[] = {
{ "trigduration", 0, 0, 0, FTrigduration, NULL },
{ "trigeventduration", 0, 0, 0, FTrigeventduration, NULL },
{ "trigeventstart", 0, 0, 0, FTrigeventstart, NULL },
{ "trigeventstarttz", 0, 0, 0, FTrigeventstarttz, NULL },
{ "trigfrom", 0, 0, 0, FTrigfrom, NULL },
{ "trigger", 1, 3, 0, FTrigger, NULL },
{ "triginfo", 1, 1, 0, FTriginfo, NULL },
@@ -369,6 +384,8 @@ BuiltinFunc Func[] = {
{ "trigtime", 0, 0, 0, FTrigtime, NULL },
{ "trigtimedelta",0, 0, 0, FTrigtimedelta, NULL },
{ "trigtimerep", 0, 0, 0, FTrigtimerep, NULL },
{ "trigtimetz", 0, 0, 0, FTrigtimetz, NULL },
{ "trigtz", 0, 0, 0, FTrigtz, NULL },
{ "triguntil", 0, 0, 0, FTriguntil, NULL },
{ "trigvalid", 0, 0, 0, FTrigvalid, NULL },
{ "typeof", 1, 1, 1, FTypeof, NULL },
@@ -471,6 +488,28 @@ static int FStrlen(func_info *info)
return OK;
}
/***************************************************************/
/* */
/* FMBstrlen - string length in wide characters */
/* */
/***************************************************************/
static int FMbstrlen(func_info *info)
{
#ifdef REM_USE_WCHAR
ASSERT_TYPE(0, STR_TYPE);
size_t l = mbstowcs(NULL, ARGSTR(0), 0);
if (l == (size_t) -1) {
return E_BAD_MB_SEQ;
}
if (l > INT_MAX) return E_2HIGH;
RetVal.type = INT_TYPE;
RETVAL = (int) l;
return OK;
#else
return E_NO_MB;
#endif
}
/***************************************************************/
/* */
/* FBaseyr - system base year */
@@ -693,10 +732,50 @@ static int FAsc(func_info *info)
{
ASSERT_TYPE(0, STR_TYPE);
RetVal.type = INT_TYPE;
RETVAL = *(ARGSTR(0));
RETVAL = (int) *( (unsigned char const *) ARGSTR(0));
return OK;
}
/***************************************************************/
/* */
/* FHex - return hexadecimal representation of an integer */
/* */
/***************************************************************/
static int FHex(func_info *info)
{
char buf[64];
ASSERT_TYPE(0, INT_TYPE);
snprintf(buf, sizeof(buf), "%X", (unsigned int) ARGV(0));
return RetStrVal(buf, info);
}
/***************************************************************/
/* */
/* FCodepoint - wide-character codepoint of start of str */
/* */
/***************************************************************/
static int FCodepoint(func_info *info)
{
#ifdef REM_USE_WCHAR
wchar_t arr[2];
size_t len;
ASSERT_TYPE(0, STR_TYPE);
len = mbstowcs(arr, ARGSTR(0), sizeof(arr) / sizeof(arr[0]));
if (len == (size_t) -1) {
return E_BAD_MB_SEQ;
}
RetVal.type = INT_TYPE;
RETVAL = (int) arr[0];
return OK;
#else
return E_NO_MB;
#endif
}
/***************************************************************/
/* */
/* FChar - build a string from ASCII values */
@@ -745,6 +824,65 @@ static int FChar(func_info *info)
*(RetVal.v.str + Nargs) = 0;
return OK;
}
/***************************************************************/
/* */
/* FMbchar - build a string from wide character code points */
/* */
/***************************************************************/
static int FMbchar(func_info *info)
{
#ifdef REM_USE_WCHAR
int i;
size_t len;
wchar_t *arr;
char *s;
for (i=0; i<Nargs; i++) {
ASSERT_TYPE(i, INT_TYPE);
}
/* Special case of one arg - if given value 0, create empty string */
if (Nargs == 1) {
if (ARGV(0) == 0) {
return RetStrVal("", info);
}
}
arr = calloc(Nargs+1, sizeof(wchar_t));
if (!arr) {
return E_NO_MEM;
}
for (i=0; i<Nargs; i++) {
if (ARGV(i) <= 0) {
return E_2LOW;
}
arr[i] = (wchar_t) ARGV(i);
}
arr[Nargs] = (wchar_t) 0;
len = wcstombs(NULL, arr, 0);
if (len == (size_t) -1) {
free( (void *) arr);
return E_BAD_MB_SEQ;
}
s = malloc(len+1);
if (!s) {
free( (void *) arr);
return E_NO_MEM;
}
(void) wcstombs(s, arr, len+1);
free( (void *) arr);
RetVal.type = STR_TYPE;
RetVal.v.str = s;
return OK;
#else
return E_NO_MB;
#endif
}
/***************************************************************/
/* */
/* Functions for extracting the components of a date. */
@@ -1165,11 +1303,14 @@ static int FOrd(func_info *info)
ASSERT_TYPE(0, INT_TYPE);
if (!in_ford && UserFuncExists("ordx") == 1) {
/* Can't use EvalExprRunDisabled here because we need
the nonconst result */
expr_node *n;
int r;
char const *e = buf;
Value val;
int nonconst;
int old_rundisabled;
val.type = ERR_TYPE;
snprintf(buf, sizeof(buf), "ordx(%d)", ARGV(0));
@@ -1178,7 +1319,10 @@ static int FOrd(func_info *info)
return r;
}
in_ford = 1;
old_rundisabled = RunDisabled;
RunDisabled |= RUN_CB;
r = evaluate_expr_node(n, NULL, &val, &nonconst);
RunDisabled = old_rundisabled;
in_ford = 0;
free_expr_tree(n);
if (r != OK) {
@@ -1950,6 +2094,14 @@ static int FTrigtimerep(func_info *info)
return OK;
}
static int FTrigtz(func_info *info)
{
if (!LastTrigger.tz) {
return RetStrVal("", info);
}
return RetStrVal(LastTrigger.tz, info);
}
static int FTrigeventduration(func_info *info)
{
if (LastTrigger.eventduration == NO_TIME) {
@@ -1985,6 +2137,22 @@ static int FTrigeventstart(func_info *info)
return OK;
}
static int FTrigeventstarttz(func_info *info)
{
if (LastTrigger.eventstart == NO_TIME) {
RetVal.type = INT_TYPE;
RETVAL = -1;
} else {
RetVal.type = DATETIME_TYPE;
if (LastTrigger.eventstart_orig != NO_TIME) {
RETVAL = LastTrigger.eventstart_orig;
} else {
RETVAL = LastTrigger.eventstart;
}
}
return OK;
}
static int FTrigduration(func_info *info)
{
if (LastTimeTrig.duration == NO_TIME) {
@@ -2071,6 +2239,22 @@ static int FTrigtime(func_info *info)
return OK;
}
static int FTrigtimetz(func_info *info)
{
if (LastTriggerTime != NO_TIME) {
RetVal.type = TIME_TYPE;
if (LastTimeTrig.ttime_orig != NO_TIME) {
RETVAL = LastTimeTrig.ttime_orig;
} else {
RETVAL = LastTriggerTime;
}
} else {
RetVal.type = INT_TYPE;
RETVAL = 0;
}
return OK;
}
static int FTrigdatetime(func_info *info)
{
if (!LastTrigValid) {
@@ -2173,6 +2357,10 @@ static int FTrigger(func_info *info)
tim = ARGV(1);
if (ARGV(2)) {
UTCToLocal(date, tim, &date, &tim);
if (date < 0) {
date = 0;
tim = 0;
}
}
} else if (Nargs > 1) {
/* Date Time */
@@ -2187,6 +2375,10 @@ static int FTrigger(func_info *info)
ASSERT_TYPE(1, INT_TYPE);
if (ARGV(1)) {
UTCToLocal(date, tim, &date, &tim);
if (date < 0) {
date = 0;
tim = 0;
}
}
}
}
@@ -2319,6 +2511,75 @@ static int FSubstr(func_info *info)
return RetStrVal(t, info);
}
/***************************************************************/
/* */
/* FMbubstr */
/* */
/* The mbsubstr function. */
/* */
/***************************************************************/
static int FMbsubstr(func_info *info)
{
#ifdef REM_USE_WCHAR
wchar_t *str;
wchar_t *s;
wchar_t const *t;
size_t mblen;
char *converted;
size_t len;
int start;
int end;
if (ARG(0).type != STR_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
if (Nargs == 3 && ARG(2).type != INT_TYPE) return E_BAD_TYPE;
mblen = mbstowcs(NULL, ARGSTR(0), 0);
if (mblen == (size_t) -1) {
return E_BAD_MB_SEQ;
}
str = calloc(mblen+1, sizeof(wchar_t));
if (!str) {
return E_NO_MEM;
}
(void) mbstowcs(str, ARGSTR(0), mblen+1);
s = str;
start = 1;
while (start < ARGV(1)) {
if (!*s) break;
s++;
start++;
}
t = s;
if (Nargs >= 3) {
end = start;
while (end <= ARGV(2)) {
if (!*s) break;
s++;
end++;
}
*s = (wchar_t) 0;
}
len = wcstombs(NULL, t, 0);
if (len == (size_t) -1) {
free( (void *) str);
return E_BAD_MB_SEQ;
}
converted = malloc(len+1);
if (!converted) {
free( (void *) str);
return E_NO_MEM;
}
(void) wcstombs(converted, t, len+1);
RetVal.type = STR_TYPE;
RetVal.v.str = converted;
free( (void *) str);
return OK;
#else
return E_NO_MB;
#endif
}
/***************************************************************/
/* */
/* FIndex */
@@ -2357,6 +2618,73 @@ static int FIndex(func_info *info)
return OK;
}
/***************************************************************/
/* */
/* FMbindex */
/* */
/* The wide-char of one string embedded in another. */
/* */
/***************************************************************/
static int FMbindex(func_info *info)
{
#ifdef REM_USE_WCHAR
wchar_t *haystack;
wchar_t *needle;
wchar_t const *s;
size_t haylen, needlelen;
if (ARG(0).type != STR_TYPE || ARG(1).type != STR_TYPE ||
(Nargs == 3 && ARG(2).type != INT_TYPE)) return E_BAD_TYPE;
haylen = mbstowcs(NULL, ARGSTR(0), INT_MAX);
if (haylen == (size_t) -1) {
return E_BAD_MB_SEQ;
}
haystack = calloc(haylen+1, sizeof(wchar_t));
if (!haystack) {
return E_NO_MEM;
}
(void) mbstowcs(haystack, ARGSTR(0), haylen+1);
needlelen = mbstowcs(NULL, ARGSTR(1), INT_MAX);
if (needlelen == (size_t) -1) {
free( (void *) haystack);
return E_BAD_MB_SEQ;
}
needle = calloc(needlelen+1, sizeof(wchar_t));
if (!needle) {
free( (void *) haystack);
return E_NO_MEM;
}
(void) mbstowcs(needle, ARGSTR(1), needlelen+1);
s = haystack;
/* If 3 args, bump up the start */
if (Nargs == 3) {
if (ARGV(2) > (int) haylen) {
s += haylen;
} else {
s += ARGV(2) - 1;
}
}
/* Find the string */
RetVal.type = INT_TYPE;
s = wcsstr(s, needle);
if (!s) {
free( (void *) haystack);
free( (void *) needle);
RETVAL = 0;
return OK;
}
RETVAL = s - haystack + 1;
free( (void *) haystack);
free( (void *) needle);
return OK;
#else
return E_NO_MB;
#endif
}
/***************************************************************/
/* */
/* FIif */
@@ -3865,7 +4193,7 @@ static void unsetenv(char const *varname)
/* Conversion between different timezones. */
/* */
/***************************************************************/
static int tz_set_tz(char const *tz)
int tz_set_tz(char const *tz)
{
int r;
if (tz == NULL) {
@@ -3878,7 +4206,7 @@ static int tz_set_tz(char const *tz)
return r;
}
static int tz_convert(int year, int month, int day,
int tz_convert(int year, int month, int day,
int hour, int minute,
char const *src_tz, char const *tgt_tz,
struct tm *tm)
@@ -3905,9 +4233,12 @@ static int tz_convert(int year, int month, int day,
old_tz = StrDup(old_tz);
if (!old_tz) return E_NO_MEM;
}
if (tgt_tz == NULL) {
if (tgt_tz == NULL || !*tgt_tz) {
tgt_tz = old_tz;
}
if (src_tz == NULL || !*src_tz) {
src_tz = old_tz;
}
/* set source TZ */
r = tz_set_tz(src_tz);
@@ -4156,7 +4487,7 @@ FEval(func_info *info)
{
expr_node *n;
int r;
int run_was_enabled = 0;
int old_run_disabled;
ASSERT_TYPE(0, STR_TYPE);
char const *e = ARGSTR(0);
@@ -4168,14 +4499,13 @@ FEval(func_info *info)
}
/* Disable shell() command in eval */
if (! (RunDisabled & RUN_IN_EVAL)) {
run_was_enabled = 1;
RunDisabled |= RUN_IN_EVAL;
}
old_run_disabled = RunDisabled;
RunDisabled |= RUN_IN_EVAL;
r = evaluate_expr_node(n, NULL, &(info->retval), &(info->nonconst));
if (run_was_enabled) {
RunDisabled &= ~RUN_IN_EVAL;
}
RunDisabled = old_run_disabled;
free_expr_tree(n);
return r;
}
@@ -4204,25 +4534,32 @@ FEvalTrig(func_info *info)
DestroyParser(&p);
return r;
}
if (trig.tz != NULL && tim.ttime == NO_TIME) {
FreeTrig(&trig);
return E_TZ_NO_AT;
}
if (trig.typ != NO_TYPE) {
DestroyParser(&p);
FreeTrig(&trig);
return E_PARSE_ERR;
}
if (scanfrom == NO_DATE) {
EnterTimezone(trig.tz);
dse = ComputeTrigger(get_scanfrom(&trig), &trig, &tim, &r, 0);
ExitTimezone(trig.tz);
} else {
/* Hokey... */
if (get_scanfrom(&trig) != DSEToday) {
Wprint(tr("Warning: SCANFROM is ignored in two-argument form of evaltrig()"));
}
EnterTimezone(trig.tz);
dse = ComputeTrigger(scanfrom, &trig, &tim, &r, 0);
ExitTimezone(trig.tz);
}
if (r == E_CANT_TRIG && trig.maybe_uncomputable) {
r = 0;
dse = -1;
}
FreeTrig(&trig);
DestroyParser(&p);
if (r) return r;
if (dse < 0) {
@@ -4232,9 +4569,11 @@ FEvalTrig(func_info *info)
RetVal.type = DATE_TYPE;
RETVAL = dse;
} else {
dse = AdjustTriggerForTimeZone(&trig, dse, &tim);
RetVal.type = DATETIME_TYPE;
RETVAL = (MINUTES_PER_DAY * dse) + tim.ttime;
}
FreeTrig(&trig);
return OK;
}
@@ -4263,6 +4602,10 @@ FMultiTrig(func_info *info)
DestroyParser(&p);
return r;
}
if (trig.tz != NULL && tim.ttime == NO_TIME) {
FreeTrig(&trig);
return E_TZ_NO_AT;
}
if (trig.typ != NO_TYPE) {
DestroyParser(&p);
FreeTrig(&trig);
@@ -4273,6 +4616,9 @@ FMultiTrig(func_info *info)
return E_PARSE_ERR;
}
dse = ComputeTrigger(get_scanfrom(&trig), &trig, &tim, &r, 0);
/* multitrig only does untimed reminders, so no need to worry
about time zones */
DestroyParser(&p);
if (r != E_CANT_TRIG) {
@@ -4320,18 +4666,25 @@ FTrig(func_info *info)
DestroyParser(&p);
return r;
}
if (trig.tz != NULL && tim.ttime == NO_TIME) {
FreeTrig(&trig);
return E_TZ_NO_AT;
}
if (trig.typ != NO_TYPE) {
DestroyParser(&p);
FreeTrig(&trig);
return E_PARSE_ERR;
}
EnterTimezone(trig.tz);
dse = ComputeTrigger(get_scanfrom(&trig), &trig, &tim, &r, 0);
ExitTimezone(trig.tz);
DestroyParser(&p);
if (r == E_CANT_TRIG) {
FreeTrig(&trig);
continue;
}
dse = AdjustTriggerForTimeZone(&trig, dse, &tim);
if (ShouldTriggerReminder(&trig, &tim, dse, &r)) {
LastTrig = dse;
RETVAL = dse;

View File

@@ -47,6 +47,7 @@ EXTERN FILE *ErrFp;
EXTERN int DSEToday;
EXTERN int RealToday;
EXTERN int LocalDSEToday;
EXTERN int CurDay;
EXTERN int CurMon;
EXTERN int CurYear;
@@ -87,6 +88,10 @@ EXTERN INIT( int DeltaOverride, 0);
EXTERN INIT( int RunDisabled, 0);
EXTERN INIT( int ExpressionEvaluationDisabled, 0);
EXTERN INIT( int ExpressionEvaluationTimeLimit, 0);
EXTERN INIT( int ExpressionNodesEvaluated, 0);
EXTERN INIT( int MaxExprNodesPerLine, 0);
EXTERN INIT( int ExpressionNodesEvaluatedThisLine, 0);
EXTERN INIT( int ExpressionNodeLimitPerLine, 0);
EXTERN INIT( volatile sig_atomic_t ExpressionTimeLimitExceeded, 0);
EXTERN INIT( int IgnoreOnce, 0);
EXTERN INIT( char const *OnceFile, NULL);
@@ -98,9 +103,11 @@ EXTERN INIT( int SortByPrio, SORT_NONE);
EXTERN INIT( int UntimedBeforeTimed, 0);
EXTERN INIT( int DefaultPrio, NO_PRIORITY);
EXTERN INIT( int SysTime, -1);
EXTERN INIT( int ParseUntriggered, 1);
EXTERN INIT( int LocalSysTime, -1);
EXTERN INIT( int ParseUntriggered, 0);
EXTERN char const *InitialFile;
EXTERN char const *LocalTimeZone;
EXTERN int FileAccessDate;
EXTERN INIT( int WeekdayOmits, 0);

View File

@@ -177,8 +177,25 @@ void InitRemind(int argc, char const *argv[])
int dse;
int ttyfd;
/* Make sure remind is not installed set-uid or set-gid */
if (getgid() != getegid() ||
getuid() != geteuid()) {
fprintf(ErrFp, "\nRemind should not be installed set-uid or set-gid.\nCHECK YOUR SYSTEM SECURITY.\n");
exit(EXIT_FAILURE);
}
dse = NO_DATE;
/* Initialize local time zone */
LocalTimeZone = getenv("TZ");
if (LocalTimeZone) {
LocalTimeZone = StrDup(LocalTimeZone);
if (!LocalTimeZone) {
fprintf(stderr, "Out of memory!\n");
exit(1);
}
}
/* Initialize variable hash table */
InitVars();
@@ -203,13 +220,6 @@ void InitRemind(int argc, char const *argv[])
InitDedupeTable();
/* Make sure remind is not installed set-uid or set-gid */
if (getgid() != getegid() ||
getuid() != geteuid()) {
fprintf(ErrFp, "\nRemind should not be installed set-uid or set-gid.\nCHECK YOUR SYSTEM SECURITY.\n");
exit(EXIT_FAILURE);
}
y = NO_YR;
m = NO_MON;
d = NO_DAY;
@@ -221,7 +231,9 @@ void InitRemind(int argc, char const *argv[])
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
LocalSysTime = SystemTime(0);
DSEToday = RealToday;
LocalDSEToday = DSEToday;
FromDSE(DSEToday, &CurYear, &CurMon, &CurDay);
/* Initialize Latitude and Longitude */
@@ -646,6 +658,7 @@ void InitRemind(int argc, char const *argv[])
case 'q': case 'Q': DebugFlag |= DB_TRANSLATE; break;
case 'n': case 'N': DebugFlag |= DB_NONCONST; break;
case 'u': case 'U': DebugFlag |= DB_UNUSED_VARS; break;
case 'z': case 'Z': DebugFlag |= DB_SWITCH_ZONE; break;
default:
fprintf(ErrFp, GetErr(M_BAD_DB_FLAG), *(arg-1));
fprintf(ErrFp, "\n");
@@ -711,6 +724,7 @@ void InitRemind(int argc, char const *argv[])
if (SysTime != -1L) Usage();
else {
SysTime = (long) tok.val * 60L;
LocalSysTime = SysTime;
DontQueue = 1;
Daemon = 0;
}
@@ -720,6 +734,7 @@ void InitRemind(int argc, char const *argv[])
if (SysTime != -1L) Usage();
if (m != NO_MON || d != NO_DAY || y != NO_YR || dse != NO_DATE) Usage();
SysTime = (tok.val % MINUTES_PER_DAY) * 60;
LocalSysTime = SysTime;
DontQueue = 1;
Daemon = 0;
dse = tok.val / MINUTES_PER_DAY;
@@ -790,6 +805,7 @@ void InitRemind(int argc, char const *argv[])
fprintf(ErrFp, "%s", BadDate);
Usage();
}
LocalDSEToday = DSEToday;
CurYear = y;
CurMon = m;
CurDay = d;
@@ -864,6 +880,7 @@ void Usage(void)
fprintf(ErrFp, " --only-events Do not issue TODO reminders\n");
fprintf(ErrFp, " --json Use JSON output instead of plain-text\n");
fprintf(ErrFp, " --max-execution-time=n Limit execution time to n seconds\n");
fprintf(ErrFp, " --max-expr-complexity=n Limit expression evaluation to n nodes per line\n");
fprintf(ErrFp, " --print-config-cmd Print ./configure cmd used to build Remind\n");
fprintf(ErrFp, " --print-errs Print all possible error messages\n");
fprintf(ErrFp, " --print-tokens Print all possible Remind tokens\n");
@@ -1017,17 +1034,15 @@ static void InitializeVar(char const *str)
return;
}
if (!*str) {
/* Setting a system var does require =expr on the commandline */
if (*varname == '$') {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_EQ));
fprintf(ErrFp, "\n");
return;
}
val.type = INT_TYPE;
val.v.val = 0;
r = SetVar(varname, &val, 1);
if (!r) {
r = PreserveVar(varname);
if (*varname == '$') {
r = SetSysVar(varname+1, &val);
} else {
r = SetVar(varname, &val, 1);
if (!r) {
r = PreserveVar(varname);
}
}
if (r) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
@@ -1146,6 +1161,7 @@ ProcessLongOption(char const *arg)
/* Update RealToday because of TestMode */
RealToday = SystemDate(&CurYear, &CurMon, &CurDay);
DSEToday = RealToday;
LocalDSEToday = DSEToday;
FromDSE(DSEToday, &CurYear, &CurMon, &CurDay);
return;
@@ -1166,6 +1182,11 @@ ProcessLongOption(char const *arg)
TodoFilter = ONLY_EVENTS;
return;
}
if (!strcmp(arg, "flush")) {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
return;
}
if (!strcmp(arg, "json")) {
JSONMode = 1;
DontQueue = 1;
@@ -1201,6 +1222,14 @@ ProcessLongOption(char const *arg)
print_sysvar_tokens();
exit(0);
}
if (sscanf(arg, "max-expr-complexity=%d", &t) == 1) {
if (t < 0) {
fprintf(ErrFp, "%s: --max-expr-complexity must be non-negative\n", ArgV[0]);
return;
}
ExpressionNodeLimitPerLine = t;
return;
}
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]);

View File

@@ -84,6 +84,8 @@ exitfunc(void)
UnsetAllUserFuncs();
print_expr_nodes_stats();
fprintf(ErrFp, "Max expr node evaluations per line: %d\n", MaxExprNodesPerLine);
fprintf(ErrFp, "Total expression node evaluations: %d\n", ExpressionNodesEvaluated);
}
}
@@ -153,6 +155,7 @@ int main(int argc, char *argv[])
DBufInit(&(LastTrigger.tags));
LastTrigger.infos = NULL;
LastTrigger.tz = NULL;
ClearLastTriggers();
atexit(exitfunc);
@@ -1290,7 +1293,9 @@ int DoIfTrig(ParsePtr p)
} else {
if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if (trig.typ != NO_TYPE) return E_PARSE_ERR;
EnterTimezone(trig.tz);
dse = ComputeTrigger(get_scanfrom(&trig), &trig, &tim, &r, 1);
ExitTimezone(trig.tz);
if (r) {
if (r != E_CANT_TRIG || !trig.maybe_uncomputable) {
if (!Hush || r != E_RUN_DISABLED) {
@@ -1299,6 +1304,9 @@ int DoIfTrig(ParsePtr p)
}
push_if(0, 0);
} else {
if (dse >= 0) {
dse = AdjustTriggerForTimeZone(&trig, dse, &tim);
}
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
push_if(1, 0);
} else {
@@ -1439,6 +1447,11 @@ static int DoDebug(ParsePtr p)
if (val) DebugFlag |= DB_UNUSED_VARS;
else DebugFlag &= ~DB_UNUSED_VARS;
break;
case 'z':
case 'Z':
if (val) DebugFlag |= DB_SWITCH_ZONE;
else DebugFlag &= ~DB_SWITCH_ZONE;
break;
default:
Wprint(GetErr(M_BAD_DB_FLAG), ch);
break;
@@ -2039,6 +2052,10 @@ FreeTrig(Trigger *t)
if (t->infos) {
FreeTrigInfoChain(t->infos);
}
if (t->tz) {
free( (void *) t->tz);
}
t->tz = NULL;
t->infos = NULL;
}
@@ -2069,7 +2086,9 @@ ClearLastTriggers(void)
LastTrigger.complete_through = NO_DATE;
LastTrigger.max_overdue = -1;
FreeTrig(&LastTrigger);
LastTimeTrig.ttime = NO_TIME;
LastTimeTrig.ttime_orig = NO_TIME;
LastTimeTrig.delta = NO_DELTA;
LastTimeTrig.rep = NO_REP;
LastTimeTrig.duration = NO_TIME;
@@ -2098,6 +2117,9 @@ SaveLastTrigger(Trigger const *t)
LastTrigger.infos = NULL;
DBufInit(&(LastTrigger.tags));
if (LastTrigger.tz) {
LastTrigger.tz = StrDup(LastTrigger.tz);
}
DBufPuts(&(LastTrigger.tags), DBufValue(&(t->tags)));
TrigInfo *cur = t->infos;
while(cur) {
@@ -2122,6 +2144,7 @@ System(char const *cmd, int is_queued)
int fd;
int status;
int do_exit = 0;
if (is_queued && IsServerMode()) {
do_exit = 1;
/* Server mode... redirect stdin and stdout to /dev/null */
@@ -2151,10 +2174,9 @@ System(char const *cmd, int is_queued)
}
/* This is the child process or original if we never forked */
if (JSONMode) {
(void) system_to_stderr(cmd);
} else {
(void) system(cmd);
(void) system1(cmd);
}
if (do_exit) {
/* In the child process, so exit! */

View File

@@ -66,8 +66,9 @@ void unlimit_execution_time(void);
expr_node *free_expr_tree(expr_node *node);
expr_node *clone_expr_tree(expr_node const *node, int *r);
int EvalExpr (char const **e, Value *v, ParsePtr p);
int EvalExprRunDisabled(char const **e, Value *v, ParsePtr p);
int DoCoerce (char type, Value *v);
char const *PrintValue (Value *v, FILE *fp);
char const *PrintValue (Value const *v, FILE *fp);
int CopyValue (Value *dest, const Value *src);
int ReadLine (void);
int DoInclude (ParsePtr p, enum TokTypes tok);
@@ -294,3 +295,9 @@ int get_scanfrom(Trigger const *t);
void remove_trailing_newlines(DynamicBuffer *buf);
void set_cloexec(int fd);
int system_to_stderr(char const *cmd);
int system1(char const *cmd);
int tz_set_tz (char const *tz);
int tz_convert(int year, int month, int day, int hour, int minute, char const *src_tz, char const *tgt_tz, struct tm *tm);
int AdjustTriggerForTimeZone(Trigger *trig, int dse, TimeTrig *tim);
void EnterTimezone(char const *tz);
void ExitTimezone(char const *tz);

View File

@@ -395,7 +395,6 @@ void HandleQueuedReminders(void)
(MaxLateMinutes == 0 || SystemTime(1) - (q->tt.nexttime * 60) <= 60 * MaxLateMinutes))) {
/* Trigger the reminder */
CreateParser(q->text, &p);
RunDisabled = q->RunDisabled;
if (IsServerMode() && q->typ != RUN_TYPE) {
if (DaemonJSON) {
printf("{\"response\":\"reminder\",");
@@ -640,13 +639,15 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
return NO_TIME;
}
RunDisabled = q->RunDisabled; /* Don't want weird scheduling functions
to be a security hole! */
while(1) {
char exprBuf[VAR_NAME_LEN+32];
snprintf(exprBuf, sizeof(exprBuf), "%s(%d)", q->sched, q->ntrig);
s = exprBuf;
r = EvalExpr(&s, &v, NULL);
if (q->RunDisabled) {
r = EvalExprRunDisabled(&s, &v, NULL);
} else {
r = EvalExpr(&s, &v, NULL);
}
if (r) {
q->sched[0] = 0;
return NO_TIME;

View File

@@ -123,6 +123,7 @@ Token TokArray[] = {
{ "todo", 4, T_Todo, 0 },
{ "translate", 5, T_Translate, 0 },
{ "tuesday", 3, T_WkDay, 1 },
{ "tz", 2, T_Tz, 0 },
{ "unset", 5, T_UnSet, 0 },
{ "until", 5, T_Until, 0 },
{ "warn", 4, T_Warn, 0 },

View File

@@ -453,6 +453,7 @@ AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int sav
/* If we have an AT, save the original event start */
if (tim->ttime != NO_TIME) {
trig->eventstart = MINUTES_PER_DAY * r + tim->ttime;
trig->eventstart_orig = trig->eventstart;
if (tim->duration != NO_TIME) {
trig->eventduration = tim->duration;
}
@@ -515,6 +516,7 @@ int ComputeTrigger(int today, Trigger *trig, TimeTrig *tim,
if (r == today) {
if (tim->ttime != NO_TIME) {
trig->eventstart = MINUTES_PER_DAY * r + tim->ttime;
trig->eventstart_orig = trig->eventstart;
if (tim->duration != NO_TIME) {
trig->eventduration = tim->duration;
}
@@ -613,6 +615,21 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig const *tim
omit = 0;
}
/** FIXME: If a timed reminder moves to yesterday because of a time
zone adjustment, try again! */
if (trig->tz) {
TimeTrig copy = *tim;
int new_result;
ExitTimezone(trig->tz);
new_result = AdjustTriggerForTimeZone(trig, result, &copy);
EnterTimezone(trig->tz);
if (new_result + duration_days < today) {
nextstart = start+1;
start = nextstart;
continue;
}
}
/** FIXME: Fix bad interaction with SATISFY... need to rethink!!! */
if (result+duration_days >= today &&
(trig->skip != SKIP_SKIP || !omit)) {

View File

@@ -141,6 +141,7 @@ typedef struct {
int priority;
int duration_days; /* Duration converted to days to search */
int eventstart; /* Original event start (datetime) */
int eventstart_orig; /* Original event start in TZ (datetime) */
int eventduration; /* Original event duration (minutes) */
int maybe_uncomputable; /* Suppress "can't compute trigger" warnings */
int addomit; /* Add trigger date to global OMITs */
@@ -152,10 +153,12 @@ typedef struct {
DynamicBuffer tags;
char passthru[PASSTHRU_LEN+1];
TrigInfo *infos;
char const *tz; /* Time Zone */
} Trigger;
/* A time trigger */
typedef struct {
int ttime_orig;
int ttime;
int nexttime;
int delta;
@@ -226,6 +229,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_TRANSLATE 0x100
#define DB_NONCONST 0x200
#define DB_UNUSED_VARS 0x400
#define DB_SWITCH_ZONE 0x800
/* Enumeration of the tokens */
enum TokTypes
@@ -239,8 +243,8 @@ enum TokTypes
T_Omit, T_OmitFunc, T_Once, T_Ordinal, T_Pop, T_PopFuncs, T_PopVars,
T_Preserve, T_Priority, T_Push, T_PushFuncs, T_PushVars, T_Rem,
T_RemType, T_Rep, T_Return, T_Scanfrom, T_Sched, T_Set, T_Skip, T_Tag,
T_Through, T_Time, T_Todo, T_Translate, T_UnSet, T_Until, T_Warn, T_WkDay,
T_Year
T_Through, T_Time, T_Todo, T_Translate, T_Tz, T_UnSet, T_Until, T_Warn,
T_WkDay, T_Year,
};
/* The structure of a token */
@@ -263,6 +267,8 @@ typedef struct {
#define RUN_SCRIPT 0x02
#define RUN_NOTOWNER 0x04
#define RUN_IN_EVAL 0x08
#define RUN_UF 0x10 /* A user-function defined with RUN OFF */
#define RUN_CB 0x20 /* A callback */
/* Flags for the SimpleCalendar format */
#define SC_AMPM 0 /* Time shown as 3:00am, etc. */
@@ -317,6 +323,7 @@ typedef struct udf_struct {
int lineno_start;
int recurse_flag;
int been_pushed;
int run_disabled;
} UserFunc;
/* A pushed systtem variable */

View File

@@ -275,6 +275,11 @@ int DoFset(ParsePtr p)
func->lineno_start = LineNoStart;
func->recurse_flag = 0;
func->been_pushed = 0;
if (RunDisabled) {
func->run_disabled = 1;
} else {
func->run_disabled = 0;
}
if (in_constant_context()) {
func->is_constant = 1;
} else {

View File

@@ -31,6 +31,13 @@ static char const DontEscapeMe[] =
#include "globals.h"
#include "protos.h"
/* Call this instead of system() so if called ignores return code,
we don't get a compiler warning */
int system1(char const *cmd)
{
return system(cmd);
}
/***************************************************************/
/* */
/* system_to_stderr */

View File

@@ -786,9 +786,14 @@ int DoDump(ParsePtr p)
DumpSysVarByName(DBufValue(&buf)+1);
} else {
v = FindVar(DBufValue(&buf), 0);
DBufValue(&buf)[VAR_NAME_LEN] = 0;
if (!v) fprintf(ErrFp, "%s %s\n",
if (!v) {
if (DBufLen(&buf) > VAR_NAME_LEN) {
/* Truncate over-long variable name */
DBufValue(&buf)[VAR_NAME_LEN] = 0;
}
fprintf(ErrFp, "%s %s\n",
DBufValue(&buf), UNDEF);
}
else {
fprintf(ErrFp, "%s ", v->name);
PrintValue(&(v->v), ErrFp);

2
tests/Makefile Normal file
View File

@@ -0,0 +1,2 @@
%:
@make -s -C.. $@

44
tests/safety.rem Normal file
View File

@@ -0,0 +1,44 @@
BANNER %
SET $AddBlankLines 0
FSET danger(x) shell("echo oops")
RUN OFF
FSET safe(x) shell("echo nope")
FSET safe2(x) danger(x)
RUN ON
DEBUG +x
set a danger(1)
set b safe(1)
set c safe2(1)
PUSH-FUNCS safe
FSET safe(x) shell("echo this will work")
SET b safe(1)
POP-FUNCS
SET b safe(1)
FRENAME safe gloopy
FRENAME danger bork
set a bork(1)
set b gloopy(1)
FRENAME gloopy safe
FRENAME bork danger
RUN OFF
set a danger(1)
set b safe(1)
set b safe2(1)
RUN ON
DEBUG -x
FSET subst_b(a, b, c) shell("echo nooooo....")
REM MSG [subst_b(1, 2, 3)]
FLUSH
REM MSG %b
FLUSH
REM MSG [subst_b(1, 2, 3)]
FLUSH

View File

@@ -1,4 +1,6 @@
set $AddBlankLines 0
SET $Latitude "45.42055556"
SET $Longitude "-75.68944444"
banner %
set d '2011-01-01'

View File

@@ -33,7 +33,7 @@ fi
alias remind="echo You should be using ../src/remind explicitly in test-rem >&2; exit 1"
# Set a known timezone so moon phases show up in predictable places
TZ=UTC
TZ=Universal
export TZ
# If we're already in a utf-8 locale, do
@@ -59,87 +59,87 @@ chmod 000 include_dir/04cantread.rem
TEST_GETENV="foo bar baz" ; export TEST_GETENV
echo "Test 1" > ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 2>&1 | grep -v 'TimetIs64bit' >> ../tests/test.out
../src/remind --flush -e -dxte ../tests/test.rem 16 feb 1991 12:13 2>&1 | grep -v -a 'TimetIs64bit' >> ../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
../src/remind --flush -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo "Test 3" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -s ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -s ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo "Test 4" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -sa ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -sa ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
echo "Test 5" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l -b0 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -p -l -b0 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1
echo "Test 6" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l -b1 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -p -l -b1 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1
echo "Test 7" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l -b2 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -p -l -b2 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out 2>&1
echo "Test 8" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -df -p -l -b2 ../tests/include_dir 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -df -p -l -b2 ../tests/include_dir 1 aug 2007 >> ../tests/test.out 2>&1
echo "Test 9" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -df -p -l -b2 ../tests/nonexistent_include_dir 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind -df -p -l -b2 ../tests/include_dir_no_rems 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind -df -p -l -b2 ../tests/include_test.rem 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -df -p -l -b2 ../tests/nonexistent_include_dir 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -df -p -l -b2 ../tests/include_dir_no_rems 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -df -p -l -b2 ../tests/include_test.rem 1 aug 2007 >> ../tests/test.out 2>&1
chmod 644 include_dir/04cantread.rem
# Feb 29 bug
echo "Feb 29 Bug Test" >> ../tests/test.out
echo 'REM Sun 29 Feb MSG [$T]' | ../src/remind -dt - 1 feb 2021 >> ../tests/test.out 2>&1
echo 'REM Sun 29 Feb MSG [$T]' | ../src/remind --flush -dt - 1 feb 2021 >> ../tests/test.out 2>&1
# Day Weekday Year out-of-year bug
echo "Mon 31 Dec Bug Test" >> ../tests/test.out
echo 'REM Mon 31 2021 MSG [$T]' | ../src/remind -dt - 31 dec 2021 >> ../tests/test.out 2>&1
echo 'REM Mon 31 2021 MSG [$T]' | ../src/remind --flush -dt - 31 dec 2021 >> ../tests/test.out 2>&1
echo "Color Test" >> ../tests/test.out
../src/remind -ccl ../tests/colors.rem 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind --flush -ccl ../tests/colors.rem 1 aug 2007 >> ../tests/test.out 2>&1
echo "ANSI Color Test" >> ../tests/test.out
../src/remind ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@2 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@0,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@1,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@2,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@2,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@0,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@1,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@2,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@0,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@1,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@2,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@0,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@1,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind -@2,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@2 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@0,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@1,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@2,0 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@2,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@0,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@1,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@2,,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@0,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@1,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@2,0,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@0,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@1,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush -@2,1,1 ../tests/ansicolors.rem 1 Jan 2022 >> ../tests/test.out 2>&1
echo '$AddBlankLines test' >> ../tests/test.out
../src/remind ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind '-i$AddBlankLines=1' ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind '-i$AddBlankLines=0' ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush '-i$AddBlankLines=1' ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1
../src/remind --flush '-i$AddBlankLines=0' ../tests/blanks.rem 1 Jan 2022 >> ../tests/test.out 2>&1
echo "MON WKDAY DAY across year test" >> ../tests/test.out
echo 'REM Mon 29 Dec MSG x' | ../src/remind -dt - 1 Jan 2000 >> ../tests/test.out 2>&1
echo 'REM Mon 29 Dec MSG x' | ../src/remind --flush -dt - 1 Jan 2000 >> ../tests/test.out 2>&1
echo "Sort Test" >> ../tests/test.out
(echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind -q -gaaa - 1 Jan 2000 >> ../tests/test.out 2>&1
(echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind -q -gaaad - 1 Jan 2000 >> ../tests/test.out 2>&1
(echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind --flush -q -gaaa - 1 Jan 2000 >> ../tests/test.out 2>&1
(echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind --flush -q -gaaad - 1 Jan 2000 >> ../tests/test.out 2>&1
echo "Purge Test" >> ../tests/test.out
../src/remind -j999 ../tests/purge_dir/f1.rem 3 Feb 2012 >> ../tests/test.out 2>&1
../src/remind --flush -j999 ../tests/purge_dir/f1.rem 3 Feb 2012 >> ../tests/test.out 2>&1
echo "F1" >> ../tests/test.out
cat ../tests/purge_dir/f1.rem.purged >> ../tests/test.out
echo "F2" >> ../tests/test.out
@@ -150,7 +150,7 @@ cat ../tests/purge_dir/f3.rem.purged >> ../tests/test.out
rm -f ../tests/purge_dir/*.rem.purged >> ../tests/test.out 2>&1
echo "Hush Purge Test" >> ../tests/test.out
../src/remind -h -j999 ../tests/purge_dir/f1.rem 3 Feb 2012 >> ../tests/test.out 2>&1
../src/remind --flush -h -j999 ../tests/purge_dir/f1.rem 3 Feb 2012 >> ../tests/test.out 2>&1
echo "F1" >> ../tests/test.out
cat ../tests/purge_dir/f1.rem.purged >> ../tests/test.out
echo "F2" >> ../tests/test.out
@@ -160,22 +160,22 @@ cat ../tests/purge_dir/f3.rem.purged >> ../tests/test.out
rm -f ../tests/purge_dir/*.rem.purged >> ../tests/test.out 2>&1
../src/remind ../tests/runtest.rem >> ../tests/test.out 2>&1
../src/remind --flush ../tests/runtest.rem >> ../tests/test.out 2>&1
../src/remind -p ../tests/shade.rem 1 August 2009 | ../src/rem2ps -e -l -c3 >> ../tests/test.out 2>&1
../src/remind -pp ../tests/shade.rem 1 August 2009 | ../src/rem2ps -e -l -c3 >> ../tests/test.out 2>&1
../src/remind --flush -p ../tests/shade.rem 1 August 2009 | ../src/rem2ps -e -l -c3 >> ../tests/test.out 2>&1
../src/remind --flush -pp ../tests/shade.rem 1 August 2009 | ../src/rem2ps -e -l -c3 >> ../tests/test.out 2>&1
TZ=America/Toronto ../src/remind ../tests/sunmoon.rem 1 Jan 2011 >> ../tests/test.out 2>&1
TZ=America/Toronto ../src/remind --flush ../tests/sunmoon.rem 1 Jan 2011 >> ../tests/test.out 2>&1
# Test -a vs -aa
../src/remind -q -a - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -q -a - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM 1 Jan 2012 AT 8:00 MSG 8am: Should not show up
REM 1 Jan 2012 AT 9:00 MSG 9am: Should not show up
REM 1 Jan 2012 AT 10:00 MSG 10am: Should not show up
MSG [$DontTrigAts]
EOF
../src/remind -q -a -a - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -q -a -a - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM 1 Jan 2012 AT 8:00 MSG 8am: Should not show up
REM 1 Jan 2012 AT 9:00 MSG 9am: Should show up
REM 1 Jan 2012 AT 10:00 MSG 10am: Should show up
@@ -183,12 +183,12 @@ MSG [$DontTrigAts]
EOF
# OMITFUNC should indicate nonconst_expr
../src/remind -pp - 1 jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -pp - 1 jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM Mon OMITFUNC foo MSG bar
EOF
# Test default color
../src/remind -pppq - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -pppq - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM 2 MSG Normal
SET $DefaultColor "255 0 0"
REM 3 \
@@ -201,7 +201,7 @@ SET $DefaultColor \
EOF
# Test default color with weekly calendar
../src/remind -pq+ - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -pq+ - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM 2 MSG Normal
SET $DefaultColor "255 0 0"
REM 3 MSG %"Red%" on the calendar!
@@ -212,18 +212,18 @@ SET $DefaultColor "256 0 0"
EOF
# Test stdout
../src/remind - 1 jan 2012 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush - 1 jan 2012 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
MSG STDOUT is a: [stdout()]%
EOF
../src/remind - 1 jan 2012 <<'EOF' 2>&1 | cat >> ../tests/test.out
../src/remind --flush - 1 jan 2012 <<'EOF' 2>&1 | cat >> ../tests/test.out
BANNER %
MSG STDOUT is a: [stdout()]%
EOF
# Test -@ option
../src/remind -w,0,0 -@0,,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@0,,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -246,7 +246,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@0,0,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@0,0,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -269,7 +269,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@0,1,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@0,1,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -292,7 +292,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@1,,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@1,,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -315,7 +315,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@1,0,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@1,0,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -338,7 +338,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@1,1,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@1,1,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -361,7 +361,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@2,,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@2,,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -384,7 +384,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@2,0,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@2,0,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -407,7 +407,7 @@ rem 23 SPECIAL COLOR 200 0 200 BRIGHT MAGENTA
rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w,0,0 -@2,1,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -w,0,0 -@2,1,1 -c - 1 Jan 2020 <<'EOF' >> ../tests/test.out 2>&1
SET $SuppressLRM 1
rem 1 SPECIAL COLOR 0 0 0 BLACK
rem 2 SPECIAL COLOR 0 0 65 BLUE
@@ -431,59 +431,59 @@ rem 24 SPECIAL COLOR 200 200 0 BRIGHT YELLOW
rem 25 SPECIAL COLOR 200 200 200 BRIGHT WHITE
EOF
../src/remind -w128 -c ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
../src/remind -c ../tests/test-addomit.rem 1 Sep 2021 >> ../tests/test.out
../src/remind --flush -w128 -c ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
../src/remind --flush -c ../tests/test-addomit.rem 1 Sep 2021 >> ../tests/test.out
../src/remind -cu ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
../src/remind -cu '-i$SuppressLRM=1' ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
../src/remind --flush -cu ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
../src/remind --flush -cu '-i$SuppressLRM=1' ../tests/utf-8.rem 1 Nov 2019 >> ../tests/test.out
TZ=America/Toronto ../src/remind -dxe ../tests/tz.rem >> ../tests/test.out 2>&1
TZ=Europe/Berlin ../src/remind -dxe ../tests/tz.rem >> ../tests/test.out 2>&1
TZ=America/Toronto ../src/remind --flush -dxe ../tests/tz.rem >> ../tests/test.out 2>&1
TZ=Europe/Berlin ../src/remind --flush -dxe ../tests/tz.rem >> ../tests/test.out 2>&1
../src/remind ../tests/soleq.rem 1 April 2044 >> ../tests/test.out 2>&1
../src/remind --flush ../tests/soleq.rem 1 April 2044 >> ../tests/test.out 2>&1
# Test that banner is printed on every iteration
echo "MSG Should be three banners." | ../src/remind - 2022-10-20 '*3' >> ../tests/test.out 2>&1
echo "MSG Should be three banners." | ../src/remind --flush - 2022-10-20 '*3' >> ../tests/test.out 2>&1
# Test the -tn option
echo "REM May 23 +10 MSG Orange %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 +10 MSG Quux %b" | ../src/remind -t1 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 +10 MSG Cabbage %b" | ../src/remind -t2 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Banana %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Carrot %b" | ../src/remind -t1 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Apple %b" | ../src/remind -t2 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 +10 MSG Orange %b" | ../src/remind --flush - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 +10 MSG Quux %b" | ../src/remind --flush -t1 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 +10 MSG Cabbage %b" | ../src/remind --flush -t2 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Banana %b" | ../src/remind --flush - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Carrot %b" | ../src/remind --flush -t1 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Apple %b" | ../src/remind --flush -t2 - 2023-05-21 >> ../tests/test.out 2>&1
# Test the -tz option
echo "REM May 22 +10 MSG Foo %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 22 +10 MSG Bar %b" | ../src/remind -tz - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 22 +10 MSG Foo %b" | ../src/remind --flush - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 22 +10 MSG Bar %b" | ../src/remind --flush -tz - 2023-05-21 >> ../tests/test.out 2>&1
# World-writable file
rm -rf include_dir/ww
touch include_dir/ww
chmod 0666 include_dir/ww
../src/remind include_dir/ww >> ../tests/test.out 2>&1
../src/remind --flush include_dir/ww >> ../tests/test.out 2>&1
rm -rf include_dir/ww
# World-writable directory
mkdir -p include_dir/ww
touch include_dir/ww/0.rem
chmod 0777 include_dir/ww
../src/remind include_dir/ww >> ../tests/test.out 2>&1
../src/remind --flush include_dir/ww >> ../tests/test.out 2>&1
rm -rf include_dir/ww
# This segfaulted in 04.02.03
../src/remind -h '-imsgprefix(x)="foo"' /dev/null >> ../tests/test.out 2>&1
../src/remind --flush -h '-imsgprefix(x)="foo"' /dev/null >> ../tests/test.out 2>&1
# Test --version long option
../src/remind --version >> ../tests/test.out 2>&1
../src/remind --flush --version >> ../tests/test.out 2>&1
# Test queueing. Because eventstart depends on the actual system
# date, we use the --test flag to fake the date and time.
echo JSONQUEUE | ../src/remind --test -z0 ../tests/queue1.rem >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind --test -zj ../tests/queue1.rem >> ../tests/test.out 2>&1
echo JSONQUEUE | ../src/remind --flush --test -z0 ../tests/queue1.rem >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind --flush --test -zj ../tests/queue1.rem >> ../tests/test.out 2>&1
# Test for leap year bug that was fixed
../src/remind -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
@@ -503,7 +503,7 @@ REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 1 Mar 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -dte - 1 Mar 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
@@ -523,7 +523,7 @@ REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 28 Feb 2025 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -dte - 28 Feb 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
@@ -543,7 +543,7 @@ REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
../src/remind -dte - 1 Mar 2025 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -dte - 1 Mar 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
@@ -564,21 +564,21 @@ REM Friday 29 Feb 2025 MSG four
EOF
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind --flush -dt - 29 Feb 2024 >> ../tests/test.out 2>&1
../src/remind -ifoo - <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -ifoo - <<'EOF' >> ../tests/test.out 2>&1
BANNER %
DUMP
EOF
../src/remind '-i$AddBlankLines' - <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush '-i$AddBlankLines' - <<'EOF' >> ../tests/test.out 2>&1
BANNER %
DUMP
DUMP $AddBlankLines
EOF
../src/remind ../tests/expr.rem >> ../tests/test.out 2>&1
../src/remind --flush ../tests/expr.rem >> ../tests/test.out 2>&1
../src/remind - <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush - <<'EOF' >> ../tests/test.out 2>&1
PUSH
POP
PUSH
@@ -594,48 +594,53 @@ PUSH
POP
EOF
../src/remind ../tests/if1.rem 2020-03-03 >> ../tests/test.out 2>&1
../src/remind --flush ../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
../src/remind --flush ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind --flush ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind --flush ../tests/test-once.rem >> ../tests/test.out 2>&1
tail -n+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
../src/remind --flush - < ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind --flush - < ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind --flush - < ../tests/test-once.rem >> ../tests/test.out 2>&1
tail -n+2 ../tests/once.timestamp >> ../tests/test.out 2>&1
rm -f ../tests/once.timestamp
# Newlines in calendar output
(echo 'SET $SuppressLRM 1'; echo 'REM 16 MSG foo%_bar%_baz wookie quux apple %_ %_ %_ blech'; echo "REM 16 MSG ANOTHER") | ../src/remind -c -w80 - 1 sep 1990 >> ../tests/test.out 2>&1
(echo 'SET $SuppressLRM 1'; echo 'REM 16 MSG foo%_bar%_baz wookie quux apple %_ %_ %_ blech'; echo "REM 16 MSG ANOTHER") | ../src/remind --flush -c -w80 - 1 sep 1990 >> ../tests/test.out 2>&1
# Dedupe feature
../src/remind -c ../tests/dedupe.rem 1 November 2023 >> ../tests/test.out 2>&1
../src/remind -q ../tests/dedupe.rem 8 November 2023 >> ../tests/test.out 2>&1
../src/remind --flush -c ../tests/dedupe.rem 1 November 2023 >> ../tests/test.out 2>&1
../src/remind --flush -q ../tests/dedupe.rem 8 November 2023 >> ../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
grep -F -v -a '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out
# If "man" accepts the --warnings flag, test all the man pages.
man man | grep -e --warnings > /dev/null 2>&1
if test $? = 0 ; then
for i in ../man/*.1 ; do
man --warnings=w $i 2>>../tests/test.out 1>/dev/null
done
RUNMAN=0
man man 2>&1 | grep -e --warnings > /dev/null 2>&1
if test "$?" = 0 ; then
RUNMAN=1
fi
for i in ../man/*.1 ; do
echo "Checking $i..." >> ../tests/test.out
if test "$RUNMAN" = 1 ; then
man --warnings=w $i 2>>../tests/test.out 1>/dev/null
fi
done
# Test --print-tokens long option
../src/remind --print-tokens < /dev/null >> ../tests/test.out 2>&1
../src/remind --flush --print-tokens < /dev/null >> ../tests/test.out 2>&1
# Torture test #2
../src/remind ../tests/torture2.rem >> ../tests/test.out 2>&1
../src/remind --flush ../tests/torture2.rem >> ../tests/test.out 2>&1
# Expression error-reporting
../src/remind -de - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -de - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
set a 8 * "]]]" & 6
msg [8 * "]]]" & 6] is weird
set a 9 *
@@ -643,13 +648,13 @@ set a 9 * ]
EOF
# Translation template generateion
../src/remind -h - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -h - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
TRANSLATE GENERATE
EOF
# Make sure stupidly-long translations of "am" and "pm" can't cause a
# segmentation fault
../src/remind -c - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -c - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
TRANS "am" "alsdkjalksdj alksjd alksdj alksjd laksjd laksjd laksjd laksjd laksjd laksjd laksjd laksjd lkasjd laksjd laksjd lkajs dlkajs dlkasj dlkasjd lkajsd lkajs dlkasjd lkasj dlkajsd lkasjd lkasjd laksjd laksjd laksjd alskdj alskdj alksdj alksdj alskdj alksdj aslkdj"
TRANS "pm" "oiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwjwwwwwwwwwwwwwwwjwpqoejkpqwojepqowjepqojwepqowjepqowjepqowjepqowjepqowjepqowjepqojwepqowjepqowjepqowjepqowjepqowjeqpweoj"
@@ -658,12 +663,12 @@ REM WED AT 13:00 MSG blah
EOF
# The INFO keyword
../src/remind -pp - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -pp - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
REM Wed INFO "Location: here" INFO "Summary: Nope" MSG Meeting [triginfo("location")] %<summary> %<nonexist> [triginfo("cabbage")]
EOF
# Invalid info strings
../src/remind - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
REM Thu INFO "Invalid" MSG wookie
REM Fri INFO ": foo" MSG blat
REM Sun INFO "foo bar baz : blork" MSG uua
@@ -673,7 +678,7 @@ REM Sat INFO "Location: here" INFO "location: there" MSG blort
EOF
# Test parsing of quoted strings and the "escape" function
../src/remind - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
SET $AddBlankLines 0
TRANSLATE "foo" "test: \\\" \a\b\f\\n\r\t\v\x3\x1b haha"
@@ -703,40 +708,40 @@ set a "\x00P"
EOF
# Test diagnostics when using a timed substitution without an AT clause
../src/remind - 1 Feb 2024 1:00 <<EOF >> ../tests/test.out 2>&1
../src/remind --flush - 1 Feb 2024 1:00 <<EOF >> ../tests/test.out 2>&1
REM MSG %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 hahaha
EOF
# Test translate table dumping
../src/remind - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
../src/remind --flush - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
TRANSLATE "\x03" "BREAK"
TRANSLATE DUMP
EOF
../src/remind -ppp - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
../src/remind --flush -ppp - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
TRANSLATE "\x03" "BREAK"
EOF
# SCANFROM should be preserved even if it is today
../src/remind -ppp - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
../src/remind --flush -ppp - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
REM SCANFROM 2024-02-01 MSG Preserve SCANFROM
EOF
# Languages
for i in ../include/lang/??.rem ; do
../src/remind -r -q "-ii=\"$i\"" ../tests/tstlang.rem 1 Feb 2024 13:34 >> ../tests/test.out 2>&1
../src/remind --flush -r -q "-ii=\"$i\"" ../tests/tstlang.rem 1 Feb 2024 13:34 >> ../tests/test.out 2>&1
done
# Fix for $DefaultColor bug with remind -s
../src/remind -s - 1 Feb 2024 >> ../tests/test.out 2>&1 <<'EOF'
../src/remind --flush -s - 1 Feb 2024 >> ../tests/test.out 2>&1 <<'EOF'
SET $DefaultColor "255 0 0"
REM Wed MSG Wookie
EOF
# Test year-folding
TZ=America/Toronto ../src/remind -dx ../tests/yearfold.rem >> ../tests/test.out 2>&1
TZ=America/Toronto ../src/remind --flush -dx ../tests/yearfold.rem >> ../tests/test.out 2>&1
# Test unused-variable debugging
../src/remind -du - <<'EOF' >> ../tests/test.out 2>&1
../src/remind --flush -du - <<'EOF' >> ../tests/test.out 2>&1
set a 1
set b a*2
set c "What"
@@ -751,36 +756,36 @@ unset y
EOF
# Test RETURN statement
../src/remind ../tests/ret1.rem 4 June 2000 >> ../tests/test.out 2>&1
../src/remind ../tests/ret1.rem 5 June 2000 >> ../tests/test.out 2>&1
../src/remind ../tests/ret1.rem 7 June 2000 >> ../tests/test.out 2>&1
../src/remind -s ../tests/ret1.rem 1 June 2000 >> ../tests/test.out 2>&1
../src/remind --flush ../tests/ret1.rem 4 June 2000 >> ../tests/test.out 2>&1
../src/remind --flush ../tests/ret1.rem 5 June 2000 >> ../tests/test.out 2>&1
../src/remind --flush ../tests/ret1.rem 7 June 2000 >> ../tests/test.out 2>&1
../src/remind --flush -s ../tests/ret1.rem 1 June 2000 >> ../tests/test.out 2>&1
# Make sure all the include files are ok
find ../include -type f -name '*.rem' | while read x; do ../src/remind -du -n $x 1 Jan 2024 2>>../tests/test.out 1>/dev/null; done
find ../include -type f -name '*.rem' | while read x; do ../src/remind --flush -du -n $x 1 Jan 2024 2>>../tests/test.out 1>/dev/null; done
# Test todos
echo "" >> ../tests/test.out 2>&1
echo "Testing TODOS in agenda mode" >> ../tests/test.out 2>&1
../src/remind ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
../src/remind --flush ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out 2>&1
echo "Testing TODOS in calendar mode" >> ../tests/test.out 2>&1
../src/remind -s ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
../src/remind --flush -s ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out 2>&1
echo "Testing TODOS in calendar mode with completed todos hidden" >> ../tests/test.out 2>&1
../src/remind -s --hide-completed-todos ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
../src/remind --flush -s --hide-completed-todos ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
echo "Testing TODOS and JSON mode" >> ../tests/test.out 2>&1
../src/remind --json ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
../src/remind --flush --json ../tests/todos.rem 2025-08-13 >> ../tests/test.out 2>&1
echo "Testing proper redirection of RUN stdout in JSON mode... here's stdout" >> ../tests/test.out 2>&1
../src/remind --json ../tests/json-redirect.rem 1 Jan 2025 >> ../tests/test.out 2>/dev/null
../src/remind --flush --json ../tests/json-redirect.rem 1 Jan 2025 >> ../tests/test.out 2>/dev/null
echo "... and here is stderr" >> ../tests/test.out 2>&1
../src/remind --json ../tests/json-redirect.rem 1 Jan 2025 > /dev/null 2>> ../tests/test.out
../src/remind --flush --json ../tests/json-redirect.rem 1 Jan 2025 > /dev/null 2>> ../tests/test.out
# Test %: substitution sequence in all the languages
for i in ../include/lang/??.rem ; do
../src/remind "-ii=\"$i\"" -p - 2025-08-13 <<'EOF' 2>&1 | grep 2025/ >> ../tests/test.out
../src/remind --flush "-ii=\"$i\"" -p - 2025-08-13 <<'EOF' 2>&1 | grep -a 2025/ >> ../tests/test.out
DO [i]
REM TODO 2025-08-13 MSG %(LANGID) Task1%:
REM TODO 2025-08-13 COMPLETE-THROUGH 2025-08-12 MSG %(LANGID) Task2%:
@@ -788,6 +793,51 @@ REM TODO 2025-08-13 COMPLETE-THROUGH 2025-08-13 MSG %(LANGID) Task3%:
EOF
done
../src/remind --flush -q ../tests/safety.rem 2025-08-13 >> ../tests/test.out 2>&1
# Test --max-expr-complexity
../src/remind -dh --flush -q --max-expr-complexity=1000000 - 2025-01-01 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
SET $AddBlankLines 0
FSET fib(n) iif(n < 3, 1, fib(n-1) + fib(n-2))
REM MSG fib(10) = [fib(10)]
REM MSG fib(15) = [fib(15)]
REM MSG fib(20) = [fib(20)]
REM MSG fib(21) = [fib(21)]
REM MSG fib(22) = [fib(22)]
REM MSG fib(23) = [fib(23)]
REM MSG fib(24) = [fib(24)]
REM MSG fib(25) = [fib(25)]
REM MSG fib(30) = [fib(30)]
EOF
# Test hex constants
../src/remind -dh --flush -q - 2025-01-01 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
SET $AddBlankLines 0
set a 0xfe + 0xef
rem msg a = [a]; hex(a) = [hex(a)]
rem msg hex(-1) = [hex(-1)]
set a 0x7fff
rem msg a = [a]; hex(a) = [hex(a)]
EOF
# Test proper truncation by "dumpvars"
../src/remind -h --flush -q - 2025-01-01 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
SET $AddBlankLines 0
set a "¢" * 41
set b "D" * 41
set c mbchar(0x1F642) * 41
DUMP
set a "¢" * 40
set b "D" * 40
set c mbchar(0x1F642) * 40
DUMP
EOF
cmp -s ../tests/test.out ../tests/test.cmp
if [ "$?" = "0" ]; then
echo "Remind: Acceptance test PASSED"

177
tests/test-timezone-support Executable file
View File

@@ -0,0 +1,177 @@
#!/bin/sh
DIR=`dirname $0`
cd $DIR
if test $? != 0 ; then
echo ""
echo "Unable to cd $DIR" >&2
echo ""
exit 1
fi
if test `id -u` = 0 ; then
echo ""
echo "*** Please do not run the test suite as root; it will fail."
echo ""
exit 1
fi
# We alias "remind" here so that we don't inadvertently add code that
# runs the system-installed verion of Remind rather than
# ../src/remind. This trick was suggested by Jochen Sprickerhof
alias remind="echo You should be using ../src/remind explicitly in test-rem >&2; exit 1"
REMIND="../src/remind -h -q --flush"
OUT=../tests/tz.out
CMP=../tests/tz.cmp
echo -n "" > $OUT 2>&1
TZ=Europe/Amsterdam $REMIND - 2025-09-03@14:00 <<'EOF' >> $OUT 2>&1
SET $AddBlankLines=0
BANNER %
REM Fri AT 23:30 TZ America/Toronto +1000 SATISFY [$Td == 13] MSG Fri 13th @23:30 Eastern is %a %2 here
DEBUG +x
SET a $T
SET b $Tt
SET c trigtz()
SET d trigtime()
SET e trigtimetz()
SET f trigeventstart()
SET g trigeventstarttz()
DEBUG -x
EOF
TZ=Europe/Amsterdam $REMIND - 2026-02-14@14:00 <<'EOF' >> $OUT 2>&1
SET $AddBlankLines=0
BANNER %
REM Fri AT 23:30 TZ America/Toronto +1000 SATISFY [$Td == 13] MSG Fri 13th @23:30 Eastern is %a %2 here
DEBUG +x
SET a $T
SET b $Tt
SET c trigtz()
SET d trigtime()
SET e trigtimetz()
SET f trigeventstart()
SET g trigeventstarttz()
DEBUG -x
EOF
TZ=Europe/Amsterdam $REMIND - 2026-02-15@14:00 <<'EOF' >> $OUT 2>&1
SET $AddBlankLines=0
BANNER %
REM Fri AT 23:30 TZ America/Toronto +1000 SATISFY [$Td == 13] MSG Fri 13th @23:30 Eastern is %a %2 here
SET a $T
SET b $Tt
SET c trigtz()
SET d trigtime()
SET e trigtimetz()
SET f trigeventstart()
SET g trigeventstarttz()
EOF
TZ=America/Toronto $REMIND -p12 - 2025-01-01 <<'EOF' 2>&1 | grep Locally >> $OUT 2>&1
REM Second Thursday AT 15:00 TZ Europe/Amsterdam MSG Locally: %3
EOF
TZ=America/Toronto $REMIND -g - 2026-01-01 <<'EOF' >> $OUT 2>&1
BANNER %
SET $AddBlankLines 0
SET new '2026-01-18@14:53'
SET first '2026-01-25@23:48'
SET full '2026-01-03@05:04'
SET last '2026-01-10@10:49'
REM [new] +100 MSG New Moon %*l %3.
SET a trigtz()
SET b trigtime()
SET c trigtimetz()
SET d trigeventstart()
SET e trigeventstarttz()
REM MSG trigtz = [a], trigtime = [b], trigtimetz = [c], trigeventstart = [d], trigeventstarttz = [e]
REM [first] +100 MSG First Quarter %*l %3.
REM [full] +100 MSG Full Moon %*l %3.
REM [last] +100 MSG Last Quarter %*l %3.
SET new tzconvert(new, "America/Toronto", "Australia/Sydney")
SET first tzconvert(first, "America/Toronto", "Australia/Sydney")
SET full tzconvert(full, "America/Toronto", "Australia/Sydney")
SET last tzconvert(last, "America/Toronto", "Australia/Sydney")
REM [new] +100 TZ Australia/Sydney MSG New Moon %*l %3. (*)
SET a trigtz()
SET b trigtime()
SET c trigtimetz()
SET d trigeventstart()
SET e trigeventstarttz()
REM MSG trigtz = [a], trigtime = [b], trigtimetz = [c], trigeventstart = [d], trigeventstarttz = [e]
REM [first] +100 TZ Australia/Sydney MSG First Quarter %*l %3. (*)
REM [full] +100 TZ Australia/Sydney MSG Full Moon %*l %3. (*)
REM [last] +100 TZ Australia/Sydney MSG Last Quarter %*l %3. (*)
REM MSG new=[new] first=[first] full=[full] last=[last]
EOF
TZ=America/Los_Angeles $REMIND - 2026-01-01 <<'EOF' >> $OUT 2>&1
BANNER %
SET $AddBlankLines 0
REM AT 13:33 MSG Whatsup? %*l %3.
REM AT 13:33 TZ "" MSG Whatsup? %*l %3.
EOF
TZ=America/Los_Angeles $REMIND - 2026-01-01 <<'EOF' >> $OUT 2>&1
BANNER %
SET $AddBlankLines 0
REM AT 13:33 TZ America/Los_Angeles TZ Universal MSG Whatsup? %*l %3.
REM AT 13:33 TZ "" TZ America/Los_Angeles MSG Whatsup? %*l %3.
REM TZ Universal MSG Borked
EOF
TZ=America/Toronto $REMIND -dx - 2025-09-01@01:00 <<'EOF' >> $OUT 2>&1
SET a tzconvert('2025-09-01@14:44', "", "Europe/Berlin")
SET a tzconvert('2025-09-01@14:44', "America/Toronto", "Europe/Berlin")
SET a tzconvert('2025-09-01@14:44', "Europe/Berlin", "")
SET a tzconvert('2025-09-01@14:44', "Europe/Berlin")
SET a tzconvert('2025-09-01@14:44', "", "")
SET a tzconvert('2025-09-01@14:44', "")
SET b evaltrig("Wednesday at 23:00 TZ Australia/Sydney")
SET b evaltrig("Wednesday at 23:00 TZ Europe/Amsterdam")
SET b evaltrig("Wednesday at 23:00 TZ America/Los_Angeles")
set b evaltrig("Wednesday TZ Europe/Amsterdam")
debug +t
set c trig("Mon at 00:00 TZ Australia/Sydney",\
"Tue at 00:00 TZ America/Toronto",\
"Wed at 00:00 TZ Europe/Amsterdam",\
"Thu at 00:00 TZ America/Los_Angeles",\
"Fri at 00:00 TZ Australia/Sydney",\
"Sat at 00:00 TZ Australia/Sydney",\
"Sun at 00:00 TZ Australia/Sydney", \
"at 14:42")
debug -t
EOF
cmp -s $OUT $CMP
if [ "$?" = "0" ] ; then
echo "Remind: Time zone test PASSED"
exit 0
else
echo "Remind: Time zone test FAILED"
echo "Examine the file tz.out to see where it differs from the"
echo "reference file tz.cmp. Here are the first 200 lines of"
echo "diff -u tz.out tz.cmp"
echo ""
diff -u $OUT $CMP | head -n 200
echo ""
exit 1
fi

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,9 @@
#
# ./test-rem # From WITHIN Remind source directory!
SET $Latitude "45.42055556"
SET $Longitude "-75.68944444"
# Should issue a warning
fset year(x) 1
@@ -977,10 +980,17 @@ set a htmlstriptags("this is > whut <b>foo</b>")
set a htmlstriptags("<img src=\"foo\">")
# $ParseUntriggered
# Default is 0
REM 2 Jan 1990 MSG ["bad_expr" / 2]
# Set to 1 explicitly
SET $ParseUntriggered 1
REM 2 Jan 1990 MSG ["bad_expr" / 2]
# Set to 0 explicitly
SET $ParseUntriggered 0
REM 2 Jan 1990 MSG ["bad_expr" / 2]
SET $ParseUntriggered 1
# String multiplication
@@ -1715,7 +1725,47 @@ fset subst_hash(a, b, c) "subst_hash"
REM MSG Overridden: %: %! %? %@ %#
# mbstrlen and friends
DEBUG -xe
set bad char(255)
set faces "🙂" * 5 + "xyz" + "çççéfoo"
DEBUG +x
set a mbstrlen(bad)
set a strlen(bad)
set a mbstrlen(faces)
set a strlen(faces)
set a mbindex(faces, "ç")
set a index(faces, "ç")
set a mbindex(bad, bad)
set a mbindex(faces, "ç", 11)
set a index(faces, "ç", 25)
set a index(faces, "whoopér")
set a mbindex(faces, "whoopér")
set a index(faces, "whoopér", 3)
set a mbindex(faces, "whoopér", 3)
set a mbsubstr(faces, 2)
set a mbsubstr(faces, 2, 9)
set a mbsubstr(bad, 1)
set a mbsubstr(bad, 1, 20)
set a substr(faces, 2)
set a substr(faces, 2, 9)
set a codepoint(faces)
set a mbchar(128578, 162, 122)
set a codepoint(bad)
set a codepoint("")
set a mbchar(0)
set a mbchar(0, 120)
set a mbchar(120, 0)
DEBUG -x
# Don't want Remind to queue reminders
EXIT

80
tests/tz.cmp Normal file
View File

@@ -0,0 +1,80 @@
Fri 13th @23:30 Eastern is on Saturday, 14 February, 2026 at 5:30am here
$T => 2026-02-14
$Tt => 05:30
trigtz() => "America/Toronto"
trigtime() => 05:30
trigtimetz() => 23:30
trigeventstart() => 2026-02-14@05:30
trigeventstarttz() => 2026-02-13@23:30
Fri 13th @23:30 Eastern is today at 5:30am here
$T => 2026-02-14
$Tt => 05:30
trigtz() => "America/Toronto"
trigtime() => 05:30
trigtimetz() => 23:30
trigeventstart() => 2026-02-14@05:30
trigeventstarttz() => 2026-02-13@23:30
Fri 13th @23:30 Eastern is on Saturday, 14 March, 2026 at 4:30am here
2025/01/09 * * * 540 9:00am Locally: at 09:00
2025/02/13 * * * 540 9:00am Locally: at 09:00
2025/03/13 * * * 600 10:00am Locally: at 10:00
2025/04/10 * * * 540 9:00am Locally: at 09:00
2025/05/08 * * * 540 9:00am Locally: at 09:00
2025/06/12 * * * 540 9:00am Locally: at 09:00
2025/07/10 * * * 540 9:00am Locally: at 09:00
2025/08/14 * * * 540 9:00am Locally: at 09:00
2025/09/11 * * * 540 9:00am Locally: at 09:00
2025/10/09 * * * 540 9:00am Locally: at 09:00
2025/11/13 * * * 540 9:00am Locally: at 09:00
2025/12/11 * * * 540 9:00am Locally: at 09:00
trigtz = , trigtime = 14:53, trigtimetz = 14:53, trigeventstart = 2026-01-18@14:53, trigeventstarttz = 2026-01-18@14:53
trigtz = Australia/Sydney, trigtime = 14:53, trigtimetz = 06:53, trigeventstart = 2026-01-18@14:53, trigeventstarttz = 2026-01-19@06:53
new=2026-01-19@06:53 first=2026-01-26@15:48 full=2026-01-03@21:04 last=2026-01-11@02:49
Full Moon 2026-01-03 at 05:04.
Full Moon 2026-01-03 at 05:04. (*)
Last Quarter 2026-01-10 at 10:49.
Last Quarter 2026-01-10 at 10:49. (*)
New Moon 2026-01-18 at 14:53.
New Moon 2026-01-18 at 14:53. (*)
First Quarter 2026-01-25 at 23:48.
First Quarter 2026-01-25 at 23:48. (*)
Whatsup? today at 13:33.
Whatsup? today at 13:33.
-stdin-(4): TZ specified twice
-stdin-(5): TZ specified twice
-stdin-(6): TZ specified for non-timed reminder
tzconvert(2025-09-01@14:44, "", "Europe/Berlin") => 2025-09-01@20:44
tzconvert(2025-09-01@14:44, "America/Toronto", "Europe/Berlin") => 2025-09-01@20:44
tzconvert(2025-09-01@14:44, "Europe/Berlin", "") => 2025-09-01@08:44
tzconvert(2025-09-01@14:44, "Europe/Berlin") => 2025-09-01@08:44
tzconvert(2025-09-01@14:44, "", "") => 2025-09-01@14:44
tzconvert(2025-09-01@14:44, "") => 2025-09-01@14:44
evaltrig("Wednesday at 23:00 TZ Australia/Sydney") => 2025-09-03@09:00
evaltrig("Wednesday at 23:00 TZ Europe/Amsterdam") => 2025-09-03@17:00
evaltrig("Wednesday at 23:00 TZ America/Los_Angele"...) => 2025-09-04@02:00
evaltrig("Wednesday TZ Europe/Amsterdam") => TZ specified for non-timed reminder
-stdin-(11): evaltrig(): TZ specified for non-timed reminder
trig("Mon at 00:00 TZ Australia/Sydney", "Tue at 00:00 TZ America/Toronto", "Wed at 00:00 TZ Europe/Amsterdam", "Thu at 00:00 TZ America/Los_Angeles", "Fri at 00:00 TZ Australia/Sydney", "Sat at 00:00 TZ Australia/Sydney", "Sun at 00:00 TZ Australia/Sydney", "at 14:42") => -stdin-(14:21): Trig(tz_adj Australia/Sydney) = Sunday, 31 August, 2025 AT 10:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Sunday, 7 September, 2025 AT 10:00
-stdin-(14:21): Trig = Monday, 8 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Sunday, 7 September, 2025 AT 10:00
-stdin-(14:21): Trig(tz_adj America/Toronto) = Tuesday, 2 September, 2025 AT 00:00
-stdin-(14:21): Trig = Tuesday, 2 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj America/Toronto) = Tuesday, 2 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj Europe/Amsterdam) = Tuesday, 2 September, 2025 AT 18:00
-stdin-(14:21): Trig = Wednesday, 3 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj Europe/Amsterdam) = Tuesday, 2 September, 2025 AT 18:00
-stdin-(14:21): Trig(tz_adj America/Los_Angeles) = Thursday, 4 September, 2025 AT 03:00
-stdin-(14:21): Trig = Thursday, 4 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj America/Los_Angeles) = Thursday, 4 September, 2025 AT 03:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Thursday, 4 September, 2025 AT 10:00
-stdin-(14:21): Trig = Friday, 5 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Thursday, 4 September, 2025 AT 10:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Friday, 5 September, 2025 AT 10:00
-stdin-(14:21): Trig = Saturday, 6 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Friday, 5 September, 2025 AT 10:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Saturday, 6 September, 2025 AT 10:00
-stdin-(14:21): Trig = Sunday, 7 September, 2025 AT 00:00
-stdin-(14:21): Trig(tz_adj Australia/Sydney) = Saturday, 6 September, 2025 AT 10:00
-stdin-(14:21): Trig = Monday, 1 September, 2025 AT 14:42
2025-09-01

View File

@@ -34,3 +34,5 @@ set a utctolocal('2051-01-01@04:44')
set a utctolocal('2050-03-13@05:00')
set a utctolocal('2050-11-06@04:00')
set a utctolocal('2050-11-06@07:00')
set a trigger('1990-01-01@1:22', 1)