Compare commits

..

111 Commits

Author SHA1 Message Date
Dianne Skoll
52ce99af80 Document changes since 05.03.02. 2025-03-03 11:27:29 -05:00
Dianne Skoll
5915eb4973 Bump version to 05.03.03. 2025-03-03 11:23:10 -05:00
Dianne Skoll
aa8d23fd87 Add a couple of Dutch translations. 2025-03-01 13:34:37 -05:00
Dianne Skoll
917d943953 Avoid memory leak. 2025-03-01 12:01:15 -05:00
Dianne Skoll
9ade3876b2 Make TkRemind open editor at first line of reminder. 2025-02-16 20:36:50 -05:00
Dianne Skoll
471ecff267 Report both starting and ending lines for commands spanning multiple lines because of \-line continuation.
This affects error and warning messages primarily.  The JSON
interchange format has an additional lineno_start entry for
reminders that span multiple lines.  (Historically, lineno
was the *last* line of the reminder statement and I kept
that for compatibility.)
2025-02-16 20:30:16 -05:00
Dianne Skoll
762bf97473 Add some URLs and a couple of miscellaneous days. 2025-02-13 17:43:02 -05:00
Dianne Skoll
563f3ea088 Remove obsolete files. 2025-02-12 14:39:55 -05:00
Dianne Skoll
939078428f Add some miscellaneous holidays not found in other files. 2025-02-12 13:13:08 -05:00
Dianne Skoll
5acbb907b4 Issue warning if a substitution sequence related to time is used without an AT clause.
All checks were successful
Remind unit tests / tests (push) Successful in 31s
2025-02-10 09:23:43 -05:00
Dianne Skoll
21ecc28ea4 Prep for 05.03.02 release.
All checks were successful
Remind unit tests / tests (push) Successful in 34s
2025-02-09 09:56:45 -05:00
Dianne Skoll
b37a7cd993 Revert change to how -y generates tags.
All checks were successful
Remind unit tests / tests (push) Successful in 48s
Commit e7ec975ff0 changed how the
MD5 sum was calculated to include the filename and line number.

This commit reverts that change; the tag is generated purely
on the REM command line.
2025-02-08 22:25:09 -05:00
Dianne Skoll
64679817ae Update WHATSNEW in preparation for release.
All checks were successful
Remind unit tests / tests (push) Successful in 52s
2025-02-07 13:08:20 -05:00
Dianne Skoll
1ef1033379 Add dependencies to Makefile.PL.in
All checks were successful
Remind unit tests / tests (push) Successful in 45s
2025-02-05 10:58:47 -05:00
Dianne Skoll
7d42750043 Better checks for Perl modules. 2025-02-05 10:57:28 -05:00
Dianne Skoll
1dc0afc0ca Improve small calendar display.
All checks were successful
Remind unit tests / tests (push) Successful in 26s
2025-02-04 22:27:32 -05:00
Dianne Skoll
a0aede4069 Add clickable URL in popup reminder if we have an INFO "Url: ..." string. 2025-02-04 13:58:23 -05:00
Dianne Skoll
a5a7637696 Bump version in preparation for next release. 2025-02-04 13:49:00 -05:00
Dianne Skoll
38a597a374 We're releasing today!
All checks were successful
Remind unit tests / tests (push) Successful in 53s
2025-02-04 10:28:25 -05:00
Dianne Skoll
66ba9257a5 Add INFO strings for Jewish holidays. 2025-02-04 10:23:13 -05:00
Dianne Skoll
c5374c09fb Yet more INFO strings. 2025-02-04 10:11:36 -05:00
Dianne Skoll
9c93e7e6a1 Better error message.
All checks were successful
Remind unit tests / tests (push) Successful in 1m17s
2025-02-03 23:09:17 -05:00
Dianne Skoll
3487f6f46a Document that it takes at least -pp to pass info strings to a back-end. 2025-02-03 23:05:18 -05:00
Dianne Skoll
da8a72d7cd Add info strings. 2025-02-03 22:59:43 -05:00
Dianne Skoll
f391b6221f Add INFO strings. 2025-02-03 22:54:10 -05:00
Dianne Skoll
a8c0b20f9e Line things up better. 2025-02-03 22:47:40 -05:00
Dianne Skoll
5684a86df9 Add INFO strings. 2025-02-03 22:45:26 -05:00
Dianne Skoll
3abaaacd98 Add some more INFO strings. 2025-02-03 22:38:50 -05:00
Dianne Skoll
7eae7a9157 Add some INFO strings. 2025-02-03 22:36:09 -05:00
Dianne Skoll
a0d8c93a34 Make tkremind handle "Url:" info strings.
All checks were successful
Remind unit tests / tests (push) Successful in 39s
2025-02-03 15:18:44 -05:00
Dianne Skoll
8bf22dbb36 Document support for "Url:" info string. 2025-02-03 14:52:42 -05:00
Dianne Skoll
6b2622f3d3 Document "Url" INFO string support. 2025-02-03 14:38:23 -05:00
Dianne Skoll
8abdf6d988 Make rem2html respect "Url:" INFO string for moon phases and week numbers. 2025-02-03 14:32:52 -05:00
Dianne Skoll
991e409739 Make rem2pdf obey the "Url:" INFO string. 2025-02-03 14:20:45 -05:00
Dianne Skoll
3c2bb76523 Turn reminders with a "Url:" info string into hyper-links. 2025-02-03 13:56:21 -05:00
Dianne Skoll
8555352c18 Add popup for location and description in HTML calendar. 2025-02-03 11:36:19 -05:00
Dianne Skoll
34f8486c10 Update docs.
All checks were successful
Remind unit tests / tests (push) Successful in 42s
2025-02-02 11:21:38 -05:00
Dianne Skoll
5adb5d893e Final (??) tweaks of popup appearance. :)
All checks were successful
Remind unit tests / tests (push) Successful in 34s
2025-02-01 15:58:51 -05:00
Dianne Skoll
2f11b6fdc8 Tweak appearance of popups and background reminders. 2025-02-01 15:52:13 -05:00
Dianne Skoll
49d46c1397 Improve reminder popups. 2025-02-01 15:38:13 -05:00
Dianne Skoll
1641f99f97 Include the "info" element in pop-up reminders. 2025-02-01 15:02:54 -05:00
Dianne Skoll
f9f9552850 Avoid segfault if we call dosubst("%<foo>") 2025-02-01 14:50:49 -05:00
Dianne Skoll
3b43222585 Add the triginfo("header") function and corresponding %<...> substitution sequence. 2025-02-01 14:39:06 -05:00
Dianne Skoll
231d9d77e7 Save the info chain when saving the last trigger. 2025-02-01 14:16:17 -05:00
Dianne Skoll
6140221bf3 Make a function static. 2025-02-01 11:15:52 -05:00
Dianne Skoll
51b831fb6a Check for proper escaping in JSON and TRANSLATE DUMP. 2025-02-01 11:15:25 -05:00
Dianne Skoll
35a4994b3e Document changes. 2025-02-01 10:59:04 -05:00
Dianne Skoll
0ebaaa4097 Add optional add_quote argument to escape() 2025-02-01 10:58:55 -05:00
Dianne Skoll
2f43aca21c Update version to 05.03.00 2025-02-01 10:58:34 -05:00
Dianne Skoll
930bab0fde Add more tests. 2025-02-01 10:45:34 -05:00
Dianne Skoll
694c4099d1 Add \xAA sequence for parsing quoted strings; add the escape() built-in function; update docs. 2025-02-01 10:36:38 -05:00
Dianne Skoll
ca56b4c90e Disallow "\x00" 2025-02-01 10:12:51 -05:00
Dianne Skoll
5c965e2083 Add "\xAB" escapes to string parser.
\x followed by one or two hex digits gets converted to that character.
2025-02-01 10:08:12 -05:00
Dianne Skoll
d58ccbef69 Improve how add/edit reminder dialog resizes.
All checks were successful
Remind unit tests / tests (push) Successful in 37s
2025-01-31 21:31:47 -05:00
Dianne Skoll
17ad03be69 Add a bit of space after labels. 2025-01-31 21:22:48 -05:00
Dianne Skoll
75a4e98de2 Call the reminder body the "Summary" rather than "Subject" to be consistent with ical. 2025-01-31 21:21:30 -05:00
Dianne Skoll
1408f77303 Use concat instead of list to flatten lists.
Bug report: https://dianne.skoll.ca/pipermail/remind-fans/2025/004986.html
2025-01-31 19:36:41 -05:00
Dianne Skoll
af76dd67fb Remove dead code; count lines better. 2025-01-31 17:02:03 -05:00
Dianne Skoll
f7a19d1570 Change "Body:" to "Subject:" and update man page. 2025-01-31 16:48:48 -05:00
Dianne Skoll
e7ec975ff0 Support location and description fields in tkremind. 2025-01-31 16:30:07 -05:00
Dianne Skoll
8c4ca12ca7 When creating the "info" JSON hash, make the keys lower-case instead of upper-case.
All checks were successful
Remind unit tests / tests (push) Successful in 46s
2025-01-31 08:07:53 -05:00
Dianne Skoll
e832eb868c Make INFO require "Header: Value" strings; make the "info" element in the JSON output a hash instead of an array.
All checks were successful
Remind unit tests / tests (push) Successful in 36s
2025-01-30 16:58:56 -05:00
Dianne Skoll
cb0acb3077 Document INFO
All checks were successful
Remind unit tests / tests (push) Successful in 54s
2025-01-29 19:07:26 -05:00
Dianne Skoll
9376c7a36d Add INFO keyword. 2025-01-29 18:55:22 -05:00
Dianne Skoll
e6ceeee2ec Add the "INFO" clause to the REM command.
All checks were successful
Remind unit tests / tests (push) Successful in 33s
Intended to pass additional information to a back-end to use as it wishes.
One example is to add extra info such as locaiton, description, etc. to ical
files.
2025-01-28 15:51:28 -05:00
Dianne Skoll
bbeece644e Use "custom.h" rather than <custom.h> for our header file. 2025-01-24 08:10:06 -05:00
Dianne Skoll
8d09abc363 Use snprintf in favor of sprintf almost everywhere.
All checks were successful
Remind unit tests / tests (push) Successful in 35s
2025-01-22 11:11:08 -05:00
Dianne Skoll
3dcd353fb5 Update release notes. 2025-01-22 10:56:05 -05:00
Dianne Skoll
124c5c4e7e Make test mode warning more verbose.
All checks were successful
Remind unit tests / tests (push) Successful in 35s
2025-01-21 11:54:24 -05:00
Dianne Skoll
77024562b3 Fix SystemDate to always return 2025-01-06 in --test mode.
All checks were successful
Remind unit tests / tests (push) Successful in 38s
2025-01-21 11:52:20 -05:00
Dianne Skoll
35c33ae915 Prevent infinite test loop. 2025-01-21 11:48:56 -05:00
Dianne Skoll
901831ff75 Add --test long option to make the test suite repeatable.
Some checks failed
Remind unit tests / tests (push) Failing after 3h14m50s
2025-01-20 13:33:00 -05:00
Dianne Skoll
e0c5e878a8 Explicitly unset REMIND_RUNNING_TEST at the start. 2025-01-20 11:35:32 -05:00
Dianne Skoll
ffba7fcb03 Make queue tests work at any time of the day.
All checks were successful
Remind unit tests / tests (push) Successful in 33s
If the REMIND_RUNNING_TEST environment variable is set to 1, then
SystemTime adjusts times near midnight so the queue tests pass.

Remind prints a warning if it is set so you don't accidentally
set it in normal use.
2025-01-20 10:15:19 -05:00
Dianne Skoll
b3f3cb9ce0 Add test for previous change
All checks were successful
Remind unit tests / tests (push) Successful in 31s
2025-01-19 22:05:25 -05:00
Dianne Skoll
6f11e727f8 Truncate any absurdly-long translations of "am" or "pm" rather than letting a buffer overflow. 2025-01-19 22:01:45 -05:00
Dianne Skoll
9f7ea96e87 Make it a bit easier to read
All checks were successful
Remind unit tests / tests (push) Successful in 41s
2025-01-18 10:45:35 -05:00
Dianne Skoll
d650b8564c Update release notes
All checks were successful
Remind unit tests / tests (push) Successful in 33s
2025-01-17 13:26:06 -05:00
Dianne Skoll
4f2e4030eb Bump version go 05.02.03. 2025-01-17 13:21:11 -05:00
Dianne Skoll
447bda5c91 Warn if given a bad debug flag. 2025-01-17 10:22:22 -05:00
Dianne Skoll
4f351c089e Add the "-dq" debugging flag, for outputing TRANSLATE commands needed by a reminder script.
All checks were successful
Remind unit tests / tests (push) Successful in 44s
2025-01-17 08:46:29 -05:00
Dianne Skoll
6b31778973 Better error message.
All checks were successful
Remind unit tests / tests (push) Successful in 32s
2025-01-16 19:24:57 -05:00
Dianne Skoll
dcf8b46beb Skip queueing tests between 23:55 and 00:00 UTC. 2025-01-16 19:23:49 -05:00
Dianne Skoll
0f582ccb60 Sleep until midnight if test starts after 23:55 UTC
All checks were successful
Remind unit tests / tests (push) Successful in 35s
2025-01-16 13:52:34 -05:00
Dianne Skoll
8d0743dd3e Don't allow a translation entry if printf-style formatters differ. 2025-01-16 12:53:23 -05:00
Dianne Skoll
436526c27d Fix -Wformat-security warnings. 2025-01-16 12:37:13 -05:00
Dianne Skoll
5f3f3b410f Fix tests for previous commit.
All checks were successful
Remind unit tests / tests (push) Successful in 52s
2025-01-15 11:02:30 -05:00
Dianne Skoll
4f79b0d42a Remove leading space from translatable. 2025-01-15 11:02:06 -05:00
Dianne Skoll
c96f9f21ed Properly print already-translated TRANS-type system variables in TRANSLATE GENERATE output.
All checks were successful
Remind unit tests / tests (push) Successful in 36s
2025-01-14 15:02:08 -05:00
Dianne Skoll
dc192f2a69 Final good version of TRANSLATE GENERATE. 2025-01-14 14:59:01 -05:00
Dianne Skoll
ac3dd1ec7c Better translation template 2025-01-14 14:54:26 -05:00
Dianne Skoll
b5717828f0 Make "TRANSLATE GENERATE" emit a BANNER command. 2025-01-14 14:08:46 -05:00
Dianne Skoll
c0d73fb9d1 Use @ to suppress echoing, not -. 2025-01-14 13:55:47 -05:00
Dianne Skoll
3a2ac067b0 Fix tests. 2025-01-14 13:49:13 -05:00
Dianne Skoll
1d467ab9ed Remove trailing \n from translation strings. 2025-01-14 13:48:45 -05:00
Dianne Skoll
c2ec4e9d29 Don't require any translation strings to include a trailing "\n" 2025-01-14 13:46:23 -05:00
Dianne Skoll
96c11e89eb Add the TRANSLATE GENERATE command to generate a skeleton .rem file for localization. 2025-01-14 13:27:04 -05:00
Dianne Skoll
a35920f28e Put quotes around function name when issuing "redefined" warning. 2025-01-14 11:30:06 -05:00
Dianne Skoll
17ccf5d2b4 Make more messages translatable. 2025-01-14 11:27:47 -05:00
Dianne Skoll
788a09b2cd Properly fix printing of ^-- here in error diagnosis.
All checks were successful
Remind unit tests / tests (push) Successful in 1m36s
2025-01-13 12:55:14 -05:00
Dianne Skoll
9f0e23a307 More localization. 2025-01-13 11:58:42 -05:00
Dianne Skoll
542620c188 Allow "did you mean" to be localized. 2025-01-13 11:56:43 -05:00
Dianne Skoll
50419bd83a Translate "here" to French. 2025-01-13 11:55:32 -05:00
Dianne Skoll
2b7c582392 Let "here" be localized. 2025-01-13 11:54:14 -05:00
Dianne Skoll
812a2af64b Better location of the ^-- here marker. 2025-01-13 11:52:55 -05:00
Dianne Skoll
17d7abd4a4 Add country-specific LGBTQ holidays.
All checks were successful
Remind unit tests / tests (push) Successful in 34s
2025-01-11 14:12:56 -05:00
Dianne Skoll
098e98c59e Add holidays/lgbtq.rem
All checks were successful
Remind unit tests / tests (push) Successful in 37s
2025-01-10 20:34:37 -05:00
Dianne Skoll
c5e0dbf2e5 Add license identifier. 2025-01-10 20:34:29 -05:00
Dianne Skoll
18c8bc719f Update tests to match commit 195ed15167
All checks were successful
Remind unit tests / tests (push) Successful in 35s
2025-01-07 17:59:06 -05:00
Dianne Skoll
195ed15167 Print ^-- here for a few more error types. 2025-01-07 17:58:03 -05:00
Dianne Skoll
51677cd7b5 Use "info patchlevel" to display detailed Tcl/Tk version. 2025-01-07 15:57:07 -05:00
70 changed files with 2677 additions and 626 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,2 +0,0 @@
liberapay: dskoll

7
.github/README.md vendored
View File

@@ -1,7 +0,0 @@
# Remind has moved
For various reasons, I have decided to move Remind off GitHub. This repo
will be archived. To create merge requests or issues, please visit
Remind's new home at https://salsa.debian.org/dskoll/remind
-- Dianne Skoll

View File

@@ -1,29 +0,0 @@
# language: bash
---
name: Remind unit tests
on:
push
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout Remind
uses: actions/checkout@v2
- name: Add test user
run: |
sudo adduser --home /home/testuser --gecos 'Test User' --disabled-password testuser
- name: Fix ownership
run: |
sudo chown -R testuser .
- name: Build
run: |
sudo su -c './configure && make' testuser
- name: Run Tests
run: |
sudo su -c 'make test' testuser
- name: Fix up permissions so GitHub does not complain
run: |
sudo chmod -R a+rwX .

1
.gitignore vendored
View File

@@ -34,3 +34,4 @@ tests/test.out
www/Makefile
gmon.out
tests/once.timestamp
src/xlat.c

View File

@@ -23,8 +23,7 @@ install:
@$(MAKE) -C rem2html install
@$(MAKE) -C rem2pdf -f Makefile.top install INSTALL_BASE=$(INSTALL_BASE)
clean:
find . -name '*~' -exec rm {} \;
-rm man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1 scripts/tkremind
-find . -name '*~' -exec rm {} \;
-$(MAKE) -C src clean
-$(MAKE) -C rem2pdf clean
@@ -44,7 +43,8 @@ test:
@$(MAKE) -C src -s test
distclean: clean
rm -f config.cache config.log config.status src/Makefile 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/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 man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1 scripts/tkremind
src/Makefile: src/Makefile.in
./configure

View File

@@ -6,11 +6,11 @@ the GNU General Public License, Vesion 2.
## Prerequisites:
`remind` and `rem2ps` have no prerequisites beyond the standard C library and
remind and rem2ps have no prerequisites beyond the standard C library and
the standard math library.
`rem2html` requires the `JSON::MaybeXS` Perl module and `rem2pdf`
requires the `JSON::MaybeXS`, `Pango` and `Cairo` Perl modules.
rem2html requires the JSON::MaybeXS Perl module and rem2pdf
requires the JSON::MaybeXS, Pango and Cairo Perl modules.
- On Debian-like systems, these prerequisites may be installed with:
@@ -24,7 +24,7 @@ requires the `JSON::MaybeXS`, `Pango` and `Cairo` Perl modules.
- On Arch linux, you need `pango-perl`, `cairo-perl` and `perl-json-maybexs`
TkRemind requires Tcl/Tk and the `tcllib` library.
TkRemind requires Tcl/Tk and the tcllib library.
- On Debian-like systems, install with:
@@ -49,9 +49,9 @@ Remind can be installed with the usual:
`./configure && make && make test && sudo make install`
You can edit `custom.h` to configure some aspects of Remind. Or, if
You can edit custom.h to configure some aspects of Remind. Or, if
you have Tcl/Tk installed, you can use the graphical build tool to
edit `custom.h` on your behalf:
edit custom.h on your behalf:
`wish ./build.tk`

