mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-16 14:28:40 +02:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23081b556f | ||
|
|
e2cfa46289 | ||
|
|
71384da875 | ||
|
|
af69f8779d | ||
|
|
131e77fa18 | ||
|
|
f153acf7ce | ||
|
|
d75587fa7b | ||
|
|
d9a2b15814 | ||
|
|
aa090bf319 | ||
|
|
dc6a229f5f | ||
|
|
5a3840759a | ||
|
|
779174ae32 | ||
|
|
2f70b37d4c | ||
|
|
d15c8f106b | ||
|
|
9d999a0074 | ||
|
|
097dda1750 | ||
|
|
565bca4380 | ||
|
|
adb38fe82e | ||
|
|
57545ddc3f | ||
|
|
804cf14a78 | ||
|
|
641b5cec57 | ||
|
|
df53db19c4 | ||
|
|
d9bf902153 | ||
|
|
53a12de2f9 | ||
|
|
fe2b34da68 | ||
|
|
7e70ffe7f5 | ||
|
|
55975154b1 | ||
|
|
3c6191ba61 | ||
|
|
b00cf9c5b7 | ||
|
|
a6838802ad | ||
|
|
375576fcc5 | ||
|
|
2c9087aa79 | ||
|
|
928f65ae47 | ||
|
|
ed7b4f5ecc | ||
|
|
9aad9401ea | ||
|
|
4574b552b6 | ||
|
|
21e0940257 | ||
|
|
7064893ff2 | ||
|
|
f8781276e2 | ||
|
|
b02f61ea05 | ||
|
|
60447d530d | ||
|
|
2835176ad3 | ||
|
|
b0784feb1a | ||
|
|
ef7021972e | ||
|
|
e449fcdfcf | ||
|
|
47a4456c20 | ||
|
|
e9ee7492f7 | ||
|
|
2baa0405f3 | ||
|
|
3425530c1c | ||
|
|
eeae3298ef | ||
|
|
4237dc4a3f | ||
|
|
faf8947dda | ||
|
|
41a3db3a37 | ||
|
|
f12bcf3fe6 | ||
|
|
f9261bb24f | ||
|
|
2ff9aedba5 | ||
|
|
1b793eff2e | ||
|
|
e4c41e0f45 | ||
|
|
9ec1006804 | ||
|
|
dd048c0557 | ||
|
|
dce1f282f4 | ||
|
|
7120d07d84 | ||
|
|
127358db02 | ||
|
|
a89aaf18b6 | ||
|
|
5734bd7524 | ||
|
|
dfabda7dee | ||
|
|
da146d7989 | ||
|
|
e147925462 | ||
|
|
703907c94d | ||
|
|
bcfee04cae | ||
|
|
5f9e71f9eb | ||
|
|
272336226e | ||
|
|
32f4b125b5 | ||
|
|
7979a69cb9 | ||
|
|
651368fedc | ||
|
|
fe002557cf | ||
|
|
d2785d909c | ||
|
|
c2345a240b | ||
|
|
5b6bf25a20 | ||
|
|
44f9f0f0d4 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -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
|
||||
|
||||
13
Makefile
13
Makefile
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
18
configure
vendored
18
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.72 for remind 06.00.02.
|
||||
# Generated by GNU Autoconf 2.72 for remind 06.01.02.
|
||||
#
|
||||
#
|
||||
# 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.02'
|
||||
PACKAGE_STRING='remind 06.00.02'
|
||||
PACKAGE_VERSION='06.01.02'
|
||||
PACKAGE_STRING='remind 06.01.02'
|
||||
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.02 to adapt to many kinds of systems.
|
||||
'configure' configures remind 06.01.02 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.02:";;
|
||||
short | recursive ) echo "Configuration of remind 06.01.02:";;
|
||||
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.02
|
||||
remind configure 06.01.02
|
||||
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.02, which was
|
||||
It was created by remind $as_me 06.01.02, which was
|
||||
generated by GNU Autoconf 2.72. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@@ -4848,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.02, which was
|
||||
This file was extended by remind $as_me 06.01.02, which was
|
||||
generated by GNU Autoconf 2.72. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -4913,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.02
|
||||
remind config.status 06.01.02
|
||||
configured by $0, generated by GNU Autoconf 2.72,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_INIT(remind, 06.00.02, , , https://dianne.skoll.ca/projects/remind/)
|
||||
AC_INIT(remind, 06.01.02, , , https://dianne.skoll.ca/projects/remind/)
|
||||
AC_CONFIG_SRCDIR([src/queue.c])
|
||||
|
||||
cat <<'EOF'
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -1,5 +1,72 @@
|
||||
CHANGES TO REMIND
|
||||
|
||||
* VERSION 6.1 Patch 2 - 2025-09-19
|
||||
|
||||
- BUG FIX: remind: A reminder on a specific fixed date and time that
|
||||
uses the TZ feature could erroneously issue a "Can't compute trigger"
|
||||
error. This has been fixed.
|
||||
|
||||
- CHANGE: remind: The default for --max-expr-complexity is 10,000,000
|
||||
(ten million) instead of unlimited. This should not affect any
|
||||
real-world Remind scripts.
|
||||
|
||||
* 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
|
||||
@@ -11,8 +78,8 @@ CHANGES TO REMIND
|
||||
- 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: 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
|
||||
|
||||
1
examples/alignment.rem
Normal file → Executable file
1
examples/alignment.rem
Normal file → Executable 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
24
examples/tflag.rem
Executable 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]
|
||||
|
||||
@@ -547,9 +547,17 @@ will produce the following \fBinfo\fR hash:
|
||||
.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
|
||||
@@ -570,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
|
||||
|
||||
238
man/remind.1.in
238
man/remind.1.in
@@ -616,6 +616,15 @@ 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,
|
||||
the limit is set to 10000000 (ten million). You can explicitly set it
|
||||
to zero if you don't want any limit to apply. The default limit of ten
|
||||
million should never be triggered by any sensible Remind script, however,
|
||||
and we don't recommend changing the limit.
|
||||
.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.
|
||||
@@ -693,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
|
||||
@@ -2206,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,
|
||||
@@ -2264,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
|
||||
@@ -2464,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
|
||||
@@ -2500,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", ""
|
||||
@@ -3273,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)
|
||||
@@ -3285,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)
|
||||
@@ -3294,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)
|
||||
@@ -3555,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
|
||||
@@ -3616,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
|
||||
@@ -3687,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)
|
||||
@@ -3930,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
|
||||
@@ -3955,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.
|
||||
@@ -4566,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
|
||||
@@ -4703,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
|
||||
@@ -4753,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
|
||||
@@ -4814,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')
|
||||
@@ -4852,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
|
||||
@@ -4873,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")
|
||||
|
||||
@@ -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" -----------------
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
167
src/dorem.c
167
src/dorem.c
@@ -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;
|
||||
}
|
||||
|
||||
11
src/err.h
11
src/err.h
@@ -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 */
|
||||
;
|
||||
|
||||
83
src/expr.c
83
src/expr.c
@@ -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);
|
||||
@@ -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;
|
||||
@@ -888,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:
|
||||
@@ -2132,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');
|
||||
@@ -2930,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 */
|
||||
@@ -2940,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 */
|
||||
@@ -2951,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;
|
||||
@@ -2979,7 +3048,7 @@ char const *PrintValue (Value *v, FILE *fp)
|
||||
s++;
|
||||
}
|
||||
PV_PUTC(fp, '"');
|
||||
if (*s) {
|
||||
if (*s || truncated) {
|
||||
if (fp) {
|
||||
fprintf(fp, "...");
|
||||
} else {
|
||||
|
||||
12
src/files.c
12
src/files.c
@@ -187,6 +187,7 @@ got_a_fresh_line(void)
|
||||
{
|
||||
FreshLine = 1;
|
||||
WarnedAboutImplicit = 0;
|
||||
ExpressionNodesEvaluatedThisLine = 0;
|
||||
}
|
||||
|
||||
void set_cloexec(int fd)
|
||||
@@ -304,6 +305,10 @@ static int ReadLineFromFile(int use_pclose)
|
||||
DBufFree(&LineBuffer);
|
||||
|
||||
LineNoStart = LineNo+1;
|
||||
|
||||
if (!fp) {
|
||||
return E_EOF;
|
||||
}
|
||||
while(fp) {
|
||||
#ifdef USE_READLINE
|
||||
if (IS_INTERACTIVE()) {
|
||||
@@ -454,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;
|
||||
@@ -671,6 +677,7 @@ static int PopFile(void)
|
||||
IncludeStruct *i;
|
||||
|
||||
pop_excess_ifs(FileName);
|
||||
fp = NULL;
|
||||
|
||||
if (!IStackPtr) return E_EOF;
|
||||
i = &IStack[IStackPtr-1];
|
||||
@@ -683,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;
|
||||
|
||||
352
src/funcs.c
352
src/funcs.c
@@ -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. */
|
||||
@@ -1956,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) {
|
||||
@@ -1991,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) {
|
||||
@@ -2077,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) {
|
||||
@@ -2333,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 */
|
||||
@@ -2371,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 */
|
||||
@@ -3879,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) {
|
||||
@@ -3892,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)
|
||||
@@ -3919,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);
|
||||
@@ -4217,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) {
|
||||
@@ -4245,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;
|
||||
}
|
||||
|
||||
@@ -4276,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);
|
||||
@@ -4286,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) {
|
||||
@@ -4333,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;
|
||||
|
||||
@@ -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( unsigned long ExpressionNodesEvaluated, 0);
|
||||
EXTERN INIT( unsigned long MaxExprNodesPerLine, 0);
|
||||
EXTERN INIT( unsigned long ExpressionNodesEvaluatedThisLine, 0);
|
||||
EXTERN INIT( unsigned long ExpressionNodeLimitPerLine, 10000000);
|
||||
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 LocalSysTime, -1);
|
||||
EXTERN INIT( int ParseUntriggered, 0);
|
||||
|
||||
EXTERN char const *InitialFile;
|
||||
EXTERN char const *LocalTimeZone;
|
||||
EXTERN int FileAccessDate;
|
||||
|
||||
EXTERN INIT( int WeekdayOmits, 0);
|
||||
|
||||
38
src/init.c
38
src/init.c
@@ -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");
|
||||
@@ -1137,6 +1154,8 @@ static void
|
||||
ProcessLongOption(char const *arg)
|
||||
{
|
||||
int t;
|
||||
unsigned long tt;
|
||||
|
||||
if (!strcmp(arg, "test")) {
|
||||
fprintf(stderr, "Enabling test mode: This is meant for the acceptance test.\nDo not use --test in production.\nIn test mode, the system time is fixed at 2025-01-06@19:00\n");
|
||||
TestMode = 1;
|
||||
@@ -1144,6 +1163,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;
|
||||
@@ -1204,6 +1224,10 @@ ProcessLongOption(char const *arg)
|
||||
print_sysvar_tokens();
|
||||
exit(0);
|
||||
}
|
||||
if (sscanf(arg, "max-expr-complexity=%lu", &tt) == 1) {
|
||||
ExpressionNodeLimitPerLine = tt;
|
||||
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]);
|
||||
|
||||
22
src/main.c
22
src/main.c
@@ -84,6 +84,8 @@ exitfunc(void)
|
||||
|
||||
UnsetAllUserFuncs();
|
||||
print_expr_nodes_stats();
|
||||
fprintf(ErrFp, "Max expr node evaluations per line: %lu\n", MaxExprNodesPerLine);
|
||||
fprintf(ErrFp, "Total expression node evaluations: %lu\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) {
|
||||
|
||||
@@ -68,7 +68,7 @@ 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);
|
||||
@@ -296,3 +296,8 @@ 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);
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -552,6 +554,7 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig const *tim
|
||||
y, m, d, omit,
|
||||
result;
|
||||
|
||||
int save_nextstart = 0;
|
||||
trig->expired = 0;
|
||||
if (save_in_globals) {
|
||||
LastTrigValid = 0;
|
||||
@@ -613,6 +616,31 @@ 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, ©);
|
||||
EnterTimezone(trig->tz);
|
||||
if (result + duration_days >= today &&
|
||||
new_result + duration_days < today) {
|
||||
/* If we are not making progress, then give up: It's expired */
|
||||
if (nextstart <= save_nextstart) {
|
||||
trig->expired = 1;
|
||||
if (DebugFlag & DB_PRTTRIG) {
|
||||
fprintf(ErrFp, "%s(%s): %s\n",
|
||||
GetCurrentFilename(), line_range(LineNoStart, LineNo), GetErr(E_EXPIRED));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
save_nextstart = nextstart;
|
||||
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)) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
2
tests/Makefile
Normal file
2
tests/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
%:
|
||||
@make -s -C.. $@
|
||||
@@ -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,7 +59,7 @@ 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 --flush -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
|
||||
@@ -618,7 +618,7 @@ rm -f ../tests/once.timestamp
|
||||
../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.
|
||||
RUNMAN=0
|
||||
@@ -785,7 +785,7 @@ echo "... and here is stderr" >> ../tests/test.out 2>&1
|
||||
|
||||
# Test %: substitution sequence in all the languages
|
||||
for i in ../include/lang/??.rem ; do
|
||||
../src/remind --flush "-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%:
|
||||
@@ -795,6 +795,49 @@ 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"
|
||||
|
||||
180
tests/test-timezone-support
Executable file
180
tests/test-timezone-support
Executable file
@@ -0,0 +1,180 @@
|
||||
#!/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
|
||||
|
||||
TZ=America/Toronto $REMIND -s - 2025-09-01@00:00 <<'EOF' >> $OUT 2>&1
|
||||
REM 2025-09-24 AT 09:00 TZ America/Denver MSG Dr. Smith
|
||||
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
|
||||
159
tests/test.cmp
159
tests/test.cmp
@@ -1045,7 +1045,7 @@ set a057 value("a05"+"6")
|
||||
"a05" + "6" => "a056"
|
||||
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
|
||||
set a058 version()
|
||||
version() => "06.00.02"
|
||||
version() => "06.01.02"
|
||||
set a059 wkday(today())
|
||||
today() => 1991-02-16
|
||||
wkday(1991-02-16) => "Saturday"
|
||||
@@ -2608,7 +2608,7 @@ a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
|
||||
a007 "1991-02-16"
|
||||
a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
|
||||
a008 "11:44"
|
||||
a058 "06.00.02"
|
||||
a058 "06.01.02"
|
||||
a059 "Saturday"
|
||||
a010 12
|
||||
a060 6
|
||||
@@ -5561,8 +5561,8 @@ REM SATISFY ""
|
||||
REM SATISFY [version() > "01.00.00"]
|
||||
../tests/test.rem(1074): SATISFY: expression has no reference to trigdate() or $T...
|
||||
../tests/test.rem(1074): Trig = Saturday, 16 February, 1991
|
||||
version() => "06.00.02"
|
||||
"06.00.02" > "01.00.00" => 1
|
||||
version() => "06.01.02"
|
||||
"06.01.02" > "01.00.00" => 1
|
||||
../tests/test.rem(1074): Trig(satisfied) = Saturday, 16 February, 1991
|
||||
REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * 5]
|
||||
../tests/test.rem(1075): SATISFY: expression has no reference to trigdate() or $T...
|
||||
@@ -16592,8 +16592,63 @@ Leaving UserFN c() => 33
|
||||
|
||||
DEBUG -xe
|
||||
Overridden: subst_colon subst_bang subst_question subst_at subst_hash
|
||||
bad => "ÿ"
|
||||
mbstrlen("ÿ") => Invalid multibyte sequence
|
||||
../tests/test.rem(1734): mbstrlen(): Invalid multibyte sequence
|
||||
bad => "ÿ"
|
||||
strlen("ÿ") => 1
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
mbstrlen("🙂🙂🙂🙂🙂xyzçççéfoo") => 15
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
strlen("🙂🙂🙂🙂🙂xyzçççéfoo") => 34
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
mbindex("🙂🙂🙂🙂🙂xyzçççéfoo", "ç") => 9
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
index("🙂🙂🙂🙂🙂xyzçççéfoo", "ç") => 24
|
||||
bad => "ÿ"
|
||||
bad => "ÿ"
|
||||
mbindex("ÿ", "ÿ") => Invalid multibyte sequence
|
||||
../tests/test.rem(1742): mbindex(): Invalid multibyte sequence
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
mbindex("🙂🙂🙂🙂🙂xyzçççéfoo", "ç", 11) => 11
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
index("🙂🙂🙂🙂🙂xyzçççéfoo", "ç", 25) => 26
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
index("🙂🙂🙂🙂🙂xyzçççéfoo", "whoopér") => 0
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
mbindex("🙂🙂🙂🙂🙂xyzçççéfoo", "whoopér") => 0
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
index("🙂🙂🙂🙂🙂xyzçççéfoo", "whoopér", 3) => 0
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
mbindex("🙂🙂🙂🙂🙂xyzçççéfoo", "whoopér", 3) => 0
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
mbsubstr("🙂🙂🙂🙂🙂xyzçççéfoo", 2) => "🙂🙂🙂🙂xyzçççéfoo"
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
mbsubstr("🙂🙂🙂🙂🙂xyzçççéfoo", 2, 9) => "🙂🙂🙂🙂xyzç"
|
||||
bad => "ÿ"
|
||||
mbsubstr("ÿ", 1) => Invalid multibyte sequence
|
||||
../tests/test.rem(1754): mbsubstr(): Invalid multibyte sequence
|
||||
bad => "ÿ"
|
||||
mbsubstr("ÿ", 1, 20) => Invalid multibyte sequence
|
||||
../tests/test.rem(1755): mbsubstr(): Invalid multibyte sequence
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
substr("🙂🙂🙂🙂🙂xyzçççéfoo", 2) => "Ÿ™‚🙂🙂🙂🙂xyzçççéfoo"
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
substr("🙂🙂🙂🙂🙂xyzçççéfoo", 2, 9) => "Ÿ™‚🙂ð"
|
||||
faces => "🙂🙂🙂🙂🙂xyzçççéfoo"
|
||||
codepoint("🙂🙂🙂🙂🙂xyzçççéfoo") => 128578
|
||||
mbchar(128578, 162, 122) => "🙂¢z"
|
||||
bad => "ÿ"
|
||||
codepoint("ÿ") => Invalid multibyte sequence
|
||||
../tests/test.rem(1762): codepoint(): Invalid multibyte sequence
|
||||
codepoint("") => 0
|
||||
mbchar(0) => ""
|
||||
mbchar(0, 120) => Number too low
|
||||
../tests/test.rem(1765): mbchar(): Number too low
|
||||
mbchar(120, 0) => Number too low
|
||||
../tests/test.rem(1766): mbchar(): Number too low
|
||||
Variable hash table statistics:
|
||||
Entries: 100144; Buckets: 87719; Non-empty Buckets: 66301
|
||||
Entries: 100146; Buckets: 87719; Non-empty Buckets: 66303
|
||||
Maxlen: 5; Minlen: 0; Avglen: 1.142; Stddev: 0.878; Avg nonempty len: 1.510
|
||||
Growths: 13; Shrinks: 0
|
||||
Function hash table statistics:
|
||||
@@ -16612,6 +16667,8 @@ Translation hash table statistics:
|
||||
Expression nodes high-water: 302076
|
||||
Expression nodes leaked: 0
|
||||
Parse level high-water: 34
|
||||
Max expr node evaluations per line: 2001
|
||||
Total expression node evaluations: 106467
|
||||
|
||||
Test 2
|
||||
|
||||
@@ -23658,7 +23715,7 @@ SECURITY: Won't read world-writable file or directory!
|
||||
Error reading include_dir/ww: Can't open file
|
||||
SECURITY: Won't read world-writable file or directory!
|
||||
Error reading include_dir/ww: No files matching *.rem
|
||||
06.00.02
|
||||
06.01.02
|
||||
Enabling test mode: This is meant for the acceptance test.
|
||||
Do not use --test in production.
|
||||
In test mode, the system time is fixed at 2025-01-06@19:00
|
||||
@@ -24234,6 +24291,8 @@ Translation hash table statistics:
|
||||
Expression nodes high-water: 499
|
||||
Expression nodes leaked: 0
|
||||
Parse level high-water: 2001
|
||||
Max expr node evaluations per line: 499
|
||||
Total expression node evaluations: 632
|
||||
-stdin-(14): Unmatched PUSH-OMIT-CONTEXT at -stdin-(7)
|
||||
-stdin-(14): Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT
|
||||
No reminders.
|
||||
@@ -24485,6 +24544,7 @@ through
|
||||
todo
|
||||
trans
|
||||
translate
|
||||
tz
|
||||
unset
|
||||
until
|
||||
warn
|
||||
@@ -24548,6 +24608,7 @@ catch
|
||||
catcherr
|
||||
char
|
||||
choose
|
||||
codepoint
|
||||
coerce
|
||||
columns
|
||||
const
|
||||
@@ -24574,6 +24635,7 @@ hebdate
|
||||
hebday
|
||||
hebmon
|
||||
hebyear
|
||||
hex
|
||||
hour
|
||||
htmlescape
|
||||
htmlstriptags
|
||||
@@ -24588,6 +24650,10 @@ language
|
||||
localtoutc
|
||||
lower
|
||||
max
|
||||
mbchar
|
||||
mbindex
|
||||
mbstrlen
|
||||
mbsubstr
|
||||
min
|
||||
minsfromutc
|
||||
minute
|
||||
@@ -24642,6 +24708,7 @@ trigdelta
|
||||
trigduration
|
||||
trigeventduration
|
||||
trigeventstart
|
||||
trigeventstarttz
|
||||
trigfrom
|
||||
trigger
|
||||
triginfo
|
||||
@@ -24654,6 +24721,8 @@ trigtags
|
||||
trigtime
|
||||
trigtimedelta
|
||||
trigtimerep
|
||||
trigtimetz
|
||||
trigtz
|
||||
triguntil
|
||||
trigvalid
|
||||
typeof
|
||||
@@ -24809,6 +24878,8 @@ Translation hash table statistics:
|
||||
Expression nodes high-water: 1800000
|
||||
Expression nodes leaked: 0
|
||||
Parse level high-water: 7
|
||||
Max expr node evaluations per line: 3
|
||||
Total expression node evaluations: 300001
|
||||
set a 8 * "]]]" & 6
|
||||
-stdin-(1): Parse error `&' (did you mean `&&'?)
|
||||
8 * "]]]" & 6
|
||||
@@ -24987,6 +25058,11 @@ TRANSLATE "Time limit for expression evaluation exceeded" ""
|
||||
TRANSLATE "COMPLETE-THROUGH specified without TODO" ""
|
||||
TRANSLATE "MAX-OVERDUE specified twice" ""
|
||||
TRANSLATE "MAX-OVERDUE specified without TODO" ""
|
||||
TRANSLATE "TZ specified twice" ""
|
||||
TRANSLATE "TZ specified for non-timed reminder" ""
|
||||
TRANSLATE "C library does not support multibyte characters" ""
|
||||
TRANSLATE "Invalid multibyte sequence" ""
|
||||
TRANSLATE "Maximum expression complexity exceeded" ""
|
||||
|
||||
# Other Messages
|
||||
TRANSLATE "%s function `%s' defined at %s(%s) does not use its argument" ""
|
||||
@@ -25004,6 +25080,7 @@ TRANSLATE "Cannot stat %s - not running as daemon!" ""
|
||||
TRANSLATE "Cannot use AT clause in multitrig() function" ""
|
||||
TRANSLATE "Do not use ["["]] around expression in SET command" ""
|
||||
TRANSLATE "Duplicate INFO headers are not permitted" ""
|
||||
TRANSLATE "Error adjusting trigger to local time zone" ""
|
||||
TRANSLATE "Error: THROUGH date earlier than start date" ""
|
||||
TRANSLATE "Executing `%s' for INCLUDECMD and caching as `%s'" ""
|
||||
TRANSLATE "Found cached directory listing for `%s'" ""
|
||||
@@ -39751,3 +39828,73 @@ nooooo....
|
||||
../tests/safety.rem(37): [#0] In function `subst_b'
|
||||
today
|
||||
nooooo....
|
||||
fib(10) = 55
|
||||
fib(15) = 610
|
||||
fib(20) = 6765
|
||||
fib(21) = 10946
|
||||
fib(22) = 17711
|
||||
fib(23) = 28657
|
||||
fib(24) = 46368
|
||||
-stdin-(12): `-': Maximum expression complexity exceeded
|
||||
-stdin-(3): [#0] In function `fib'
|
||||
[remaining call frames omitted]
|
||||
-stdin-(13): `-': Maximum expression complexity exceeded
|
||||
-stdin-(3): [#0] In function `fib'
|
||||
[remaining call frames omitted]
|
||||
Variable hash table statistics:
|
||||
Entries: 0; Buckets: 7; Non-empty Buckets: 0
|
||||
Maxlen: 0; Minlen: 0; Avglen: 0.000; Stddev: 0.000; Avg nonempty len: 0.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Function hash table statistics:
|
||||
Entries: 1; Buckets: 7; Non-empty Buckets: 1
|
||||
Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Dedupe hash table statistics:
|
||||
Entries: 0; Buckets: 7; Non-empty Buckets: 0
|
||||
Maxlen: 0; Minlen: 0; Avglen: 0.000; Stddev: 0.000; Avg nonempty len: 0.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Translation hash table statistics:
|
||||
Entries: 1; Buckets: 7; Non-empty Buckets: 1
|
||||
Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Expression nodes allocated: 256
|
||||
Expression nodes high-water: 16
|
||||
Expression nodes leaked: 0
|
||||
Parse level high-water: 25
|
||||
Max expr node evaluations per line: 1000000
|
||||
Total expression node evaluations: 3999940
|
||||
a = 493; hex(a) = 1ED
|
||||
hex(-1) = FFFFFFFF
|
||||
a = 32767; hex(a) = 7FFF
|
||||
Variable hash table statistics:
|
||||
Entries: 1; Buckets: 7; Non-empty Buckets: 1
|
||||
Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Function hash table statistics:
|
||||
Entries: 0; Buckets: 7; Non-empty Buckets: 0
|
||||
Maxlen: 0; Minlen: 0; Avglen: 0.000; Stddev: 0.000; Avg nonempty len: 0.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Dedupe hash table statistics:
|
||||
Entries: 0; Buckets: 7; Non-empty Buckets: 0
|
||||
Maxlen: 0; Minlen: 0; Avglen: 0.000; Stddev: 0.000; Avg nonempty len: 0.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Translation hash table statistics:
|
||||
Entries: 1; Buckets: 7; Non-empty Buckets: 1
|
||||
Maxlen: 1; Minlen: 0; Avglen: 0.143; Stddev: 0.350; Avg nonempty len: 1.000
|
||||
Growths: 0; Shrinks: 0
|
||||
Expression nodes allocated: 256
|
||||
Expression nodes high-water: 3
|
||||
Expression nodes leaked: 0
|
||||
Parse level high-water: 17
|
||||
Max expr node evaluations per line: 3
|
||||
Total expression node evaluations: 13
|
||||
Variable Value
|
||||
|
||||
a "¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢"...
|
||||
b "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"...
|
||||
c "🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂"...
|
||||
Variable Value
|
||||
|
||||
a "¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢¢"
|
||||
b "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
|
||||
c "🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂🙂"
|
||||
|
||||
@@ -1725,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
|
||||
|
||||
|
||||
81
tests/tz.cmp
Normal file
81
tests/tz.cmp
Normal file
@@ -0,0 +1,81 @@
|
||||
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
|
||||
2025/09/24 * * * 660 11:00am Dr. Smith
|
||||
Reference in New Issue
Block a user