18
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for remind 05.02.02.
# Generated by GNU Autoconf 2.71 for remind 05.03.03.
#
#
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -608,8 +608,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='remind'
PACKAGE_TARNAME='remind'
PACKAGE_VERSION='05.02.02'
PACKAGE_STRING='remind 05.02.02'
PACKAGE_VERSION='05.03.03'
PACKAGE_STRING='remind 05.03.03'
PACKAGE_BUGREPORT=''
PACKAGE_URL='https://dianne.skoll.ca/projects/remind/'
@@ -1265,7 +1265,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures remind 05.02.02 to adapt to many kinds of systems.
\`configure' configures remind 05.03.03 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1327,7 +1327,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of remind 05.02.02:";;
short | recursive ) echo "Configuration of remind 05.03.03:";;
esac
cat <<\_ACEOF
@@ -1415,7 +1415,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
remind configure 05.02.02
remind configure 05.03.03
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1865,7 +1865,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by remind $as_me 05.02.02, which was
It was created by remind $as_me 05.03.03, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -4710,7 +4710,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by remind $as_me 05.02.02, which was
This file was extended by remind $as_me 05.03.03, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -4775,7 +4775,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
remind config.status 05.02.02
remind config.status 05.03.03
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View File

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

View File

@@ -112,7 +112,7 @@
(list "ADDOMIT" "AFTER" "AT" "BAN" "BANNER" "BEFORE" "CAL" "CLEAR"
"CLEAR-OMIT-CONTEXT" "DEBUG" "DO" "DUMP" "DUMPVARS" "DURATION" "ELSE"
"ENDIF" "ERRMSG" "EXIT" "EXPR" "FIRST" "FLUSH" "FOURTH" "FRENAME" "FROM" "FSET"
"FUNSET" "IF" "IFTRIG" "IN" "INC" "INCLUDE" "INCLUDECMD" "LAST"
"FUNSET" "IF" "IFTRIG" "IN" "INC" "INCLUDE" "INCLUDECMD" "INFO" "LAST"
"LASTDAY" "LASTWORKDAY" "MAYBE" "MAYBE-UNCOMPUTABLE" "MSF" "MSG"
"NOQUEUE" "OMIT" "OMITFUNC" "ONCE" "POP" "POP-OMIT-CONTEXT" "PRESERVE"
"PRIORITY" "PS" "PSFILE" "PUSH" "PUSH-OMIT-CONTEXT" "REM" "RUN"
@@ -166,7 +166,7 @@
(list "_" "abs" "access" "adawn" "adusk" "ampm" "ansicolor" "args" "asc"
"baseyr" "char" "choose" "coerce" "columns" "current" "date"
"datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst"
"dusk" "easterdate" "evaltrig" "filedate" "filedatetime" "filedir"
"dusk" "easterdate" "escape" "evaltrig" "filedate" "filedatetime" "filedir"
"filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear" "hour"
"htmlescape" "htmlstriptags" "iif" "index" "isany" "isdst" "isleap"
"isomitted" "language" "localtoutc" "lower" "max" "min" "minsfromutc"
@@ -177,7 +177,7 @@
"slide" "soleq" "stdout" "strlen" "substr" "sunrise" "sunset" "time"
"timepart" "timezone" "today" "trig" "trigback" "trigdate"
"trigdatetime" "trigdelta" "trigduration" "trigeventduration"
"trigeventstart" "trigfrom" "trigger" "trigpriority" "trigrep"
"trigeventstart" "trigfrom" "trigger" "triginfo" "trigpriority" "trigrep"
"trigscanfrom" "trigtags" "trigtime" "trigtimedelta" "trigtimerep"
"triguntil" "trigvalid" "typeof" "tzconvert" "upper" "utctolocal"
"value" "version" "weekno" "wkday" "wkdaynum" "year")

View File

@@ -1,5 +1,101 @@
CHANGES TO REMIND
* VERSION 5.3 Patch 3 - 2025-03-03
- NEW FEATURE: remind: If a command spans more than one line (because of
backslash line continuation) output both the starting and ending line
number in error messages.
- NEW FEATURE: remind: In the JSON -pp and -ppp output, include a new key
lineno_start to specify the starting line of a multi-line reminder.
The existing lineno key specifies the ending line; this is maintained
for backward-compatibility.
- MINOR IMPROVEMENT: include/holidays/misc.rem: Add a few new holidays and
URL INFO strings.
- CHANGE: remind: Issue a warning if a time-related subsitution sequence
is used with a non-timed REM command.
- BUG FIX: remind: Fix a memory leak.
* VERSION 5.3 Patch 2 - 2025-02-09
- CHANGE: remind: Revert a change to the way "-y" tags are generated that
was introduced in 05.03.01. The change broke a library that depended
on it being generated in the old way.
* VERSION 5.3 Patch 1 - 2025-02-07
- IMPROVEMENT: TkRemind: When we pop up a timed reminder, make any "Url:"
info string into a clickable link.
- IMPROVEMENT: rem2pdf: Improve the layout of the small monthly calendars.
- BUG FIX: rem2pdf: Add checks for all Perl dependencies.
* VERSION 5.3 Patch 0 - 2025-02-04
- NEW FEATURE: remind: Add the "INFO" clause to the REM command. This
is intended for storing additional metadata about an event, such as
the location and a longer description. The intention is to make
Remind <-> iCal conversions preserve as much information as possible.
- NEW FEATURE: rem2html, rem2pdf, tkremind: Add support for the "Url:"
info string that turns reminders into hyper-links. For example,
consider this reminder:
REM 15 INFO "Url: https://foo.example" MSG Foo
The text "Foo" will be made into a link to "https://foo.example"
by rem2html and rem2pdf. If you middle-click it in tkremind, it
will open the URL.
- NEW FEATURE: remind: Add the triginfo() built-in function so a reminder
body can refer to INFO data. Add the %<...> substitution filter as a
shorthand for [triginfo("...")]
- NEW FEATURE: TkRemind: Add "Location" and "Description" fields when
creating a reminder; these are converted to INFO clauses. Also support
a popup window with the extra information when hovering over a reminder
in the calendar display.
- IMPROVEMENT: Update the reminder files included with Remind to add
INFO strings with Wikipedia URLs for various holidays and
astronomical events.
- IMPROVEMENT: remind: Add the "\xAB" escape sequence for parsing quoted
strings, where "AB" is a pair of hex digits.
- NEW FUNCTION: remind: Add the escape() built-in function that converts
problematic characters within a string to the \-escaped versions.
It's essentially the inverse of how Remind parses a quoted string.
* VERSION 5.2 Patch 3 - 2025-01-22
- NEW FEATURE: remind: Add "TRANSLATE GENERATE" command for generating
a skeleton set of TRANSLATE commands to make it easier to localize
Remind.
- NEW FEATURE: remind: Add "q" debug flag for tracing calls to _()
or %(...) in the substitution filter; this will help with localizing
reminder files.
- NEW FILES: remind: Add holidays/lgbtq.rem for LGBTQ holidays. Add
country-specific files in holidays/lgbtq/*.rem
- IMPROVEMENT: TkRemind: use "info patchlevel" to display full Tcl/Tk
version.
- IMPROVEMENT: remind: The DEBUG command issues a warning if given an
unknown debug flag.
- BUG FIX: remind: "make test" will now succeed even if run between
23:55 and 00:00 UTC. This is done with a new --test flag for remind.
- BUG FIX: remind: Avoid potential buffer overflow if someone supplies
ridiculously-long translations for "am" or "pm".
* VERSION 5.2 Patch 2 - 2025-01-06
- NEW FEATURE: remind: The "-p+" option lets you produce weekly calendars;

View File

@@ -1,29 +1,30 @@
REM 1 Feb 2022 MSG %(Chinese New Year) (%(Tiger))
REM 22 Jan 2023 MSG %(Chinese New Year) (%(Rabbit))
REM 10 Feb 2024 MSG %(Chinese New Year) (%(Dragon))
REM 29 Jan 2025 MSG %(Chinese New Year) (%(Snake))
REM 17 Feb 2026 MSG %(Chinese New Year) (%(Horse))
REM 6 Feb 2027 MSG %(Chinese New Year) (%(Goat))
REM 26 Jan 2028 MSG %(Chinese New Year) (%(Monkey))
REM 13 Feb 2029 MSG %(Chinese New Year) (%(Rooster))
REM 3 Feb 2030 MSG %(Chinese New Year) (%(Dog))
REM 23 Jan 2031 MSG %(Chinese New Year) (%(Pig))
REM 11 Feb 2032 MSG %(Chinese New Year) (%(Rat))
REM 31 Jan 2033 MSG %(Chinese New Year) (%(Ox))
REM 19 Feb 2034 MSG %(Chinese New Year) (%(Tiger))
REM 8 Feb 2035 MSG %(Chinese New Year) (%(Rabbit))
REM 28 Jan 2036 MSG %(Chinese New Year) (%(Dragon))
REM 15 Feb 2037 MSG %(Chinese New Year) (%(Snake))
REM 4 Feb 2038 MSG %(Chinese New Year) (%(Horse))
REM 24 Jan 2039 MSG %(Chinese New Year) (%(Goat))
REM 12 Feb 2040 MSG %(Chinese New Year) (%(Monkey))
REM 1 Feb 2041 MSG %(Chinese New Year) (%(Rooster))
REM 22 Jan 2042 MSG %(Chinese New Year) (%(Dog))
REM 10 Feb 2043 MSG %(Chinese New Year) (%(Pig))
REM 30 Jan 2044 MSG %(Chinese New Year) (%(Rat))
REM 17 Feb 2045 MSG %(Chinese New Year) (%(Ox))
REM 6 Feb 2046 MSG %(Chinese New Year) (%(Tiger))
REM 26 Jan 2047 MSG %(Chinese New Year) (%(Rabbit))
REM 14 Feb 2048 MSG %(Chinese New Year) (%(Dragon))
REM 2 Feb 2049 MSG %(Chinese New Year) (%(Snake))
REM 23 Jan 2050 MSG %(Chinese New Year) (%(Horse))
# SPDX-License-Identifier: GPL-2.0-only
REM 1 Feb 2022 INFO "Url: https://en.wikipedia.org/wiki/Tiger_(zodiac)" MSG %(Chinese New Year) (%(Tiger))
REM 22 Jan 2023 INFO "Url: https://en.wikipedia.org/wiki/Rabbit_(zodiac)" MSG %(Chinese New Year) (%(Rabbit))
REM 10 Feb 2024 INFO "Url: https://en.wikipedia.org/wiki/Dragon_(zodiac)" MSG %(Chinese New Year) (%(Dragon))
REM 29 Jan 2025 INFO "Url: https://en.wikipedia.org/wiki/Snake_(zodiac)" MSG %(Chinese New Year) (%(Snake))
REM 17 Feb 2026 INFO "Url: https://en.wikipedia.org/wiki/Horse_(zodiac)" MSG %(Chinese New Year) (%(Horse))
REM 6 Feb 2027 INFO "Url: https://en.wikipedia.org/wiki/Goat_(zodiac)" MSG %(Chinese New Year) (%(Goat))
REM 26 Jan 2028 INFO "Url: https://en.wikipedia.org/wiki/Monkey_(zodiac)" MSG %(Chinese New Year) (%(Monkey))
REM 13 Feb 2029 INFO "Url: https://en.wikipedia.org/wiki/Rooster_(zodiac)" MSG %(Chinese New Year) (%(Rooster))
REM 3 Feb 2030 INFO "Url: https://en.wikipedia.org/wiki/Dog_(zodiac)" MSG %(Chinese New Year) (%(Dog))
REM 23 Jan 2031 INFO "Url: https://en.wikipedia.org/wiki/Pig_(zodiac)" MSG %(Chinese New Year) (%(Pig))
REM 11 Feb 2032 INFO "Url: https://en.wikipedia.org/wiki/Rat_(zodiac)" MSG %(Chinese New Year) (%(Rat))
REM 31 Jan 2033 INFO "Url: https://en.wikipedia.org/wiki/Ox_(zodiac)" MSG %(Chinese New Year) (%(Ox))
REM 19 Feb 2034 INFO "Url: https://en.wikipedia.org/wiki/Tiger_(zodiac)" MSG %(Chinese New Year) (%(Tiger))
REM 8 Feb 2035 INFO "Url: https://en.wikipedia.org/wiki/Rabbit_(zodiac)" MSG %(Chinese New Year) (%(Rabbit))
REM 28 Jan 2036 INFO "Url: https://en.wikipedia.org/wiki/Dragon_(zodiac)" MSG %(Chinese New Year) (%(Dragon))
REM 15 Feb 2037 INFO "Url: https://en.wikipedia.org/wiki/Snake_(zodiac)" MSG %(Chinese New Year) (%(Snake))
REM 4 Feb 2038 INFO "Url: https://en.wikipedia.org/wiki/Horse_(zodiac)" MSG %(Chinese New Year) (%(Horse))
REM 24 Jan 2039 INFO "Url: https://en.wikipedia.org/wiki/Goat_(zodiac)" MSG %(Chinese New Year) (%(Goat))
REM 12 Feb 2040 INFO "Url: https://en.wikipedia.org/wiki/Monkey_(zodiac)" MSG %(Chinese New Year) (%(Monkey))
REM 1 Feb 2041 INFO "Url: https://en.wikipedia.org/wiki/Rooster_(zodiac)" MSG %(Chinese New Year) (%(Rooster))
REM 22 Jan 2042 INFO "Url: https://en.wikipedia.org/wiki/Dog_(zodiac)" MSG %(Chinese New Year) (%(Dog))
REM 10 Feb 2043 INFO "Url: https://en.wikipedia.org/wiki/Pig_(zodiac)" MSG %(Chinese New Year) (%(Pig))
REM 30 Jan 2044 INFO "Url: https://en.wikipedia.org/wiki/Rat_(zodiac)" MSG %(Chinese New Year) (%(Rat))
REM 17 Feb 2045 INFO "Url: https://en.wikipedia.org/wiki/Ox_(zodiac)" MSG %(Chinese New Year) (%(Ox))
REM 6 Feb 2046 INFO "Url: https://en.wikipedia.org/wiki/Tiger_(zodiac)" MSG %(Chinese New Year) (%(Tiger))
REM 26 Jan 2047 INFO "Url: https://en.wikipedia.org/wiki/Rabbit_(zodiac)" MSG %(Chinese New Year) (%(Rabbit))
REM 14 Feb 2048 INFO "Url: https://en.wikipedia.org/wiki/Dragon_(zodiac)" MSG %(Chinese New Year) (%(Dragon))
REM 2 Feb 2049 INFO "Url: https://en.wikipedia.org/wiki/Snake_(zodiac)" MSG %(Chinese New Year) (%(Snake))
REM 23 Jan 2050 INFO "Url: https://en.wikipedia.org/wiki/Horse_(zodiac)" MSG %(Chinese New Year) (%(Horse))

View File

@@ -18,90 +18,90 @@ FSET _BackTwoSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)-2)
SET InIsrael VALUE("InIsrael", 0)
SET Reform VALUE("Reform", 0)
REM [hebdate(1, "Tishrey")] MSG Rosh Hashana 1
REM [hebdate(1, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Rosh_Hashanah" MSG Rosh Hashana 1
# No RH-2 or Tzom Gedalia in Reform
IF !Reform
REM [hebdate(2, "Tishrey")] MSG Rosh Hashana 2
REM [_PastSat(3, "Tishrey")] MSG Tzom Gedalia
REM [hebdate(2, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Rosh_Hashanah" MSG Rosh Hashana 2
REM [_PastSat(3, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Fast_of_Gedalia" MSG Tzom Gedalia
ENDIF
REM [hebdate(10, "Tishrey")] MSG Yom Kippur
REM [hebdate(15, "Tishrey")] MSG Sukkot 1
REM [hebdate(10, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Yom_Kippur" MSG Yom Kippur
REM [hebdate(15, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Sukkot" MSG Sukkot 1
IF !InIsrael
REM [hebdate(16, "Tishrey")] MSG Sukkot 2
REM [hebdate(16, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Sukkot" MSG Sukkot 2
ENDIF
REM [hebdate(21, "Tishrey")] MSG Hoshana Rabba
REM [hebdate(22, "Tishrey")] MSG Shemini Atzeret
REM [hebdate(21, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Hoshana_Rabbah" MSG Hoshana Rabba
REM [hebdate(22, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Shemini_Atzeret" MSG Shemini Atzeret
IF InIsrael
REM [hebdate(22, "Tishrey")] MSG Simchat Torah
REM [hebdate(22, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Simchat_Torah" MSG Simchat Torah
ELSE
REM [hebdate(23, "Tishrey")] MSG Simchat Torah
REM [hebdate(23, "Tishrey")] INFO "Url: https://en.wikipedia.org/wiki/Simchat_Torah" MSG Simchat Torah
ENDIF
# Because Kislev can change length, we must be more careful about Chanukah
FSET _chan(x) HEBDATE(24, "Kislev", $U-9)+x
REM [_chan(1)] MSG Chanukah 1
REM [_chan(2)] MSG Chanukah 2
REM [_chan(3)] MSG Chanukah 3
REM [_chan(4)] MSG Chanukah 4
REM [_chan(5)] MSG Chanukah 5
REM [_chan(6)] MSG Chanukah 6
REM [_chan(7)] MSG Chanukah 7
REM [_chan(8)] MSG Chanukah 8
REM [_chan(1)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 1
REM [_chan(2)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 2
REM [_chan(3)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 3
REM [_chan(4)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 4
REM [_chan(5)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 5
REM [_chan(6)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 6
REM [_chan(7)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 7
REM [_chan(8)] INFO "Url: https://en.wikipedia.org/wiki/Hanukkah" MSG Chanukah 8
# Not sure about Reform's position on the next one.
IF !Reform
# 10 Tevet will never be a Saturday, so whether or not to
# move it is moot. (Thanks to Art Werschulz.)
REM [hebdate(10, "Tevet")] MSG Tzom Tevet
REM [hebdate(10, "Tevet")] INFO "Url: https://en.wikipedia.org/wiki/Tenth_of_Tevet" MSG Tzom Tevet
ENDIF
REM [hebdate(15, "Shvat")] MSG Tu B'Shvat
REM [hebdate(14, "Adar A")] MSG Purim Katan
REM [hebdate(15, "Shvat")] INFO "Url: https://en.wikipedia.org/wiki/Tu_BiShvat" MSG Tu B'Shvat
REM [hebdate(14, "Adar A")] INFO "Url: https://en.wikipedia.org/wiki/Purim#Purim_Katan" MSG Purim Katan
# If Purim is on Sunday, then Fast of Esther is 11 Adar.
IF WKDAYNUM(_h2(13, "Adar")) != 6
REM [_h2(13, "Adar")] MSG Fast of Esther
REM [_h2(13, "Adar")] INFO "Url: https://en.wikipedia.org/wiki/Fast_of_Esther" MSG Fast of Esther
ELSE
REM [_h2(11, "Adar")] MSG Fast of Esther
REM [_h2(11, "Adar")] INFO "Url: https://en.wikipedia.org/wiki/Fast_of_Esther" MSG Fast of Esther
ENDIF
REM [hebdate(14, "Adar")] MSG Purim
REM [hebdate(15, "Nisan")] MSG Pesach
REM [hebdate(14, "Adar")] INFO "Url: https://en.wikipedia.org/wiki/Purim" MSG Purim
REM [hebdate(15, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach
IF !InIsrael
REM [hebdate(16, "Nisan")] MSG Pesach 2
REM [hebdate(16, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach 2
ENDIF
REM [hebdate(21, "Nisan")] MSG Pesach 7
REM [hebdate(21, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach 7
IF !InIsrael && !Reform
REM [hebdate(22, "Nisan")] MSG Pesach 8
REM [hebdate(22, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Passover" MSG Pesach 8
ENDIF
REM [hebdate(27, "Nisan")] MSG Yom HaShoah
REM [_BackTwoFri(4, "Iyar")] MSG Yom HaZikaron
REM [_BackTwoSat(5, "Iyar")] MSG Yom Ha'atzmaut
REM [hebdate(27, "Nisan")] INFO "Url: https://en.wikipedia.org/wiki/Yom_HaShoah" MSG Yom HaShoah
REM [_BackTwoFri(4, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Yom_HaZikaron" MSG Yom HaZikaron
REM [_BackTwoSat(5, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Independence_Day_(Israel)" MSG Yom Ha'atzmaut
# Not sure about Reform's position on Lag B'Omer
IF !Reform
REM [hebdate(18, "Iyar")] MSG Lag B'Omer
REM [hebdate(18, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Lag_BaOmer" MSG Lag B'Omer
ENDIF
REM [hebdate(28, "Iyar")] MSG Yom Yerushalayim
REM [hebdate(6, "Sivan")] MSG Shavuot
REM [hebdate(28, "Iyar")] INFO "Url: https://en.wikipedia.org/wiki/Jerusalem_Day" MSG Yom Yerushalayim
REM [hebdate(6, "Sivan")] INFO "Url: https://en.wikipedia.org/wiki/Shavuot" MSG Shavuot
IF !InIsrael && !Reform
REM [hebdate(7, "Sivan")] MSG Shavuot 2
REM [hebdate(7, "Sivan")] INFO "Url: https://en.wikipedia.org/wiki/Shavuot" MSG Shavuot 2
ENDIF
# Fairly sure Reform Jews don't observe the next two
IF !Reform
# Tzom Tamuz and Tish'a B'Av are moved to Sunday if they normally
# fall on a Saturday
REM [_PastSat(17, "Tamuz")] MSG Tzom Tammuz
REM [_PastSat(9, "Av")] MSG Tish'a B'Av
REM [_PastSat(17, "Tamuz")] INFO "Url: https://en.wikipedia.org/wiki/Seventeenth_of_Tammuz" MSG Tzom Tammuz
REM [_PastSat(9, "Av")] INFO "Url: https://en.wikipedia.org/wiki/Tisha_B%27Av" MSG Tish'a B'Av
ENDIF

View File

@@ -0,0 +1,34 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the internationally-recognized days.
#
# SPDX-License-Identifier: GPL-2.0-only
REM Sun 14 Feb INFO "Url: https://en.wikipedia.org/wiki/Aromantic_Spectrum_Awareness_Week" MSG Start of Aromantic Spectrum Awareness Week
REM 1 Mar INFO "Url: https://en.wikipedia.org/wiki/Zero_Discrimination_Day" MSG Zero Discrimination Day
REM 31 Mar INFO "Url: https://en.wikipedia.org/wiki/International_Transgender_Day_of_Visibility" MSG Trans Day of Visibility
REM 6 Apr INFO "Url: https://en.wikipedia.org/wiki/Asexuality#International_Asexuality_Day" MSG International Asexuality Day
REM Wed 8 Apr INFO "Url: https://en.wikipedia.org/wiki/International_Day_of_Pink" MSG International Day of Pink
REM 26 Apr INFO "Url: https://en.wikipedia.org/wiki/Lesbian_Visibility_Day" MSG Lesbian Visibility Day
REM 17 May INFO "Url: https://en.wikipedia.org/wiki/International_Day_Against_Homophobia,_Biphobia_and_Transphobia" MSG International Day Against Homophobia, Biphobia and Transphobia
REM 19 May MSG Agender Pride Day
REM 24 May MSG Pansexual & Panromantic Awareness Day
REM 1 Jun INFO "Url: https://en.wikipedia.org/wiki/LGBT_Pride_Month" MSG Start of LGBT Pride Month
REM 5 Jun INFO "Url: https://en.wikipedia.org/wiki/Aromantic_Visibility_Day" MSG Aromantic Visibility Day
REM Mon 8 Jul MSG Start of Non-Binary Awareness Week
REM 14 Jul INFO "Url: https://en.wikipedia.org/wiki/International_Non-Binary_People%27s_Day" MSG International Non-Binary People's Day
REM 16 Jul INFO "Url: https://en.wikipedia.org/wiki/International_Drag_Day" MSG International Drag Day
REM 9 Aug MSG Start of Dyke Week
REM 16 Sep INFO "Url: https://en.wikipedia.org/wiki/Bisexual_Awareness_Week" MSG Start of Bisexual Awareness Week
REM 23 Sep INFO "Url: https://en.wikipedia.org/wiki/Celebrate_Bisexuality_Day" MSG Celebrate Bisexuality Day
REM 8 Oct MSG Lesbian Day
REM 11 Oct INFO "Url: https://en.wikipedia.org/wiki/National_Coming_Out_Day" MSG National Coming Out Day
REM Wed 15 Oct INFO "Url: https://en.wikipedia.org/wiki/Preferred_gender_pronoun" MSG Pronouns Day
REM 17 Oct MSG Start of Genderfluid Visibility Week
REM Sun 19 Oct INFO "Url: https://en.wikipedia.org/wiki/Asexuality#Ace_Week" MSG Start of Ace Week
REM 26 Oct INFO "Url: https://en.wikipedia.org/wiki/Intersex_Awareness_Day" MSG Intersex Awareness Day
REM 8 Nov INFO "Url: https://en.wikipedia.org/wiki/Intersex_Day_of_Remembrance" MSG Intersex Day of Remembrance
REM 1 Nov MSG Start of Trans Awareness Month
REM Sun 1 Nov MSG Trans Parent Day
REM 13 Nov INFO "Url: https://en.wikipedia.org/wiki/Transgender_Awareness_Week" MSG Start of Trans Awareness Week
REM 20 Nov INFO "Url: https://en.wikipedia.org/wiki/Transgender_Day_of_Remembrance" MSG Transgender Day of Remembrance

View File

@@ -0,0 +1,8 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in Australia
#
# SPDX-License-Identifier: GPL-2.0-only
REM Sat 1 Mar INFO "Url: https://en.wikipedia.org/wiki/Sydney_Gay_and_Lesbian_Mardi_Gras" MSG Mardi Gras Parade
REM Fri 1 Sep --7 INFO "Url: https://en.wikipedia.org/wiki/Wear_it_Purple_Day" MSG Wear it Purple Day
REM 1 Oct INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month

View File

@@ -0,0 +1,10 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in Brazil
#
# SPDX-License-Identifier: GPL-2.0-only
REM 29 Jan MSG National Day of Transgender Visibility
REM 20 Feb MSG Trans Men and Transmasculine Day
REM 25 Mar MSG National Gay Pride Day
REM 29 Aug MSG National Day of Lesbian Visibility

View File

@@ -0,0 +1,7 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in Canada
#
# SPDX-License-Identifier: GPL-2.0-only
REM 1 Oct INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month

View File

@@ -0,0 +1,7 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in Chile
#
# SPDX-License-Identifier: GPL-2.0-only
REM 9 Jul INFO "Url: https://en.wikipedia.org/wiki/Lesbian_Visibility_Day" MSG Lesbian Visibility Day

View File

@@ -0,0 +1,7 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in Ireland
#
# SPDX-License-Identifier: GPL-2.0-only
REM 22 May INFO "Url: https://en.wikipedia.org/wiki/Thirty-fourth_Amendment_of_the_Constitution_of_Ireland" MSG Irish Marriage Referendum Day

View File

@@ -0,0 +1,7 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in India
#
# SPDX-License-Identifier: GPL-2.0-only
REM 2 Jul INFO "Url: https://en.wikipedia.org/wiki/Indian_Coming_Out_Day" MSG Indian Coming Out Day

View File

@@ -0,0 +1,7 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in the Netherlands
#
# SPDX-License-Identifier: GPL-2.0-only
REM Fri 8 Dec MSG Purple Friday

View File

@@ -0,0 +1,7 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in Ukraine
#
# SPDX-License-Identifier: GPL-2.0-only
REM 12 Dec MSG Day of Remembrance of Homosexuals in Ukraine (victims of the totalitarian Soviet regime)

View File

@@ -0,0 +1,8 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in the United Kingdom
#
# SPDX-License-Identifier: GPL-2.0-only
REM 1 Feb INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month
REM 6 May INFO "Url: https://en.wikipedia.org/wiki/Trans%2B_History_Week" MSG Start of Trans+ History Week

View File

@@ -0,0 +1,16 @@
# List of LGBTQ awareness days
# Sourced from https://en.wikipedia.org/wiki/List_of_LGBTQ_awareness_periods
# These are the holidays recognized in the United States of America
#
# SPDX-License-Identifier: GPL-2.0-only
REM 1 Mar MSG Start of Bisexual Health Awareness Month
REM Fri 8 Apr INFO "Url: https://en.wikipedia.org/wiki/Day_of_Silence" MSG Day of Silence
REM 9 Apr MSG Sapphic Visibility Day
REM 22 May INFO "Url: https://en.wikipedia.org/wiki/Harvey_Milk_Day" MSG Harvey Milk Day
REM 5 Jun MSG HIV Long-Term Survivors Awareness Day
REM 12 Jun INFO "Url: https://en.wikipedia.org/wiki/Orlando_nightclub_shooting" MSG Pulse Night of Remembrance
REM 28 Jun INFO "Url: https://en.wikipedia.org/wiki/Stonewall_Riots" MSG Stonewall Riots Anniversary
REM 1 Aug MSG Start of Transgender History Month (CA)
REM 1 Oct INFO "Url: https://en.wikipedia.org/wiki/LGBT_History_Month" MSG Start of LGBT History Month
REM Thu 15 Oct INFO "Url: https://en.wikipedia.org/wiki/Spirit_Day" MSG Spirit Day

View File

@@ -0,0 +1,9 @@
# Miscellaneous holidays
#
# SPDX-License-Identifier: GPL-2.0-only
REM 1 February INFO "Url: https://en.wikipedia.org/wiki/Black_History_Month" MSG Start of Black History Month
REM 1 March INFO "Url: https://en.wikipedia.org/wiki/Women%27s_History_Month" MSG Start of Women's History Month
REM 8 March INFO "Url: https://en.wikipedia.org/wiki/International_Women%27s_Day" MSG International Women's Day
REM Thu 1 May INFO "Url: https://en.wikipedia.org/wiki/National_Day_of_Reason" MSG National Day of Reason
REM 23 March INFO "Url: https://www.atheistrepublic.com/atheist-day" MSG Atheist Day

View File

@@ -170,19 +170,19 @@ TRANSLATE "Leaving UserFN" "Poistutaan funktiosta"
TRANSLATE "Expired" "Vanhentunut"
TRANSLATE "fork() failed - can't do queued reminders" "fork() epäonnistui - jonomuistutukset eivät toimi"
TRANSLATE "Can't access file" "Tiedoston avaus ei onnistu"
TRANSLATE "Illegal system date: Year is less than %d\n" "Virheellinen järjestelmäpäiväys: vuosi on vähemmän kuin %d\n"
TRANSLATE "Unknown debug flag '%c'\n" "Tuntematon virheenetsintätarkenne '%c'\n"
TRANSLATE "Unknown option '%c'\n" "Tuntematon tarkenne '%c'\n"
TRANSLATE "Unknown user '%s'\n" "Tuntematon käyttäjä '%s'\n"
TRANSLATE "Could not change gid to %d\n" "Ryhmänumeron vaihto %d:ksi ei onnistunut\n"
TRANSLATE "Could not change uid to %d\n" "Käyttäjänumeron vaihto %d:ksi ei onnistunut\n"
TRANSLATE "Out of memory for environment\n" "Muisti ei riitä ympäristölle\n"
TRANSLATE "Illegal system date: Year is less than %d" "Virheellinen järjestelmäpäiväys: vuosi on vähemmän kuin %d"
TRANSLATE "Unknown debug flag '%c'" "Tuntematon virheenetsintätarkenne '%c'"
TRANSLATE "Unknown option '%c'" "Tuntematon tarkenne '%c'"
TRANSLATE "Unknown user '%s'" "Tuntematon käyttäjä '%s'"
TRANSLATE "Could not change gid to %d" "Ryhmänumeron vaihto %d:ksi ei onnistunut"
TRANSLATE "Could not change uid to %d" "Käyttäjänumeron vaihto %d:ksi ei onnistunut"
TRANSLATE "Out of memory for environment" "Muisti ei riitä ympäristölle"
TRANSLATE "Missing '=' sign" "Puuttuva '='-merkki"
TRANSLATE "Missing variable name" "Puuttuva muuttujanimi"
TRANSLATE "Missing expression" "Puuttuva lauseke"
TRANSLATE "Remind: '-i' option: %s\n" "Remind: tarkenne '-i': %s\n"
TRANSLATE "Remind: '-i' option: %s" "Remind: tarkenne '-i': %s"
TRANSLATE "No reminders." "Ei viestejä."
TRANSLATE "%d reminder(s) queued for later today.\n" "%d viesti(ä) tämän päivän jonossa.\n"
TRANSLATE "%d reminder(s) queued for later today." "%d viesti(ä) tämän päivän jonossa."
TRANSLATE "Expecting number" "Numero puuttuu"
TRANSLATE "Undefined WARN function" "Virheellinen funktio WARN-lausekkeessa"
TRANSLATE "Can't convert between time zones" "Aikavyöhykkeiden välillä ei voi muuntaa"

View File

@@ -138,17 +138,17 @@ TRANSLATE "Leaving UserFN" "Sortie de UserFN"
TRANSLATE "Expired" "Expiré"
TRANSLATE "fork() failed - can't do queued reminders" "fork() échoué - impossible de faire les appels en queue"
TRANSLATE "Can't access file" "Impossible d'accéder au fichier"
TRANSLATE "Illegal system date: Year is less than %d\n" "Date système illégale: Année est inférieure à %d\n"
TRANSLATE "Unknown debug flag '%c'\n" "Option de déverminage inconnue '%c'\n"
TRANSLATE "Unknown option '%c'\n" "Option inconnue '%c'\n"
TRANSLATE "Unknown user '%s'\n" "Usager inconnu '%s'\n"
TRANSLATE "Could not change gid to %d\n" "Impossible de changer gid pour %d\n"
TRANSLATE "Could not change uid to %d\n" "Impossible de changer uid pour %d\n"
TRANSLATE "Out of memory for environment\n" "Manque de mémoire pour environnement\n"
TRANSLATE "Illegal system date: Year is less than %d" "Date système illégale: Année est inférieure à %d"
TRANSLATE "Unknown debug flag '%c'" "Option de déverminage inconnue '%c'"
TRANSLATE "Unknown option '%c'" "Option inconnue '%c'"
TRANSLATE "Unknown user '%s'" "Usager inconnu '%s'"
TRANSLATE "Could not change gid to %d" "Impossible de changer gid pour %d"
TRANSLATE "Could not change uid to %d" "Impossible de changer uid pour %d"
TRANSLATE "Out of memory for environment" "Manque de mémoire pour environnement"
TRANSLATE "Missing '=' sign" "Signe '=' manquant"
TRANSLATE "Missing variable name" "Nom de variable absent"
TRANSLATE "Missing expression" "Expression absente"
TRANSLATE "%d reminder(s) queued for later today.\n" "%d rappel(s) en file pour aujourd'hui.\n"
TRANSLATE "%d reminder(s) queued for later today." "%d rappel(s) en file pour aujourd'hui."
TRANSLATE "Expecting number" "Nombre attendu"
TRANSLATE "Undefined WARN function" "Fonction illégale après WARN"
TRANSLATE "Can't convert between time zones" "Impossible de convertir entre les fuseaux horaires"
@@ -160,3 +160,5 @@ TRANSLATE "Expecting weekday name" "Nom du jour de la semaine attendu"
TRANSLATE "Duplicate argument name" "Nom de l'argument en double"
TRANSLATE "Expression evaluation is disabled" "L'évaluation de l'expression est désactivée"
TRANSLATE "Time limit for expression evaluation exceeded" "Délai d'évaluation de l'expression dépassé"
TRANSLATE "here" "ici"
TRANSLATE "did you mean" "voulais-vous dire"

View File

@@ -86,3 +86,6 @@ TRANSLATE "Sunrise" "Zonsopgang"
TRANSLATE "Sunset" "Zonsondergang"
TRANSLATE "No reminders." "Geen herinneringen."
TRANSLATE "Daylight Saving Time Begins" "Daglicht-sparende tijd begint"
TRANSLATE "Daylight Saving Time Ends" "Daglicht-sparende tijd eindigt"

View File

@@ -146,19 +146,19 @@ TRANSLATE "Leaving UserFN" "Koniec UserFN"
TRANSLATE "Expired" "Przemineło"
TRANSLATE "fork() failed - can't do queued reminders" "Niepowodzenie w funkcji fork() - nie mogę kolejkować przypomnień"
TRANSLATE "Can't access file" "Nie ma dostępu do pliku"
TRANSLATE "Illegal system date: Year is less than %d\n" "Błędna data systemowa: Rok mniejszy niż %d\n"
TRANSLATE "Unknown debug flag '%c'\n" "Nieznana flaga odpluskwiania '%c'\n"
TRANSLATE "Unknown option '%c'\n" "Nieznana opcja '%c'\n"
TRANSLATE "Unknown user '%s'\n" "Nieznany użytkownik '%s'\n"
TRANSLATE "Could not change gid to %d\n" "Nie mogę zmienić gid na %d\n"
TRANSLATE "Could not change uid to %d\n" "Nie mogę zmienić uid na %d\n"
TRANSLATE "Out of memory for environment\n" "Brak pamięci na zmienne środowiska\n"
TRANSLATE "Illegal system date: Year is less than %d" "Błędna data systemowa: Rok mniejszy niż %d"
TRANSLATE "Unknown debug flag '%c'" "Nieznana flaga odpluskwiania '%c'"
TRANSLATE "Unknown option '%c'" "Nieznana opcja '%c'"
TRANSLATE "Unknown user '%s'" "Nieznany użytkownik '%s'"
TRANSLATE "Could not change gid to %d" "Nie mogę zmienić gid na %d"
TRANSLATE "Could not change uid to %d" "Nie mogę zmienić uid na %d"
TRANSLATE "Out of memory for environment" "Brak pamięci na zmienne środowiska"
TRANSLATE "Missing '=' sign" "Brak znaku '='"
TRANSLATE "Missing variable name" "Brak nazwy zmiennej"
TRANSLATE "Missing expression" "Brak wyrażenia"
TRANSLATE "Remind: '-i' option: %s\n" "Remind: '-i' option: %s\n"
TRANSLATE "Remind: '-i' option: %s" "Remind: '-i' option: %s"
TRANSLATE "No reminders." "Brak przypomnień."
TRANSLATE "%d reminder(s) queued for later today.\n" "%d Przypomnienia zakolejkowane na później.\n"
TRANSLATE "%d reminder(s) queued for later today." "%d Przypomnienia zakolejkowane na później."
TRANSLATE "Expecting number" "Spodziewana liczba"
TRANSLATE "Undefined WARN function" "Nielegalna funkcja w klauzuli WARN:"
TRANSLATE "Can't convert between time zones" "Nie można konwertować między strefami czasowymi"

View File

@@ -145,19 +145,19 @@ TRANSLATE "Leaving UserFN" "Saindo UserFN"
TRANSLATE "Expired" "Expirou"
TRANSLATE "fork() failed - can't do queued reminders" "fork() falhou - Nao posso processar compromissos na fila"
TRANSLATE "Can't access file" "Nao consigo acessar o arquivo"
TRANSLATE "Illegal system date: Year is less than %d\n" "Data do sistema ilegal: Ano e menor que %d\n"
TRANSLATE "Unknown debug flag '%c'\n" "Flag de debug desconhecido '%c'\n"
TRANSLATE "Unknown option '%c'\n" "Opcao desconhecida '%c'\n"
TRANSLATE "Unknown user '%s'\n" "Usuario desconhecido '%s'\n"
TRANSLATE "Could not change gid to %d\n" "Nao consigo mudar gid para %d\n"
TRANSLATE "Could not change uid to %d\n" "Nao consigo mudar uid para %d\n"
TRANSLATE "Out of memory for environment\n" "Sem memoria para o environment\n"
TRANSLATE "Illegal system date: Year is less than %d" "Data do sistema ilegal: Ano e menor que %d"
TRANSLATE "Unknown debug flag '%c'" "Flag de debug desconhecido '%c'"
TRANSLATE "Unknown option '%c'" "Opcao desconhecida '%c'"
TRANSLATE "Unknown user '%s'" "Usuario desconhecido '%s'"
TRANSLATE "Could not change gid to %d" "Nao consigo mudar gid para %d"
TRANSLATE "Could not change uid to %d" "Nao consigo mudar uid para %d"
TRANSLATE "Out of memory for environment" "Sem memoria para o environment"
TRANSLATE "Missing '=' sign" "Falta o sinal de '='"
TRANSLATE "Missing variable name" "Falta o nome da variavel"
TRANSLATE "Missing expression" "Falta a expressao"
TRANSLATE "Remind: '-i' option: %s\n" "Remind: '-i' opcao: %s\n"
TRANSLATE "Remind: '-i' option: %s" "Remind: '-i' opcao: %s"
TRANSLATE "No reminders." "Sem compromissos."
TRANSLATE "%d reminder(s) queued for later today.\n" "%d compromisso(s) colocados na fila para mais tarde.\n"
TRANSLATE "%d reminder(s) queued for later today." "%d compromisso(s) colocados na fila para mais tarde."
TRANSLATE "Expecting number" "Esperando numero"
TRANSLATE "Undefined WARN function" "Funcao ilegal na clausula WARN"
TRANSLATE "Can't convert between time zones" "Não consigo converter entre fusos horários"

View File

@@ -2,13 +2,13 @@
# SPDX-License-Identifier: GPL-2.0-only
IF $CalMode || $PsCal
REM [moondate(0)] SPECIAL MOON 0 -1 -1 [moontime(0)]
REM [moondate(1)] SPECIAL MOON 1 -1 -1 [moontime(1)]
REM [moondate(2)] SPECIAL MOON 2 -1 -1 [moontime(2)]
REM [moondate(3)] SPECIAL MOON 3 -1 -1 [moontime(3)]
REM [moondate(0)] INFO "Url: https://en.wikipedia.org/wiki/New_moon" SPECIAL MOON 0 -1 -1 [moontime(0)]
REM [moondate(1)] INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" SPECIAL MOON 1 -1 -1 [moontime(1)]
REM [moondate(2)] INFO "Url: https://en.wikipedia.org/wiki/Full_moon" SPECIAL MOON 2 -1 -1 [moontime(2)]
REM [moondate(3)] INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" SPECIAL MOON 3 -1 -1 [moontime(3)]
ELSE
REM NOQUEUE [moondatetime(0)] MSG %(New Moon) (%2)
REM NOQUEUE [moondatetime(1)] MSG %(First Quarter) (%2)
REM NOQUEUE [moondatetime(2)] MSG %(Full Moon) (%2)
REM NOQUEUE [moondatetime(3)] MSG %(Last Quarter) (%2)
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/New_moon" [moondatetime(0)] MSG %(New Moon) (%2)
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" [moondatetime(1)] MSG %(First Quarter) (%2)
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/Full_moon" [moondatetime(2)] MSG %(Full Moon) (%2)
REM NOQUEUE INFO "Url: https://en.wikipedia.org/wiki/Lunar_phase" [moondatetime(3)] MSG %(Last Quarter) (%2)
ENDIF

View File

@@ -3,14 +3,14 @@
IF $LatDeg >= 0
# Northern Hemisphere
REM NOQUEUE [soleq(0)] MSG %"%(Vernal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(1)] MSG %"%(Summer Solstice)%" [$Is] %3.
REM NOQUEUE [soleq(2)] MSG %"%(Autumnal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(3)] MSG %"%(Winter Solstice)%" [$Is] %3.
REM NOQUEUE [soleq(0)] INFO "Url: https://en.wikipedia.org/wiki/March_equinox" MSG %"%(Vernal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(1)] INFO "Url: https://en.wikipedia.org/wiki/Summer_solstice" MSG %"%(Summer Solstice)%" [$Is] %3.
REM NOQUEUE [soleq(2)] INFO "Url: https://en.wikipedia.org/wiki/September_equinox" MSG %"%(Autumnal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(3)] INFO "Url: https://en.wikipedia.org/wiki/Winter_solstice" MSG %"%(Winter Solstice)%" [$Is] %3.
ELSE
# Southern Hemisphere
REM NOQUEUE [soleq(0)] MSG %"%(Autumnal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(1)] MSG %"%(Winter Solstice)%" [$Is] %3.
REM NOQUEUE [soleq(2)] MSG %"%(Vernal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(3)] MSG %"%(Summer Solstice)%" [$Is] %3.
REM NOQUEUE [soleq(0)] INFO "Url: https://en.wikipedia.org/wiki/March_equinox" MSG %"%(Autumnal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(1)] INFO "Url: https://en.wikipedia.org/wiki/Winter_solstice" MSG %"%(Winter Solstice)%" [$Is] %3.
REM NOQUEUE [soleq(2)] INFO "Url: https://en.wikipedia.org/wiki/September_equinox" MSG %"%(Vernal Equinox)%" [$Is] %3.
REM NOQUEUE [soleq(3)] INFO "Url: https://en.wikipedia.org/wiki/Summer_solstice" MSG %"%(Summer Solstice)%" [$Is] %3.
ENDIF

View File

@@ -492,6 +492,13 @@ The filename in which the reminder was found.
.B lineno \fIn\fR
The line number within the file on which the reminder was found.
.TP
.B lineno_start \fIn\fR
If a reminder spans multiple lines because of backslash
line-continuation, then the \fBlineno\fR entry is the \fIlast\fR line
of the reminder; the \fBlineno_start\fR entry is the \fIfirst\fR line. If
a reminder does \fInot\fR span multiple lines, then only the \fBlineno\fR
entry is present; the \fBlineno_start\fR entry is absent in that case.
.TP
.B nonconst_expr 1
If the reminder contained a non-constant expression that had to be evaluated
to determine the trigger date, this key will be present with the value 1.
@@ -516,6 +523,29 @@ MOON, etc.)
If any TAG clauses are present, the \fBtags\fR key will be present and consist
of a comma-separated list of tags.
.TP
.B info \fR{ \fIhash\fR }
If any INFO clauses are present, the \fBinfo\fR key will be present. Its
value will be a hash of info key-value pairs. Each key is the header
from an INFO string, \fIconverted to all lower-case\fR. The value is the
value from the INFO string.
.RS
.PP
For example, the following REM command:
.PP
.nf
REM INFO "Location: Boardroom" INFO "Summary: None" MSG whatever
.fi
.PP
will produce the following \fBinfo\fR hash:
.PP
.nf
"info" : {
"location" : "Boardroom",
"summary" : "None"
},
.fi
.RE
.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.

View File

@@ -184,14 +184,14 @@ by the reminder's \fIdelta\fR.
.TP
.B \-p\fR[\fBa\fR][\fBp\fR][\fBp\fR][\fBq\fR][+]\fIn\fR
The \fB\-p\fR option is very similar to the \fB\-s\fR option, except
that the output contains additional information for use by a back-end such as the
\fBRem2PS\fR program, which creates a PostScript calendar, and various
other back-end programs. If \fIn\fR starts with "+", then it specifies
a number of weeks rather than a number of months, and back-ends are expected
to produce weekly calendars. Note that not all back-ends support
weekly calendars; currently, only \fBrem2pdf\fR does. Specifying a weekly
calendar implicitly enables the pure JSON interchange format, similar
to \fB\-ppp\fR.
that the output contains additional information for use by a back-end
such as the \fBRem2PS\fR program, which creates a PostScript calendar,
and various other back-end programs. If \fIn\fR starts with "+", then
it specifies a number of weeks rather than a number of months, and
back-ends are expected to produce weekly calendars. Note that not all
back-ends support weekly calendars; currently, only \fBrem2pdf\fR and
\fBrem2html\fR do. Specifying a weekly calendar implicitly enables
the pure JSON interchange format, similar to \fB\-ppp\fR.
.RS
.PP
The format of the \fB\-p\fR output is described in the \fBrem2ps(1)\fR
@@ -207,6 +207,10 @@ format, again documented in \fBrem2ps(1)\fR. If you include a \fBq\fR
letter with this option, then the normal calendar-mode substitution filter
is disabled and the %"...%" sequences are preserved in the output.
.PP
Note that to pass INFO strings to a back-end, you must use \fB\-pp\fR
or \fB\-ppp\fR. The simpler \fB\-p\fR format is not capable of
transmitting the INFO strings to the back-end.
.PP
The \fB\-p\fR, \fB\-pp\fR and \fB\-ppp\fR options implicitly enable
the \fB\-o\fR option.
.PP
@@ -318,6 +322,10 @@ tree. This is unlikely to be useful unless you are working on
.TP
.B h
Dump hash-table statistics on exit.
.TP
.B q
Output a TRANSLATE command each time the \fB_()\fR built-in function
is called or the \fB%(...)\fR substitution sequence is encountered.
.RE
.TP
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
@@ -478,11 +486,13 @@ The \fB\-\-version\fR option causes \fBRemind\fR to print its version number
to standard output and then exit.
.TP
.B \-\-print-errs
The \fB\-\-print-errs\fR option causes \fBRemind\fR to print all possible
error messages to standard output and then exit. The messages are printed
in a format suitable for the first argument of a TRANSLATE command. If
you TRANSLATE the error messages, then \fBRemind\fR will use the translated
versions when outputting error and warning messages.
The \fB\-\-print-errs\fR option causes \fBRemind\fR to print all
possible error messages to standard output and then exit. The
messages are printed in a format suitable for the first argument of a
TRANSLATE command. If you TRANSLATE the error messages, then
\fBRemind\fR will use the translated versions when outputting error
and warning messages. See also TRANSLATE GENERATE in the section
"THE TRANSLATION TABLE".
.RS
.PP
Note that if an untranslated message contains printf-style formatting
@@ -524,11 +534,16 @@ resources. Note that the limit \fIn\fR is approximate and
\fBRemind\fR might execute for one or two more seconds before it is
killed. If \fIn\fR is specified as zero, then no limit is applied, just
as if the option had not been used at all.
.RS
.PP
If a limit is applied, it applies only to the foreground run of \fBRemind\fR.
If \fBRemind\fR finishes processing the script and then starts handling
queued reminders, the time limit is reset to no limit.
.PP
.RE
.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.
.SH REMINDER FILES
.PP
\fBRemind\fR uses scripts to control its operation. You can use any
@@ -595,8 +610,9 @@ Its syntax is:
[\fBSCANFROM\fR \fIscan_date\fR | \fBFROM\fR \fIstart_date\fR]
[\fBDURATION\fR \fIduration\fR]
[\fBTAG\fR \fItag\fR]
<\fBMSG\fR | \fBMSF\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR |
\fBSPECIAL\fR \fIspecial\fR | \fBPS\fR | \fBPSFILE\fR>
[\fBINFO\fR "\fIinfo_string\fR"]
\fBMSG\fR | \fBMSF\fR | \fBRUN\fR | \fBCAL\fR | \fBSATISFY\fR |
\fBSPECIAL\fR \fIspecial\fR | \fBPS\fR | \fBPSFILE\fR
.I body
.RE
.PP
@@ -1332,7 +1348,7 @@ However, discussion must be deferred until after
expressions and user-defined functions are explained. See the subsection
"PRECISE SCHEDULING" further on.
.PP
.B TAG AND DURATION
.B TAG, INFO AND DURATION
.PP
The \fBTAG\fR keyword lets you "tag" certain reminders. This facility
is used by certain back-ends or systems built around \fBRemind\fR,
@@ -1351,15 +1367,40 @@ by the hexadecimal representation of the MD5 sum of the REM
command line. This lets you give a more-or-less unique identifier
to each distinct REM command.
.PP
The \fBINFO\fR keyword is similar to \fBTAG\fR but is intended to convey
metadata about an event, such as its location. Back-ends will have their
own rules about which \fIinfo_string\fRs they recognize, and must ignore
\fIinfo_string\fRs they don't recognize. Note that \fBINFO\fR must
be followed by a quoted string; you can include newlines in the string
by supplying them as "\\n".
.PP
An INFO string \fImust\fR be of the form "Header: Value". The header
can consist of any printable character, but cannot contain whitespace.
The value can consist of any characters you like. Space may not
appear before the colon, but can appear afterwards; such space is not
considered to be part of the value. If there is more than one INFO
string for a given reminder, there cannot be any duplicate headers.
Case is ignored when determining if a header is a duplicate of an
existing one.
.PP
For example, a hypothetical back-end might let you set the location
and description of a reminder like this:
.PP
.nf
REM 26 Feb 2025 INFO "Location: Boardroom #2" \\
INFO "Description: Go over latest pull requests" \\
MSG Engineering meeting
.fi
.PP
The \fBDURATION\fR keyword makes sense only for timed reminders; it
specifies the duration of an event. For example, if you have a
90-minute meeting starting at 1:00pm, you could use any of the following:
.PP
.nf
REM 5 March 2021 AT 13:00 DURATION 1:30 MSG Meeting
REM 5 March 2021 AT 13:00 DURATION 90 MSG Meeting
REM 5 March 2021 AT 1:00pm DURATION 1:30 MSG Meeting
REM 5 March 2021 AT 1:00pm DURATION 90 MSG Meeting
REM 5 March 2021 AT 13:00 DURATION 1:30 MSG Meeting
REM 5 March 2021 AT 13:00 DURATION 90 MSG Meeting
REM 5 March 2021 AT 1:00pm DURATION 1:30 MSG Meeting
REM 5 March 2021 AT 1:00pm DURATION 90 MSG Meeting
.fi
.PP
For long-duration reminders, it is convenient to use expressions
@@ -1389,26 +1430,26 @@ The REM command has syntactic sugar to let you express common
reminders. The following pairs of reminders are equivalent:
.PP
.nf
REM First Monday April MSG Foo
REM Mon 1 April MSG Foo
REM First Monday April MSG Foo
REM Mon 1 April MSG Foo
REM Second Monday May MSG Bar
REM Mon 8 May MSG Bar
REM Second Monday May MSG Bar
REM Mon 8 May MSG Bar
REM Third Monday MSG Third Monday of every month
REM Mon 15 MSG Third Monday of every month
REM Third Monday MSG Third Monday of every month
REM Mon 15 MSG Third Monday of every month
REM Fourth Sunday June 2025 MSG Fourth Sunday in June 2025
REM Sun 22 June 2025 MSG Fourth Sunday in June 2025
REM Fourth Sunday June 2025 MSG Fourth Sunday in June 2025
REM Sun 22 June 2025 MSG Fourth Sunday in June 2025
REM Last Monday MSG Last Monday of every month
REM Mon 1 --7 MSG Last Monday of every month
REM Last Monday MSG Last Monday of every month
REM Mon 1 --7 MSG Last Monday of every month
REM Last Monday April MSG Last Monday of every April
REM Mon 1 May --7 MSG Last Monday of every April
REM Last Monday April MSG Last Monday of every April
REM Mon 1 May --7 MSG Last Monday of every April
REM Last Monday December 2025 MSG Last Monday of Dec 2025
REM Monday 1 Jan 2026 --7 MSG Last Monday of Dec 2025
REM Last Monday December 2025 MSG Last Monday of Dec 2025
REM Monday 1 Jan 2026 --7 MSG Last Monday of Dec 2025
.fi
.PP
Note that \fBLast\fR effectively adjusts the month and year, if necessary, to
@@ -1417,35 +1458,35 @@ make the reminder trigger on the correct date.
The keyword \fBIN\fR is completely ignored, so you can write (for example):
.PP
.nf
REM Second Monday in May MSG foo
REM Last Monday in December 2025 MSG Bar
REM Second Monday in May MSG foo
REM Last Monday in December 2025 MSG Bar
.fi
.PP
An alternate form of \fIback\fR makes writing reminders easier.
The following groups of reminders are equivalent:
.PP
.nf
REM ~~1 MSG Last day of every month
REM Lastday MSG Last day of every month
REM 1 --1 MSG Last day of every month
REM ~~1 MSG Last day of every month
REM Lastday MSG Last day of every month
REM 1 --1 MSG Last day of every month
REM May ~~1 MSG Last day of May
REM Lastday May MSG Last day of May
REM 1 June --1 MSG Last day of May
REM May ~~1 MSG Last day of May
REM Lastday May MSG Last day of May
REM 1 June --1 MSG Last day of May
REM Dec 2025 ~~1 MSG Last day of December 2025
REM Lastday Dec 2025 MSG Last day of December 2025
REM 1 Jan 2026 --1 MSG Last day of December 2025
REM Dec 2025 ~~1 MSG Last day of December 2025
REM Lastday Dec 2025 MSG Last day of December 2025
REM 1 Jan 2026 --1 MSG Last day of December 2025
REM Apr ~1 OMIT SAT SUN MSG Last workday of April
REM Lastworkday April OMIT SAT SUN MSG Last workday of April
REM 1 May -1 OMIT SAT SUN MSG Last workday of April
REM Apr ~1 OMIT SAT SUN MSG Last workday of April
REM Lastworkday April OMIT SAT SUN MSG Last workday of April
REM 1 May -1 OMIT SAT SUN MSG Last workday of April
REM Apr ~~7 MSG Seventh-last day of April
REM 1 May --7 MSG Seventh-last day of April
REM Apr ~~7 MSG Seventh-last day of April
REM 1 May --7 MSG Seventh-last day of April
REM Apr ~2 OMIT SAT SUN MSG Second-last workday of April
REM 1 May -2 OMIT SAT SUN MSG Second-last workday of April
REM Apr ~2 OMIT SAT SUN MSG Second-last workday of April
REM 1 May -2 OMIT SAT SUN MSG Second-last workday of April
.fi
.PP
As we see, "Lastday" is equivalent to ~~1 and "Lastworkday" to ~1.
@@ -1456,9 +1497,9 @@ be combined with a day. Additionally, First/Second/Third/Fourth/Last
must have at least one weekday name. The following are illegal:
.PP
.nf
REM First Monday 3 June MSG Huh?
REM April 3 ~~1 MSG What?
REM Second June MSG Where's the weekday???
REM First Monday 3 June MSG Huh?
REM April 3 ~~1 MSG What?
REM Second June MSG Where's the weekday???
.fi
.PP
.SH THE SUBSTITUTION FILTER
@@ -1624,6 +1665,11 @@ is replaced with "\fIyy\fR", the last two digits of the year.
is replaced with the lookup of \fIany_text\fR in the translation table.
It is the equivalent of [_("any_text")] but is more convenient to type.
.TP
.B %<\fIany_text\fR\fB>
is replaced with the INFO value associated with the header \fIany_text\fR
or the empty string if no such INFO value exists. It is the
equivalent of [triginfo("any_text")] but is more convenient to type.
.TP
.B %_
(percent-underscore) is replaced with a newline. You can use this to
achieve multi-line reminders. Note that calendar back-ends vary in
@@ -2217,6 +2263,10 @@ The following examples illustrate constants in \fBRemind\fR expressions:
Note that the empty string is represented by "". Remind supports
the escape sequences "\\a", "\\b", "\\f", "\\n", "\\r", "\\t"
and "\\v" which have the same meanings as their counterparts in C.
It also supports "\\xAB" where A and B are hexadecimal digits;
this operates just as in C. The "\\x" must be followed by one or
two hex digits; the escape sequence "\\x00" is not permitted.
.PP
To include a quote in a string, use "\\"". Any other character
preceded by a backslash is inserted into the string as-is, but the
backslash itself is removed. To include a backslash in a string,
@@ -3385,6 +3435,25 @@ Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox
Easter date, see \fBorthodoxeaster\fR.
.RE
.TP
.B escape(s_string [,i_add_quotes])
Returns a \fBSTRING\fR that is the same as the input string, but with
all special characters backslashed-escaped. For example, the following
command:
.RS
.PP
.nf
set a escape("foo" + char(10) + "bar")
.fi
.PP
will set a to "foo\\nbar" where "\\n" is the literal sequence "\\" followed
by "n". This is useful if you want to compute a string in a pasted-in
expression that \fBRemind\fR will then parse as a quoted string again,
such as in a TRANSLATE command or an INFO clause.
.PP
If the optional \fIadd_quotes\fR argument is supplied and is non-zero, then
the return value from \fBescape\fR will include surrounding double-quotes.
.RE
.TP
.B evaltrig(s_trigger [,dq_start])
Evaluates \fItrigger\fR as if it were a REM or IFTRIG trigger specification
and returns the trigger date as a \fBDATE\fR (or as a \fBDATETIME\fR if
@@ -3679,18 +3748,18 @@ In general, \fBmultitrig\fR works better with the Remind algorithm than
reminder is issued at the end of each quarter:
.PP
.nf
REM [multitrig("Mar 31", "Jun 30", "Sep 30", "Dec 31")] +7 MSG \\
%"End of [ord($Tm/3)] quarter%" is %b.
REM [multitrig("Mar 31", "Jun 30", "Sep 30", "Dec 31")] +7 MSG \\
%"End of [ord($Tm/3)] quarter%" is %b.
.fi
.PP
If you want the last working day of each quarter, you could use:
.PP
.nf
PUSH-OMIT-CONTEXT
OMIT Sat Sun
REM [multitrig("Mar ~1", "Jun ~1", "Sep ~1", "Dec ~1")] +7 MSG \\
%"Last working day of [ord($Tm/3)] quarter%" is %b.
POP-OMIT-CONTEXT
PUSH-OMIT-CONTEXT
OMIT Sat Sun
REM [multitrig("Mar ~1", "Jun ~1", "Sep ~1", "Dec ~1")] +7 MSG \\
%"Last working day of [ord($Tm/3)] quarter%" is %b.
POP-OMIT-CONTEXT
.fi
.PP
Note that unlike \fBevaltrig\fR, \fBmultitrig\fR always returns a \fBDATE\fR
@@ -4232,7 +4301,23 @@ whose value is the maximum of "yyyy-mm-dd" and today.
.B trigfrom()
Returns (as a \fBDATE\fR type) the \fBFROM\fR parameter of the last \fBREM\fR or
\fBIFTRIG\fR command. If there was no \fBFROM\fR parameter, returns the integer -1.
.TP
.B triginfo(s_header)
Returns a \fBSTRING\fR that is the INFO item associated with the header
\fIheader\fR. The header should \fInot\fR contain a colon. Header name
comparisons are case-insensitive.
.RS
.PP
For example, the following will assign "At home" to the variable a and
the empty string to variable b:
.PP
.nf
REM INFO "Location: At home" MSG test
SET a triginfo("location")
SET b triginfo("no_such_header")
.fi
.PP
.RE
.TP
.B trigger(d_date [,t_time [,i_utcflag]]) \fRor\fB trigger(q_datetime [,i_utcflag])
Returns a string suitable for use in a \fBREM\fR command or a
@@ -5754,13 +5839,48 @@ function, as follows:
By using \fBTRANSLATE\fR and \fB_\fR judiciously, you can make your
reminder files easy to translate.
.PP
\fBTRANSLATE\fR has three additional forms: If it is followed
\fBTRANSLATE\fR has four additional forms: If it is followed
by \fIone\fR quoted string instead of two, then \fBRemind\fR deletes the
translation table entry for that string. If it is followed by
the keyword \fBDUMP\fR, then \fBRemind\fR dumps all translation table entries
to standard output. And if it is followed by \fBCLEAR\fR, then
\fBRemind\fR deletes all of the translation table entries.
.PP
The fourth form, \fBTRANSLATE GENERATE\fR, dumps all of the
strings that can be localized (as a series of TRANSLATE commands) to
standard output. Strings that are already localized are output
with their localization; strings that are not localized are
output as:
.PP
.nf
TRANSLATE "untranslated" ""
.nf
.PP
If you want to add a new language, you can obtain a skeleton translation
file by running:
.PP
.nf
echo "TRANSLATE GENERATE" | remind -h - > /tmp/skeleton.rem
.fi
.PP
If you have an existing language file that is missing some translations,
you can update it by running:
.PP
.nf
(echo INCLUDE mylang.rem; echo TRANSLATE GENERATE) | \\
remind -h - > /tmp/mylang-update.rem
.fi
.PP
and then editing \fBmylang-update.rem\fR to add in the missing translations.
.PP
If you have some reminder scripts that use the \fB_()\fR built-in function
or \fB%(...)\fR substitution sequence, you can generate a list of needed
TRANSLATE commands by running:
.PP
.nf
remind -q -n -dq myscript.rem 2>&1 | grep ^TRANSLATE | sort | uniq
.fi
.PP
Note that if you \fBSET\fR various translation-related system
variables such as \fB$Monday\fR, \fB$December\fR, \fB$Ago\fR, etc,
then \fBRemind\fR \fIalso\fR makes a corresponding translation

View File

@@ -102,7 +102,11 @@ You can also specify advance notice, possibly repeating.
The sixth control specifies what \fBRemind\fR should do if a reminder
falls on a holiday or weekend.
Enter the body of the reminder into the \fBBody:\fR text entry.
Enter the body of the reminder into the \fBSummary:\fR text entry. If
you want, you can enter a location, a URL, and and longer description
in the \fBLocation:\fR, \fBURL:\fR and \fBDescription:\fR boxes. If
you enter anything here, they will be added as \fBINFO\fR items to the
reminder.
To add the reminder to the reminder file, click \fBAdd to reminder file\fR.
To close the dialog without adding the reminder to the file, click
@@ -178,6 +182,15 @@ is editable with an editor but was not created using \fBTkRemind\fR,
it will be underlined when you move the cursor over it, and
you can edit it in a text editor by either left- or right-clicking
on the reminder.
.PP
If a reminder has location or description \fBINFO\fR items, then
hovering over the reminder will pop up a window containing the
location and/or description information.
.SH HYPERLINKS
If a reminder has a "Url:" INFO string set, then middle-clicking
will open the URL using \fBxdg-open\fR.
.SH ERRORS

View File

@@ -128,6 +128,13 @@ The standard SPECIALs supported by all back-ends
=back
=head1 INFO STRINGS SUPPORTED
Rem2html supports one INFO string, namely C<Url>. Its value
should be a URL. If the C<Url> INFO string is supplied for
a normal reminder, a COLOR special, a MOON special or a WEEK
special, the corresponding output is turned into a hyperlink.
=head1 HIGHLIGHTING TODAY
Older versions of rem2html used to highlight today's date with a red outline.
@@ -359,10 +366,14 @@ sub parse_input
chomp;
last if /^\# rem2ps2? end$/;
next if /^\#/;
my ($y, $m, $d, $special, $tag, $duration, $time, $body);
my ($y, $m, $d, $special, $tag, $duration, $time, $body, $title);
my $url_pre = '';
my $url_post = '';
my $url;
if (m/^(\d*).(\d*).(\d*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/) {
($y, $m, $d, $special, $tag, $duration, $time, $body) =
($1, $2, $3, $4, $5, $6, $7, $8);
$title = '';
} elsif (/\{/) {
my $obj;
if ($Options{utf8}) {
@@ -371,6 +382,11 @@ sub parse_input
$obj = decode_json($_);
}
next unless ($obj->{date} =~ /^(\d+)-(\d+)-(\d+)$/);
if ($obj->{info} && $obj->{info}->{url}) {
$url = $obj->{info}->{url};
$url_pre = "<a href=\"$url\">";
$url_post = "</a>";
}
$y = $1;
$m = $2;
$d = $3;
@@ -379,6 +395,7 @@ sub parse_input
$duration = $obj->{duration} || '*';
$time = $obj->{time} || '*';
$body = $obj->{body};
$title = info_to_title($obj->{info});
} else {
next;
}
@@ -392,7 +409,8 @@ sub parse_input
} elsif ($special eq 'WEEK') {
$body =~ s/^\s+//;
$body =~ s/\s+$//;
$weeks->{$d1} = $body;
$weeks->{$d1}->{body} = $body;
$weeks->{$d1}->{url} = $url;
} elsif ($special eq 'MOON') {
if ($body =~ /(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
my ($phase, $moonsize, $fontsize, $msg) = ($1, $2, $3, $4);
@@ -402,6 +420,7 @@ sub parse_input
$moons->[$d]->{'phase'} = $1;
$moons->[$d]->{'msg'} = '';
}
$moons->[$d]->{url} = $url;
} elsif ($special eq 'SHADE') {
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)/) {
$shades->[$d] = sprintf("#%02X%02X%02X",
@@ -415,10 +434,10 @@ sub parse_input
my($r, $g, $b, $text) = ($1, $2, $3, $4);
my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
$r % 256, $g % 256, $b % 256);
push(@{$days->[$d]}, "<p$class $color>" . fix_whitespace(escape_html($text)) . '</p>');
push(@{$days->[$d]}, "<p$class$title $color>" . $url_pre . fix_whitespace(escape_html($text)) . $url_post . '</p>');
}
} elsif ($special eq '*') {
push(@{$days->[$d]}, "<p$class>" . fix_whitespace(escape_html($body)) . '</p>');
push(@{$days->[$d]}, "<p$class$title>" . $url_pre . fix_whitespace(escape_html($body)) . $url_post . '</p>');
}
}
return $found_data;
@@ -477,6 +496,29 @@ sub emit_ppp_calendars
}
}
sub info_to_title
{
my ($info) = @_;
if (!$info) {
return '';
}
my $done_one = 0;
my $title = '';
foreach my $key ('location', 'description') {
next unless $info->{$key};
if ($done_one) {
$title .= "&#010";
}
$done_one = 1;
my $val = escape_html($info->{$key});
$val =~ s/\n/&#010/g;
$val =~ s/"/&#034;/g;
$val =~ s/</&lt;/g;
$title .= ucfirst($key) . ': ' . $val;
}
return " title=\"$title\"";
}
sub emit_one_ppp_calendar
{
my ($c, $type) = @_;
@@ -537,6 +579,15 @@ sub emit_one_ppp_calendar
my $duration = $obj->{duration} || '*';
my $time = $obj->{time} || '*';
my $body = $obj->{body};
my $title = info_to_title($obj->{info});
my $url_pre = '';
my $url_post = '';
my $url = '';
if ($obj->{info} && $obj->{info}->{url}) {
$url = $obj->{info}->{url};
$url_pre = "<a href=\"$url\">";
$url_post = "</a>";
}
$special = uc($special);
if ($special eq 'HTML') {
push(@{$days->[$col]}, $body);
@@ -545,7 +596,8 @@ sub emit_one_ppp_calendar
} elsif ($special eq 'WEEK') {
$body =~ s/^\s+//;
$body =~ s/\s+$//;
$weeks->{$col} = $body;
$weeks->{$col}->{body} = $body;
$weeks->{$col}->{url} = $url;
} elsif ($special eq 'MOON') {
if ($body =~ /(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
my ($phase, $moonsize, $fontsize, $msg) = ($1, $2, $3, $4);
@@ -555,6 +607,7 @@ sub emit_one_ppp_calendar
$moons->[$col]->{'phase'} = $1;
$moons->[$col]->{'msg'} = '';
}
$moons->[$col]->{url} = $url;
} elsif ($special eq 'SHADE') {
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)/) {
$shades->[$col] = sprintf("#%02X%02X%02X",
@@ -568,10 +621,10 @@ sub emit_one_ppp_calendar
my($r, $g, $b, $text) = ($1, $2, $3, $4);
my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
$r % 256, $g % 256, $b % 256);
push(@{$days->[$col]}, "<p$class $color>" . fix_whitespace(escape_html($text)) . '</p>');
push(@{$days->[$col]}, "<p$class$title $color>" . $url_pre . fix_whitespace(escape_html($text)) . $url_post . '</p>');
}
} elsif ($special eq '*') {
push(@{$days->[$col]}, "<p$class>" . fix_whitespace(escape_html($body)) . '</p>');
push(@{$days->[$col]}, "<p$class$title>" . $url_pre . fix_whitespace(escape_html($body)) . $url_post . '</p>');
}
}
output_calendar($type, $cols_to_date_info);
@@ -846,7 +899,11 @@ sub draw_day_cell
my $shade = $shades->[$day];
my $week = '';
if (exists($weeks->{$day})) {
$week = ' ' . $weeks->{$day};
if ($weeks->{$day}->{url}) {
$week = ' <a href="' . $weeks->{$day}->{url} . '">' . escape_html($weeks->{$day}->{body}) . '</a>';
} else {
$week = ' ' . escape_html($weeks->{$day}->{body});
}
}
my $class;
if ($Options{nostyle}) {
@@ -907,10 +964,16 @@ sub draw_day_cell
$alt = 'last';
$title = escape_html(t('Last Quarter'));
}
my $url_pre = '';
my $url_post = '';
if ($moons->[$day]->{url}) {
$url_pre = '<a href="' . $moons->[$day]->{url} . '">';
$url_post = '</a>';
}
if ($Options{nostyle}) {
print("<div style=\"float: left\"><img border=\"0\" width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
print("<div style=\"float: left\">$url_pre<img border=\"0\" width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">msg$url_post</div>");
} else {
print("<div class=\"rem-moon\"><img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
print("<div class=\"rem-moon\">$url_pre<img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg$url_post</div>");
}
}

View File

@@ -18,6 +18,8 @@ WriteMakefile(
'Getopt::Long' => 0,
'Cairo' => 0,
'Pango' => 0,
'JSON::MaybeXS' => 0,
'Encode' => 0,
},
EXE_FILES => [ 'bin/rem2pdf' ]
);

View File

@@ -7,16 +7,18 @@ bindir=@bindir@
datadir=@datadir@
datarootdir=@datarootdir@
PERL=@PERL@
PERLMODS_NEEDED=Getopt::Long Cairo Pango
PERLMODS_NEEDED=Cairo Encode ExtUtils::MakeMaker Getopt::Long JSON::MaybeXS Pango
all: Makefile
@if test "$(PERL)" = "" ; then \
echo "Not building rem2pdf; Perl is required"; exit 0; fi; \
OK=1; \
for m in $(PERLMODS_NEEDED) ; \
do \
$(PERL) -M$$m -e 1 > /dev/null 2>&1; \
if test $$? != 0 ; then echo "Not building rem2pdf; missing $$m"; exit 0; fi; \
if test $$? != 0 ; then echo "Missing Perl module: $$m"; OK=0; fi; \
done; \
if test "$$OK" != "1" ; then echo "Not building rem2pdf because of missing perl module(s)"; exit 0; fi; \
$(MAKE) all && exit 0; \
exit 1;

View File

@@ -573,6 +573,13 @@ it is syntactically correct. If you use invalid Pango markup, the
Pango library will print a warning and B<rem2pdf> will not render any
output for the invalid reminder.
=head1 INFO STRINGS SUPPORTED
rem2pdf supports one INFO string, namely C<Url>. Its value should be
a URL. If the C<Url> INFO string is supplied for a normal reminder, a
COLOR special, a MOON special, a PANGO special or a WEEK special, the
corresponding output is turned into a hyperlink.
=head1 WEEKLY CALENDARS
B<rem2pdf> will produce weekly calendars if you invoke B<remind> with the

View File

@@ -821,7 +821,6 @@ sub draw_small_calendar
$layout->set_text('88 ');
my ($wid, $h) = $layout->get_pixel_size();
$h += 1;
# Month name
$layout = Pango::Cairo::create_layout($cr);
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
@@ -835,6 +834,7 @@ sub draw_small_calendar
$y += $h;
# Day names
$wid = $width / 7;
for (my $col=0; $col <7; $col++) {
my $j;
if ($self->{mondayfirst}) {
@@ -860,7 +860,11 @@ sub draw_small_calendar
for (my $d=1; $d <= $days; $d++) {
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
$layout->set_font_description($desc);
$layout->set_text($d);
my $dt = $d;
if (length($dt) < 2) {
$dt = ' ' . $dt;
}
$layout->set_text($dt);
$cr->save();
$cr->move_to($x + $col*$wid, $y);
Pango::Cairo::show_layout($cr, $layout);
@@ -892,20 +896,23 @@ sub calculate_small_calendar_font_size
my $font_size = int($scale * 10);
# Check
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
$layout->set_font_description($desc);
$layout->set_text('88 88 88 88 88 88 88');
($wid, $h) = $layout->get_pixel_size();
$h += 1;
$h *= ($rows + 2); # row for month name; row for day names
while(1) {
$desc = Pango::FontDescription->from_string($settings->{small_cal_font} . ' ' . $font_size . 'px');
$layout->set_font_description($desc);
$layout->set_text('88 88 88 88 88 88 88');
($wid, $h) = $layout->get_pixel_size();
$h += 1;
$h *= ($rows + 2); # row for month name; row for day names
$scale = $width / $wid;
if (($height / $h) < $scale) {
$scale = $height / $h;
}
$scale = $width / $wid;
if (($height / $h) < $scale) {
$scale = $height / $h;
}
if ($scale < 1) { # Font size is too big
$font_size--;
if ($scale >= 1) { # Font size is OK
last;
}
$font_size -= 0.1;
}
return $font_size;
}

View File

@@ -75,6 +75,10 @@ sub render
my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings);
my $layout = Pango::Cairo::create_layout($cr);
my $url;
if ($self->{info} && $self->{info}->{url}) {
$url = $self->{info}->{url};
}
$layout->set_width(1024 * ($x2 - $x1 - 2 * $settings->{border_size}));
$layout->set_wrap('word-char');
my $body;
@@ -108,7 +112,13 @@ sub render
$self->{g} / 255,
$self->{b} / 255);
$cr->move_to($x1 + $settings->{border_size}, $so_far);
if ($url) {
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
}
Pango::Cairo::show_layout($cr, $layout);
if ($url) {
$cr->tag_end(Cairo::TAG_LINK);
}
$cr->restore();
}
return $h;
@@ -142,7 +152,18 @@ sub render
$cr->save();
$cr->move_to($x2 - $settings->{border_size}/4 - $wid, $y2 - $settings->{border_size}/4 - $h);
my $url;
if ($self->{info} && $self->{info}->{url}) {
$url = $self->{info}->{url};
}
if ($url) {
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
}
Pango::Cairo::show_layout($cr, $layout);
if ($url) {
$cr->tag_end(Cairo::TAG_LINK);
}
$cr->restore();
return 0;
@@ -204,12 +225,30 @@ sub render
$xc = $x1 + $settings->{border_size} + ($self->{size} / 2);
$yc = $so_far + $settings->{border_size} + ($self->{size} / 2);
}
my $url;
if ($self->{info} && $self->{info}->{url}) {
$url = $self->{info}->{url};
}
if ($url) {
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
}
$self->draw_moon($xc, $yc, $cr);
if ($url) {
$cr->tag_end(Cairo::TAG_LINK);
}
if ($layout) {
$cr->save();
$cr->move_to ($xc + ($self->{size}/2) + $settings->{border_size},
$yc + ($self->{size}/2) - $self->{fontsize} );
if ($url) {
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
}
Pango::Cairo::show_layout($cr, $layout);
if ($url) {
$cr->tag_end(Cairo::TAG_LINK);
}
$cr->restore();
}
}
@@ -284,6 +323,10 @@ sub render
my ($self, $pdf, $cr, $settings, $so_far, $day, $col, $height) = @_;
my ($x1, $y1, $x2, $y2) = $pdf->col_box_coordinates($so_far, $col, $height, $settings);
my $url;
if ($self->{info} && $self->{info}->{url}) {
$url = $self->{info}->{url};
}
my $layout = Pango::Cairo::create_layout($cr);
$layout->set_width(1024 * ($x2 - $x1 - 2 * $settings->{border_size}));
@@ -316,7 +359,13 @@ sub render
} else {
$cr->move_to($x1 + $settings->{border_size}, $so_far);
}
if ($url) {
$cr->tag_begin(Cairo::TAG_LINK, "uri='$url'");
}
Pango::Cairo::show_layout($cr, $layout);
if ($url) {
$cr->tag_end(Cairo::TAG_LINK);
}
$cr->restore();
}
return $h;

View File

@@ -280,6 +280,9 @@ set AppendFile $ReminderFile
# Array of tags -> JSON dicts
array unset TagToObj
# Array of __syn__ tags -> JSON dicts
array unset SynToObj
set SetFontsWorked 0
#---------------- DON'T CHANGE STUFF BELOW HERE ------------------
@@ -582,6 +585,7 @@ proc CreateCalFrame { w dayNames } {
-highlightthickness 0
frame $w.f$f -padx 0 -pady 0 -highlightthickness 0 -relief flat -bd 0
$w.t$f tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$f"
$w.t$f tag bind REM <ButtonPress-2> "OpenUrl $w.t$f"
$w.t$f tag bind REM <ButtonPress-3> "FireEditor $w.t$f"
pack $w.l$f -in $w.f$f -side top -expand 0 -fill x
pack $w.t$f -in $w.f$f -side top -expand 1 -fill both
@@ -632,6 +636,7 @@ proc ConfigureCalFrame { w firstDay numDays } {
$w.t$i tag delete $t
}
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
$w.t$i tag bind REM <ButtonPress-2> "OpenUrl $w.t$i"
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
$w.t$i configure -state disabled -takefocus 0
}
@@ -653,6 +658,7 @@ proc ConfigureCalFrame { w firstDay numDays } {
$w.t$i tag delete $t
}
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
$w.t$i tag bind REM <ButtonPress-2> "OpenUrl $w.t$i"
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
$w.t$i configure -state disabled -takefocus 0
}
@@ -683,6 +689,7 @@ proc ConfigureCalFrame { w firstDay numDays } {
$w.t$i tag delete $t
}
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
$w.t$i tag bind REM <ButtonPress-2> "OpenUrl $w.t$i"
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
$w.t$i configure -state disabled -takefocus 0
}
@@ -798,7 +805,7 @@ proc EditOptions {} {
pack $w.f -side top -expand 1 -fill both
pack $w.b -side top -expand 0 -fill x
label $w.ver -text "TkRemind version @VERSION@ on Tcl/Tk version [info tclversion] with Remind version $ver"
label $w.ver -text "TkRemind version @VERSION@ on Tcl/Tk version [info patchlevel] with Remind version $ver"
pack $w.ver -in $w.f -side top -expand 0 -fill x
# Start iconified
checkbutton $w.startIconified -text "Start up Iconified" \
@@ -1138,9 +1145,10 @@ proc ConfigureCalWindow { month year firstDay numDays } {
proc FillCalWindow {} {
set FileName ""
set LineNo 0
global DayNames CurYear CurMonth MonthNames CommandLine Option TagToObj RemindErrors MondayFirst
global DayNames CurYear CurMonth MonthNames CommandLine Option TagToObj SynToObj RemindErrors MondayFirst
array unset TagToObj
array unset SynToObj
Status "Firing off Remind..."
set_button_to_queue
@@ -1204,7 +1212,12 @@ proc FillCalWindow {} {
set fname [dict get $obj filename]
# Don't make INCLUDECMD output editable
if {![string match "*|" $fname]} {
set fntag [string cat "FILE_" [dict get $obj lineno] "_" $fname]
if {[dict exists $obj lineno_start]} {
set l [dict get $obj lineno_start]
} else {
set l [dict get $obj lineno]
}
set fntag [string cat "FILE_" $l "_" $fname]
}
}
@@ -1230,7 +1243,7 @@ proc FillCalWindow {} {
set day [string trimleft $day 0]
set n [expr $day+$offset]
set month [string trimleft $month 0]
set extratags ""
set extratags {}
switch -nocase -- $type {
"WEEK" {
set stuff [string trimleft $stuff]
@@ -1269,9 +1282,9 @@ proc FillCalWindow {} {
set b 0
}
set color [format "%02X%02X%02X" $r $g $b]
set extratags "clr$color"
lappend extratags "clr$color"
.cal.t$n configure -state normal
.cal.t$n tag configure $extratags -foreground "#$color"
.cal.t$n tag configure "clr$color" -foreground "#$color"
.cal.t$n configure -state disabled -takefocus 0
set stuff $stuff
set type "COLOR"
@@ -1288,18 +1301,27 @@ proc FillCalWindow {} {
set stuff [regsub -all {\n[ \t]} $stuff "\n"]
set stuff [regsub -all {\n+} $stuff "\n"]
if {[regexp {__syn__([0-9a-f]+)} $tag syntag]} {
set SynToObj($syntag) $obj
lappend extratags $syntag
.cal.t$n tag bind $syntag <Enter> [list details_enter .cal.t$n]
.cal.t$n tag bind $syntag <Leave> [list details_leave .cal.t$n]
} else {
set syntag ""
}
if {[regexp {TKTAG([0-9]+)} $tag all tagno] && "$fntag" != "x"} {
.cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag]
.cal.t$n tag bind "TKTAG$tagno" <Enter> "TaggedEnter .cal.t$n"
.cal.t$n tag bind "TKTAG$tagno" <Leave> "TaggedLeave .cal.t$n"
set TagToObj(TKTAG$tagno) $obj
.cal.t$n insert end [string trim $stuff] [concat REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag]
.cal.t$n tag bind "TKTAG$tagno" <Enter> [list TaggedEnter .cal.t$n]
.cal.t$n tag bind "TKTAG$tagno" <Leave> [list TaggedLeave .cal.t$n]
set TagToObj($all) $obj
} else {
if {"$fntag" == "x" } {
.cal.t$n insert end [string trim $stuff] [list REM $extratags]
.cal.t$n insert end [string trim $stuff] [concat REM $extratags]
} else {
.cal.t$n insert end [string trim $stuff] [list REM $extratags $fntag]
.cal.t$n tag bind $fntag <Enter> "EditableEnter .cal.t$n"
.cal.t$n tag bind $fntag <Leave> "EditableLeave .cal.t$n"
.cal.t$n insert end [string trim $stuff] [concat REM $extratags $fntag]
.cal.t$n tag bind $fntag <Enter> [list EditableEnter .cal.t$n]
.cal.t$n tag bind $fntag <Leave> [list EditableLeave .cal.t$n]
.cal.t$n tag bind $fntag <ButtonPress-1> "FireEditor .cal.t$n"
}
}
@@ -1707,8 +1729,9 @@ proc CreateModifyDialog {w day firstDay args} {
frame $w.msg
frame $w.buttons
pack $w.o1 $w.o2 $w.o3 -side top -anchor w -in $w.o
pack $w.o $w.exp $w.adv $w.weekend $w.time $w.durationbox $w.hol $w.msg -side top -anchor w -pady 4 -expand 1 -fill both
pack $w.buttons -side top -anchor w -pady 4 -expand 1 -fill x
pack $w.o $w.exp $w.adv $w.weekend $w.time $w.durationbox $w.hol $w.msg -side top -anchor w -pady 4 -expand 0 -fill both
pack $w.msg -side top -anchor w -pady 4 -padx 4 -expand true -fill both
pack $w.buttons -side top -anchor w -pady 4 -expand 0 -fill x
# TYPE 1 REMINDER
radiobutton $w.type1 -variable OptionType -value 1
@@ -1905,12 +1928,37 @@ proc CreateModifyDialog {w day firstDay args} {
pack $w.labhol $w.issue $w.skip $w.before $w.after -side top -anchor w -in $w.hol
# TEXT ENTRY
label $w.msglab -text "Body:"
label $w.msglab -text "Summary: "
entry $w.entry
balloon_add_help $w.entry "Enter the text of the reminder"
pack $w.msglab -side left -anchor w -in $w.msg
pack $w.entry -side left -anchor w -expand 1 -fill x -in $w.msg
grid $w.msglab -row 0 -column 0 -in $w.msg -sticky e
grid $w.entry -row 0 -column 1 -in $w.msg -sticky nsew
# LOCATION, DESCRIPTION and URL
label $w.loclab -text "Location: "
entry $w.location
balloon_add_help $w.location "Enter the location, if any"
grid $w.loclab -row 1 -column 0 -in $w.msg -sticky e
grid $w.location -row 1 -column 1 -in $w.msg -sticky nsew
label $w.urllab -text "URL: "
entry $w.url
balloon_add_help $w.url "Enter the URL, if any"
grid $w.urllab -row 2 -column 0 -in $w.msg -sticky e
grid $w.url -row 2 -column 1 -in $w.msg -sticky nsew
label $w.desclab -text "Description: "
text $w.description -width 80 -height 5
balloon_add_help $w.description "Enter a detailed description, if any"
grid $w.desclab -row 3 -column 0 -in $w.msg -sticky e
grid $w.description -row 3 -column 1 -in $w.msg -sticky nsew
grid columnconfigure $w.msg 0 -weight 0
grid columnconfigure $w.msg 1 -weight 1
grid rowconfigure $w.msg 0 -weight 0
grid rowconfigure $w.msg 1 -weight 0
grid rowconfigure $w.msg 2 -weight 0
grid rowconfigure $w.msg 3 -weight 1
# BUTTONS
set nbut 0
foreach but $args {
@@ -1988,6 +2036,11 @@ proc OptionsToRemindDialog { w opts } {
set hour $value
}
}
"-txtentry-*" {
set win [string range $flag 10 end]
$w.$win delete 1.0 end
$w.$win insert end $value
}
"-global-*" {
set win [string range $flag 8 end]
set $win $value
@@ -2170,6 +2223,13 @@ proc CenterWindow {w {parent {}}} {
wm deiconify $w
}
#---------------------------------------------------------------------------
# RemQuotedString - return a quoted string with difficult characters escaped
#---------------------------------------------------------------------------
proc RemQuotedString { str } {
set str [string map {"\n" "\\n" "\"" "\\\"" "[" "[\"[\"]"} $str]
return "\"$str\""
}
#---------------------------------------------------------------------------
# CreateReminder -- create the reminder
# Arguments:
@@ -2255,7 +2315,21 @@ proc CreateReminder {w} {
append rem " OMIT [GetWeekend $w 1]"
}
set location [string trim [$w.location get]]
if {$location != ""} {
set location "Location: $location"
append rem " INFO [RemQuotedString $location]"
}
set description [string trim [$w.description get 1.0 end]]
if {$description != ""} {
set description "Description: $description"
append rem " INFO [RemQuotedString $description]"
}
set url [string trim [$w.url get]]
if {$url != ""} {
set url "Url: $url"
append rem " INFO [RemQuotedString $url]"
}
# Check it out!
global Remind
set f [open "|$Remind -arq -e - 2>@1" r+]
@@ -2798,7 +2872,11 @@ proc ShowQueue { queue } {
set fntag ""
catch {
set fname [dict get $q filename]
set lineno [dict get $q lineno]
if {[dict exists $obj lineno_start]} {
set lineno [dict get $obj lineno_start]
} else {
set lineno [dict get $obj lineno]
}
set fntag [string cat "FILE_" $lineno "_" $fname]
}
if { "$fntag" != "" } {
@@ -2916,11 +2994,16 @@ proc DaemonReadable { file } {
set tag [dict get $obj tags]
}
set body [dict get $obj body]
if {[dict exists $obj info]} {
set info [dict get $obj info]
} else {
set info [dict create]
}
set qid "*"
if {[dict exists $obj qid]} {
set qid [dict get $obj qid]
}
IssueBackgroundReminder $body $time $now $tag $qid
IssueBackgroundReminder $body $time $now $tag $qid $info
}
"queue" {
set queue [dict get $obj queue]
@@ -2964,7 +3047,7 @@ proc DaemonReadable { file } {
# Description:
# Reads a background reminder from daemon and pops up window.
#---------------------------------------------------------------------------
proc IssueBackgroundReminder { body time now tag qid } {
proc IssueBackgroundReminder { body time now tag qid info } {
global BgCounter Option Ignore DaemonFile HAVE_SYSNOTIFY NOTIFY_SEND_PATH
if {$Option(Deiconify)} {
wm deiconify .
@@ -2993,7 +3076,7 @@ proc IssueBackgroundReminder { body time now tag qid } {
wm iconname $w "Reminder"
wm title $w "Timed reminder ($time)"
label $w.l -text "Reminder for $time issued at $now"
message $w.msg -width 6i -text $body
message $w.msg -aspect 2000 -text $body -justify left -anchor w -font {-weight bold} -relief groove -bd 2
frame $w.b
# Automatically shut down window after a minute if option says so
@@ -3009,7 +3092,36 @@ proc IssueBackgroundReminder { body time now tag qid } {
button $w.nomore -text "Don't remind me again today" -command [list ClosePopup $w $after_token "" 1 "ignore" $tag $body $time $qid]
}
pack $w.l -side top
pack $w.msg -side top -expand 1 -fill both
pack $w.msg -side top -expand 1 -fill both -anchor w
frame $w.f
pack $w.f -side top -expand 1 -fill both
set row 0
if {[dict exists $info location]} {
label $w.f.l1 -text "Location: " -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight bold}
message $w.f.l2 -text [dict get $info location] -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight normal}
grid $w.f.l1 -row $row -column 0 -sticky nw
grid $w.f.l2 -row $row -column 1 -sticky new
incr row
}
if {[dict exists $info description]} {
label $w.f.m1 -text "Description: " -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight bold}
message $w.f.m2 -text [dict get $info description] -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight normal}
grid $w.f.m1 -row $row -column 0 -sticky nw
grid $w.f.m2 -row $row -column 1 -sticky new
incr row
}
if {[dict exists $info url]} {
set url [dict get $info url]
message $w.f.u -text $url -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -font {-weight normal -underline 0} -fg #0000F0
grid $w.f.u -row $row -column 0 -columnspan 2 -sticky new
bind $w.f.u <Button-1> [list exec xdg-open "$url"]
bind $w.f.u <Button-2> [list exec xdg-open "$url"]
bind $w.f.u <Button-3> [list exec xdg-open "$url"]
bind $w.f.u <Enter> [list $w.f.u configure -font {-weight normal -underline 1}]
bind $w.f.u <Leave> [list $w.f.u configure -font {-weight normal -underline 0}]
balloon_add_help $w.f.u "Click to open $url"
incr row
}
pack $w.b -side top
pack $w.ok -in $w.b -side left
if {$qid != "*"} {
@@ -3443,6 +3555,19 @@ proc ReadTaggedOptions { tag date } {
lappend ans -text-year3 $y
}
}
if {[dict exists $obj info]} {
set info [dict get $obj info]
if {[dict exists $info location]} {
lappend ans -entry-location [dict get $info location]
}
if {[dict exists $info url]} {
lappend ans -entry-url [dict get $info url]
}
if {[dict exists $info description]} {
lappend ans -txtentry-description [dict get $info description]
}
}
return $ans
}
@@ -3573,6 +3698,93 @@ proc EditableLeave { w } {
set tag [lindex $tags $index]
$w tag configure $tag -underline 0
}
proc OpenUrl { w } {
global SynToObj Balloon
set tags [$w tag names current]
set index [lsearch -glob $tags "__syn__*"]
if {$index < 0} {
return
}
set syntag [lindex $tags $index]
if {![info exists SynToObj($syntag)]} {
return
}
set obj $SynToObj($syntag)
if {![dict exists $obj info]} {
return
}
set info [dict get $obj info]
if {![dict exists $info url]} {
return
}
set url [dict get $info url]
exec xdg-open "$url"
}
proc details_enter { w } {
global SynToObj Balloon
set tags [$w tag names current]
set index [lsearch -glob $tags "__syn__*"]
if {$index < 0} {
return
}
set syntag [lindex $tags $index]
if {![info exists SynToObj($syntag)]} {
return
}
set obj $SynToObj($syntag)
set lines {}
if {![dict exists $obj info]} {
return
}
set info [dict get $obj info]
set llen 0
if {[dict exists $info location]} {
lappend lines [list "Location:" [dict get $info location]]
}
if {[dict exists $info description]} {
lappend lines [list "Description:" [dict get $info description]]
}
if {[dict exists $info url]} {
lappend lines [list "URL:" "Middle-click to open [dict get $info url]"]
}
if {[llength $lines] < 1} {
return;
}
balloon_cancel_timer
set Balloon(HelpId) [after $Balloon(HelpTime) [list details_popup $lines]]
}
proc details_leave { w } {
balloon_cancel_timer
catch { destroy .balloonhelp }
}
proc details_popup { pairs } {
global Balloon
set maxwid 80
set h .balloonhelp
set c 0
toplevel $h -bg #000000
frame $h.l -padx 0 -pady 0 -highlightthickness 0 -relief flat -bd 0 -bg #FFFFC0
pack $h.l -side top -padx 1 -pady 1 -ipadx 2 -ipady 1
foreach pair $pairs {
label $h.lab$c -text "[lindex $pair 0] " -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -bg #FFFFC0 -font {-weight bold}
message $h.m$c -text "[lindex $pair 1] " -justify left -anchor w -aspect 2000 -padx 1 -pady 1 -highlightthickness 0 -relief flat -bd 0 -bg #FFFFC0 -font {-weight normal}
grid $h.lab$c -in $h.l -row $c -column 0 -sticky nw
grid $h.m$c -in $h.l -row $c -column 1 -sticky new
incr c
}
wm overrideredirect $h 1
set geom [balloon_calculate_geometry $h]
wm geometry $h $geom
set Balloon(HelpId) [after 10000 "catch { destroy $h }"]
set Balloon(MustLeave) 1
}
#***********************************************************************
# %PROCEDURE: EditTaggedReminder
# %ARGUMENTS:
@@ -4421,7 +4633,7 @@ proc ShowErrors {} {
set l [split $RemindErrors "\n"]
set i 0
foreach line $l {
if {[regexp {^(.*)\(([0-9]+)\)} $line dummy fname lineno]} {
if {[regexp {^(.*)\(([0-9]+)} $line dummy fname lineno]} {
incr i
set fntag [string cat "FILE_" $lineno "_" $fname]
$w.t insert end $line [list ERR "ERR$i" $fntag]

View File

@@ -31,9 +31,11 @@ REMINDSRCS= calendar.c dedupe.c dynbuf.c dorem.c dosubst.c expr.c \
hbcal.c init.c main.c md5.c moon.c omit.c queue.c \
sort.c token.c trans.c trigger.c userfns.c utils.c var.c
XLATSRC= xlat.c
REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h hashtab.h \
md5.h protos.h rem2ps.h types.h version.h
REMINDOBJS= $(REMINDSRCS:.c=.o)
REMINDOBJS= $(REMINDSRCS:.c=.o) $(XLATSRC:.c=.o)
all: remind rem2ps
@@ -43,6 +45,13 @@ test: all
.c.o:
@CC@ -c @CPPFLAGS@ @CFLAGS@ @DEFS@ $(CEXTRA) -DSYSDIR=$(datarootdir)/remind -I. -I$(srcdir) $<
xlat.c: $(REMINDSRCS)
@echo "#include <stddef.h>" > xlat.c
@echo "char const *translatables[] = {" >> xlat.c
@cat $(REMINDSRCS) | grep 'tr(".*")' | sed -e 's/.*tr."/"/' -e 's/").*/"/' | sort | uniq | grep -E -v '^"(am|at|from now|hour|minute|now|on|pm|today|tomorrow|was)"$$' | sed -e 's/^/ /' -e 's/$$/,/' >> xlat.c
@echo " NULL" >> xlat.c
@echo "};" >> xlat.c
$(REMINDOBJS): $(REMINDHDRS)
rem2ps: rem2ps.o dynbuf.o json.o
@@ -80,7 +89,7 @@ install-stripped: install
strip $(DESTDIR)$(bindir)/rem2ps || true
clean:
rm -f *.o *~ core *.bak $(PROGS)
rm -f *.o *~ core *.bak $(PROGS) $(XLATSRC)
clobber:
rm -f *.o *~ remind rem2ps test.out core *.bak

View File

@@ -57,10 +57,12 @@ typedef struct cal_entry {
int duration;
char *filename;
int lineno;
int lineno_start;
Trigger trig;
TimeTrig tt;
int nonconst_expr;
int if_depth;
TrigInfo *infos;
} CalEntry;
/* Line-drawing sequences */
@@ -387,7 +389,13 @@ void PrintJSONChar(char c) {
case '\t': printf("\\t"); break;
case '"': printf("\\\""); break;
case '\\': printf("\\\\"); break;
default: printf("%c", c);
default:
if ((c > 0 && c < 32) || c == 0x7f) {
printf("\\u%04x", (unsigned int) c);
} else {
printf("%c", c);
}
break;
}
}
@@ -402,7 +410,36 @@ void PrintJSONString(char const *s)
case '\t': printf("\\t"); break;
case '"': printf("\\\""); break;
case '\\': printf("\\\\"); break;
default: printf("%c", *s);
default:
if ((*s > 0 && *s < 32) || *s == 0x7f) {
printf("\\u%04x", (unsigned int) *s);
} else {
printf("%c", *s);
}
break;
}
s++;
}
}
void PrintJSONStringLC(char const *s)
{
while (*s) {
switch(*s) {
case '\b': printf("\\b"); break;
case '\f': printf("\\f"); break;
case '\n': printf("\\n"); break;
case '\r': printf("\\r"); break;
case '\t': printf("\\t"); break;
case '"': printf("\\\""); break;
case '\\': printf("\\\\"); break;
default:
if ((*s > 0 && *s < 32) || *s == 0x7f) {
printf("\\u%04x", (unsigned int) *s);
} else {
printf("%c", tolower(*s));
}
break;
}
s++;
}
@@ -499,7 +536,7 @@ get_month_abbrev(char const *mon)
{
static char buf[80];
#ifndef REM_USE_WCHAR
sprintf(buf, "%.3s", mon);
snprintf(buf, sizeof(buf), "%.3s", mon);
return buf;
#else
char *s;
@@ -626,9 +663,9 @@ Colorize256(int r, int g, int b, int bg, int clamp)
}
}
if (bg) {
sprintf(buf, "\x1B[48;5;%dm", best);
snprintf(buf, sizeof(buf), "\x1B[48;5;%dm", best);
} else {
sprintf(buf, "\x1B[38;5;%dm", best);
snprintf(buf, sizeof(buf), "\x1B[38;5;%dm", best);
}
return buf;
}
@@ -641,9 +678,9 @@ ColorizeTrue(int r, int g, int b, int bg, int clamp)
ClampColor(&r, &g, &b);
}
if (bg) {
sprintf(buf, "\x1B[48;2;%d;%d;%dm", r, g, b);
snprintf(buf, sizeof(buf), "\x1B[48;2;%d;%d;%dm", r, g, b);
} else {
sprintf(buf, "\x1B[38;2;%d;%d;%dm", r, g, b);
snprintf(buf, sizeof(buf), "\x1B[38;2;%d;%d;%dm", r, g, b);
}
return buf;
}
@@ -1504,6 +1541,7 @@ static int WriteOneColLine(int col)
free(e->raw_text);
free(e->filename);
if (e->wc_text) free(e->wc_text);
FreeTrigInfoChain(e->infos);
free(e);
return 1;
}
@@ -1590,6 +1628,7 @@ static int WriteOneColLine(int col)
free(e->raw_text);
free(e->filename);
if (e->wc_text) free(e->wc_text);
FreeTrigInfoChain(e->infos);
free(e);
} else {
e->wc_pos = ws;
@@ -1611,6 +1650,7 @@ static int WriteOneColLine(int col)
if (e->wc_text) free(e->wc_text);
#endif
free(e->raw_text);
FreeTrigInfoChain(e->infos);
free(e);
return 1;
}
@@ -1673,6 +1713,7 @@ static int WriteOneColLine(int col)
if (e->wc_text) free(e->wc_text);
#endif
free(e->raw_text);
FreeTrigInfoChain(e->infos);
free(e);
} else {
e->pos = s;
@@ -1812,7 +1853,7 @@ static void WriteCalHeader(void)
int y, m, d;
FromDSE(DSEToday, &y, &m, &d);
sprintf(buf, "%s %d", get_month_name(m), y);
snprintf(buf, sizeof(buf), "%s %d", get_month_name(m), y);
WriteTopCalLine();
@@ -2016,7 +2057,7 @@ static int DoCalRem(ParsePtr p, int col)
trig.typ == MSF_TYPE) {
if (PsCal && is_color) {
char cbuf[24];
sprintf(cbuf, "%d %d %d ", col_r, col_g, col_b);
snprintf(cbuf, sizeof(cbuf), "%d %d %d ", col_r, col_g, col_b);
DBufPuts(&pre_buf, cbuf);
strcpy(trig.passthru, "COLOR");
/* Don't change trig.typ or next if() will trigger! */
@@ -2149,7 +2190,7 @@ static int DoCalRem(ParsePtr p, int col)
if (trig.typ != PASSTHRU_TYPE &&
UserFuncExists("calprefix")==1) {
char evalBuf[64];
sprintf(evalBuf, "calprefix(%d)", trig.priority);
snprintf(evalBuf, sizeof(evalBuf), "calprefix(%d)", trig.priority);
s2 = evalBuf;
r = EvalExpr(&s2, &v, NULL);
if (!r) {
@@ -2192,7 +2233,7 @@ static int DoCalRem(ParsePtr p, int col)
if (trig.typ != PASSTHRU_TYPE &&
UserFuncExists("calsuffix")==1) {
char evalBuf[64];
sprintf(evalBuf, "calsuffix(%d)", trig.priority);
snprintf(evalBuf, sizeof(evalBuf), "calsuffix(%d)", trig.priority);
s2 = evalBuf;
r = EvalExpr(&s2, &v, NULL);
if (!r) {
@@ -2232,6 +2273,7 @@ static int DoCalRem(ParsePtr p, int col)
FreeTrig(&trig);
return E_NO_MEM;
}
e->infos = NULL;
e->nonconst_expr = nonconst_expr;
e->if_depth = NumIfs;
e->trig = trig;
@@ -2252,6 +2294,7 @@ static int DoCalRem(ParsePtr p, int col)
if (!e->text || !e->raw_text) {
if (e->text) free(e->text);
if (e->raw_text) free(e->raw_text);
FreeTrigInfoChain(e->infos);
free(e);
FreeTrig(&trig);
return E_NO_MEM;
@@ -2265,12 +2308,17 @@ static int DoCalRem(ParsePtr p, int col)
AppendTag(&(e->tags), SynthesizeTag());
}
/* Take over any TrigInfo! */
e->infos = trig.infos;
trig.infos = NULL;
/* Don't need tags any more */
FreeTrig(&trig);
e->duration = tim.duration;
e->priority = trig.priority;
e->filename = StrDup(FileName);
if(!e->filename) {
FreeTrigInfoChain(e->infos);
if (e->text) free(e->text);
if (e->raw_text) free(e->raw_text);
#ifdef REM_USE_WCHAR
@@ -2280,6 +2328,7 @@ static int DoCalRem(ParsePtr p, int col)
return E_NO_MEM;
}
e->lineno = LineNo;
e->lineno_start = LineNoStart;
if (trig.typ == PASSTHRU_TYPE || is_color) {
StrnCpy(e->passthru, trig.passthru, PASSTHRU_LEN);
@@ -2342,6 +2391,41 @@ void WriteJSONTimeTrigger(TimeTrig const *tt)
}
}
void
WriteJSONInfoChain(TrigInfo *ti)
{
printf("\"info\":{");
while (ti) {
/* Skanky... */
char *colon = (char *) strchr(ti->info, ':');
char const *value;
if (!colon) {
/* Should be impossible... */
ti = ti->next;
continue;
}
/* Terminate the string at the colon */
*colon = 0;
value = colon+1;
while(*value && isspace(*value)) {
value++;
}
printf("\"");
PrintJSONStringLC(ti->info);
printf("\":\"");
PrintJSONString(value);
printf("\"");
/* Restore the value of the colon */
*colon = ':';
if (ti->next) {
printf(",");
}
ti = ti->next;
}
printf("},");
}
void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
{
/* wd is an array of days from 0=monday to 6=sunday.
@@ -2433,6 +2517,9 @@ void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
PrintJSONKeyPairInt("addomit", 1);
}
if (include_tags) {
if (t->infos) {
WriteJSONInfoChain(t->infos);
}
PrintJSONKeyPairString("tags", DBufValue(&(t->tags)));
}
}
@@ -2443,9 +2530,15 @@ static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
if (DoPrefixLineNo) {
PrintJSONKeyPairString("filename", e->filename);
PrintJSONKeyPairInt("lineno", e->lineno);
if (e->lineno != e->lineno_start) {
PrintJSONKeyPairInt("lineno_start", e->lineno_start);
}
}
PrintJSONKeyPairString("passthru", e->passthru);
PrintJSONKeyPairString("tags", DBufValue(&(e->tags)));
if (e->infos) {
WriteJSONInfoChain(e->infos);
}
if (e->duration != NO_TIME) {
PrintJSONKeyPairInt("duration", e->duration);
}
@@ -2576,6 +2669,7 @@ static void WriteSimpleEntries(int col, int dse)
free(e->text);
free(e->raw_text);
free(e->filename);
FreeTrigInfoChain(e->infos);
#ifdef REM_USE_WCHAR
if (e->wc_text) free(e->wc_text);
#endif
@@ -2736,7 +2830,7 @@ CalendarTime(int tim, int duration)
else hh2 = h2;
if (days) {
sprintf(daybuf, "+%d", days);
snprintf(daybuf, sizeof(daybuf), "+%d", days);
} else {
daybuf[0] = 0;
}
@@ -2759,12 +2853,12 @@ CalendarTime(int tim, int duration)
switch(ScFormat) {
case SC_AMPM:
sprintf(buf, "%d%c%02d%s-%d%c%02d%s%s ",
snprintf(buf, sizeof(buf), "%d%c%02d%s-%d%c%02d%s%s ",
hh, TimeSep, min, ampm1, hh2, TimeSep, min2, ampm2, daybuf);
break;
case SC_MIL:
sprintf(buf, "%02d%c%02d-%02d%c%02d%s ",
snprintf(buf, sizeof(buf), "%02d%c%02d-%02d%c%02d%s ",
h, TimeSep, min, h2, TimeSep, min2, daybuf);
break;
}
@@ -2782,7 +2876,7 @@ CalendarTime(int tim, int duration)
/***************************************************************/
char const *SimpleTime(int tim)
{
static char buf[32];
static char buf[128];
int h, min, hh;
buf[0] = 0;
@@ -2796,7 +2890,7 @@ char const *SimpleTime(int tim)
if (h == 0) hh=12;
else if (h > 12) hh=h-12;
else hh=h;
sprintf(buf, "%d%c%02d%s ", hh, TimeSep, min, (h>=12) ? tr("pm") : tr("am"));
snprintf(buf, sizeof(buf), "%d%c%02d%.64s ", hh, TimeSep, min, (h>=12) ? tr("pm") : tr("am"));
}
break;
@@ -2804,7 +2898,7 @@ char const *SimpleTime(int tim)
if (tim != NO_TIME) {
h = tim / 60;
min = tim % 60;
sprintf(buf, "%02d%c%02d ", h, TimeSep, min);
snprintf(buf, sizeof(buf), "%02d%c%02d ", h, TimeSep, min);
}
break;
}
@@ -2856,7 +2950,7 @@ char const *SynthesizeTag(void)
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char *) CurLine, strlen(CurLine));
MD5Final(buf, &ctx);
sprintf(out, "__syn__%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
snprintf(out, sizeof(out), "__syn__%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
(unsigned int) buf[0], (unsigned int) buf[1],
(unsigned int) buf[2], (unsigned int) buf[3],
(unsigned int) buf[4], (unsigned int) buf[5],

View File

@@ -116,4 +116,4 @@
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
#include <custom.h>
#include "custom.h"

View File

@@ -67,18 +67,18 @@ check_trigger_function(char const *fname, char const *type)
if (!f) {
if (strcmp(type, "WARN")) {
/* Undefined WARN functions are diagnosed elsewhere... */
Wprint("Undefined %s function: `%s'", type, fname);
Wprint(tr("Undefined %s function: `%s'"), type, fname);
}
return;
}
if (f->nargs != 1) {
Wprint("%s function `%s' defined at %s:%d should take 1 argument but actually takes %d", type, fname, f->filename, f->lineno, f->nargs);
Wprint(tr("%s function `%s' defined at %s(%s) should take 1 argument but actually takes %d"), type, fname, f->filename, line_range(f->lineno_start, f->lineno), f->nargs);
return;
}
if (ensure_expr_references_first_local_arg(f->node)) {
return;
}
Wprint("%s function `%s' defined at %s:%d does not use its argument", type, fname, f->filename, f->lineno);
Wprint(tr("%s function `%s' defined at %s(%s) does not use its argument"), type, fname, f->filename, line_range(f->lineno_start, f->lineno));
}
static void
@@ -154,7 +154,7 @@ static void ensure_satnode_mentions_trigdate(expr_node *node)
if (node->type == N_CONSTANT) {
if (node->u.value.type == INT_TYPE) {
if (node->u.value.v.val == 0) {
Wprint("SATISFY: constant 0 will never be true");
Wprint(tr("SATISFY: constant 0 will never be true"));
}
return;
}
@@ -166,14 +166,14 @@ static void ensure_satnode_mentions_trigdate(expr_node *node)
str = node->u.name;
}
if (!*str) {
Wprint("SATISFY: constant \"\" will never be true");
Wprint(tr("SATISFY: constant \"\" will never be true"));
}
return;
}
ensure_satnode_mentions_trigdate_aux(node, &mentioned);
if (!mentioned) {
Wprint("SATISFY: expression has no reference to trigdate() or $T...");
Wprint(tr("SATISFY: expression has no reference to trigdate() or $T..."));
}
}
@@ -408,6 +408,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
tim->duration = NO_TIME;
trig->need_wkday = 0;
trig->adj_for_last = 0;
trig->infos = NULL;
int parsing = 1;
while(parsing) {
@@ -615,7 +616,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
case T_Omit:
DBufFree(&buf);
if (trig->omitfunc[0]) {
Wprint("Warning: OMIT is ignored if you use OMITFUNC");
Wprint(tr("Warning: OMIT is ignored if you use OMITFUNC"));
}
r = ParseLocalOmit(s, trig);
@@ -629,7 +630,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
case T_OmitFunc:
if (trig->localomit) {
Wprint("Warning: OMIT is ignored if you use OMITFUNC");
Wprint(tr("Warning: OMIT is ignored if you use OMITFUNC"));
}
r=ParseToken(s, &buf);
if (r) return r;
@@ -649,6 +650,15 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
DBufFree(&buf);
break;
case T_Info:
r = ParseQuotedString(s, &buf);
if (r != OK) {
return r;
}
r = AppendTrigInfo(trig, DBufValue(&buf));
DBufFree(&buf);
if (r) return r;
break;
case T_Tag:
r = ParseToken(s, &buf);
if (r) return r;
@@ -707,7 +717,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
trig->typ = MSG_TYPE;
if (s->isnested) return E_CANT_NEST_RTYPE;
if (!WarnedAboutImplicit && !SuppressImplicitRemWarnings) {
Wprint("Missing REM type; assuming MSG");
Wprint(tr("Missing REM type; assuming MSG"));
WarnedAboutImplicit = 1;
}
parsing = 0;
@@ -738,22 +748,22 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
if (!s->nonconst_expr) {
if (trig->y != NO_YR && trig->m != NO_MON && trig->d != NO_DAY && trig->until != NO_UNTIL) {
if (DSE(trig->y, trig->m, trig->d) > trig->until) {
Wprint("Warning: UNTIL/THROUGH date earlier than start date");
Wprint(tr("Warning: UNTIL/THROUGH date earlier than start date"));
}
}
if (trig->from != NO_DATE) {
if (trig->until != NO_UNTIL && trig->until < trig->from) {
Wprint("Warning: UNTIL/THROUGH date earlier than FROM date");
Wprint(tr("Warning: UNTIL/THROUGH date earlier than FROM date"));
}
} else if (trig->scanfrom != NO_DATE) {
if (trig->until != NO_UNTIL && trig->until < trig->scanfrom) {
Wprint("Warning: UNTIL/THROUGH date earlier than SCANFROM date");
Wprint(tr("Warning: UNTIL/THROUGH date earlier than SCANFROM date"));
}
}
}
if (trig->y != NO_YR && trig->m != NO_MON && trig->d != NO_DAY && trig->until != NO_UNTIL && trig->rep == NO_REP) {
Wprint("Warning: Useless use of UNTIL with fully-specified date and no *rep");
Wprint(tr("Warning: Useless use of UNTIL with fully-specified date and no *rep"));
}
/* Set scanfrom to default if not set explicitly */
@@ -1180,7 +1190,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
return OK;
}
FromDSE(dse, &y, &m, &d);
sprintf(tmpBuf, "%04d/%02d/%02d ", y, m+1, d);
snprintf(tmpBuf, sizeof(tmpBuf), "%04d/%02d/%02d ", y, m+1, d);
if (DBufPuts(&calRow, tmpBuf) != OK) {
DBufFree(&calRow);
DBufFree(&pre_buf);
@@ -1201,9 +1211,9 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
DBufPuts(&calRow, "* ");
}
if (tim->duration != NO_TIME) {
sprintf(tmpBuf, "%d ", tim->duration);
snprintf(tmpBuf, sizeof(tmpBuf), "%d ", tim->duration);
} else {
sprintf(tmpBuf, "* ");
snprintf(tmpBuf, sizeof(tmpBuf), "* ");
}
if (DBufPuts(&calRow, tmpBuf) != OK) {
DBufFree(&calRow);
@@ -1211,9 +1221,9 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
return E_NO_MEM;
}
if (tim->ttime != NO_TIME) {
sprintf(tmpBuf, "%d ", tim->ttime);
snprintf(tmpBuf, sizeof(tmpBuf), "%d ", tim->ttime);
} else {
sprintf(tmpBuf, "* ");
snprintf(tmpBuf, sizeof(tmpBuf), "* ");
}
if (DBufPuts(&calRow, tmpBuf) != OK) {
DBufFree(&calRow);
@@ -1263,7 +1273,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
/* Don't use msgprefix() on RUN-type reminders */
if (t->typ != RUN_TYPE) {
if (UserFuncExists("msgprefix") == 1) {
sprintf(PrioExpr, "msgprefix(%d)", t->priority);
snprintf(PrioExpr, sizeof(PrioExpr), "msgprefix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v, NULL);
if (!r) {
@@ -1289,7 +1299,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
if (t->typ != RUN_TYPE) {
if (UserFuncExists("msgsuffix") == 1) {
sprintf(PrioExpr, "msgsuffix(%d)", t->priority);
snprintf(PrioExpr, sizeof(PrioExpr), "msgsuffix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v, NULL);
if (!r) {
@@ -1548,8 +1558,8 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
if (DebugFlag & DB_PRTTRIG) {
int y, m, d;
FromDSE(LastTriggerDate, &y, &m, &d);
fprintf(ErrFp, "%s(%d): Trig(satisfied) = %s, %d %s, %d",
FileName, LineNo,
fprintf(ErrFp, "%s(%s): Trig(satisfied) = %s, %d %s, %d",
FileName, line_range(LineNoStart, LineNo),
get_day_name(LastTriggerDate % 7),
d,
get_month_name(m),
@@ -1694,7 +1704,7 @@ static int ShouldTriggerBasedOnWarn(Trigger *t, int dse, int *err)
return (dse == DSEToday);
}
for (i=1; ; i++) {
sprintf(buffer, "%s(%d)", t->warn, i);
snprintf(buffer, sizeof(buffer), "%s(%d)", t->warn, i);
s = buffer;
r = EvalExpr(&s, &v, NULL);
if (r) {

View File

@@ -40,8 +40,8 @@ check_subst_args(UserFunc *f, int n)
if (f->nargs == n) {
return 1;
}
Wprint("Function `%s' defined at %s:%d should take %d argument%s, but actually takes %d",
f->name, f->filename, f->lineno, n, (n == 1 ? "" : "s"), f->nargs);
Wprint(tr("Function `%s' defined at %s(%s) should take %d argument%s, but actually takes %d"),
f->name, f->filename, line_range(f->lineno_start, f->lineno), n, (n == 1 ? "" : "s"), f->nargs);
return 0;
}
/***************************************************************/
@@ -79,6 +79,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int origLen = DBufLen(dbuf);
int altmode;
int r;
int origtime;
Value v;
UserFunc *func;
@@ -87,6 +88,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
if (tt) {
tim = tt->ttime;
}
origtime = tim;
if (tim == NO_TIME) tim = curtime;
tdiff = tim - curtime;
adiff = ABS(tdiff);
@@ -215,6 +217,33 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
if (!c) {
break;
}
if (c == '<') {
DynamicBuffer header;
char const *val;
DBufInit(&header);
while(1) {
c = ParseChar(p, &err, 0);
if (err) {
DBufFree(&header);
return err;
}
if (!c || c == '>') {
break;
}
DBufPutc(&header, c);
}
if (!c) {
Wprint(tr("Warning: Unterminated %%<...> substitution sequence"));
}
err = OK;
val = FindTrigInfo(t, DBufValue(&header));
DBufFree(&header);
if (val) {
SHIP_OUT(val);
}
continue;
}
if (c == '(') {
DynamicBuffer orig;
DynamicBuffer translated;
@@ -232,7 +261,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
DBufPutc(&orig, c);
}
if (!c) {
Wprint("Warning: Unterminated %%(...) substitution sequence");
Wprint(tr("Warning: Unterminated %%(...) substitution sequence"));
}
err = OK;
if (GetTranslatedStringTryingVariants(DBufValue(&orig), &translated)) {
@@ -240,6 +269,9 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else {
err = DBufPuts(dbuf, DBufValue(&orig));
}
if (DebugFlag & DB_TRANSLATE) {
TranslationTemplate(DBufValue(&orig));
}
DBufFree(&orig);
DBufFree(&translated);
if (err) return err;
@@ -275,11 +307,11 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
}
}
if (!c) {
Wprint("Warning: Unterminated %%{...} substitution sequence");
Wprint(tr("Warning: Unterminated %%{...} substitution sequence"));
}
func = FindUserFunc(s);
if (!func) {
Wprint("No substition function `%s' defined", s);
Wprint(tr("No substition function `%s' defined"), s);
continue;
}
@@ -375,6 +407,12 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
Eprint("%s", GetErr(r));
}
}
if (origtime == NO_TIME) {
if ((c >= '0' && c <= '9') || (c == '!')) {
Wprint(tr("`%%%c' substitution sequence should not be used without an AT clause"), c);
}
}
switch(UPPER(c)) {
case 'A':
if (altmode == '*' || !strcmp(tr("on"), "")) {
@@ -752,6 +790,8 @@ int DoSubstFromString(char const *source, DynamicBuffer *dbuf,
if (tim == NO_TIME) tim=MinutesPastMidnight(0);
CreateParser(source, &tempP);
tempP.allownested = 0;
tempTrig.infos = NULL;
DBufInit(&tempTrig.tags);
tempTrig.typ = MSG_TYPE;
tempTime.ttime = tim;

View File

@@ -230,20 +230,20 @@ EXTERN char *ErrMsg[]
/* E_EXPIRED */ "Expired",
/* E_CANTFORK */ "fork() failed - can't do queued reminders",
/* E_CANTACCESS */ "Can't access file",
/* M_BAD_SYS_DATE */ "Illegal system date: Year is less than %d\n",
/* M_BAD_DB_FLAG */ "Unknown debug flag '%c'\n",
/* M_BAD_OPTION */ "Unknown option '%c'\n",
/* M_BAD_USER */ "Unknown user '%s'\n",
/* M_NO_CHG_GID */ "Could not change gid to %d\n",
/* M_NO_CHG_UID */ "Could not change uid to %d\n",
/* M_NOMEM_ENV */ "Out of memory for environment\n",
/* M_BAD_SYS_DATE */ "Illegal system date: Year is less than %d",
/* M_BAD_DB_FLAG */ "Unknown debug flag '%c'",
/* M_BAD_OPTION */ "Unknown option '%c'",
/* M_BAD_USER */ "Unknown user '%s'",
/* M_NO_CHG_GID */ "Could not change gid to %d",
/* M_NO_CHG_UID */ "Could not change uid to %d",
/* M_NOMEM_ENV */ "Out of memory for environment",
/* E_MISS_EQ */ "Missing '=' sign",
/* E_MISS_VAR */ "Missing variable name",
/* E_MISS_EXPR */ "Missing expression",
/* M_CANTSET_ACCESS */ "",
/* M_I_OPTION */ "Remind: '-i' option: %s\n",
/* M_I_OPTION */ "Remind: '-i' option: %s",
/* E_NOREMINDERS */ "No reminders.",
/* M_QUEUED */ "%d reminder(s) queued for later today.\n",
/* M_QUEUED */ "%d reminder(s) queued for later today.",
/* E_EXPECTING_NUMBER */ "Expecting number",
/* M_BAD_WARN_FUNC */ "Undefined WARN function",
/* E_CANT_CONVERT_TZ */ "Can't convert between time zones",

View File

@@ -199,6 +199,46 @@ static UserFunc *CurrentUserFunc = NULL;
/* How many expr_node objects to allocate at a time */
#define ALLOC_CHUNK 64
static char const *
find_end_of_expr(char const *s)
{
char const *e = s;
int in_quoted_string = 0;
int escaped = 0;
while(*e) {
if (in_quoted_string) {
if (escaped) {
escaped = 0;
e++;
continue;
}
if (*e == '\\') {
escaped = 1;
e++;
continue;
}
if (*e == '"') {
in_quoted_string = 0;
e++;
continue;
}
e++;
continue;
}
if (*e == '"') {
in_quoted_string = 1;
e++;
continue;
}
if (*e == ']') {
break;
}
e++;
}
return e;
}
/***************************************************************/
/* */
/* alloc_expr_node - allocate an expr_node object */
@@ -683,7 +723,7 @@ eval_userfunc(expr_node *node, Value *locals, Value *ans, int *nonconst)
FuncRecursionLevel++;
/* Add a call to the call stack for better error messages */
pushed = push_call(f->filename, f->name, f->lineno);
pushed = push_call(f->filename, f->name, f->lineno, f->lineno_start);
DBG(debug_enter_userfunc(node, new_locals, f->nargs));
@@ -1090,7 +1130,7 @@ static int add(expr_node *node, Value *locals, Value *ans, int *nonconst)
/* If either is a string, coerce them both to strings and concatenate */
if (v1.type == STR_TYPE || v2.type == STR_TYPE) {
/* Skanky... copy the values shallowly fode debug */
/* Skanky... copy the values shallowly for debug */
Value o1 = v1;
Value o2 = v2;
if ( (r = DoCoerce(STR_TYPE, &v1)) ) {
@@ -1124,6 +1164,8 @@ static int add(expr_node *node, Value *locals, Value *ans, int *nonconst)
strcpy(ans->v.str, v1.v.str);
strcpy(ans->v.str+l1, v2.v.str);
DBG(debug_evaluation_binop(ans, OK, &o1, &o2, "+"));
DestroyValue(v1);
DestroyValue(v2);
return OK;
}
@@ -1506,6 +1548,8 @@ static int parse_expr_token(DynamicBuffer *buf, char const **in)
{
char c;
char c2;
char hexbuf[3];
DBufFree(buf);
@@ -1541,7 +1585,7 @@ static int parse_expr_token(DynamicBuffer *buf, char const **in)
}
(*in)++;
} else {
Eprint("%s `%c' (did you mean `%c%c'?)", GetErr(E_PARSE_ERR), c, c, c);
Eprint("%s `%c' (%s `%c%c'?)", GetErr(E_PARSE_ERR), c, tr("did you mean"), c, c);
return E_PARSE_ERR;
}
return OK;
@@ -1593,8 +1637,32 @@ static int parse_expr_token(DynamicBuffer *buf, char const **in)
case 'v':
r = DBufPutc(buf, '\v');
break;
case 'x':
c2 = *(*in + 1);
if (!isxdigit(c2)) {
r = DBufPutc(buf, **in);
break;
}
hexbuf[0] = c2;
hexbuf[1] = 0;
(*in)++;
c2 = *(*in + 1);
if (isxdigit(c2)) {
hexbuf[1] = c2;
hexbuf[2] = 0;
(*in)++;
}
c2 = (int) strtol(hexbuf, NULL, 16);
if (!c2) {
Eprint(tr("\\x00 is not a valid escape sequence"));
r = E_PARSE_ERR;
} else {
r = DBufPutc(buf, c2);
}
break;
default:
r = DBufPutc(buf, **in);
break;
}
(*in)++;
if (r != OK) {
@@ -1837,8 +1905,8 @@ static expr_node * parse_function_call(char const **e, int *r, Var *locals, int
}
}
}
ptr = *e;
if (TOKEN_IS(")")) {
ptr = *e;
*r = GET_TOKEN();
if (*r != OK) {
return free_expr_tree(node);
@@ -2137,6 +2205,7 @@ static expr_node *parse_atom(char const **e, int *r, Var *locals, int level)
}
/* It's a constant or a variable reference */
char const *olds = *e;
*r = GET_TOKEN();
if (*r != OK) return NULL;
node = alloc_expr_node(r);
@@ -2145,6 +2214,8 @@ static expr_node *parse_atom(char const **e, int *r, Var *locals, int level)
}
*r = make_atom(node, locals);
if (*r != OK) {
/* Preserve location for error position when we print ^-- here */
*e = olds;
return free_expr_tree(node);
}
return node;
@@ -2498,6 +2569,7 @@ expr_node *parse_expression(char const **e, int *r, Var *locals)
{
char const *orig = *e;
char const *o2 = *e;
char const *end_of_expr;
if (ExpressionEvaluationDisabled) {
*r = E_EXPR_DISABLED;
return NULL;
@@ -2529,25 +2601,29 @@ expr_node *parse_expression(char const **e, int *r, Var *locals)
*r == E_2FEW_ARGS ||
*r == E_PARSE_ERR ||
*r == E_EOLN ||
*r == E_BAD_NUMBER ||
*r == E_BAD_DATE ||
*r == E_BAD_TIME ||
*r == E_ILLEGAL_CHAR) {
orig = o2;
while (*orig) {
end_of_expr = find_end_of_expr(orig);
while (**e && isempty(**e)) {
(*e)++;
}
while (*orig && ((orig < end_of_expr) || (orig <= *e))) {
if (*orig == '\n') {
fprintf(ErrFp, " ");
orig++;
} else if (*orig == ']' && ! *(orig+1)) {
break;
} else {
fprintf(ErrFp, "%c", *orig++);
fprintf(ErrFp, "%c", *orig);
}
orig++;
}
fprintf(ErrFp, "\n");
orig = o2;
while ((orig < *e) && *orig) {
while (*orig && (orig < *e || isspace(*orig))) {
orig++;
fprintf(ErrFp, " ");
}
fprintf(ErrFp, "^-- here\n");
fprintf(ErrFp, "^-- %s\n", tr("here"));
}
return node;
}
@@ -2966,12 +3042,12 @@ int DoCoerce(char type, Value *v)
}
case STR_TYPE:
switch(v->type) {
case INT_TYPE: sprintf(coerce_buf, "%d", v->v.val); break;
case TIME_TYPE: sprintf(coerce_buf, "%02d%c%02d", v->v.val / 60,
case INT_TYPE: snprintf(coerce_buf, sizeof(coerce_buf), "%d", v->v.val); break;
case TIME_TYPE: snprintf(coerce_buf, sizeof(coerce_buf), "%02d%c%02d", v->v.val / 60,
TimeSep, v->v.val % 60);
break;
case DATE_TYPE: FromDSE(v->v.val, &y, &m, &d);
sprintf(coerce_buf, "%04d%c%02d%c%02d",
snprintf(coerce_buf, sizeof(coerce_buf), "%04d%c%02d%c%02d",
y, DateSep, m+1, DateSep, d);
break;
case DATETIME_TYPE:
@@ -2980,7 +3056,7 @@ int DoCoerce(char type, Value *v)
k = v->v.val % MINUTES_PER_DAY;
h = k / 60;
i = k % 60;
sprintf(coerce_buf, "%04d%c%02d%c%02d%c%02d%c%02d",
snprintf(coerce_buf, sizeof(coerce_buf), "%04d%c%02d%c%02d%c%02d%c%02d",
y, DateSep, m+1, DateSep, d, DateTimeSep, h, TimeSep, i);
break;
default: return E_CANT_COERCE;

View File

@@ -50,6 +50,7 @@ typedef struct cache {
struct cache *next;
char const *text;
int LineNo;
int LineNoStart;
} CachedLine;
typedef struct cheader {
@@ -77,6 +78,7 @@ typedef struct {
char const *filename;
FilenameChain *chain;
int LineNo;
int LineNoStart;
unsigned int IfFlags;
int NumIfs;
int IfLinenos[IF_NEST];
@@ -145,7 +147,8 @@ static void OpenPurgeFile(char const *fname, char const *mode)
if (DBufPuts(&fname_buf, ".purged") != OK) return;
PurgeFP = fopen(DBufValue(&fname_buf), mode);
if (!PurgeFP) {
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
fprintf(ErrFp, tr("Cannot open `%s' for writing: %s"), DBufValue(&fname_buf), strerror(errno));
fprintf(ErrFp, "\n");
}
set_cloexec(PurgeFP);
DBufFree(&fname_buf);
@@ -188,6 +191,7 @@ int ReadLine(void)
if (CLine) {
CurLine = CLine->text;
LineNo = CLine->LineNo;
LineNoStart = CLine->LineNoStart;
CLine = CLine->next;
got_a_fresh_line();
clear_callstack();
@@ -217,6 +221,7 @@ static int ReadLineFromFile(int use_pclose)
DBufInit(&buf);
DBufFree(&LineBuffer);
LineNoStart = LineNo+1;
while(fp) {
if (DBufGets(&buf, fp) != OK) {
DBufFree(&LineBuffer);
@@ -325,11 +330,13 @@ int OpenFile(char const *fname)
while (h) {
if (!strcmp(fname, h->filename)) {
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Found in cache\n", fname);
fprintf(ErrFp, tr("Reading `%s': Found in cache"), fname);
fprintf(ErrFp, "\n");
}
CLine = h->cache;
STRSET(FileName, fname);
LineNo = 0;
LineNoStart = 0;
if (!h->ownedByMe) {
RunDisabled |= RUN_NOTOWNER;
} else {
@@ -348,13 +355,14 @@ int OpenFile(char const *fname)
PurgeFP = stdout;
}
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `-': Reading stdin\n");
fprintf(ErrFp, "%s\n", tr("Reading `-': Reading stdin"));
}
} else {
fp = fopen(fname, "r");
set_cloexec(fp);
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
fprintf(ErrFp, tr("Reading `%s': Opening file on disk"), fname);
fprintf(ErrFp, "\n");
}
if (PurgeMode) {
OpenPurgeFile(fname, "w");
@@ -364,6 +372,7 @@ int OpenFile(char const *fname)
CLine = NULL;
if (ShouldCache) {
LineNo = 0;
LineNoStart = 0;
r = CacheFile(fname, 0);
if (r == OK) {
fp = NULL;
@@ -382,6 +391,7 @@ int OpenFile(char const *fname)
}
STRSET(FileName, fname);
LineNo = 0;
LineNoStart = 0;
if (FileName) return OK; else return E_NO_MEM;
}
@@ -401,7 +411,8 @@ static int CacheFile(char const *fname, int use_pclose)
char const *s;
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Caching file `%s' in memory\n", fname);
fprintf(ErrFp, tr("Caching file `%s' in memory"), fname);
fprintf(ErrFp, "\n");
}
cl = NULL;
/* Create a file header */
@@ -483,6 +494,7 @@ static int CacheFile(char const *fname, int use_pclose)
}
cl->next = NULL;
cl->LineNo = LineNo;
cl->LineNoStart = LineNoStart;
cl->text = StrDup(s);
DBufFree(&LineBuffer);
if (!cl->text) {
@@ -539,7 +551,8 @@ static int PopFile(void)
if (!Hush && NumIfs) {
Eprint("%s", GetErr(E_MISS_ENDIF));
for (j=NumIfs-1; j >=0; j--) {
fprintf(ErrFp, "%s(%d): IF without ENDIF\n", FileName, IfLinenos[j]);
fprintf(ErrFp, tr("%s(%d): IF without ENDIF"), FileName, IfLinenos[j]);
fprintf(ErrFp, "\n");
}
}
if (!IStackPtr) return E_EOF;
@@ -560,6 +573,7 @@ static int PopFile(void)
IStackPtr--;
LineNo = i->LineNo;
LineNoStart = i->LineNoStart;
IfFlags = i->IfFlags;
memcpy(IfLinenos, i->IfLinenos, IF_NEST);
NumIfs = i->NumIfs;
@@ -760,8 +774,9 @@ static int SetupGlobChain(char const *dirname, IncludeStruct *i)
while(dc) {
if (!strcmp(dc->dirname, dir)) {
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Found cached directory listing for `%s'\n",
fprintf(ErrFp, tr("Found cached directory listing for `%s'"),
dir);
fprintf(ErrFp, "\n");
}
free(dir);
i->chain = dc->chain;
@@ -771,7 +786,8 @@ static int SetupGlobChain(char const *dirname, IncludeStruct *i)
}
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Scanning directory `%s' for *.rem files\n", dir);
fprintf(ErrFp, tr("Scanning directory `%s' for *.rem files"), dir);
fprintf(ErrFp, "\n");
}
if (ShouldCache) {
@@ -785,7 +801,8 @@ static int SetupGlobChain(char const *dirname, IncludeStruct *i)
}
if (dc) {
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Caching directory `%s' listing\n", dir);
fprintf(ErrFp, tr("Caching directory `%s' listing"), dir);
fprintf(ErrFp, "\n");
}
dc->chain = NULL;
@@ -898,6 +915,7 @@ static int IncludeCmd(char const *cmd)
}
i->ownedByMe = 1;
i->LineNo = LineNo;
i->LineNoStart = LineNo;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
@@ -917,12 +935,14 @@ static int IncludeCmd(char const *cmd)
while(h) {
if (!strcmp(fname, h->filename)) {
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading command `%s': Found in cache\n", fname);
fprintf(ErrFp, tr("Reading command `%s': Found in cache"), fname);
fprintf(ErrFp, "\n");
}
CLine = h->cache;
STRSET(FileName, fname);
DBufFree(&buf);
LineNo = 0;
LineNoStart = 0;
if (!h->ownedByMe) {
RunDisabled |= RUN_NOTOWNER;
} else {
@@ -934,8 +954,9 @@ static int IncludeCmd(char const *cmd)
}
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Executing `%s' for INCLUDECMD and caching as `%s'\n",
fprintf(ErrFp, tr("Executing `%s' for INCLUDECMD and caching as `%s'"),
cmd, fname);
fprintf(ErrFp, "\n");
}
/* Not found in cache */
@@ -953,6 +974,7 @@ static int IncludeCmd(char const *cmd)
}
fp = fp2;
LineNo = 0;
LineNoStart = 0;
/* Temporarily turn of file tracing */
old_flag = DebugFlag;
@@ -968,6 +990,7 @@ static int IncludeCmd(char const *cmd)
fp = NULL;
CLine = CachedFiles->cache;
LineNo = 0;
LineNoStart = 0;
STRSET(FileName, fname);
DBufFree(&buf);
return OK;
@@ -1004,6 +1027,7 @@ int IncludeFile(char const *fname)
i->filename = NULL;
}
i->LineNo = LineNo;
i->LineNoStart = LineNoStart;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
@@ -1201,7 +1225,7 @@ static int CheckSafetyAux(struct stat *statbuf)
if (!geteuid()) {
/* Reject files not owned by root or group/world writable */
if (statbuf->st_uid != 0) {
fprintf(ErrFp, "SECURITY: Won't read non-root-owned file or directory when running as root!\n");
fprintf(ErrFp, "%s\n", tr("SECURITY: Won't read non-root-owned file or directory when running as root!"));
return 0;
}
}
@@ -1211,7 +1235,7 @@ static int CheckSafetyAux(struct stat *statbuf)
return 1;
}
if ((statbuf->st_mode & S_IWOTH)) {
fprintf(ErrFp, "SECURITY: Won't read world-writable file or directory!\n");
fprintf(ErrFp, "%s\n", tr("SECURITY: Won't read world-writable file or directory!"));
return 0;
}

View File

@@ -93,6 +93,7 @@ static int FDefined (func_info *);
static int FDosubst (func_info *);
static int FDusk (func_info *);
static int FEasterdate (func_info *);
static int FEscape (func_info *);
static int FEvalTrig (func_info *);
static int FFiledate (func_info *);
static int FFiledatetime (func_info *);
@@ -161,6 +162,7 @@ static int FTrigdate (func_info *);
static int FTrigdatetime (func_info *);
static int FTrigdelta (func_info *);
static int FTrigduration (func_info *);
static int FTriginfo (func_info *);
static int FTrigeventduration(func_info *);
static int FTrigeventstart (func_info *);
static int FTrigfrom (func_info *);
@@ -252,6 +254,7 @@ BuiltinFunc Func[] = {
{ "dosubst", 1, 3, 0, FDosubst, NULL },
{ "dusk", 0, 1, 0, FDusk, NULL },
{ "easterdate", 0, 1, 0, FEasterdate, NULL },
{ "escape", 1, 2, 1, FEscape, NULL },
{ "evaltrig", 1, 2, 0, FEvalTrig, NULL },
{ "filedate", 1, 1, 0, FFiledate, NULL },
{ "filedatetime", 1, 1, 0, FFiledatetime, NULL },
@@ -324,6 +327,7 @@ BuiltinFunc Func[] = {
{ "trigeventstart", 0, 0, 0, FTrigeventstart, NULL },
{ "trigfrom", 0, 0, 0, FTrigfrom, NULL },
{ "trigger", 1, 3, 0, FTrigger, NULL },
{ "triginfo", 1, 1, 1, FTriginfo, NULL },
{ "trigpriority", 0, 0, 0, FTrigpriority, NULL },
{ "trigrep", 0, 0, 0, FTrigrep, NULL },
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom, NULL },
@@ -392,6 +396,9 @@ static int F_(func_info *info)
}
r = RetStrVal(DBufValue(&translated), info);
DBufFree(&translated);
if (DebugFlag & DB_TRANSLATE) {
TranslationTemplate(ARGSTR(0));
}
return r;
}
@@ -1073,7 +1080,7 @@ static int FOrd(func_info *info)
if (u == 1 && t != 11) s = "st";
if (u == 2 && t != 12) s = "nd";
if (u == 3 && t != 13) s = "rd";
sprintf(buf, "%d%s", v, s);
snprintf(buf, sizeof(buf), "%d%s", v, s);
return RetStrVal(buf, info);
}
@@ -1615,6 +1622,17 @@ static int FTrigeventduration(func_info *info)
return OK;
}
static int FTriginfo(func_info *info)
{
char const *s;
ASSERT_TYPE(0, STR_TYPE);
s = FindTrigInfo(&LastTrigger, ARGSTR(0));
if (!s) {
return RetStrVal("", info);
}
return RetStrVal(s, info);
}
static int FTrigeventstart(func_info *info)
{
if (LastTrigger.eventstart == NO_TIME) {
@@ -1825,10 +1843,10 @@ static int FTrigger(func_info *info)
FromDSE(date, &y, &m, &d);
if (tim != NO_TIME) {
sprintf(buf, "%d %s %d AT %02d:%02d", d, MonthName[m], y,
snprintf(buf, sizeof(buf), "%d %s %d AT %02d:%02d", d, MonthName[m], y,
tim/60, tim%60);
} else {
sprintf(buf, "%d %s %d", d, MonthName[m], y);
snprintf(buf, sizeof(buf), "%d %s %d", d, MonthName[m], y);
}
return RetStrVal(buf, info);
}
@@ -2360,6 +2378,89 @@ static int FHebyear(func_info *info)
return OK;
}
/****************************************************************/
/* */
/* escape - escape special characters with "\xx" sequences */
/* */
/****************************************************************/
static int FEscape(func_info *info)
{
DynamicBuffer dbuf;
char const *s;
char hexbuf[16];
int r;
int include_quotes = 0;
ASSERT_TYPE(0, STR_TYPE);
if (Nargs >= 2) {
ASSERT_TYPE(1, INT_TYPE);
include_quotes = ARGV(1);
}
DBufInit(&dbuf);
if (include_quotes) {
r = DBufPutc(&dbuf, '"');
if (r != OK) {
DBufFree(&dbuf);
return r;
}
}
s = ARGSTR(0);
while(*s) {
switch(*s) {
case '\a':
r = DBufPuts(&dbuf, "\\a");
break;
case '\b':
r = DBufPuts(&dbuf, "\\b");
break;
case '\f':
r = DBufPuts(&dbuf, "\\f");
break;
case '\n':
r = DBufPuts(&dbuf, "\\n");
break;
case '\r':
r = DBufPuts(&dbuf, "\\r");
break;
case '\t':
r = DBufPuts(&dbuf, "\\t");
break;
case '\v':
r = DBufPuts(&dbuf, "\\v");
break;
case '\\':
r = DBufPuts(&dbuf, "\\\\");
break;
case '"':
r = DBufPuts(&dbuf, "\\\"");
break;
default:
if ((*s > 0 && *s < ' ') || *s == 0x7f) {
snprintf(hexbuf, sizeof(hexbuf), "\\x%02x", (unsigned int) *s);
r = DBufPuts(&dbuf, hexbuf);
} else {
r = DBufPutc(&dbuf, *s);
}
break;
}
if (r != OK) {
DBufFree(&dbuf);
return r;
}
s++;
}
if (include_quotes) {
r = DBufPutc(&dbuf, '"');
if (r != OK) {
DBufFree(&dbuf);
return r;
}
}
r = RetStrVal(DBufValue(&dbuf), info);
DBufFree(&dbuf);
return r;
}
/****************************************************************/
/* */
/* htmlescape - replace <. > and & by &lt; &gt; and &amp; */
@@ -3009,6 +3110,7 @@ static int FPsshade(func_info *info)
char psbuff[256];
char *s = psbuff;
int i;
size_t len = sizeof(psbuff);
/* 1 or 3 args */
if (Nargs != 1 && Nargs != 3) return E_2MANY_ARGS;
@@ -3021,19 +3123,22 @@ static int FPsshade(func_info *info)
if (!psshade_warned) {
psshade_warned = 1;
Wprint("psshade() is deprecated; use SPECIAL SHADE instead.");
Wprint(tr("psshade() is deprecated; use SPECIAL SHADE instead."));
}
sprintf(s, "/_A LineWidth 2 div def ");
snprintf(s, len, "/_A LineWidth 2 div def ");
len -= strlen(s);
s += strlen(s);
sprintf(s, "_A _A moveto ");
snprintf(s, len, "_A _A moveto ");
len -= strlen(s);
s += strlen(s);
sprintf(s, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto ");
snprintf(s, len, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto ");
len -= strlen(s);
s += strlen(s);
if (Nargs == 1) {
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARGV(0));
snprintf(s, len, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARGV(0));
} else {
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARGV(0), ARGV(1), ARGV(2));
snprintf(s, len, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARGV(0), ARGV(1), ARGV(2));
}
return RetStrVal(psbuff, info);
}
@@ -3056,6 +3161,7 @@ static int FPsmoon(func_info *info)
char const *extra = NULL;
int size = -1;
int fontsize = -1;
size_t len = sizeof(psbuff);
ASSERT_TYPE(0, INT_TYPE);
if (ARGV(0) < 0) return E_2LOW;
@@ -3076,63 +3182,74 @@ static int FPsmoon(func_info *info)
}
if (!psmoon_warned) {
psmoon_warned = 1;
Wprint("psmoon() is deprecated; use SPECIAL MOON instead.");
Wprint(tr("psmoon() is deprecated; use SPECIAL MOON instead."));
}
if (size > 0) {
sprintf(sizebuf, "%d", size);
snprintf(sizebuf, sizeof(sizebuf), "%d", size);
} else {
strcpy(sizebuf, "DaySize 2 div");
}
if (fontsize > 0) {
sprintf(fontsizebuf, "%d", fontsize);
snprintf(fontsizebuf, sizeof(fontsizebuf), "%d", fontsize);
} else {
strcpy(fontsizebuf, "EntrySize");
}
sprintf(s, "gsave 0 setgray newpath Border %s add BoxHeight Border sub %s sub",
snprintf(s, len, "gsave 0 setgray newpath Border %s add BoxHeight Border sub %s sub",
sizebuf, sizebuf);
len -= strlen(s);
s += strlen(s);
sprintf(s, " %s 0 360 arc closepath", sizebuf);
snprintf(s, len, " %s 0 360 arc closepath", sizebuf);
len -= strlen(s);
s += strlen(s);
switch(ARGV(0)) {
case 0:
sprintf(s, " fill");
snprintf(s, len, " fill");
len -= strlen(s);
s += strlen(s);
break;
case 2:
sprintf(s, " stroke");
snprintf(s, len, " stroke");
len -= strlen(s);
s += strlen(s);
break;
case 1:
sprintf(s, " stroke");
snprintf(s, len, " stroke");
len -= strlen(s);
s += strlen(s);
sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
snprintf(s, len, " newpath Border %s add BoxHeight Border sub %s sub",
sizebuf, sizebuf);
len -= strlen(s);
s += strlen(s);
sprintf(s, " %s 90 270 arc closepath fill", sizebuf);
snprintf(s, len, " %s 90 270 arc closepath fill", sizebuf);
len -= strlen(s);
s += strlen(s);
break;
default:
sprintf(s, " stroke");
snprintf(s, len, " stroke");
len -= strlen(s);
s += strlen(s);
sprintf(s, " newpath Border %s add BoxHeight Border sub %s sub",
snprintf(s, len, " newpath Border %s add BoxHeight Border sub %s sub",
sizebuf, sizebuf);
len -= strlen(s);
s += strlen(s);
sprintf(s, " %s 270 90 arc closepath fill", sizebuf);
snprintf(s, len, " %s 270 90 arc closepath fill", sizebuf);
len -= strlen(s);
s += strlen(s);
break;
}
if (extra) {
sprintf(s, " Border %s add %s add Border add BoxHeight border sub %s sub %s sub moveto /EntryFont findfont %s scalefont setfont (%s) show",
snprintf(s, len, " Border %s add %s add Border add BoxHeight border sub %s sub %s sub moveto /EntryFont findfont %s scalefont setfont (%s) show",
sizebuf, sizebuf, sizebuf, sizebuf, fontsizebuf, extra);
len -= strlen(s);
s += strlen(s);
}
sprintf(s, " grestore");
snprintf(s, len, " grestore");
return RetStrVal(psbuff, info);
}
@@ -3263,7 +3380,7 @@ static int FDatepart(func_info *info)
* used for the timezone stuff! */
static int setenv(char const *varname, char const *val, int overwrite)
{
static char tzbuf[256];
static char tzbuf[128];
if (strcmp(varname, "TZ")) {
fprintf(ErrFp, "built-in setenv can only be used with TZ\n");
abort();
@@ -3276,7 +3393,7 @@ static int setenv(char const *varname, char const *val, int overwrite)
if (strlen(val) > 250) {
return -1;
}
sprintf(tzbuf, "%s=%s", varname, val);
snprintf(tzbuf, sizeof(tzbuf), "%s=%s", varname, val);
return(putenv(tzbuf));
}
#endif
@@ -3285,12 +3402,12 @@ static int setenv(char const *varname, char const *val, int overwrite)
* used for the timezone stuff! */
static void unsetenv(char const *varname)
{
static char tzbuf[8];
static char tzbuf[128];
if (strcmp(varname, "TZ")) {
fprintf(ErrFp, "built-in unsetenv can only be used with TZ\n");
abort();
}
sprintf(tzbuf, "%s", varname);
snprintf(tzbuf, sizeof(tzbuf), "%s", varname);
putenv(tzbuf);
}
#endif
@@ -3622,7 +3739,7 @@ FEvalTrig(func_info *info)
} else {
/* Hokey... */
if (trig.scanfrom != DSEToday) {
Wprint("Warning: SCANFROM is ignored in two-argument form of evaltrig()");
Wprint(tr("Warning: SCANFROM is ignored in two-argument form of evaltrig()"));
}
dse = ComputeTrigger(scanfrom, &trig, &tim, &r, 0);
}
@@ -3677,7 +3794,7 @@ FMultiTrig(func_info *info)
return E_PARSE_ERR;
}
if (tim.ttime != NO_TIME) {
Eprint("Cannot use AT clause in multitrig() function");
Eprint(tr("Cannot use AT clause in multitrig() function"));
return E_PARSE_ERR;
}
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0);

View File

@@ -47,6 +47,7 @@ EXTERN int CurDay;
EXTERN int CurMon;
EXTERN int CurYear;
EXTERN int LineNo;
EXTERN int LineNoStart;
EXTERN int FreshLine;
EXTERN int WarnedAboutImplicit;
EXTERN uid_t TrustedUsers[MAX_TRUSTED_USERS];
@@ -119,7 +120,7 @@ EXTERN INIT( int PurgeIncludeDepth, 0);
EXTERN INIT( FILE *PurgeFP, NULL);
EXTERN INIT( int NumIfs, 0);
EXTERN INIT( unsigned int IfFlags, 0);
EXTERN INIT( int IfLinenos[IF_NEST], {0});
EXTERN INIT( int IfLinenos[IF_NEST], {0});
EXTERN INIT( int LastTrigValid, 0);
EXTERN Trigger LastTrigger;
EXTERN TimeTrig LastTimeTrig;
@@ -176,6 +177,9 @@ EXTERN INIT( unsigned int FuncRecursionLevel, 0);
/* Suppress warnings about implicit REM and MSG */
EXTERN INIT( int SuppressImplicitRemWarnings, 0);
/* Test mode - used by the acceptance tests */
EXTERN INIT( int TestMode, 0);
extern int NumFullOmits, NumPartialOmits;
/* List of months */
@@ -237,3 +241,6 @@ EXTERN int SuppressLRM
= 0
#endif
;
/* Translatable messages */
extern char const *translatables[];

View File

@@ -324,7 +324,7 @@ int GetValidHebDate(int yin, int min, int din, int adarbehave,
case ADAR2ADARB: *mout = min = ADARB; break;
default:
Eprint("GetValidHebDate: Bad adarbehave value %d", adarbehave);
Eprint(tr("GetValidHebDate: Bad adarbehave value %d"), adarbehave);
return E_SWERR;
}
}
@@ -464,7 +464,7 @@ int ComputeJahr(int y, int m, int d, int *ans)
/* Check for Adar A */
if (m == ADARA && monlen[m] == 0) {
Eprint("No Adar A in %d", y);
Eprint(tr("No Adar A in %d"), y);
return E_BAD_HEBDATE;
}

View File

@@ -217,6 +217,7 @@ void InitRemind(int argc, char const *argv[])
RealToday = SystemDate(&CurYear, &CurMon, &CurDay);
if (RealToday < 0) {
fprintf(ErrFp, GetErr(M_BAD_SYS_DATE), BASE);
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
DSEToday = RealToday;
@@ -641,8 +642,10 @@ void InitRemind(int argc, char const *argv[])
case 'v': case 'V': DebugFlag |= DB_DUMP_VARS; break;
case 'l': case 'L': DebugFlag |= DB_PRTLINE; break;
case 'f': case 'F': DebugFlag |= DB_TRACE_FILES; break;
case 'q': case 'Q': DebugFlag |= DB_TRANSLATE; break;
default:
fprintf(ErrFp, GetErr(M_BAD_DB_FLAG), *(arg-1));
fprintf(ErrFp, "\n");
}
}
break;
@@ -678,6 +681,7 @@ void InitRemind(int argc, char const *argv[])
default:
fprintf(ErrFp, GetErr(M_BAD_OPTION), *(arg-1));
fprintf(ErrFp, "\n");
}
}
@@ -791,10 +795,11 @@ void InitRemind(int argc, char const *argv[])
}
/* Figure out the offset from UTC */
if (CalculateUTC)
/* Figure out the offset from UTC */
if (CalculateUTC) {
(void) CalcMinsFromUTC(DSEToday, MinutesPastMidnight(0),
&MinsFromUTC, NULL);
}
}
/***************************************************************/
@@ -870,6 +875,7 @@ static void ChgUser(char const *user)
if (!pwent) {
fprintf(ErrFp, GetErr(M_BAD_USER), user);
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
@@ -878,16 +884,19 @@ static void ChgUser(char const *user)
#ifdef HAVE_INITGROUPS
if (initgroups(pwent->pw_name, pwent->pw_gid) < 0) {
fprintf(ErrFp, GetErr(M_NO_CHG_GID), pwent->pw_gid);
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
};
#endif
if (setgid(pwent->pw_gid) < 0) {
fprintf(ErrFp, GetErr(M_NO_CHG_GID), pwent->pw_gid);
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
if (setuid(pwent->pw_uid) < 0) {
fprintf(ErrFp, GetErr(M_NO_CHG_UID), pwent->pw_uid);
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
}
@@ -895,6 +904,7 @@ static void ChgUser(char const *user)
home = malloc(strlen(pwent->pw_dir) + 6);
if (!home) {
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
sprintf(home, "HOME=%s", pwent->pw_dir);
@@ -903,6 +913,7 @@ static void ChgUser(char const *user)
shell = malloc(strlen(pwent->pw_shell) + 7);
if (!shell) {
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
sprintf(shell, "SHELL=%s", pwent->pw_shell);
@@ -912,6 +923,7 @@ static void ChgUser(char const *user)
username = malloc(strlen(pwent->pw_name) + 6);
if (!username) {
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
sprintf(username, "USER=%s", pwent->pw_name);
@@ -919,6 +931,7 @@ static void ChgUser(char const *user)
logname= malloc(strlen(pwent->pw_name) + 9);
if (!logname) {
fprintf(ErrFp, "%s", GetErr(M_NOMEM_ENV));
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
sprintf(logname, "LOGNAME=%s", pwent->pw_name);
@@ -964,6 +977,7 @@ static void InitializeVar(char const *str)
varname[r++] = *str;
} else {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_ILLEGAL_CHAR));
fprintf(ErrFp, "\n");
return;
}
}
@@ -977,12 +991,14 @@ static void InitializeVar(char const *str)
varname[r] = 0;
if (!*varname) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_VAR));
fprintf(ErrFp, "\n");
return;
}
if (!*str) {
/* Setting a system var does require =expr on the commandline */
if (*varname == '$') {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_EQ));
fprintf(ErrFp, "\n");
return;
}
val.type = INT_TYPE;
@@ -993,40 +1009,51 @@ static void InitializeVar(char const *str)
}
if (r) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
fprintf(ErrFp, "\n");
}
return;
}
if (!*varname) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_VAR));
fprintf(ErrFp, "\n");
return;
}
expr = str+1;
if (!*expr) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(E_MISS_EXPR));
fprintf(ErrFp, "\n");
return;
}
r=EvalExpr(&expr, &val, NULL);
if (r) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
fprintf(ErrFp, "\n");
return;
}
if (*varname == '$') {
r=SetSysVar(varname+1, &val);
DestroyValue(val);
if (r) fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
if (r) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
fprintf(ErrFp, "\n");
}
return;
}
r=SetVar(varname, &val);
if (r) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
fprintf(ErrFp, "\n");
return;
}
r=PreserveVar(varname);
if (r) fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
if (r) {
fprintf(ErrFp, GetErr(M_I_OPTION), GetErr(r));
fprintf(ErrFp, "\n");
}
return;
}
@@ -1043,6 +1070,7 @@ AddTrustedUser(char const *username)
pwent = getpwnam(username);
if (!pwent) {
fprintf(ErrFp, GetErr(M_BAD_USER), username);
fprintf(ErrFp, "\n");
exit(EXIT_FAILURE);
}
TrustedUsers[NumTrustedUsers] = pwent->pw_uid;
@@ -1094,6 +1122,17 @@ static void
ProcessLongOption(char const *arg)
{
int t;
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;
/* Update RealToday because of TestMode */
RealToday = SystemDate(&CurYear, &CurMon, &CurDay);
DSEToday = RealToday;
FromDSE(DSEToday, &CurYear, &CurMon, &CurDay);
return;
}
if (!strcmp(arg, "version")) {
printf("%s\n", VERSION);
exit(EXIT_SUCCESS);

View File

@@ -299,7 +299,7 @@ json_value * json_parse_ex (json_settings * settings,
if (flags & flag_string)
{
if (!b)
{ sprintf (error, "Unexpected EOF in string (at %u:%u)", line_and_col);
{ snprintf (error, sizeof(error), "Unexpected EOF in string (at %u:%u)", line_and_col);
goto e_failed;
}
@@ -325,7 +325,7 @@ json_value * json_parse_ex (json_settings * settings,
(uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
{
sprintf (error, "Invalid character value `%c` (at %u:%u)", b, line_and_col);
snprintf (error, sizeof(error), "Invalid character value `%c` (at %u:%u)", b, line_and_col);
goto e_failed;
}
@@ -342,7 +342,7 @@ json_value * json_parse_ex (json_settings * settings,
(uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
(uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
{
sprintf (error, "Invalid character value `%c` (at %u:%u)", b, line_and_col);
snprintf (error, sizeof(error), "Invalid character value `%c` (at %u:%u)", b, line_and_col);
goto e_failed;
}
@@ -472,7 +472,7 @@ json_value * json_parse_ex (json_settings * settings,
if (flags & flag_block_comment)
{
if (!b)
{ sprintf (error, "%u:%u: Unexpected EOF in block comment", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: Unexpected EOF in block comment", line_and_col);
goto e_failed;
}
@@ -488,12 +488,12 @@ json_value * json_parse_ex (json_settings * settings,
else if (b == '/')
{
if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object)
{ sprintf (error, "%u:%u: Comment not allowed here", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: Comment not allowed here", line_and_col);
goto e_failed;
}
if (++ state.ptr == end)
{ sprintf (error, "%u:%u: EOF unexpected", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: EOF unexpected", line_and_col);
goto e_failed;
}
@@ -508,7 +508,7 @@ json_value * json_parse_ex (json_settings * settings,
continue;
default:
sprintf (error, "%u:%u: Unexpected `%c` in comment opening sequence", line_and_col, b);
snprintf (error, sizeof(error), "%u:%u: Unexpected `%c` in comment opening sequence", line_and_col, b);
goto e_failed;
};
}
@@ -526,7 +526,7 @@ json_value * json_parse_ex (json_settings * settings,
default:
sprintf (error, "%u:%u: Trailing garbage: `%c`",
snprintf (error, sizeof(error), "%u:%u: Trailing garbage: `%c`",
state.cur_line, state.cur_col, b);
goto e_failed;
@@ -545,7 +545,7 @@ json_value * json_parse_ex (json_settings * settings,
if (top && top->type == json_array)
flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
else
{ sprintf (error, "%u:%u: Unexpected ]", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: Unexpected ]", line_and_col);
goto e_failed;
}
@@ -561,7 +561,7 @@ json_value * json_parse_ex (json_settings * settings,
}
else
{
sprintf (error, "%u:%u: Expected , before %c",
snprintf (error, sizeof(error), "%u:%u: Expected , before %c",
state.cur_line, state.cur_col, b);
goto e_failed;
@@ -576,7 +576,7 @@ json_value * json_parse_ex (json_settings * settings,
}
else
{
sprintf (error, "%u:%u: Expected : before %c",
snprintf (error, sizeof(error), "%u:%u: Expected : before %c",
state.cur_line, state.cur_col, b);
goto e_failed;
@@ -702,7 +702,7 @@ json_value * json_parse_ex (json_settings * settings,
continue;
}
else
{ sprintf (error, "%u:%u: Unexpected %c when seeking value", line_and_col, b);
{ snprintf (error, sizeof(error), "%u:%u: Unexpected %c when seeking value", line_and_col, b);
goto e_failed;
}
};
@@ -722,7 +722,7 @@ json_value * json_parse_ex (json_settings * settings,
case '"':
if (flags & flag_need_comma)
{ sprintf (error, "%u:%u: Expected , before \"", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: Expected , before \"", line_and_col);
goto e_failed;
}
@@ -747,7 +747,7 @@ json_value * json_parse_ex (json_settings * settings,
}
/* FALLTHROUGH */
default:
sprintf (error, "%u:%u: Unexpected `%c` in object", line_and_col, b);
snprintf (error, sizeof(error), "%u:%u: Unexpected `%c` in object", line_and_col, b);
goto e_failed;
};
@@ -765,7 +765,7 @@ json_value * json_parse_ex (json_settings * settings,
if (! (flags & flag_num_e))
{
if (flags & flag_num_zero)
{ sprintf (error, "%u:%u: Unexpected `0` before `%c`", line_and_col, b);
{ snprintf (error, sizeof(error), "%u:%u: Unexpected `0` before `%c`", line_and_col, b);
goto e_failed;
}
@@ -814,7 +814,7 @@ json_value * json_parse_ex (json_settings * settings,
else if (b == '.' && top->type == json_integer)
{
if (!num_digits)
{ sprintf (error, "%u:%u: Expected digit before `.`", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: Expected digit before `.`", line_and_col);
goto e_failed;
}
@@ -831,7 +831,7 @@ json_value * json_parse_ex (json_settings * settings,
if (top->type == json_double)
{
if (!num_digits)
{ sprintf (error, "%u:%u: Expected digit after `.`", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: Expected digit after `.`", line_and_col);
goto e_failed;
}
@@ -857,7 +857,7 @@ json_value * json_parse_ex (json_settings * settings,
else
{
if (!num_digits)
{ sprintf (error, "%u:%u: Expected digit after `e`", line_and_col);
{ snprintf (error, sizeof(error), "%u:%u: Expected digit after `e`", line_and_col);
goto e_failed;
}
@@ -942,7 +942,7 @@ json_value * json_parse_ex (json_settings * settings,
e_unknown_value:
sprintf (error, "%u:%u: Unknown value", line_and_col);
snprintf (error, sizeof(error), "%u:%u: Unknown value", line_and_col);
goto e_failed;
e_alloc_failure:
@@ -952,7 +952,7 @@ e_alloc_failure:
e_overflow:
sprintf (error, "%u:%u: Too long (caught overflow)", line_and_col);
snprintf (error, sizeof(error), "%u:%u: Too long (caught overflow)", line_and_col);
goto e_failed;
e_failed:

View File

@@ -145,6 +145,7 @@ int main(int argc, char *argv[])
}
DBufInit(&(LastTrigger.tags));
LastTrigger.infos = NULL;
ClearLastTriggers();
atexit(exitfunc);
@@ -179,6 +180,7 @@ int main(int argc, char *argv[])
printf("%s\n", GetErr(E_NOREMINDERS));
} else if (!Daemon && !NextMode && !NumTriggered) {
printf(GetErr(M_QUEUED), NumQueued);
printf("\n");
}
}
@@ -373,7 +375,7 @@ static void DoReminders(void)
default:
if (!SuppressImplicitRemWarnings) {
Wprint("Unrecognized command; interpreting as REM");
Wprint(tr("Unrecognized command; interpreting as REM"));
WarnedAboutImplicit = 1;
}
DestroyParser(&p);
@@ -629,7 +631,9 @@ int ParseTokenOrQuotedString(ParsePtr p, DynamicBuffer *dbuf)
/***************************************************************/
int ParseQuotedString(ParsePtr p, DynamicBuffer *dbuf)
{
int c, err;
int c, err, c2;
char hexbuf[3];
DBufFree(dbuf);
c = ParseNonSpaceChar(p, &err, 0);
if (err) return err;
@@ -673,6 +677,34 @@ int ParseQuotedString(ParsePtr p, DynamicBuffer *dbuf)
case 'v':
err = DBufPutc(dbuf, '\v');
break;
case 'x':
/* \x Followed by one or two hex digits */
c2 = ParseChar(p, &err, 1);
if (err) break;
if (!isxdigit(c2)) {
err = DBufPutc(dbuf, c);
break;
}
hexbuf[0] = c2;
hexbuf[1] = 0;
c2 = ParseChar(p, &err, 0);
if (err) break;
c2 = ParseChar(p, &err, 1);
if (err) break;
if (isxdigit(c2)) {
hexbuf[1] = c2;
hexbuf[2] = 0;
c2 = ParseChar(p, &err, 0);
if (err) break;
}
c2 = (int) strtol(hexbuf, NULL, 16);
if (!c2) {
Eprint(tr("\\x00 is not a valid escape sequence"));
err = E_PARSE_ERR;
} else {
err = DBufPutc(dbuf, c2);
}
break;
default:
err = DBufPutc(dbuf, c);
}
@@ -865,11 +897,21 @@ void Wprint(char const *fmt, ...)
va_list argptr;
/* We can't use line_range because caller might have used it */
if (FileName) {
if (strcmp(FileName, "-"))
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
else
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
if (strcmp(FileName, "-")) {
if (LineNoStart == LineNo) {
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
} else {
(void) fprintf(ErrFp, "%s(%d:%d): ", FileName, LineNoStart, LineNo);
}
} else {
if (LineNoStart == LineNo) {
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
} else {
(void) fprintf(ErrFp, "-stdin-(%d:%d): ", LineNoStart, LineNo);
}
}
}
va_start(argptr, fmt);
@@ -901,7 +943,12 @@ void Eprint(char const *fmt, ...)
fname = "-stdin-";
}
if (FreshLine) {
(void) fprintf(ErrFp, "%s(%d): ", fname, LineNo);
/* We can't use line_range because caller might have used it */
if (LineNo == LineNoStart) {
(void) fprintf(ErrFp, "%s(%d): ", fname, LineNo);
} else {
(void) fprintf(ErrFp, "%s(%d:%d): ", fname, LineNoStart, LineNo);
}
} else {
fprintf(ErrFp, " ");
}
@@ -1009,6 +1056,11 @@ int SystemTime(int realtime)
if (!realtime && (SysTime != -1)) return SysTime;
if (TestMode) {
/* Pretend it's 7:00PM in test mode */
return 19 * 3600;
}
now = time(NULL);
t = localtime(&now);
return t->tm_hour * 3600L + t->tm_min * 60L +
@@ -1042,6 +1094,14 @@ int SystemDate(int *y, int *m, int *d)
time_t now;
struct tm *t;
/* In test mode, always return 6 January 2025 */
if (TestMode) {
*y = 2025;
*m = 0;
*d = 6;
return 12789; /* 2025-01-06 */
}
now = time(NULL);
t = localtime(&now);
@@ -1267,6 +1327,12 @@ int DoDebug(ParsePtr p)
else DebugFlag &= ~DB_ECHO_LINE;
break;
case 'q':
case 'Q':
if (val) DebugFlag |= DB_TRANSLATE;
else DebugFlag &= ~DB_TRANSLATE;
break;
case 's':
case 'S':
if (val) DebugFlag |= DB_PARSE_EXPR;
@@ -1308,6 +1374,9 @@ int DoDebug(ParsePtr p)
if (val) DebugFlag |= DB_TRACE_FILES;
else DebugFlag &= ~DB_TRACE_FILES;
break;
default:
Wprint(GetErr(M_BAD_DB_FLAG), ch);
break;
}
}
}
@@ -1892,6 +1961,10 @@ void
FreeTrig(Trigger *t)
{
DBufFree(&(t->tags));
if (t->infos) {
FreeTrigInfoChain(t->infos);
}
t->infos = NULL;
}
void
@@ -1917,8 +1990,7 @@ ClearLastTriggers(void)
LastTrigger.warn[0] = 0;
LastTrigger.omitfunc[0] = 0;
LastTrigger.passthru[0] = 0;
DBufFree(&(LastTrigger.tags));
FreeTrig(&LastTrigger);
LastTimeTrig.ttime = NO_TIME;
LastTimeTrig.delta = NO_DELTA;
LastTimeTrig.rep = NO_REP;
@@ -1938,10 +2010,19 @@ SaveAllTriggerInfo(Trigger const *t, TimeTrig const *tt, int trigdate, int trigt
void
SaveLastTrigger(Trigger const *t)
{
DBufFree(&(LastTrigger.tags));
FreeTrig(&LastTrigger);
memcpy(&LastTrigger, t, sizeof(LastTrigger));
/* DON'T hang on to the invalid info chain! */
LastTrigger.infos = NULL;
DBufInit(&(LastTrigger.tags));
DBufPuts(&(LastTrigger.tags), DBufValue(&(t->tags)));
TrigInfo *cur = t->infos;
while(cur) {
AppendTrigInfo(&LastTrigger, cur->info);
cur = cur->next;
}
}
void
@@ -2034,7 +2115,7 @@ static int GetOnceDateFromFile(void)
/* Save today to file */
fp = fopen(OnceFile, "w");
if (!fp) {
Wprint("Warning: Unable to save ONCE timestamp to %s: %s",
Wprint(tr("Warning: Unable to save ONCE timestamp to %s: %s"),
OnceFile, strerror(errno));
return once_date;
}
@@ -2055,25 +2136,9 @@ int GetOnceDate(void)
return OnceDate;
}
static void
get_printf_escapes(char const *str, DynamicBuffer *out)
{
char const *s = str;
while(*s) {
if (*s == '%' && *(s+1) != 0) {
s++;
DBufPutc(out, *s);
}
s++;
}
}
char const *GetErr(int r)
{
char const *msg;
DynamicBuffer origEscapes;
DynamicBuffer translatedEscapes;
int dangerous;
if (r < 0 || r >= NumErrs) {
r = E_SWERR;
@@ -2084,23 +2149,5 @@ char const *GetErr(int r)
return ErrMsg[r];
}
/* We need to make sure both the original and translated version
have the *SAME* printf-style escapes to avoid a malicious
translation file doing a format-string attack */
DBufInit(&origEscapes);
DBufInit(&translatedEscapes);
get_printf_escapes(ErrMsg[r], &origEscapes);
get_printf_escapes(msg, &translatedEscapes);
dangerous = strcmp(DBufValue(&origEscapes), DBufValue(&translatedEscapes));
DBufFree(&origEscapes);
DBufFree(&translatedEscapes);
if (dangerous) {
return ErrMsg[r];
} else {
return msg;
}
return msg;
}

View File

@@ -92,7 +92,7 @@ int DestroyOmitContexts(int print_unmatched)
while (c) {
if (print_unmatched) {
Wprint("Unmatched PUSH-OMIT-CONTEXT at %s(%d)",
Wprint(tr("Unmatched PUSH-OMIT-CONTEXT at %s(%d)"),
c->filename, c->lineno);
}
num++;
@@ -186,7 +186,7 @@ int PopOmitContext(ParsePtr p)
SavedOmitContexts = c->next;
if (c->filename && FileName && strcmp(c->filename, FileName)) {
Wprint("POP-OMIT-CONTEXT at %s:%d matches PUSH-OMIT-CONTEXT in different file: %s:%d", FileName, LineNo, c->filename, c->lineno);
Wprint(tr("POP-OMIT-CONTEXT at %s:%d matches PUSH-OMIT-CONTEXT in different file: %s:%d"), FileName, LineNo, c->filename, c->lineno);
}
/* Free memory used by the saved context */
if (c->partsave) free(c->partsave);
@@ -218,7 +218,7 @@ int IsOmitted(int dse, int localomit, char const *omitfunc, int *omit)
Value v;
FromDSE(dse, &y, &m, &d);
sprintf(expr, "%s('%04d-%02d-%02d')",
snprintf(expr, sizeof(expr), "%s('%04d-%02d-%02d')",
omitfunc, y, m+1, d);
s = expr;
r = EvalExpr(&s, &v, NULL);
@@ -388,6 +388,7 @@ int DoOmit(ParsePtr p)
case T_RemType:
case T_Priority:
case T_Tag:
case T_Info:
case T_Duration:
DBufFree(&buf);
parsing = 0;
@@ -395,7 +396,7 @@ int DoOmit(ParsePtr p)
default:
if (tok.type == T_Until) {
Eprint("OMIT: UNTIL not allowed; did you mean THROUGH?");
Eprint(tr("OMIT: UNTIL not allowed; did you mean THROUGH?"));
} else if (tok.type == T_Illegal && tok.val < 0) {
Eprint("%s: `%s'", GetErr(-tok.val), DBufValue(&buf));
} else {
@@ -415,7 +416,7 @@ int DoOmit(ParsePtr p)
return E_2MANY_LOCALOMIT;
}
WeekdayOmits |= wd;
if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
if (tok.type == T_Tag || tok.type == T_Info || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
return OK;
}
@@ -465,7 +466,7 @@ int DoOmit(ParsePtr p)
InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome);
NumPartialOmits++;
if (NumPartialOmits == 366) {
Wprint("You have OMITted everything! The space-time continuum is at risk.");
Wprint(tr("You have OMITted everything! The space-time continuum is at risk."));
}
}
if (mc == m[1] && dc == d[1]) {
@@ -487,7 +488,7 @@ int DoOmit(ParsePtr p)
start = DSE(y[0], m[0], d[0]);
end = DSE(y[1], m[1], d[1]);
if (end < start) {
Eprint("Error: THROUGH date earlier than start date");
Eprint(tr("Error: THROUGH date earlier than start date"));
return E_BAD_DATE;
}
@@ -500,7 +501,7 @@ int DoOmit(ParsePtr p)
}
}
if (tok.type == T_Tag || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
if (tok.type == T_Tag || tok.type == T_Info || tok.type == T_Duration || tok.type == T_RemType || tok.type == T_Priority) return E_PARSE_AS_REM;
return OK;
}

View File

@@ -226,7 +226,7 @@ char const *get_day_name(int wkday);
char const *get_month_name(int mon);
void set_cloexec(FILE *fp);
int push_call(char const *filename, char const *func, int lineno);
int push_call(char const *filename, char const *func, int lineno, int lineno_start);
void clear_callstack(void);
int print_callstack(FILE *fp);
void pop_call(void);
@@ -273,3 +273,15 @@ int GetTranslatedStringTryingVariants(char const *orig, DynamicBuffer *out);
char const *GetErr(int r);
char const *tr(char const *s);
void print_escaped_string(FILE *fp, char const *s);
void print_escaped_string_helper(FILE *fp, char const *s, int esc_for_remind, int json);
void GenerateSysvarTranslationTemplates(void);
void TranslationTemplate(char const *msg);
TrigInfo *NewTrigInfo(char const *i);
void FreeTrigInfo(TrigInfo *ti);
void FreeTrigInfoChain(TrigInfo *ti);
int AppendTrigInfo(Trigger *t, char const *info);
int TrigInfoHeadersAreTheSame(char const *i1, char const *i2);
int TrigInfoIsValid(char const *info);
char const *FindTrigInfo(Trigger *t, char const *header);
void WriteJSONInfoChain(TrigInfo *ti);
char const *line_range(int lineno_start, int lineno);

View File

@@ -61,6 +61,7 @@ typedef struct queuedrem {
char const *text;
char const *fname;
int lineno;
int lineno_start;
char passthru[PASSTHRU_LEN+1];
char sched[VAR_NAME_LEN+1];
Trigger t;
@@ -163,6 +164,7 @@ static void del_reminder(QueuedRem *qid)
if (q == qid) {
QueueHead = q->next;
if (q->text) free((void *) q->text);
FreeTrig(&(q->t));
free(q);
return;
}
@@ -171,6 +173,7 @@ static void del_reminder(QueuedRem *qid)
if (q->next == qid) {
q->next = q->next->next;
if (next->text) free((void *) next->text);
FreeTrig(&(next->t));
free(next);
return;
}
@@ -222,11 +225,16 @@ int QueueReminder(ParsePtr p, Trigger *trig,
}
qelem->lineno = LineNo;
qelem->lineno_start = LineNoStart;
NumQueued++;
qelem->typ = trig->typ;
strcpy(qelem->passthru, trig->passthru);
qelem->tt = *tim;
qelem->t = *trig;
/* Take over infos */
trig->infos = NULL;
DBufInit(&(qelem->t.tags));
DBufPuts(&(qelem->t.tags), DBufValue(&(trig->tags)));
if (SynthesizeTags) {
@@ -346,8 +354,9 @@ void HandleQueuedReminders(void)
/* If we're a daemon, get the mod time of initial file */
if (Daemon > 0) {
if (stat(InitialFile, &StatBuf)) {
fprintf(ErrFp, "Cannot stat %s - not running as daemon!\n",
fprintf(ErrFp, tr("Cannot stat %s - not running as daemon!"),
InitialFile);
fprintf(ErrFp, "\n");
Daemon = 0;
} else FileModTime = StatBuf.st_mtime;
}
@@ -459,10 +468,17 @@ void HandleQueuedReminders(void)
if (IsServerMode() && q->typ != RUN_TYPE) {
if (DaemonJSON) {
printf("{\"response\":\"reminder\",");
snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
if (TestMode) {
snprintf(qid, sizeof(qid), "42424242");
} else {
snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
}
PrintJSONKeyPairString("qid", qid);
PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
if (q->t.infos) {
WriteJSONInfoChain(q->t.infos);
}
PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
} else {
printf("NOTE reminder %s",
@@ -485,6 +501,7 @@ void HandleQueuedReminders(void)
DefaultColorB = q->blue;
/* Make a COPY of q->t because TriggerReminder can change q->t.typ */
Trigger tcopy = q->t;
if (DaemonJSON) {
DynamicBuffer out;
DBufInit(&out);
@@ -697,7 +714,7 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
to be a security hole! */
while(1) {
char exprBuf[VAR_NAME_LEN+32];
sprintf(exprBuf, "%s(%d)", q->sched, q->ntrig);
snprintf(exprBuf, sizeof(exprBuf), "%s(%d)", q->sched, q->ntrig);
s = exprBuf;
r = EvalExpr(&s, &v, NULL);
if (r) {
@@ -759,12 +776,19 @@ json_queue(QueuedRem const *q)
printf("{");
WriteJSONTrigger(&(q->t), 1, DSEToday);
WriteJSONTimeTrigger(&(q->tt));
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
if (TestMode) {
snprintf(idbuf, sizeof(idbuf), "42424242");
} else {
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
}
PrintJSONKeyPairString("qid", idbuf);
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairString("filename", q->fname);
PrintJSONKeyPairInt("lineno", q->lineno);
if (q->lineno_start != q->lineno) {
PrintJSONKeyPairInt("lineno_start", q->lineno_start);
}
switch(q->typ) {
case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break;
case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break;

View File

@@ -342,7 +342,7 @@ int main(int argc, char *argv[])
while (!feof(stdin)) {
DBufGets(&buf, stdin);
if (first_line && (!strcmp(DBufValue(&buf), "["))) {
fprintf(stderr, "Rem2PS: It appears that you have invoked Remind with the -ppp option.\n Please use either -p or -pp, but not -ppp.\n");
fprintf(stderr, "Rem2PS: It appears that you have invoked Remind with the -ppp option.\n Please use either -p or -pp, but not -ppp. Also, Rem2PS does\n not support weekly calendars, so do not use -p+ or -pp+.\n");
exit(EXIT_FAILURE);
}
first_line = 0;
@@ -1222,7 +1222,7 @@ int DoQueuedPs(void)
if (moonsize < 0) {
size = "DaySize 2 div";
} else {
sprintf(buffer, "%d", moonsize);
snprintf(buffer, sizeof(buffer), "%d", moonsize);
size = buffer;
}
@@ -1235,7 +1235,7 @@ int DoQueuedPs(void)
if (fontsize < 0) {
fsize = "EntrySize";
} else {
sprintf(fbuffer, "%d", fontsize);
snprintf(fbuffer, sizeof(fbuffer), "%d", fontsize);
fsize = fbuffer;
}
printf("/EntryFont findfont %s scalefont setfont (",
@@ -1273,7 +1273,7 @@ int DoQueuedPs(void)
if (fontsize < 0) {
fsize = "EntrySize";
} else {
sprintf(fbuffer, "%d", fontsize);
snprintf(fbuffer, sizeof(fbuffer), "%d", fontsize);
fsize = fbuffer;
}
printf("/EntryFont findfont %s scalefont setfont (",

View File

@@ -183,7 +183,7 @@ static void IssueSortBanner(int dse)
if (UserFuncExists("sortbanner") != 1) return;
FromDSE(dse, &y, &m, &d);
sprintf(BanExpr, "sortbanner('%04d/%02d/%02d')", y, m+1, d);
snprintf(BanExpr, sizeof(BanExpr), "sortbanner('%04d/%02d/%02d')", y, m+1, d);
y = EvalExpr(&s, &v, NULL);
if (y) return;
if (DoCoerce(STR_TYPE, &v)) return;

View File

@@ -71,6 +71,7 @@ Token TokArray[] = {
{ "in", 2, T_In, 0 },
{ "include", 3, T_Include, 0 },
{ "includecmd", 10, T_IncludeCmd, 0 },
{ "info", 4, T_Info, 0 },
{ "january", 3, T_Month, 0 },
{ "july", 3, T_Month, 6 },
{ "june", 3, T_Month, 5 },

View File

@@ -34,6 +34,71 @@ typedef struct xlat {
hash_table TranslationTable;
static XlateItem *FindTranslation(char const *orig);
static int printf_formatters_are_safe(char const *orig, char const *translated);
void
TranslationTemplate(char const *in)
{
if (!*in) {
return;
}
if (!strcmp(in, "LANGID")) {
return;
}
printf("TRANSLATE ");
print_escaped_string_helper(stdout, in, 1, 0);
if (FindTranslation(in)) {
printf(" ");
print_escaped_string_helper(stdout, tr(in), 1, 0);
printf("\n");
} else {
printf(" \"\"\n");
}
}
static void
GenerateTranslationTemplate(void)
{
int i;
printf("# Translation table template\n\n");
printf("TRANSLATE \"LANGID\" ");
print_escaped_string_helper(stdout, tr("LANGID"), 1, 0);
printf("\n\n");
printf("BANNER %s\n", DBufValue(&Banner));
printf("\n# Weekday Names\n");
for (i=0; i<7; i++) {
printf("SET $%s ", DayName[i]);
print_escaped_string_helper(stdout, tr(DayName[i]), 1, 0);
printf("\n");
}
printf("\n# Month Names\n");
for (i=0; i<12; i++) {
printf("SET $%s ", MonthName[i]);
print_escaped_string_helper(stdout, tr(MonthName[i]), 1, 0);
printf("\n");
}
printf("\n# Other Translation-related System Variables\n");
GenerateSysvarTranslationTemplates();
printf("\n# Error Messages\n");
for (i=0; i<NumErrs; i++) {
TranslationTemplate(ErrMsg[i]);
}
printf("\n# Other Messages\n");
for (i=0; translatables[i] != NULL; i++) {
TranslationTemplate(translatables[i]);
}
}
/***************************************************************/
/* */
/* AllocateXlateItem - Allocate a new translation item */
@@ -113,6 +178,17 @@ ClearTranslationTable(void)
void
print_escaped_string(FILE *fp, char const *s)
{
print_escaped_string_helper(fp, s, 0, 0);
}
static void
print_escaped_string_json(FILE *fp, char const *s)
{
print_escaped_string_helper(fp, s, 0, 1);
}
void
print_escaped_string_helper(FILE *fp, char const *s, int esc_for_remind, int json) {
putc('"', fp);
while(*s) {
switch(*s) {
@@ -126,7 +202,20 @@ print_escaped_string(FILE *fp, char const *s)
case '"': putc('\\', fp); putc('"', fp); break;
case '\\': putc('\\', fp); putc('\\', fp); break;
default:
putc(*s, fp); break;
if ((*s > 0 && *s < 32) || *s == 0x7f) {
if (json) {
fprintf(fp, "\\u%04x", (unsigned int) *s);
} else {
fprintf(fp, "\\x%02x", (unsigned int) *s);
}
} else {
if (esc_for_remind && *s == '[') {
fprintf(fp, "[\"[\"]");
} else {
putc(*s, fp);
}
}
break;
}
s++;
}
@@ -171,9 +260,9 @@ DumpTranslationTable(FILE *fp, int json)
fprintf(fp, ",");
}
done=1;
print_escaped_string(fp, item->orig);
print_escaped_string_json(fp, item->orig);
fprintf(fp, ":");
print_escaped_string(fp, item->translated);
print_escaped_string_json(fp, item->translated);
}
item = hash_table_next(&TranslationTable, item);
}
@@ -221,7 +310,13 @@ FindTranslation(char const *orig)
int
InsertTranslation(char const *orig, char const *translated)
{
XlateItem *item = FindTranslation(orig);
XlateItem *item;
if (!printf_formatters_are_safe(orig, translated)) {
Eprint(tr("Invalid translation: Both original and translated must have the same printf-style formatting sequences in the same order."));
return E_PARSE_ERR;
}
item = FindTranslation(orig);
if (item) {
if (!strcmp(item->translated, translated)) {
/* Translation is the same; do nothing */
@@ -230,7 +325,6 @@ InsertTranslation(char const *orig, char const *translated)
RemoveTranslation(item);
}
/* TRANSLATE "foo" "foo" means to remove the translation */
if (strcmp(orig, "LANGID") && (!strcmp(orig, translated))) {
return OK;
}
@@ -353,6 +447,12 @@ DoTranslate(ParsePtr p)
ClearTranslationTable();
return OK;
}
if (!StrCmpi(DBufValue(&orig), "generate")) {
DBufFree(&orig);
if (r) return r;
GenerateTranslationTemplate();
return OK;
}
return E_PARSE_ERR;
}
@@ -391,3 +491,46 @@ dump_translation_hash_stats(void)
hash_table_dump_stats(&TranslationTable, ErrFp);
}
static void
get_printf_escapes(char const *str, DynamicBuffer *out)
{
char const *s = str;
while(*s) {
if (*s == '%' && *(s+1) != 0) {
/* %% is safe and does not need to be replicated in translation */
if (*(s+1) == '%') {
s += 2;
continue;
}
s++;
DBufPutc(out, *s);
while (*s && *(s+1) && strchr("#0- +'I%123456789.hlqLjzZt", *s)) {
s++;
DBufPutc(out, *s);
}
}
s++;
}
}
static int
printf_formatters_are_safe(char const *orig, char const *translated)
{
DynamicBuffer origEscapes;
DynamicBuffer translatedEscapes;
int dangerous;
DBufInit(&origEscapes);
DBufInit(&translatedEscapes);
get_printf_escapes(orig, &origEscapes);
get_printf_escapes(translated, &translatedEscapes);
dangerous = strcmp(DBufValue(&origEscapes), DBufValue(&translatedEscapes));
if (dangerous) {
return 0;
}
return 1;
}

View File

@@ -12,8 +12,9 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "protos.h"
#include "globals.h"
@@ -451,8 +452,8 @@ AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int sav
r = today;
if (DebugFlag & DB_PRTTRIG) {
FromDSE(r, &y, &m, &d);
fprintf(ErrFp, "%s(%d): Trig(adj) = %s, %d %s, %d",
FileName, LineNo,
fprintf(ErrFp, "%s(%s): Trig(adj) = %s, %d %s, %d",
FileName, line_range(LineNoStart, LineNo),
get_day_name(r % 7),
d,
get_month_name(m),
@@ -579,8 +580,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
if (result == -1) {
trig->expired = 1;
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",
FileName, LineNo, GetErr(E_EXPIRED));
fprintf(ErrFp, "%s(%s): %s\n",
FileName, line_range(LineNoStart, LineNo), GetErr(E_EXPIRED));
}
return -1;
}
@@ -602,8 +603,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
}
if (DebugFlag & DB_PRTTRIG) {
FromDSE(result, &y, &m, &d);
fprintf(ErrFp, "%s(%d): Trig = %s, %d %s, %d",
FileName, LineNo,
fprintf(ErrFp, "%s(%s): Trig = %s, %d %s, %d",
FileName, line_range(LineNoStart, LineNo),
get_day_name(result % 7),
d,
get_month_name(m),
@@ -629,8 +630,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
trig->rep == NO_REP) {
trig->expired = 1;
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",
FileName, LineNo, GetErr(E_EXPIRED));
fprintf(ErrFp, "%s(%s): %s\n",
FileName, line_range(LineNoStart, LineNo), GetErr(E_EXPIRED));
}
if (save_in_globals) {
LastTriggerDate = result;
@@ -654,8 +655,8 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
}
trig->expired = 1;
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",
FileName, LineNo, GetErr(E_EXPIRED));
fprintf(ErrFp, "%s(%s): %s\n",
FileName, line_range(LineNoStart, LineNo), GetErr(E_EXPIRED));
}
return -1;
}
@@ -668,3 +669,151 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
return -1;
}
/***************************************************************/
/* */
/* NewTrigInfo */
/* */
/* Create a new TrigInfo object with the specified contents. */
/* Returns NULL if memory allocation fails. */
/* */
/***************************************************************/
TrigInfo *
NewTrigInfo(char const *i)
{
TrigInfo *ti = malloc(sizeof(TrigInfo));
if (!ti) {
return NULL;
}
ti->next = NULL;
ti->info = StrDup(i);
if (!ti->info) {
free(ti);
return NULL;
}
return ti;
}
/***************************************************************/
/* */
/* FreeTrigInfo */
/* */
/* Free a TrigInfo objects. */
/* */
/***************************************************************/
void
FreeTrigInfo(TrigInfo *ti)
{
if (ti->info) {
free( (void *) ti->info);
ti->info = NULL;
}
ti->next = NULL;
free(ti);
}
void
FreeTrigInfoChain(TrigInfo *ti)
{
TrigInfo *next;
while(ti) {
next = ti->next;
FreeTrigInfo(ti);
ti = next;
}
}
/***************************************************************/
/* */
/* AppendTrigInfo */
/* */
/* Append an info item to a trigger. */
/* */
/***************************************************************/
int
AppendTrigInfo(Trigger *t, char const *info)
{
TrigInfo *ti;
TrigInfo *last;
if (!TrigInfoIsValid(info)) {
Eprint("%s", tr("Invalid INFO string: Must be of the form \"Header: Value\""));
return E_PARSE_ERR;
}
ti = NewTrigInfo(info);
last = t->infos;
if (!ti) {
return E_NO_MEM;
}
if (!last) {
t->infos = ti;
return OK;
}
if (TrigInfoHeadersAreTheSame(info, last->info)) {
Eprint("%s", tr("Duplicate INFO headers are not permitted"));
FreeTrigInfo(ti);
return E_PARSE_ERR;
}
while (last->next) {
last = last->next;
if (TrigInfoHeadersAreTheSame(info, last->info)) {
Eprint("%s", tr("Duplicate INFO headers are not permitted"));
FreeTrigInfo(ti);
return E_PARSE_ERR;
}
}
last->next = ti;
return OK;
}
int
TrigInfoHeadersAreTheSame(char const *i1, char const *i2)
{
char const *c1 = strchr(i1, ':');
char const *c2 = strchr(i2, ':');
if (!c1 || !c2) return 1;
if (c1 - i1 != c2 - i2) return 0;
if (!strncasecmp(i1, i2, (c1 - i1))) return 1;
return 0;
}
int
TrigInfoIsValid(char const *info)
{
char const *t;
char const *s = strchr(info, ':');
if (!s) return 0;
if (s == info) return 0;
t = info;
while (t < s) {
if (isspace(*t) || iscntrl(*t)) return 0;
t++;
}
return 1;
}
char const *
FindTrigInfo(Trigger *t, char const *header)
{
TrigInfo *ti;
size_t len;
char const *s;
if (!t || !header || !*header) return NULL;
ti = t->infos;
len = strlen(header);
while(ti) {
if (!strncasecmp(ti->info, header, len) &&
ti->info[len] == ':') {
s = ti->info + len + 1;
while(isspace(*s)) s++;
return s;
}
ti = ti->next;
}
return NULL;
}

View File

@@ -107,6 +107,11 @@ typedef struct var {
Value v;
} Var;
typedef struct triginfo {
struct triginfo *next;
char const *info;
} TrigInfo;
/* A trigger */
typedef struct {
int expired;
@@ -138,6 +143,7 @@ typedef struct {
char omitfunc[VAR_NAME_LEN+1]; /* OMITFUNC function */
DynamicBuffer tags;
char passthru[PASSTHRU_LEN+1];
TrigInfo *infos;
} Trigger;
/* A time trigger */
@@ -200,14 +206,15 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define NO_MAX 127
/* DEFINES for debugging flags */
#define DB_PRTLINE 0x01
#define DB_PRTEXPR 0x02
#define DB_PRTTRIG 0x04
#define DB_DUMP_VARS 0x08
#define DB_ECHO_LINE 0x10
#define DB_TRACE_FILES 0x20
#define DB_PARSE_EXPR 0x40
#define DB_HASHSTATS 0x80
#define DB_PRTLINE 0x001
#define DB_PRTEXPR 0x002
#define DB_PRTTRIG 0x004
#define DB_DUMP_VARS 0x008
#define DB_ECHO_LINE 0x010
#define DB_TRACE_FILES 0x020
#define DB_PARSE_EXPR 0x040
#define DB_HASHSTATS 0x080
#define DB_TRANSLATE 0x100
/* Enumeration of the tokens */
enum TokTypes
@@ -216,9 +223,9 @@ enum TokTypes
T_Date, T_DateTime, T_Day, T_Debug, T_Delta, T_Dumpvars, T_Duration,
T_Else, T_Empty, T_EndIf, T_ErrMsg, T_Exit, T_Expr,
T_Flush, T_Frename, T_Fset, T_Funset, T_If, T_IfTrig, T_In,
T_Include, T_IncludeCmd, T_IncludeR, T_IncludeSys, T_LastBack, T_LongTime,
T_MaybeUncomputable, T_Month, T_NoQueue, T_Number, T_Omit, T_OmitFunc,
T_Once, T_Ordinal, T_Pop, T_Preserve, T_Priority, T_Push,T_Rem,
T_Include, T_IncludeCmd, T_IncludeR, T_IncludeSys, T_Info, T_LastBack,
T_LongTime, T_MaybeUncomputable, T_Month, T_NoQueue, T_Number, T_Omit,
T_OmitFunc, T_Once, T_Ordinal, T_Pop, T_Preserve, T_Priority, T_Push,T_Rem,
T_RemType, T_Rep, T_Scanfrom, T_Sched, T_Set, T_Skip, T_Tag, T_Through,
T_Time, T_Translate, T_UnSet, T_Until, T_Warn, T_WkDay, T_Year
};
@@ -301,5 +308,6 @@ typedef struct udf_struct {
int nargs;
char const *filename;
int lineno;
int lineno_start;
int recurse_flag;
} UserFunc;

View File

@@ -208,8 +208,8 @@ int DoFset(ParsePtr p)
return OK;
}
/* Warn about redefinition */
Wprint("Function %s redefined (previously defined at %s:%d)",
existing->name, existing->filename, existing->lineno);
Wprint(tr("Function `%s' redefined: previously defined at %s(%s)"),
existing->name, existing->filename, line_range(existing->lineno_start, existing->lineno));
}
/* Should be followed by '(' */
@@ -238,6 +238,7 @@ int DoFset(ParsePtr p)
return E_NO_MEM;
}
func->lineno = LineNo;
func->lineno_start = LineNoStart;
func->recurse_flag = 0;
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
@@ -352,7 +353,7 @@ int DoFset(ParsePtr p)
/* Add the function definition */
FSet(func);
if (orig_namelen > VAR_NAME_LEN) {
Wprint("Warning: Function name `%s...' truncated to `%s'",
Wprint(tr("Warning: Function name `%s...' truncated to `%s'"),
func->name, func->name);
}
return OK;

View File

@@ -192,6 +192,7 @@ typedef struct cs_s {
char const *filename;
char const *func;
int lineno;
int lineno_start;
} cs;
static cs *callstack = NULL;
@@ -206,7 +207,7 @@ destroy_cs(cs *entry)
int
push_call(char const *filename, char const *func, int lineno)
push_call(char const *filename, char const *func, int lineno, int lineno_start)
{
cs *entry;
if (freecs) {
@@ -222,6 +223,7 @@ push_call(char const *filename, char const *func, int lineno)
entry->filename = filename;
entry->func = func;
entry->lineno = lineno;
entry->lineno_start = lineno_start;
entry->next = callstack;
callstack = entry;
return OK;
@@ -244,17 +246,18 @@ static void
print_callstack_aux(FILE *fp, cs *entry)
{
int i = 0;
char const *in = "In";
char const *in = tr("In");
cs *prev = NULL;
while(entry) {
if (prev) {
in = "Called from";
in = tr("Called from");
}
if (!prev || strcmp(prev->func, entry->func) || strcmp(prev->filename, entry->filename) || prev->lineno != entry->lineno) {
if (prev) {
fprintf(fp, "\n");
}
(void) fprintf(fp, " %s(%d): [#%d] %s function `%s'", entry->filename, entry->lineno, i, in, entry->func);
fprintf(fp, " ");
fprintf(fp, tr("%s(%s): [#%d] %s function `%s'"), entry->filename, line_range(entry->lineno_start, entry->lineno), i, in, entry->func);
}
prev = entry;
entry = entry->next;
@@ -264,7 +267,10 @@ print_callstack_aux(FILE *fp, cs *entry)
}
}
if (entry) {
(void) fprintf(fp, "\n [remaining call frames omitted]");
(void) fprintf(fp, "\n [");
(void) fprintf(fp, "%s", tr("remaining call frames omitted"));
(void) fprintf(fp, "]");
}
}
@@ -285,3 +291,15 @@ pop_call(void)
destroy_cs(entry);
}
}
char const *
line_range(int lineno_start, int lineno)
{
static char buf[128];
if (lineno_start == lineno) {
snprintf(buf, sizeof(buf), "%d", lineno);
} else {
snprintf(buf, sizeof(buf), "%d:%d", lineno_start, lineno);
}
return buf;
}

View File

@@ -85,7 +85,7 @@ strtod_in_c_locale(char const *str, char **endptr)
if (**endptr) {
x = strtod(str, endptr);
if (!**endptr) {
Wprint("Accepting \"%s\" for $Latitude/$Longitude, but you should use the \"C\" locale decimal separator \".\" instead", str);
Wprint(tr("Accepting \"%s\" for $Latitude/$Longitude, but you should use the \"C\" locale decimal separator \".\" instead"), str);
}
}
return x;
@@ -94,7 +94,7 @@ strtod_in_c_locale(char const *str, char **endptr)
static void deprecated_var(char const *var, char const *instead)
{
if (DebugFlag & DB_PRTLINE) {
Wprint("%s is deprecated; use %s instead", var, instead);
Wprint(tr("%s is deprecated; use %s instead"), var, instead);
}
}
@@ -201,7 +201,7 @@ static int oncefile_func(int do_set, Value *val)
}
if (ProcessedOnce) {
Wprint("Not setting $OnceFile: Already processed a reminder with a ONCE clause");
Wprint(tr("Not setting $OnceFile: Already processed a reminder with a ONCE clause"));
return OK;
}
if (OnceFile) {
@@ -608,7 +608,7 @@ int DoSet (Parser *p)
}
if (p->isnested) {
Eprint("%s", "Do not use [] around expression in SET command");
Eprint("%s", tr("Do not use [] around expression in SET command"));
return E_CANTNEST_FDEF;
}
@@ -628,7 +628,7 @@ int DoSet (Parser *p)
if (*DBufValue(&buf) == '$') r = SetSysVar(DBufValue(&buf)+1, &v);
else r = SetVar(DBufValue(&buf), &v);
if (buf.len > VAR_NAME_LEN) {
Wprint("Warning: Variable name `%.*s...' truncated to `%.*s'",
Wprint(tr("Warning: Variable name `%.*s...' truncated to `%.*s'"),
VAR_NAME_LEN, DBufValue(&buf), VAR_NAME_LEN, DBufValue(&buf));
}
DBufFree(&buf);
@@ -1078,7 +1078,7 @@ int GetSysVar(char const *name, Value *val)
/* In "verbose" mode, print attempts to test $RunOff */
if (DebugFlag & DB_PRTLINE) {
if (v->value == (void *) &RunDisabled) {
Wprint("(Security note: $RunOff variable tested.)");
Wprint(tr("(Security note: $RunOff variable tested.)"));
}
}
return OK;
@@ -1239,6 +1239,46 @@ set_components_from_lat_and_long(void)
}
}
void GenerateSysvarTranslationTemplates(void)
{
int i;
int j;
char const *msg;
for (i=0; i< (int) NUMSYSVARS; i++) {
if (SysVarArr[i].type == TRANS_TYPE) {
int done=0;
msg = (char const *) SysVarArr[i].value;
/* We've already done month and day names */
for (j=0; j<7; j++) {
if (!strcmp(msg, DayName[j])) {
done=1;
break;
}
}
if (done) {
continue;
}
for (j=0; j<12; j++) {
if (!strcmp(msg, MonthName[j])) {
done=1;
break;
}
}
if (done) {
continue;
}
printf("SET $%s ", SysVarArr[i].name);
print_escaped_string_helper(stdout, tr(msg), 1, 0);
printf("\n");
} else if (!strcmp(SysVarArr[i].name, "Hplu") ||
!strcmp(SysVarArr[i].name, "Mplu")) {
msg = * (char const **) SysVarArr[i].value;
printf("SET $%s ", SysVarArr[i].name);
print_escaped_string_helper(stdout, tr(msg), 1, 0);
printf("\n");
}
}
}
void
print_sysvar_tokens(void)
{

View File

@@ -1,6 +1,6 @@
FSET msgprefix(x) "Priority: " + x + "; Filename: " + filename() + ": "
REM at 23:56 MSG foo
REM PRIORITY 42 at 23:57 MSG bar
REM PRIORITY 999 at 23:58 MSG quux
REM PRIORITY 42 at 23:57 INFO "Info: yuppers" MSG bar
REM PRIORITY 999 at 23:58 INFO "Info2: Nope" INFO "Info3: heh" MSG quux
DO queue2.rem

View File

@@ -32,20 +32,10 @@ fi
# ../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"
# Set a known timezone so moon phases show up in predictable places
TZ=UTC
export TZ
RESULT=`(echo 'BANNER %'; echo 'IF now() > 23:55'; echo 'MSG late%'; echo 'ENDIF') | ../src/remind -h -`
if test "$RESULT" = "late" ; then
echo ""
echo "*** Please do not run the test suite between 23:55 and 00:00 UTC; it will fail."
echo ""
exit 1
fi
# If we're already in a utf-8 locale, do
# nothing; otherwise, set LC_ALL
OK=0
@@ -191,11 +181,13 @@ EOF
../src/remind -pppq - 1 Jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM 2 MSG Normal
SET $DefaultColor "255 0 0"
REM 3 MSG %"Red%" on the calendar!
REM 3 \
MSG %"Red%" on the calendar!
SET $DefaultColor "-1 -1 -1"
REM 4 MSG Normal
# Should give an error
SET $DefaultColor "256 0 0"
# Should give an error - split on two lines to test line number reporting
SET $DefaultColor \
"256 0 0"
EOF
# Test default color with weekly calendar
@@ -476,10 +468,9 @@ rm -rf include_dir/ww
../src/remind --version >> ../tests/test.out 2>&1
# Test queueing. Because eventstart depends on the actual system
# date, we have to convert it to some constant (in this case,
# VOLATILE) so that tests are not dependent on the system date.
echo JSONQUEUE | ../src/remind -z0 ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind -zj ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
# date, we use the --test flag to fake the date and time.
echo JSONQUEUE | ../src/remind --test -z0 ../tests/queue1.rem >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind --test -zj ../tests/queue1.rem >> ../tests/test.out 2>&1
# Test for leap year bug that was fixed
../src/remind -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
@@ -633,6 +624,88 @@ fi
# Torture test #2
../src/remind ../tests/torture2.rem >> ../tests/test.out 2>&1
# Expression error-reporting
../src/remind -de - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
set a 8 * "]]]" & 6
msg [8 * "]]]" & 6] is weird
set a 9 *
set a 9 * ]
EOF
# Translation template generateion
../src/remind -h - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
TRANSLATE GENERATE
EOF
# Make sure stupidly-long translations of "am" and "pm" can't cause a
# segmentation fault
../src/remind -c - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
TRANS "am" "alsdkjalksdj alksjd alksdj alksjd laksjd laksjd laksjd laksjd laksjd laksjd laksjd laksjd lkasjd laksjd laksjd lkajs dlkajs dlkasj dlkasjd lkajsd lkajs dlkasjd lkasj dlkajsd lkasjd lkasjd laksjd laksjd laksjd alskdj alskdj alksdj alksdj alskdj alksdj aslkdj"
TRANS "pm" "oiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwjwwwwwwwwwwwwwwwjwpqoejkpqwojepqowjepqojwepqowjepqowjepqowjepqowjepqowjepqowjepqojwepqowjepqowjepqowjepqowjepqowjeqpweoj"
REM WED AT 11:00 MSG wookie
REM WED AT 13:00 MSG blah
EOF
# The INFO keyword
../src/remind -pp - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
REM Wed INFO "Location: here" INFO "Summary: Nope" MSG Meeting [triginfo("location")] %<summary> %<nonexist> [triginfo("cabbage")]
EOF
# Invalid info strings
../src/remind - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
REM Thu INFO "Invalid" MSG wookie
REM Fri INFO ": foo" MSG blat
REM Sun INFO "foo bar baz : blork" MSG uua
# Duplicate info string
REM Sat INFO "Location: here" INFO "location: there" MSG blort
EOF
# Test parsing of quoted strings and the "escape" function
../src/remind - 1 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
SET $AddBlankLines 0
TRANSLATE "foo" "test: \\\" \a\b\f\\n\r\t\v\x3\x1b haha"
REM msg foo translation = %(foo)
FLUSH
set a "vartest: \\\" \a\b\f\\n\r\t\v\x3\x1b haha"
REM msg a = [a]
FLUSH
dump a
set b escape(a)
REM msg b = [b]
set b escape(a,1)
REM msg b = [b]
FLUSH
dump b
FLUSH
set a "\x"
dump a
FLUSH
set a "\xPOO"
dump a
FLUSH
set a "\x0"
set a "\x00"
set a "\x0P"
set a "\x00P"
EOF
# Test diagnostics when using a timed substitution without an AT clause
../src/remind - 1 Feb 2024 1:00 <<EOF >> ../tests/test.out 2>&1
REM MSG %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %! hahaha
EOF
# Test translate table dumping
../src/remind - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
TRANSLATE "\x03" "BREAK"
TRANSLATE DUMP
EOF
../src/remind -ppp - 1 Feb 2024 <<EOF >> ../tests/test.out 2>&1
TRANSLATE "\x03" "BREAK"
EOF
# Languages
for i in ../include/lang/??.rem ; do
../src/remind -r -q "-ii=\"$i\"" ../tests/tstlang.rem 1 Feb 2024 13:34 >> ../tests/test.out 2>&1

View File

@@ -801,12 +801,12 @@ Leaving UserFN _ofunc(1991-02-28) => 0
# omitfunc ignores local/global omits
fset _ofunc(x) 0
../tests/test.rem(227): Function _ofunc redefined (previously defined at ../tests/test.rem:222)
../tests/test.rem(227): Function `_ofunc' redefined: previously defined at ../tests/test.rem(222)
OMIT 1 March
OMIT 2 March 1991
REM 1 March OMIT Sun OMITFUNC _ofunc AFTER MSG Should trigger 1 March
../tests/test.rem(230): Warning: OMIT is ignored if you use OMITFUNC
../tests/test.rem(230): OMITFUNC function `_ofunc' defined at ../tests/test.rem:227 does not use its argument
../tests/test.rem(230): OMITFUNC function `_ofunc' defined at ../tests/test.rem(227) does not use its argument
Entering UserFN _ofunc(1991-02-15)
Leaving UserFN _ofunc(1991-02-15) => 0
Entering UserFN _ofunc(1991-03-01)
@@ -1047,7 +1047,7 @@ set a057 value("a05"+"6")
"a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version()
version() => "05.02.02"
version() => "05.03.03"
set a059 wkday(today())
today() => 1991-02-16
wkday(1991-02-16) => "Saturday"
@@ -2611,7 +2611,7 @@ a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a007 "1991-02-16"
a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a008 "11:44"
a058 "05.02.02"
a058 "05.03.03"
a059 "Saturday"
a010 12
a060 6
@@ -3509,6 +3509,8 @@ REM AT 13:00PM MSG foo 13p
DEBUG +x
SET x 0:00am + 0
../tests/test.rem(629): Ill-formed time: `0:00am'
0:00am + 0
^-- here
SET x 1:00AM + 0
01:00 + 0 => 01:00
SET x 2:00am + 0
@@ -3535,9 +3537,13 @@ SET x 12:00am + 0
00:00 + 0 => 00:00
SET x 13:00AM + 0
../tests/test.rem(642): Ill-formed time: `13:00AM'
13:00AM + 0
^-- here
SET x 0:00pm + 0
../tests/test.rem(644): Ill-formed time: `0:00pm'
0:00pm + 0
^-- here
SET x 1:00PM + 0
13:00 + 0 => 13:00
SET x 2:00pm + 0
@@ -3564,9 +3570,13 @@ SET x 12:00pm + 0
12:00 + 0 => 12:00
SET x 13:00PM + 0
../tests/test.rem(657): Ill-formed time: `13:00PM'
13:00PM + 0
^-- here
SET x '2015-02-03@0:00am' + 0
../tests/test.rem(659): Ill-formed time: '2015-02-03@0:00am'
'2015-02-03@0:00am' + 0
^-- here
SET x '2015-02-03@1:00AM' + 0
2015-02-03@01:00 + 0 => 2015-02-03@01:00
SET x '2015-02-03@2:00am' + 0
@@ -3593,9 +3603,13 @@ SET x '2015-02-03@12:00am' + 0
2015-02-03@00:00 + 0 => 2015-02-03@00:00
SET x '2015-02-03@13:00AM' + 0
../tests/test.rem(672): Ill-formed time: '2015-02-03@13:00AM'
'2015-02-03@13:00AM' + 0
^-- here
SET x '2015-02-03@0:00pm' + 0
../tests/test.rem(674): Ill-formed time: '2015-02-03@0:00pm'
'2015-02-03@0:00pm' + 0
^-- here
SET x '2015-02-03@1:00PM' + 0
2015-02-03@13:00 + 0 => 2015-02-03@13:00
SET x '2015-02-03@2:00pm' + 0
@@ -3622,6 +3636,8 @@ SET x '2015-02-03@12:00pm' + 0
2015-02-03@12:00 + 0 => 2015-02-03@12:00
SET x '2015-02-03@13:00PM' + 0
../tests/test.rem(687): Ill-formed time: '2015-02-03@13:00PM'
'2015-02-03@13:00PM' + 0
^-- here
# Test the ampm function
set x ampm(0:12) + ""
@@ -3988,7 +4004,7 @@ psmoon(0) => ../tests/test.rem(813): psmoon() is deprecated; use SPECIAL MOON in
FSET _f(x) 0
SET tmp evaltrig("Wed SKIP OMITFUNC _f",date(1992,1,8))
date(1992, 1, 8) => 1992-01-08
evaltrig("Wed SKIP OMITFUNC _f", 1992-01-08) => ../tests/test.rem(817): OMITFUNC function `_f' defined at ../tests/test.rem:816 does not use its argument
evaltrig("Wed SKIP OMITFUNC _f", 1992-01-08) => ../tests/test.rem(817): OMITFUNC function `_f' defined at ../tests/test.rem(816) does not use its argument
Entering UserFN _f(1992-01-08)
Leaving UserFN _f(1992-01-08) => 0
../tests/test.rem(817): Trig = Wednesday, 8 January, 1992
@@ -4034,7 +4050,7 @@ ENDIF
# Trig with a good warnfunc
FSET w(x) choose(x, 5, 3, 1, 0)
../tests/test.rem(832): Function w redefined (previously defined at ../tests/test.rem:826)
../tests/test.rem(832): Function `w' redefined: previously defined at ../tests/test.rem(826)
# Short-circuit operators
IF trig("sun warn w") || trig("thu warn w")
@@ -4982,7 +4998,7 @@ Undefined:
FSET subst_bad() "foo"
REM MSG %{bad}
../tests/test.rem(933): Trig = Saturday, 16 February, 1991
../tests/test.rem(933): Function `subst_bad' defined at ../tests/test.rem:932 should take 3 arguments, but actually takes 0
../tests/test.rem(933): Function `subst_bad' defined at ../tests/test.rem(932) should take 3 arguments, but actually takes 0
@@ -4990,8 +5006,8 @@ FSET subst_ampm(a, b, c, d, e, f, g) "wookie"
REM AT 11:00 MSG %2
../tests/test.rem(937): Trig = Saturday, 16 February, 1991 AT 11:00
../tests/test.rem(937): Function `subst_ampm' defined at ../tests/test.rem:935 should take 1 argument, but actually takes 7
../tests/test.rem(937): Function `subst_ampm' defined at ../tests/test.rem:935 should take 1 argument, but actually takes 7
../tests/test.rem(937): Function `subst_ampm' defined at ../tests/test.rem(935) should take 1 argument, but actually takes 7
../tests/test.rem(937): Function `subst_ampm' defined at ../tests/test.rem(935) should take 1 argument, but actually takes 7
at 11:00am
FUNSET subst_ampm
@@ -5166,7 +5182,7 @@ FSET f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a1
# This should give an error
FSET f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64) 3
../tests/test.rem(1023): Function f redefined (previously defined at ../tests/test.rem:1020)
../tests/test.rem(1023): Function `f' redefined: previously defined at ../tests/test.rem(1020)
../tests/test.rem(1023): Too many arguments
# Check that SATISFY expressions that don't reference trigdate are diagnosed
@@ -5616,8 +5632,8 @@ REM SATISFY ""
REM SATISFY [version() > "01.00.00"]
../tests/test.rem(1050): SATISFY: expression has no reference to trigdate() or $T...
../tests/test.rem(1050): Trig = Saturday, 16 February, 1991
version() => "05.02.02"
"05.02.02" > "01.00.00" => 1
version() => "05.03.03"
"05.03.03" > "01.00.00" => 1
../tests/test.rem(1050): Trig(satisfied) = Saturday, 16 February, 1991
REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * 5]
../tests/test.rem(1051): SATISFY: expression has no reference to trigdate() or $T...
@@ -5631,63 +5647,63 @@ max(3, 3, 4, 5, 6) => 6
FSET gg(x) 0
REM WARN gg MSG Wookie
../tests/test.rem(1054): WARN function `gg' defined at ../tests/test.rem:1053 does not use its argument
../tests/test.rem(1054): WARN function `gg' defined at ../tests/test.rem(1053) does not use its argument
../tests/test.rem(1054): Trig = Saturday, 16 February, 1991
Entering UserFN gg(1)
Leaving UserFN gg(1) => 0
Wookie
REM AT 11:00 SCHED gg MSG blork
../tests/test.rem(1055): SCHED function `gg' defined at ../tests/test.rem:1053 does not use its argument
../tests/test.rem(1055): SCHED function `gg' defined at ../tests/test.rem(1053) does not use its argument
../tests/test.rem(1055): Trig = Saturday, 16 February, 1991 AT 11:00
blork
REM OMITFUNC gg MSG hehe
../tests/test.rem(1056): OMITFUNC function `gg' defined at ../tests/test.rem:1053 does not use its argument
../tests/test.rem(1056): OMITFUNC function `gg' defined at ../tests/test.rem(1053) does not use its argument
../tests/test.rem(1056): Trig = Saturday, 16 February, 1991
hehe
FSET gg(x,y,z) 0
../tests/test.rem(1058): Function gg redefined (previously defined at ../tests/test.rem:1053)
../tests/test.rem(1058): Function `gg' redefined: previously defined at ../tests/test.rem(1053)
REM WARN gg MSG Wookie
../tests/test.rem(1059): WARN function `gg' defined at ../tests/test.rem:1058 should take 1 argument but actually takes 3
../tests/test.rem(1059): WARN function `gg' defined at ../tests/test.rem(1058) should take 1 argument but actually takes 3
../tests/test.rem(1059): Trig = Saturday, 16 February, 1991
../tests/test.rem(1059): Undefined WARN function: `gg'
Wookie
REM AT 11:00 SCHED gg MSG blork
../tests/test.rem(1060): SCHED function `gg' defined at ../tests/test.rem:1058 should take 1 argument but actually takes 3
../tests/test.rem(1060): SCHED function `gg' defined at ../tests/test.rem(1058) should take 1 argument but actually takes 3
../tests/test.rem(1060): Trig = Saturday, 16 February, 1991 AT 11:00
blork
REM OMITFUNC gg MSG hehe
../tests/test.rem(1061): OMITFUNC function `gg' defined at ../tests/test.rem:1058 should take 1 argument but actually takes 3
../tests/test.rem(1061): OMITFUNC function `gg' defined at ../tests/test.rem(1058) should take 1 argument but actually takes 3
../tests/test.rem(1061): Trig = Saturday, 16 February, 1991
hehe
FSET gg() 0
../tests/test.rem(1063): Function gg redefined (previously defined at ../tests/test.rem:1058)
../tests/test.rem(1063): Function `gg' redefined: previously defined at ../tests/test.rem(1058)
REM WARN gg MSG Wookie
../tests/test.rem(1064): WARN function `gg' defined at ../tests/test.rem:1063 should take 1 argument but actually takes 0
../tests/test.rem(1064): WARN function `gg' defined at ../tests/test.rem(1063) should take 1 argument but actually takes 0
../tests/test.rem(1064): Trig = Saturday, 16 February, 1991
../tests/test.rem(1064): Undefined WARN function: `gg'
Wookie
REM AT 11:00 SCHED gg MSG blork
../tests/test.rem(1065): SCHED function `gg' defined at ../tests/test.rem:1063 should take 1 argument but actually takes 0
../tests/test.rem(1065): SCHED function `gg' defined at ../tests/test.rem(1063) should take 1 argument but actually takes 0
../tests/test.rem(1065): Trig = Saturday, 16 February, 1991 AT 11:00
blork
REM OMITFUNC gg MSG hehe
../tests/test.rem(1066): OMITFUNC function `gg' defined at ../tests/test.rem:1063 should take 1 argument but actually takes 0
../tests/test.rem(1066): OMITFUNC function `gg' defined at ../tests/test.rem(1063) should take 1 argument but actually takes 0
../tests/test.rem(1066): Trig = Saturday, 16 February, 1991
hehe
FSET gg(x) x-x
../tests/test.rem(1068): Function gg redefined (previously defined at ../tests/test.rem:1063)
../tests/test.rem(1068): Function `gg' redefined: previously defined at ../tests/test.rem(1063)
REM WARN gg MSG Wookie
../tests/test.rem(1069): Trig = Saturday, 16 February, 1991
Entering UserFN gg(1)
@@ -5957,7 +5973,7 @@ Hello
On the next line
FSET msgsuffix(x) char(8) + " on the same line"
../tests/test.rem(1184): Function msgsuffix redefined (previously defined at ../tests/test.rem:1181)
../tests/test.rem(1184): Function `msgsuffix' redefined: previously defined at ../tests/test.rem(1181)
REM MSG Hello
../tests/test.rem(1185): Trig = Saturday, 16 February, 1991
Entering UserFN msgsuffix(5000)
@@ -22373,44 +22389,44 @@ February 29
{"date":"2012-01-23","filename":"-","lineno":1,"wd":["Monday"],"priority":5000,"omitfunc":"foo","nonconst_expr":1,"body":"bar"}
{"date":"2012-01-30","filename":"-","lineno":1,"wd":["Monday"],"priority":5000,"omitfunc":"foo","nonconst_expr":1,"body":"bar"}
# rem2ps2 end
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(7): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
-stdin-(8:9): Number too high
[
{
"translations":{"LANGID":"en"},"caltype":"monthly","monthname":"January","year":2012,"daysinmonth":31,"firstwkday":0,"mondayfirst":0,"daynames":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"prevmonthname":"December","daysinprevmonth":31,"prevmonthyear":2011,"nextmonthname":"February","daysinnextmonth":29,"nextmonthyear":2012,"entries":[
{"date":"2012-01-02","filename":"-","lineno":1,"d":2,"priority":5000,"body":"Normal"},
{"date":"2012-01-03","filename":"-","lineno":3,"passthru":"COLOR","d":3,"priority":5000,"r":255,"g":0,"b":0,"rawbody":"%\"Red%\" on the calendar!","calendar_body":"Red","plain_body":"Red on the calendar!","body":"255 0 0 %\"Red%\" on the calendar!"},
{"date":"2012-01-04","filename":"-","lineno":5,"d":4,"priority":5000,"body":"Normal"}
{"date":"2012-01-03","filename":"-","lineno":4,"lineno_start":3,"passthru":"COLOR","d":3,"priority":5000,"r":255,"g":0,"b":0,"rawbody":"%\"Red%\" on the calendar!","calendar_body":"Red","plain_body":"Red on the calendar!","body":"255 0 0 %\"Red%\" on the calendar!"},
{"date":"2012-01-04","filename":"-","lineno":6,"d":4,"priority":5000,"body":"Normal"}
]
}
]
@@ -23184,11 +23200,17 @@ SECURITY: Won't read world-writable file or directory!
Error reading include_dir/ww: Can't open file
SECURITY: Won't read world-writable file or directory!
Error reading include_dir/ww: No files matching *.rem
05.02.02
05.03.03
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
NOTE JSONQUEUE
[{"priority":2,"eventstart":"VOLATILE","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"VOLATILE","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"VOLATILE","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"VOLATILE","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}]
[{"priority":2,"eventstart":"2025-01-06T23:59","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"2025-01-06T23:58","info":{"info2":"Nope","info3":"heh"},"time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"2025-01-06T23:57","info":{"info":"yuppers"},"time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"2025-01-06T23:56","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}]
NOTE ENDJSONQUEUE
{"response":"queue","queue":[{"priority":2,"eventstart":"VOLATILE","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"VOLATILE","time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"VOLATILE","time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"VOLATILE","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}],"command":"QUEUE"}
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
{"response":"queue","queue":[{"priority":2,"eventstart":"2025-01-06T23:59","time":"23:59","nexttime":"23:59","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue2.rem","lineno":1,"type":"MSG_TYPE","body":"XXXX"},{"priority":999,"eventstart":"2025-01-06T23:58","info":{"info2":"Nope","info3":"heh"},"time":"23:58","nexttime":"23:58","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":5,"type":"MSG_TYPE","body":"quux"},{"priority":42,"eventstart":"2025-01-06T23:57","info":{"info":"yuppers"},"time":"23:57","nexttime":"23:57","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":4,"type":"MSG_TYPE","body":"bar"},{"priority":5000,"eventstart":"2025-01-06T23:56","time":"23:56","nexttime":"23:56","tdelta":0,"trep":0,"qid":"42424242","rundisabled":0,"ntrig":1,"filename":"../tests/queue1.rem","lineno":3,"type":"MSG_TYPE","body":"foo"}],"command":"QUEUE"}
BANNER %
REM 29 MSG One
-(2): Trig = Thursday, 29 February, 2024
@@ -23394,7 +23416,7 @@ max(6, 9, 50) => 50
Parsed expression: max(1,
=> Error: Illegal character
Unparsed: ,1)
max(1,,1)
,1)
^-- here
Parsed expression: 5%0
=> (% 5 0)
@@ -23959,6 +23981,7 @@ in
inc
include
includecmd
info
last
lastday
lastworkday
@@ -24068,6 +24091,7 @@ defined
dosubst
dusk
easterdate
escape
evaltrig
filedate
filedatetime
@@ -24140,6 +24164,7 @@ trigeventduration
trigeventstart
trigfrom
trigger
triginfo
trigpriority
trigrep
trigscanfrom
@@ -24296,6 +24321,402 @@ Translation hash table statistics:
Expression nodes high-water: 300000
Expression nodes leaked: 0
Parse level high-water: 7
set a 8 * "]]]" & 6
-stdin-(1): Parse error `&' (did you mean `&&'?)
8 * "]]]" & 6
^-- here
msg [8 * "]]]" & 6] is weird
-stdin-(2): Parse error `&' (did you mean `&&'?)
8 * "]]]" & 6
^-- here
set a 9 *
-stdin-(3): Unexpected end of line
9 *
^-- here
set a 9 * ]
-stdin-(4): Illegal character `]'
9 * ]
^-- here
Reminders for Thursday, 1st February, 2024:
No reminders.
# Translation table template
TRANSLATE "LANGID" "en"
BANNER Reminders for %w, %d%s %m, %y%o:
# Weekday Names
SET $Monday "Monday"
SET $Tuesday "Tuesday"
SET $Wednesday "Wednesday"
SET $Thursday "Thursday"
SET $Friday "Friday"
SET $Saturday "Saturday"
SET $Sunday "Sunday"
# Month Names
SET $January "January"
SET $February "February"
SET $March "March"
SET $April "April"
SET $May "May"
SET $June "June"
SET $July "July"
SET $August "August"
SET $September "September"
SET $October "October"
SET $November "November"
SET $December "December"
# Other Translation-related System Variables
SET $Ago "ago"
SET $Am "am"
SET $And "and"
SET $At "at"
SET $Fromnow "from now"
SET $Hour "hour"
SET $Hplu "s"
SET $Is "is"
SET $Minute "minute"
SET $Mplu "s"
SET $Now "now"
SET $On "on"
SET $Pm "pm"
SET $Today "today"
SET $Tomorrow "tomorrow"
SET $Was "was"
# Error Messages
TRANSLATE "Ok" ""
TRANSLATE "Missing ']'" ""
TRANSLATE "Missing quote" ""
TRANSLATE "Expression too complex" ""
TRANSLATE "Missing ')'" ""
TRANSLATE "Undefined function" ""
TRANSLATE "Illegal character" ""
TRANSLATE "Expecting binary operator" ""
TRANSLATE "Out of memory" ""
TRANSLATE "Ill-formed number" ""
TRANSLATE "Can't coerce" ""
TRANSLATE "Type mismatch" ""
TRANSLATE "Date overflow" ""
TRANSLATE "Division by zero" ""
TRANSLATE "Undefined variable" ""
TRANSLATE "Unexpected end of line" ""
TRANSLATE "Unexpected end of file" ""
TRANSLATE "I/O error" ""
TRANSLATE "Internal error" ""
TRANSLATE "Bad date specification" ""
TRANSLATE "Not enough arguments" ""
TRANSLATE "Too many arguments" ""
TRANSLATE "Ill-formed time" ""
TRANSLATE "Number too high" ""
TRANSLATE "Number too low" ""
TRANSLATE "Can't open file" ""
TRANSLATE "INCLUDE nested too deeply (max. 9)" ""
TRANSLATE "Parse error" ""
TRANSLATE "Can't compute trigger" ""
TRANSLATE "Too many nested IFs" ""
TRANSLATE "ELSE with no matching IF" ""
TRANSLATE "ENDIF with no matching IF" ""
TRANSLATE "Can't OMIT every weekday" ""
TRANSLATE "Extraneous token(s) on line" ""
TRANSLATE "POP-OMIT-CONTEXT without matching PUSH-OMIT-CONTEXT" ""
TRANSLATE "RUN disabled" ""
TRANSLATE "Domain error" ""
TRANSLATE "Invalid identifier" ""
TRANSLATE "Too many recursive function calls" ""
TRANSLATE "Cannot modify system variable" ""
TRANSLATE "C library function can't represent date/time" ""
TRANSLATE "Attempt to redefine built-in function" ""
TRANSLATE "Can't nest function definition in expression" ""
TRANSLATE "Must fully specify date to use repeat factor" ""
TRANSLATE "Year specified twice" ""
TRANSLATE "Month specified twice" ""
TRANSLATE "Day specified twice" ""
TRANSLATE "Unknown token" ""
TRANSLATE "Must specify month in OMIT command" ""
TRANSLATE "Too many full OMITs (max. 1000)" ""
TRANSLATE "Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT" ""
TRANSLATE "Error reading" ""
TRANSLATE "Expecting end-of-line" ""
TRANSLATE "Invalid Hebrew date" ""
TRANSLATE "iif(): odd number of arguments required" ""
TRANSLATE "Warning: Missing ENDIF" ""
TRANSLATE "Expecting comma" ""
TRANSLATE "Weekday specified twice" ""
TRANSLATE "Only use one of BEFORE, AFTER or SKIP" ""
TRANSLATE "Can't nest MSG, MSF, RUN, etc. in expression" ""
TRANSLATE "Repeat value specified twice" ""
TRANSLATE "Delta value specified twice" ""
TRANSLATE "Back value specified twice" ""
TRANSLATE "ONCE keyword used twice. (Hah.)" ""
TRANSLATE "Expecting time after AT" ""
TRANSLATE "THROUGH/UNTIL keyword used twice" ""
TRANSLATE "Incomplete date specification" ""
TRANSLATE "FROM/SCANFROM keyword used twice" ""
TRANSLATE "Variable" ""
TRANSLATE "Value" ""
TRANSLATE "*UNDEFINED*" ""
TRANSLATE "Entering UserFN" ""
TRANSLATE "Leaving UserFN" ""
TRANSLATE "Expired" ""
TRANSLATE "fork() failed - can't do queued reminders" ""
TRANSLATE "Can't access file" ""
TRANSLATE "Illegal system date: Year is less than %d" ""
TRANSLATE "Unknown debug flag '%c'" ""
TRANSLATE "Unknown option '%c'" ""
TRANSLATE "Unknown user '%s'" ""
TRANSLATE "Could not change gid to %d" ""
TRANSLATE "Could not change uid to %d" ""
TRANSLATE "Out of memory for environment" ""
TRANSLATE "Missing '=' sign" ""
TRANSLATE "Missing variable name" ""
TRANSLATE "Missing expression" ""
TRANSLATE "Remind: '-i' option: %s" ""
TRANSLATE "No reminders." ""
TRANSLATE "%d reminder(s) queued for later today." ""
TRANSLATE "Expecting number" ""
TRANSLATE "Undefined WARN function" ""
TRANSLATE "Can't convert between time zones" ""
TRANSLATE "No files matching *.rem" ""
TRANSLATE "String too long" ""
TRANSLATE "Time specified twice" ""
TRANSLATE "Cannot specify DURATION without specifying AT" ""
TRANSLATE "Expecting weekday name" ""
TRANSLATE "Duplicate argument name" ""
TRANSLATE "Expression evaluation is disabled" ""
TRANSLATE "Time limit for expression evaluation exceeded" ""
# Other Messages
TRANSLATE "%s function `%s' defined at %s(%s) does not use its argument" ""
TRANSLATE "%s function `%s' defined at %s(%s) should take 1 argument but actually takes %d" ""
TRANSLATE "%s is deprecated; use %s instead" ""
TRANSLATE "%s(%d): IF without ENDIF" ""
TRANSLATE "%s(%s): ["["]#%d] %s function `%s'" ""
TRANSLATE "(Security note: $RunOff variable tested.)" ""
TRANSLATE "Accepting \"%s\" for $Latitude/$Longitude, but you should use the \"C\" locale decimal separator \".\" instead" ""
TRANSLATE "Caching directory `%s' listing" ""
TRANSLATE "Caching file `%s' in memory" ""
TRANSLATE "Called from" ""
TRANSLATE "Cannot open `%s' for writing: %s" ""
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: THROUGH date earlier than start date" ""
TRANSLATE "Executing `%s' for INCLUDECMD and caching as `%s'" ""
TRANSLATE "Found cached directory listing for `%s'" ""
TRANSLATE "Function `%s' defined at %s(%s) should take %d argument%s, but actually takes %d" ""
TRANSLATE "Function `%s' redefined: previously defined at %s(%s)" ""
TRANSLATE "GetValidHebDate: Bad adarbehave value %d" ""
TRANSLATE "In" ""
TRANSLATE "Invalid INFO string: Must be of the form \"Header: Value\"" ""
TRANSLATE "Invalid translation: Both original and translated must have the same printf-style formatting sequences in the same order." ""
TRANSLATE "Missing REM type; assuming MSG" ""
TRANSLATE "No Adar A in %d" ""
TRANSLATE "No substition function `%s' defined" ""
TRANSLATE "Not setting $OnceFile: Already processed a reminder with a ONCE clause" ""
TRANSLATE "OMIT: UNTIL not allowed; did you mean THROUGH?" ""
TRANSLATE "POP-OMIT-CONTEXT at %s:%d matches PUSH-OMIT-CONTEXT in different file: %s:%d" ""
TRANSLATE "Reading `%s': Found in cache" ""
TRANSLATE "Reading `%s': Opening file on disk" ""
TRANSLATE "Reading `-': Reading stdin" ""
TRANSLATE "Reading command `%s': Found in cache" ""
TRANSLATE "SATISFY: constant 0 will never be true" ""
TRANSLATE "SATISFY: constant \"\" will never be true" ""
TRANSLATE "SATISFY: expression has no reference to trigdate() or $T..." ""
TRANSLATE "SECURITY: Won't read non-root-owned file or directory when running as root!" ""
TRANSLATE "SECURITY: Won't read world-writable file or directory!" ""
TRANSLATE "Scanning directory `%s' for *.rem files" ""
TRANSLATE "Undefined %s function: `%s'" ""
TRANSLATE "Unmatched PUSH-OMIT-CONTEXT at %s(%d)" ""
TRANSLATE "Unrecognized command; interpreting as REM" ""
TRANSLATE "Warning: Function name `%s...' truncated to `%s'" ""
TRANSLATE "Warning: OMIT is ignored if you use OMITFUNC" ""
TRANSLATE "Warning: SCANFROM is ignored in two-argument form of evaltrig()" ""
TRANSLATE "Warning: UNTIL/THROUGH date earlier than FROM date" ""
TRANSLATE "Warning: UNTIL/THROUGH date earlier than SCANFROM date" ""
TRANSLATE "Warning: UNTIL/THROUGH date earlier than start date" ""
TRANSLATE "Warning: Unable to save ONCE timestamp to %s: %s" ""
TRANSLATE "Warning: Unterminated %%(...) substitution sequence" ""
TRANSLATE "Warning: Unterminated %%<...> substitution sequence" ""
TRANSLATE "Warning: Unterminated %%{...} substitution sequence" ""
TRANSLATE "Warning: Useless use of UNTIL with fully-specified date and no *rep" ""
TRANSLATE "Warning: Variable name `%.*s...' truncated to `%.*s'" ""
TRANSLATE "You have OMITted everything! The space-time continuum is at risk." ""
TRANSLATE "\\x00 is not a valid escape sequence" ""
TRANSLATE "`%%%c' substitution sequence should not be used without an AT clause" ""
TRANSLATE "did you mean" ""
TRANSLATE "here" ""
TRANSLATE "psmoon() is deprecated; use SPECIAL MOON instead." ""
TRANSLATE "psshade() is deprecated; use SPECIAL SHADE instead." ""
TRANSLATE "remaining call frames omitted" ""
+----------------------------------------------------------------------------+
| February 2024 |
+----------+----------+----------+----------+----------+----------+----------+
| Sunday | Monday | Tuesday |Wednesday | Thursday | Friday | Saturday |
+----------+----------+----------+----------+----------+----------+----------+
| | | | |1 |2 |3 |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
+----------+----------+----------+----------+----------+----------+----------+
|4 |5 |6 |7 |8 |9 |10 |
| | | | | | | |
| | | |11:00alsdk| | | |
| | | |jalksdj | | | |
| | | |alksjd | | | |
| | | |alksdj | | | |
| | | |alksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd la | | | |
| | | |wookie | | | |
| | | | | | | |
| | | |1:00oiwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wjwwwwwwww| | | |
| | | |wwwwwwwjwp| | | |
| | | |qoejkpqw | | | |
| | | |blah | | | |
+----------+----------+----------+----------+----------+----------+----------+
|11 |12 |13 |14 |15 |16 |17 |
| | | | | | | |
| | | |11:00alsdk| | | |
| | | |jalksdj | | | |
| | | |alksjd | | | |
| | | |alksdj | | | |
| | | |alksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd la | | | |
| | | |wookie | | | |
| | | | | | | |
| | | |1:00oiwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wjwwwwwwww| | | |
| | | |wwwwwwwjwp| | | |
| | | |qoejkpqw | | | |
| | | |blah | | | |
+----------+----------+----------+----------+----------+----------+----------+
|18 |19 |20 |21 |22 |23 |24 |
| | | | | | | |
| | | |11:00alsdk| | | |
| | | |jalksdj | | | |
| | | |alksjd | | | |
| | | |alksdj | | | |
| | | |alksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd la | | | |
| | | |wookie | | | |
| | | | | | | |
| | | |1:00oiwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wjwwwwwwww| | | |
| | | |wwwwwwwjwp| | | |
| | | |qoejkpqw | | | |
| | | |blah | | | |
+----------+----------+----------+----------+----------+----------+----------+
|25 |26 |27 |28 |29 | | |
| | | | | | | |
| | | |11:00alsdk| | | |
| | | |jalksdj | | | |
| | | |alksjd | | | |
| | | |alksdj | | | |
| | | |alksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd | | | |
| | | |laksjd la | | | |
| | | |wookie | | | |
| | | | | | | |
| | | |1:00oiwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wwwwwwwwww| | | |
| | | |wjwwwwwwww| | | |
| | | |wwwwwwwjwp| | | |
| | | |qoejkpqw | | | |
| | | |blah | | | |
+----------+----------+----------+----------+----------+----------+----------+
# translations
{"LANGID":"en"}
# rem2ps2 begin
February 2024 29 4 0
Sunday Monday Tuesday Wednesday Thursday Friday Saturday
January 31
March 31
{"date":"2024-02-07","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] %<summary> %<nonexist> [triginfo(\"cabbage\")]","body":"Meeting here Nope "}
{"date":"2024-02-14","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] %<summary> %<nonexist> [triginfo(\"cabbage\")]","body":"Meeting here Nope "}
{"date":"2024-02-21","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] %<summary> %<nonexist> [triginfo(\"cabbage\")]","body":"Meeting here Nope "}
{"date":"2024-02-28","filename":"-","lineno":1,"info":{"location":"here","summary":"Nope"},"wd":["Wednesday"],"priority":5000,"rawbody":"Meeting [triginfo(\"location\")] %<summary> %<nonexist> [triginfo(\"cabbage\")]","body":"Meeting here Nope "}
# rem2ps2 end
-stdin-(1): Invalid INFO string: Must be of the form "Header: Value"
-stdin-(2): Invalid INFO string: Must be of the form "Header: Value"
-stdin-(3): Invalid INFO string: Must be of the form "Header: Value"
-stdin-(6): Duplicate INFO headers are not permitted
No reminders.
foo translation = test: \"  \n  haha
a = vartest: \"  \n  haha
Variable Value
a "vartest: \\\" \a\b\f\\n\r\t\v\x03\x1b haha"
b = vartest: \\\" \a\b\f\\n\r\t\v\x03\x1b haha
b = "vartest: \\\" \a\b\f\\n\r\t\v\x03\x1b haha"
Variable Value
b "\"vartest: \\\\\\\" \\a\\b\\f\\\\n\\r\\t\\v\\x03\\x1b h"...
Variable Value
a "x"
Variable Value
a "xPOO"
-stdin-(23): \x00 is not a valid escape sequence
-stdin-(24): \x00 is not a valid escape sequence
-stdin-(25): \x00 is not a valid escape sequence
-stdin-(26): \x00 is not a valid escape sequence
-stdin-(1): `%0' substitution sequence should not be used without an AT clause
-stdin-(1): `%1' substitution sequence should not be used without an AT clause
-stdin-(1): `%2' substitution sequence should not be used without an AT clause
-stdin-(1): `%3' substitution sequence should not be used without an AT clause
-stdin-(1): `%4' substitution sequence should not be used without an AT clause
-stdin-(1): `%5' substitution sequence should not be used without an AT clause
-stdin-(1): `%6' substitution sequence should not be used without an AT clause
-stdin-(1): `%7' substitution sequence should not be used without an AT clause
-stdin-(1): `%8' substitution sequence should not be used without an AT clause
-stdin-(1): `%9' substitution sequence should not be used without an AT clause
-stdin-(1): `%!' substitution sequence should not be used without an AT clause
Reminders for Thursday, 1st February, 2024:
s now at 1:00am at 01:00 0 0 from now 0 0 s is hahaha
# Translation table
TRANSLATE "LANGID" "en"
TRANSLATE "\x03" "BREAK"
No reminders.
[
{
"translations":{"LANGID":"en","\u0003":"BREAK"},"caltype":"monthly","monthname":"February","year":2024,"daysinmonth":29,"firstwkday":4,"mondayfirst":0,"daynames":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"prevmonthname":"January","daysinprevmonth":31,"prevmonthyear":2024,"nextmonthname":"March","daysinnextmonth":31,"nextmonthyear":2024,"entries":[
]
}
]
Agenda pel dijous, 1 de febrer de 2024:
Language: ca