Compare commits

...

149 Commits

Author SHA1 Message Date
Dianne Skoll
fb688850b1 Update release notes. 2024-03-18 11:11:16 -04:00
Dianne Skoll
5b1bad2650 Document DEL server command and qid value. 2024-03-16 15:50:58 -04:00
Dianne Skoll
e5ff132c5e Remove all traces of tk_dialog 2024-03-16 15:45:29 -04:00
Dianne Skoll
a0830ad23c Bump version to 04.03.03 2024-03-16 15:27:34 -04:00
Dianne Skoll
9290f53466 Fix typo 2024-03-16 15:24:11 -04:00
Dianne Skoll
e5711032bd Keep reading inotify events until 0.2 seconds elapses between successive events. 2024-03-16 15:22:48 -04:00
Dianne Skoll
08e3c1d5a2 Use tk_messageBox instead of tk_dialog. 2024-03-16 15:22:36 -04:00
Dianne Skoll
29c579a301 Refactor some code. 2024-03-16 14:01:21 -04:00
Dianne Skoll
61f55bceee Fix "Delete this reminder completely" functionality. 2024-03-16 13:56:28 -04:00
Dianne Skoll
6586fae3eb Fix logic bug that would sometimes fail to send a queue update message to controlling process. 2024-03-16 13:36:24 -04:00
Dianne Skoll
d5a86f3e4f Actually de-queue and free reminders that expire out of the queue. 2024-03-16 13:29:59 -04:00
Dianne Skoll
96551ccaa4 Make TkRemind use the "DEL qid" facility to ignore reminders. Fix potential use of freed memory in queue.c 2024-03-16 13:25:47 -04:00
Dianne Skoll
c83ee86d10 Implement DEL command to removed a queued reminder from the queue in -zj mode. 2024-03-16 12:54:30 -04:00
Dianne Skoll
c913306cbd Add "qid" member to JSONQUEUE. 2024-03-16 11:17:28 -04:00
Dianne Skoll
03d385df97 Add a test for the "-ds" debugging flag. 2024-03-13 12:17:49 -04:00
Dianne Skoll
61fcc1b275 Add "s" debugging flag to see expression-parsing stack high-water marks.
Reduce default value stack size from 1000 to 100.
2024-03-13 12:06:55 -04:00
Dianne Skoll
26977a4ac0 Fix a bunch of cppcheck complaints and also update the cppcheck Makefile target. 2024-03-11 12:10:03 -04:00
Dianne Skoll
28acd05215 Better icon. 2024-03-08 14:28:19 -05:00
Dianne Skoll
be4eed8b20 Update TkRemind logo to include a white outline. 2024-03-07 15:36:31 -05:00
Dianne Skoll
cdb0850373 Add a comment to the troff source so it doesn't confuse Emacs syntax highlighting. 2024-03-03 14:55:40 -05:00
Dianne Skoll
0d55e04284 Make a local OMIT that doesn't specify a weekday name into a syntax error. 2024-03-03 14:51:53 -05:00
Dianne Skoll
f4cce54b70 Warn if a local OMIT doesn't actually omit any weekdays. 2024-03-03 11:00:35 -05:00
Dianne Skoll
2dc6ca44f1 Fix typo 2024-03-01 09:10:26 -05:00
Dianne Skoll
d1d833f0f3 Document fix in commit 1d44577ce9 2024-03-01 09:04:09 -05:00
Dianne Skoll
1d44577ce9 Exit rather than return if we forked in System(). 2024-03-01 09:00:36 -05:00
Dianne Skoll
1be7c2d6d7 Bump version to 04.03.02 and document fix to install: target. 2024-03-01 08:50:00 -05:00
Dianne Skoll
b1f418ee42 Install desktop and icon file in correct paths: $prefix/share/applications and $prefix/share/pixmaps 2024-03-01 08:45:43 -05:00
Dianne Skoll
72b0bf96fe Update release notes. 2024-02-29 20:28:09 -05:00
Dianne Skoll
3388849fa5 Fix test bug. 2024-02-29 20:27:19 -05:00
Dianne Skoll
dc9650d5fa Fix test bug. SIGH. 2024-02-29 20:25:13 -05:00
Dianne Skoll
8eb40ae748 Note the bug fix. 2024-02-29 15:53:31 -05:00
Dianne Skoll
89184f1d0f Update release notes. 2024-02-29 15:52:43 -05:00
Dianne Skoll
e899c790b9 Add Catalan translation file, courtesy of Eloi Torrents 2024-02-29 15:29:24 -05:00
Dianne Skoll
bd6d695020 Add some more test cases. 2024-02-29 15:27:55 -05:00
Dianne Skoll
20d4626a71 Bump version to 04.03.00 2024-02-29 13:48:15 -05:00
Dianne Skoll
8ff94c5031 Install the .desktop and icon files; add to menu. 2024-02-29 13:19:00 -05:00
Dianne Skoll
ee185a0eeb Desktop file should be executable. 2024-02-29 13:08:49 -05:00
Dianne Skoll
06f8932efd Add .desktop file and icon for TkRemind, courtesy of Eloi Torrents 2024-02-29 13:04:19 -05:00
Dianne Skoll
1dc627148c Fix tests so they don't depend on current date; add more tests for Feb 29 edge cases. 2024-02-29 12:44:59 -05:00
Dianne Skoll
3cdde5351f Issue "NOTE newdate" in legacy mode in response to an inotify event. 2024-02-29 11:28:05 -05:00
Dianne Skoll
6e93b8a73d Update TkRemind man page to properly reflect inotify support. 2024-02-29 11:26:07 -05:00
Dianne Skoll
267e8533cf Fix stupid bug. 2024-02-29 11:14:05 -05:00
Dianne Skoll
d3bfb0a28f Let Remind handle the inotify stuff. 2024-02-29 11:07:32 -05:00
Dianne Skoll
5a31bc7058 Integrate inotify support directly into Remind for server mode. 2024-02-29 11:03:28 -05:00
Dianne Skoll
746bde71bd Check for inotify_init1 2024-02-29 10:41:49 -05:00
Dianne Skoll
b274ac635c Clarify comment. 2024-02-29 09:46:56 -05:00
Dianne Skoll
9e0a74e583 Don't spit anything out to client for RUN-type reminders in server mode. 2024-02-29 09:31:37 -05:00
Dianne Skoll
0f782f7697 Set CLOEXEC flag on files we open.
When running programs in server mode, connect stdin and stdout to /dev/null
2024-02-29 09:22:15 -05:00
Dianne Skoll
8efde3e9af Fix typo 2024-02-28 10:59:51 -05:00
Dianne Skoll
3bf3137dc4 Check for existence of tags key. 2024-02-28 10:58:46 -05:00
Dianne Skoll
63ec32d28d Add missing $Sunday to man page. 2024-02-27 18:40:35 -05:00
Dianne Skoll
d2f4177cdb Update man pages. 2024-02-27 11:11:21 -05:00
Dianne Skoll
1d958fb7a8 Use JSON server mode from TkRemind. 2024-02-27 10:55:26 -05:00
Dianne Skoll
fcd580d42e Add a test for -zj 2024-02-27 10:28:08 -05:00
Dianne Skoll
34dab68805 Finish implementing "-zj" mode - Daemon mode with JSON responses. 2024-02-27 10:18:18 -05:00
Dianne Skoll
216dd03922 Start adding support for JSON-formatted daemon responses. 2024-02-27 09:54:35 -05:00
Dianne Skoll
5eef9ac621 Add test for zero-arg forms of easterdate() and orthodoxeaster() 2024-02-26 17:21:12 -05:00
Dianne Skoll
6b798d5f7c Allow arg to easterdate() and orthodoxeaster() to be omitted, defaulting it to today(). 2024-02-26 17:19:22 -05:00
Dianne Skoll
22ccce0934 Lay groundwork for having TriggerReminder put the results in a DynamicBuffer rather than sending to stdout
Eventually should allow us to make a JSON-based daemon mode.
2024-02-25 09:17:54 -05:00
Dianne Skoll
fe2af14952 Remove obsolete file 2024-02-24 09:46:56 -05:00
Dianne Skoll
8e99ed27e7 Take is_queued into account when deciding to issue banner. 2024-02-24 09:33:59 -05:00
Dianne Skoll
bb12362cc8 Make "Go To Date..." dialog non-modal. 2024-02-14 11:16:24 -05:00
Dianne Skoll
1bfc630a64 Uneascape JSON properly. 2024-02-07 10:27:38 -05:00
Dianne Skoll
987983f8ae Add empty line between queue items. 2024-02-05 13:49:11 -05:00
Dianne Skoll
657a6118aa Set -selectbackground 2024-02-05 13:46:28 -05:00
Dianne Skoll
43e7e6ec7f Alternate queue item background colors. 2024-02-05 10:10:52 -05:00
Dianne Skoll
b8b3c19fbf *sigh* A JSON key was changed. :( 2024-02-05 09:56:47 -05:00
Dianne Skoll
69298c96a5 Make the version of rem2html track the version of Remind. 2024-02-04 21:23:57 -05:00
Dianne Skoll
7356138872 Update release date. 2024-02-04 13:10:19 -05:00
Dianne Skoll
616966f5df Fix spelling in comment 2024-02-04 13:01:45 -05:00
Dianne Skoll
a59e277c21 Fix a couple of typos 2024-02-04 12:59:25 -05:00
Dianne Skoll
740ae2c3e9 Fix some spelling inconsistencies 2024-02-04 12:54:52 -05:00
Dianne Skoll
25b7a40f2b Try hard to avoid integer overflow. 2024-02-03 16:30:39 -05:00
Dianne Skoll
2beaab1a2f More checks on INT * STRING plus a man page note. 2024-02-03 16:29:05 -05:00
Dianne Skoll
60793d53c6 Don't use O(N^2) algorithm for STR * INT 2024-02-03 16:12:54 -05:00
Dianne Skoll
4f869c8c81 Update WHATSNEW 2024-02-03 16:06:13 -05:00
Dianne Skoll
8955180a35 Document INT * STRING and STRING * INT 2024-02-03 16:03:26 -05:00
Dianne Skoll
a30cbf5797 Fix tests. 2024-02-03 16:01:26 -05:00
Dianne Skoll
b2bd6109dc Allow STRING * INT or INT * STRING, which repeats STRING that many times. 2024-02-03 16:00:23 -05:00
Dianne Skoll
9455ec48d7 Include lineno element in JSONQUEUE 2024-02-03 11:06:33 -05:00
Dianne Skoll
f751f5defa Fix up tests for commit 994edbebbe 2024-02-03 11:01:10 -05:00
Dianne Skoll
994edbebbe Proper keys for tdelta, etc. 2024-02-03 11:00:27 -05:00
Dianne Skoll
70959b791c Fix typo 2024-02-03 09:44:30 -05:00
Dianne Skoll
524ece5119 Update WHATSNEW 2024-02-03 09:40:28 -05:00
Dianne Skoll
6334bd61b6 Bump version to 04.02.09 2024-02-03 09:26:42 -05:00
Dianne Skoll
2e56edd557 SystemTime can be int... no need for it to be long. 2024-02-03 09:26:30 -05:00
Dianne Skoll
8cae1d21cd Set time zone. 2024-02-02 22:20:45 -05:00
Dianne Skoll
1de6ed16eb Add check that we don't run the test suite in the failure window. :) 2024-02-02 22:18:53 -05:00
Dianne Skoll
860cb94f41 Add comment 2024-02-02 22:12:45 -05:00
Dianne Skoll
6b505704e9 Fix test failures caused by output that changes based on date. 2024-02-02 22:10:21 -05:00
Dianne Skoll
167631451d Don't alloc/free FileName unnecessarily. 2024-02-02 18:45:31 -05:00
Dianne Skoll
fa5180b94d Refactor JSON output routines. 2024-02-02 16:08:17 -05:00
Dianne Skoll
ae01d7be43 Add a test for queued reminders. 2024-02-02 15:17:01 -05:00
Dianne Skoll
d5ce39ade1 Make a note about OMIT context and queued reminders. 2024-02-02 14:51:55 -05:00
Dianne Skoll
a043dfe8b9 Optimize the search for a queued filename by assuming we're still in the same file as before.
This is very likely to be true and should avoid traversing the list of
filenames in most cases.
2024-02-02 14:25:34 -05:00
Dianne Skoll
7cfb75e3b3 Save filename when queueing reminders. Also, use original trigger structure when triggering.
Before, we'd lose the priority and msgprefix() would mess up.
2024-02-02 14:18:55 -05:00
Dianne Skoll
a18f0d982f Update rem2ps man page. Indicate that no new features will be added; all new development will be on rem2pdf. 2024-01-11 15:48:10 -05:00
Dianne Skoll
0e2dc805c2 Fix some typos; run "make" with -jnproc if possible. 2024-01-09 21:35:31 -05:00
Dianne Skoll
4c1e11df2c Make build.tk executable; update lat/long for Ottawa. 2024-01-09 21:24:55 -05:00
Dianne Skoll
76776d054a Fix typo in build.tk 2024-01-09 21:20:43 -05:00
Dianne Skoll
45ebd05cb6 Minor tweaks. 2024-01-09 21:16:32 -05:00
Dianne Skoll
0203ce3979 Correct the mailing address of the FSF (pointed out by Neil Hanlon) 2024-01-09 16:32:53 -05:00
Dianne Skoll
72d10178bf Mass-update copyright year to 2024. 2023-12-31 12:05:03 -05:00
Dianne Skoll
96f4e26d53 Add "constval" alias for "min" structure field. 2023-12-30 11:33:01 -05:00
Dianne Skoll
4fd86f1b6a Add an entry to BIBLIOGRAPHY 2023-12-29 17:34:29 -05:00
Dianne Skoll
2f3ee0aec3 Clarify limits on full OMITs; document new system variables. 2023-12-28 19:32:53 -05:00
Dianne Skoll
a5dde31160 Update test file for new system vars. 2023-12-28 19:23:27 -05:00
Dianne Skoll
b45428df05 Add system variables: $NumFullOmits, $MaxFullOmits, $NumPartialOmits, $MaxPartialOmits 2023-12-28 19:22:48 -05:00
Dianne Skoll
d938763643 Update test file for new error messages 2023-12-28 19:08:51 -05:00
Dianne Skoll
e4e2157622 Include limits in "too many XXX" error messages. 2023-12-28 19:08:15 -05:00
Dianne Skoll
04b349c6c7 Check write() call for failures. 2023-12-28 18:23:13 -05:00
Dianne Skoll
7fe3eb7391 Avoid warning about ignoring return value. *SIGH* 2023-12-28 18:20:59 -05:00
Dianne Skoll
c1992b577a Check of (very unlikely and probably harmless) integer underflow. 2023-12-27 20:28:46 -05:00
Dianne Skoll
632283d47f Issue a warning if someone OMITs every possible date. 2023-12-27 20:27:26 -05:00
Dianne Skoll
1d9e46997c Don't attempt to obtain terminal background color at startup. Instead, only obtain it if and when it is needed. 2023-12-26 10:08:00 -05:00
Dianne Skoll
861ce34022 Clarify logic. 2023-12-25 10:16:59 -05:00
Dianne Skoll
32e8db322d Stricter parsing of SET command expressions. 2023-12-22 14:22:54 -05:00
Dianne Skoll
3df2b72175 Simplify logic for updating of number of queued reminders. 2023-12-18 14:48:49 -05:00
Dianne Skoll
e7ac4f95be Issue a spontaneous NOTE queued response only if we actually de-queue a reminder. 2023-12-18 14:44:41 -05:00
Dianne Skoll
e7ed69287b Fix existing typo'd line in reminders file. 2023-12-16 13:25:30 -05:00
Dianne Skoll
2e80417f53 Fix silly typo, found by Lorenzo Bazzanini. 2023-12-16 09:35:45 -05:00
Dianne Skoll
ee435d2bb9 Clarify that in server mode, status output can happen at any time and not just in response to a stdin command. 2023-12-15 14:51:21 -05:00
Dianne Skoll
bb516946be If we de-queue a reminder without issuing it, send a NOTE queued %d message in server mode. 2023-12-15 14:16:26 -05:00
Dianne Skoll
81157e1cb5 Update queue status once a minute. 2023-12-15 12:33:41 -05:00
Dianne Skoll
51dfd707a2 One more release note 2023-12-14 16:08:46 -05:00
Dianne Skoll
7c3bf8601b Update WHATSNEW. 2023-12-14 16:05:40 -05:00
Dianne Skoll
714195efe5 Bump version to 04.02.08 2023-12-14 15:58:20 -05:00
Dianne Skoll
eaeca2d09b Minor tweak. 2023-12-12 11:52:27 -05:00
Dianne Skoll
ffa3b13437 Better method to detect if compiler supports -ffat-lto-objects
clang does not support this option, but the configure script was
supplying it anyway, causing warnings.
2023-12-12 09:23:26 -05:00
Dianne Skoll
2551e98d11 Use "now = time(NULL)" rather than (void) time(&now)" 2023-12-11 15:56:42 -05:00
Dianne Skoll
1bfd7761bc Use EXIT_FAILURE / EXIT_SUCCESS consistently. 2023-12-10 10:42:26 -05:00
Dianne Skoll
de9cb1d0a3 Use symbols STDIN_FILENO, etc. 2023-12-10 10:38:42 -05:00
Dianne Skoll
b2d32b514a Catch SIGCONT to force it to interrupt the select() system call. 2023-12-09 19:12:27 -05:00
Dianne Skoll
6e53fd6924 Refactor code: Replace SystemTime(x)/60 with MinutesPastMidnight(x) 2023-12-09 10:41:03 -05:00
Dianne Skoll
8296d2b962 Display "(Queue is empty)" if queue is empty. 2023-12-09 10:30:18 -05:00
Dianne Skoll
d6e66ee1e6 Tighten test for removing very old reminders from queue. 2023-12-09 10:25:50 -05:00
Dianne Skoll
a49532b9c5 Remove non-triggered but expired reminders from the queue. 2023-12-08 14:02:29 -05:00
Dianne Skoll
57d87f4caf Better logic for closing stdin/stdout/stderr if we fork. 2023-12-06 16:39:13 -05:00
Dianne Skoll
ec9b30c616 Don't let "REM ... MSG NOTE endreminder" mess with daemon protocol.
Pad with a leading space. :)
2023-12-06 13:28:17 -05:00
Dianne Skoll
27d8a62ab6 Update SERVER MODE documentation. 2023-12-06 13:17:53 -05:00
Dianne Skoll
5077814c4a Clarify when $MaxLateMinutes is read. 2023-12-06 13:11:28 -05:00
Dianne Skoll
ca795a352a Add the $MaxLateMinutes system variable.
Don't trigger a timed reminder if it's more than $MaxLateMinutes past
the trigger time (for example, if the computer has hibernated and then
been awoken.)
2023-12-03 17:16:48 -05:00
Dianne Skoll
e59fc36458 Use <meta charset="UTF-8"> instead of http-equiv. 2023-10-15 11:25:06 -04:00
Dianne Skoll
39e3657539 Add --utf8 flag to rem2html. 2023-10-15 10:10:27 -04:00
Dianne Skoll
6031f70701 Update the balloon help when we toggle between Queue... and Errors... 2023-10-11 17:20:41 -04:00
Dianne Skoll
3567c9e55f If errors occur during printing, set the "Errors..." button rather than popping up a dialog. 2023-10-11 17:16:27 -04:00
Dianne Skoll
26de4e3fd3 Use bold rather than italice. 2023-10-09 10:39:18 -04:00
Dianne Skoll
cd65c6144d Merge branch 'fix_man' into 'master'
Fix manpage formatting

See merge request dskoll/remind!5
2023-10-09 14:38:07 +00:00
Jochen Sprickerhof
7b64623115 Fix manpage formatting 2023-09-12 21:08:44 +02:00
89 changed files with 2811 additions and 1694 deletions

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@ man/remind.1
man/tkremind.1 man/tkremind.1
pm_to_blib pm_to_blib
rem2html/Makefile rem2html/Makefile
rem2html/rem2html
rem2html/rem2html.1 rem2html/rem2html.1
rem2pdf/Makefile.PL rem2pdf/Makefile.PL
rem2pdf/Makefile.old rem2pdf/Makefile.old

View File

@@ -3,7 +3,7 @@ THE REMIND COPYRIGHT
1. REMIND refers to the entire set of files and documentation in the 1. REMIND refers to the entire set of files and documentation in the
REMIND package. REMIND package.
2. REMIND is Copyright 1992-2023 Dianne Skoll, except where noted in 2. REMIND is Copyright 1992-2024 Dianne Skoll, except where noted in
individual files. individual files.
3. DISTRIBUTION AND USE 3. DISTRIBUTION AND USE
@@ -16,7 +16,7 @@ individual files.
Version 2, June 1991 Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.

View File

@@ -44,7 +44,7 @@ test:
@$(MAKE) -C src -s test @$(MAKE) -C src -s test
distclean: clean 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 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
src/Makefile: src/Makefile.in src/Makefile: src/Makefile.in
./configure ./configure

26
build.tk Normal file → Executable file
View File

@@ -27,10 +27,10 @@ exec wish "$0" "$@"
proc SetConfigDefaults {} { proc SetConfigDefaults {} {
global Config global Config
set Config(LAT_DEG) 45 set Config(LAT_DEG) 45
set Config(LAT_MIN) 24 set Config(LAT_MIN) 25
set Config(LAT_SEC) 14 set Config(LAT_SEC) 14
set Config(LON_DEG) 75 set Config(LON_DEG) 75
set Config(LON_MIN) 39 set Config(LON_MIN) 41
set Config(LON_SEC) 23 set Config(LON_SEC) 23
set Config(LOCATION) "Ottawa" set Config(LOCATION) "Ottawa"
set Config(DEFAULT_PAGE) "Letter" set Config(DEFAULT_PAGE) "Letter"
@@ -53,7 +53,7 @@ proc SetConfigDefaults {} {
# Pops up an error dialog; then calls exit. # Pops up an error dialog; then calls exit.
#*********************************************************************** #***********************************************************************
proc Bail { msg } { proc Bail { msg } {
tk_dialog .err "Remind Configuration Error" $msg error 0 "Bummer" tk_messageBox -message "Remind Build Error" -detail $msg -icon error -type ok
exit 1 exit 1
} }
@@ -190,8 +190,8 @@ proc CreateLocationDialog { w } {
grid $w.north $w.west grid $w.north $w.west
grid $w.south $w.east grid $w.south $w.east
grid $w.loclab -sticky e grid $w.loclab -sticky e
grid $w.location -sticky nsew -row 6 -column 1 grid $w.location -sticky nsew -row 8 -column 1
} }
#*********************************************************************** #***********************************************************************
@@ -293,9 +293,9 @@ proc BuildRemind {} {
.msgs insert end "\n>>> Creating src/custom.h...\n\n" green .msgs insert end "\n>>> Creating src/custom.h...\n\n" green
CreateCustomH CreateCustomH
.msgs insert end ">>> Calling `./configure'...\n\n" green .msgs insert end "\n>>> Calling `./configure'...\n\n" green
CallConfigure CallConfigure
.msgs insert end ">>> Calling `make'...\n\n" green .msgs insert end "\n>>> Calling `make'...\n\n" green
CallMake CallMake
.msgs insert end "\n----------------------------------------------\n\n" .msgs insert end "\n----------------------------------------------\n\n"
.msgs insert end "Remind" red .msgs insert end "Remind" red
@@ -447,12 +447,12 @@ proc CreateCustomH {} {
"#define DEFAULT_LATITUDE *" { "#define DEFAULT_LATITUDE *" {
set lat [expr $LAT_DEG + ($LAT_MIN/60.0) + ($LAT_SEC/3600.0)]; set lat [expr $LAT_DEG + ($LAT_MIN/60.0) + ($LAT_SEC/3600.0)];
puts $out "#define DEFAULT_LATITUDE $lat" puts $out "#define DEFAULT_LATITUDE $lat"
.msgs insert end "#define DEFAULT_LATITUDE $lat" .msgs insert end "#define DEFAULT_LATITUDE $lat\n"
} }
"#define DEFAULT_LONGITUDE *" { "#define DEFAULT_LONGITUDE *" {
set lon [expr -1.0 * ($LON_DEG + ($LON_MIN/60.0) + ($LON_SEC/3600.0))] set lon [expr -1.0 * ($LON_DEG + ($LON_MIN/60.0) + ($LON_SEC/3600.0))]
puts $out "#define DEFAULT_LONGITUDE $lon" puts $out "#define DEFAULT_LONGITUDE $lon"
.msgs insert end "#define DEFAULT_LONGITUDE $lon" .msgs insert end "#define DEFAULT_LONGITUDE $lon\n"
} }
"#define LOCATION *" { "#define LOCATION *" {
puts $out "#define LOCATION \"$Config(LOCATION)\"" puts $out "#define LOCATION \"$Config(LOCATION)\""
@@ -506,7 +506,13 @@ proc CallMake {} {
"Icelandic" { set lang ICELANDIC } "Icelandic" { set lang ICELANDIC }
default { set lang ENGLISH } default { set lang ENGLISH }
} }
RunCommand "make \"LANGDEF=-DLANG=$lang\"" set nproc 0
catch { set nproc [exec nproc] }
if { $nproc != 0 } {
RunCommand "make -j $nproc \"LANGDEF=-DLANG=$lang\""
} else {
RunCommand "make \"LANGDEF=-DLANG=$lang\""
}
} }

21
configure vendored
View File

@@ -4034,6 +4034,12 @@ then :
printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h
fi fi
ac_fn_c_check_header_compile "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_inotify_h" = xyes
then :
printf "%s\n" "#define HAVE_SYS_INOTIFY_H 1" >>confdefs.h
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
@@ -4134,14 +4140,14 @@ if test "$GCC" = yes; then
f=-flto=auto f=-flto=auto
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $f" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $f" >&5
printf %s "checking whether $CC supports $f... " >&6; } printf %s "checking whether $CC supports $f... " >&6; }
if $CC -E $f /dev/null > /dev/null 2>&1 ; then if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; } printf "%s\n" "yes" >&6; }
CFLAGS="$CFLAGS $f" CFLAGS="$CFLAGS $f"
f=-ffat-lto-objects f=-ffat-lto-objects
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $f" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC supports $f" >&5
printf %s "checking whether $CC supports $f... " >&6; } printf %s "checking whether $CC supports $f... " >&6; }
if $CC -E $f /dev/null > /dev/null 2>&1 ; then if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; } printf "%s\n" "yes" >&6; }
CFLAGS="$CFLAGS $f" CFLAGS="$CFLAGS $f"
@@ -4211,14 +4217,20 @@ then :
printf "%s\n" "#define HAVE_INITGROUPS 1" >>confdefs.h printf "%s\n" "#define HAVE_INITGROUPS 1" >>confdefs.h
fi fi
ac_fn_c_check_func "$LINENO" "inotify_init1" "ac_cv_func_inotify_init1"
if test "x$ac_cv_func_inotify_init1" = xyes
then :
printf "%s\n" "#define HAVE_INOTIFY_INIT1 1" >>confdefs.h
fi
VERSION=04.02.07 VERSION=04.03.03
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1" ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1"
cat >confcache <<\_ACEOF cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure # This file is a shell script that caches the results of configure
@@ -4910,6 +4922,7 @@ do
"www/Makefile") CONFIG_FILES="$CONFIG_FILES www/Makefile" ;; "www/Makefile") CONFIG_FILES="$CONFIG_FILES www/Makefile" ;;
"src/version.h") CONFIG_FILES="$CONFIG_FILES src/version.h" ;; "src/version.h") CONFIG_FILES="$CONFIG_FILES src/version.h" ;;
"rem2html/Makefile") CONFIG_FILES="$CONFIG_FILES rem2html/Makefile" ;; "rem2html/Makefile") CONFIG_FILES="$CONFIG_FILES rem2html/Makefile" ;;
"rem2html/rem2html") CONFIG_FILES="$CONFIG_FILES rem2html/rem2html" ;;
"rem2pdf/Makefile.PL") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.PL" ;; "rem2pdf/Makefile.PL") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.PL" ;;
"rem2pdf/Makefile.top") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.top" ;; "rem2pdf/Makefile.top") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.top" ;;
"rem2pdf/bin/rem2pdf") CONFIG_FILES="$CONFIG_FILES rem2pdf/bin/rem2pdf" ;; "rem2pdf/bin/rem2pdf") CONFIG_FILES="$CONFIG_FILES rem2pdf/bin/rem2pdf" ;;

View File

@@ -38,7 +38,7 @@ AC_CHECK_SIZEOF(unsigned long)
AC_CHECK_SIZEOF(time_t) AC_CHECK_SIZEOF(time_t)
dnl Checks for header files. dnl Checks for header files.
AC_CHECK_HEADERS(sys/types.h glob.h wctype.h locale.h langinfo.h) AC_CHECK_HEADERS(sys/types.h glob.h wctype.h locale.h langinfo.h sys/inotify.h)
dnl Checks for typedefs, structures, and compiler characteristics. dnl Checks for typedefs, structures, and compiler characteristics.
AC_STRUCT_TM AC_STRUCT_TM
@@ -50,12 +50,12 @@ if test "$GCC" = yes; then
# Check for link-time optimization support # Check for link-time optimization support
f=-flto=auto f=-flto=auto
AC_MSG_CHECKING([whether $CC supports $f]) AC_MSG_CHECKING([whether $CC supports $f])
if $CC -E $f /dev/null > /dev/null 2>&1 ; then if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
CFLAGS="$CFLAGS $f" CFLAGS="$CFLAGS $f"
f=-ffat-lto-objects f=-ffat-lto-objects
AC_MSG_CHECKING([whether $CC supports $f]) AC_MSG_CHECKING([whether $CC supports $f])
if $CC -E $f /dev/null > /dev/null 2>&1 ; then if $CC -Werror -E $f - < /dev/null > /dev/null 2>&1 ; then
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
CFLAGS="$CFLAGS $f" CFLAGS="$CFLAGS $f"
else else
@@ -86,13 +86,13 @@ if test "$?" != 0 ; then
echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!" echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!"
exit 1 exit 1
fi fi
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups) AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1)
VERSION=04.02.07 VERSION=04.03.03
AC_SUBST(VERSION) AC_SUBST(VERSION)
AC_SUBST(PERL) AC_SUBST(PERL)
AC_SUBST(PERLARTIFACTS) AC_SUBST(PERLARTIFACTS)
AC_SUBST(RELEASE_DATE) AC_SUBST(RELEASE_DATE)
AC_CONFIG_FILES([src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1]) AC_CONFIG_FILES([src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1])
AC_OUTPUT AC_OUTPUT
chmod a+x rem2pdf/bin/rem2pdf chmod a+x rem2pdf/bin/rem2pdf

View File

@@ -19,8 +19,7 @@
;; You should have received a copy of the GNU General Public License ;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software ;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
;; 02111-1307, USA.
;;; Commentary: ;;; Commentary:

View File

@@ -95,7 +95,7 @@ Norman Walsh.
#!/usr/local/bin/wish #!/usr/local/bin/wish
wm withdraw . wm withdraw .
after 15000 { destroy . ; exit } after 15000 { destroy . ; exit }
tk_dialog .d { Message } $argv warning 0 { OK } tk_messageBox -message Message -detail $argv -icon info -type ok
destroy . destroy .
exit exit
-------------- Cut Here ---------- Cut Here ---------- Cut Here ------------- -------------- Cut Here ---------- Cut Here ---------- Cut Here -------------

View File

@@ -1,5 +1,168 @@
CHANGES TO REMIND CHANGES TO REMIND
* VERSION 4.3 Patch 3 - 2024-03-18
* IMPROVEMENT: tkremind: Update icon to include a white border so it shows
up better on dark backgrounds.
* IMPROVEMENT: C code: Fix a number of cppcheck static-analysis warnings.
* IMPROVEMENT: remind: Update the "-zj" protocol to include a queue-id for
each queued reminder and add the DEL client command to delete a specific
item from the queue. Used by tkremind to implement "don't remind me about
this again today."
* MINOR NEW FEATURE: Add a "-ds" debugging flag to print out expression-parsing
stack high-water marks on exit. This esoteric feature is of no use to
anyone by the Remind author.
* IMPROVEMENT: tkremind: Stop using the deprecated tk_dialog command in favor
of the newer tk_messageBox command.
* IMPROVEMENT: remind: In server mode, try to minimize redraws by
consuming inotify events until at least 0.2s elapses without an
event appearing.
* BUG FIX: tkremind: The "Don't remind me about this again today" feature
was unreliable and only worked for reminders created with TkRemind itself.
It has been made more reliable and works with any reminder.
* BUG FIX: remind: Make it a syntax error if a local OMIT in a REM statement
is not followed by at least one weekday name.
* VERSION 4.3 Patch 2 - 2024-03-01
- BUG FIX: remind: Fix a logic error when implementing the RUN command in
server mode. As it turns out, the error is harmless, but it's best to do
things correctly.
- BUG FIX: The Makefile would install the tkremind.png and tkremind.desktop
files in the wrong location. This has been fixed.
* VERSION 4.3 Patch 1 - 2024-02-29
- BUG FIX: tests: "make test" could fail because of a bad test. This
has been fixed. There are no actual code changes to any of the programs
in Remind compared to 04.03.00.
* VERSION 4.3 Patch 0 - 2024-02-29
- IMPROVEMENT: remind: If Remind is compiled on a system that supports
inotify, then in server mode (-z0 or -zj) it monitors the reminders file
and restarts itself if it detects a change, and also notifies the client.
Moving inotify support directly into Remind means that tkremind no longer
has to invoke a separate inotifywait process.
- IMPROVEMENT: remind: Set the CLOEXEC flag on files we open so we
don't leak file descriptors to programs that we run. While I don't
think there's a security issue here (any program you run can do
anything as your userid anyway) it's best to be clean and tidy.
- IMPROVEMENT: remind: Add localization for the Catalan language, courtesy
of Eloi Torrents.
- IMPROVEMENT: tkremind: Add a .desktop file and icon so TkRemind can be
integrated into the desktop menu system, courtesy of Eloi Torrents.
- CHANGE: Add a new server mode with the "-zj" flag. This is just
like "-z0" except it uses JSON messages to communicate with the
client rather than an ad-hoc protocol. The "-z0" mode is still
supported, but is deprecated.
- CHANGE: In server mode (-z0 or -zj) any RUN-type reminders, or message
commands of the "-kcommand" type are run with standard input and standard
output connected to /dev/null. NOTE INCOMPATIBILITY: If you previously
relied on RUN-type reminders to pop up reminders in TkRemind, they no
longer do. If you want this, you'll have to get the command that you
run to pop up its own window with "xmessage" or something similar.
- IMPROVEMENT: tkremind: Make the "Go to date..." dialog non-modal.
- CHANGE: remind: Allow the argument to easterdate() and
orthodoxeaster() to be omitted, in which case it defaults to
today().
- BUG FIX: Miscellaneous man page fixes.
- BUG FIX: Fix a leap-year edge-case. The reminder: REM 29 MSG whatever
was not triggered on Feb 29 of leap years.
- BUG FIX: rem2html: Make the version of rem2html track the version of
Remind. Noted by Ian! D. Allen.
* VERSION 4.2 Patch 9 - 2024-02-04
- CHANGE: remind: Do not attempt to guess terminal background color on
startup. Only obtain it as needed. This can prevent mojibake from
appearing on terminals that don't support the color query escape
sequence.
- IMPROVEMENT: remind: Add new system variables $NumFullOmits,
$MaxFullOmits, $NumPartialOmits and $MaxPartialOmits.
- IMPROVEMENT: remind: Issue a warning if someone OMITs every possible date.
- IMPROVEMENT: remind: In several error messages complaining about limits
being exceeded, include the actual limit in the error message. Clarify
the man page regarding limits on the number of OMITs.
- MINOR NEW FEATURE: remind: The expression STRING * INT or INT * STRING
is now accepted and yields a string that is INT concatenations of the
original STRING. In this case, INT must be non-negative and the total
string length can't exceed $MaxStringLen.
- DOCUMENTATION: Add "Astronomical Algorithms" by Jean Meeus to bibliography.
- DOCUMENTATION FIX: Update address of the Free Software Foundation in the
license file.
- DOCUMENTATION: Note that rem2ps is deprecated and will not received any
new features. Further development will happen on rem2pdf.
- BUG FIX: Preserve the filename() and priority context for queued reminders.
Previously, the filename information was lost and the priority was
coming from uninitialized memory (yikes!). bug found by Alexander
Möller.
- BUG FIX: build.tk: Various minor improvements.
- BUG FIX: remind: In server mode, if we de-queue a reminder without
triggering it, issue a "NOTE queued %d" message to update the
client's notion of the queue size.
- BUG FIX: tkremind: Fix typo found by Lorenzo Bazzanini.
* VERSION 4.2 Patch 8 - 2023-12-14
- NEW FEATURE: Add the $MaxLateMinutes system variable. This suppresses
a queued time reminder if the current time is more than $MaxLateMinutes
past the trigger time. (This typically only occurs if the computer
has been suspended/hibernated and then resumed.)
- IMPROVEMENT: tkremind: If an error occurs during printing, catch it
and change the Queue... button to Errors... (the same way errors in
reminder files are handled.)
- IMPROVEMENT: rem2html: add the --utf8 flag to set the HTML charset to
UTF-8.
- MINOR IMPROVEMENTS: Refactor some of the C code; use symbolic exit
statuses and file descriptors for stdin/stdout/stderr where possible.
- BUG FIX: configure.in: Use better option detection so we don't use the
unsupported option -ffat-lto-objects if compiling with clang instead of gcc.
- BUG FIXES: Many fixes to man pages, some by Jochen Sprickerhof
- MINOR BUG FIX: If Remind puts itself in the background, only close
stdout/stderr if they are not associated with a terminal. If
we close a descriptor, dup /dev/null onto it.
- MINOR BUG FIX: Catch SIGCONT when running in daemon/background mode.
This forces the select() call to be interrupted so we can update the
sleep time. This really only matters if the computer or the background
Remind process is suspended and then resumed.
* VERSION 4.2 Patch 7 - 2023-10-09 * VERSION 4.2 Patch 7 - 2023-10-09
- IMPROVEMENT: remind: On 32-bit systems, attempt to use a 64-bit time_t - IMPROVEMENT: remind: On 32-bit systems, attempt to use a 64-bit time_t

View File

@@ -10,7 +10,7 @@ FSET center(x) pad("", " ", (columns() - columns(x))/2) + x
FSET right(x) pad("", " ", columns() - columns(x)) + x FSET right(x) pad("", " ", columns() - columns(x)) + x
MSG This is left-aligned. MSG This is left-aligned.
MSG [ansicolor(0,255,0)]This is also left-aligned.[ansicolor("")] MSG [ansicolor(0,255,0)]🌕 🌕 🌕 🌕 This is also left-aligned.[ansicolor("")]
MSG [center("This is centered.")] MSG [center("This is centered.")]
MSG [ansicolor(255,255,0) + center("🌕 🌕 🌕 🌕 This is also centered. ") + ansicolor("")] MSG [ansicolor(255,255,0) + center("🌕 🌕 🌕 🌕 This is also centered. ") + ansicolor("")]

View File

@@ -16,7 +16,7 @@
# "#PSSTUFF" for nifty PostScript examples # # "#PSSTUFF" for nifty PostScript examples #
# # # #
# This file is part of REMIND. # # This file is part of REMIND. #
# Copyright (C) 1992-2023 Dianne Skoll # # Copyright (C) 1992-2024 Dianne Skoll #
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# # # #
############################################################################# #############################################################################

View File

@@ -2,7 +2,7 @@
# Not all sequences are supported by all terminals. # Not all sequences are supported by all terminals.
# This file is part of REMIND # This file is part of REMIND
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
if !defined("ansi_bold") if !defined("ansi_bold")

View File

@@ -1,6 +1,6 @@
# US holidays # US holidays
# This file is part of REMIND. # This file is part of REMIND.
# Copyright (C) 1992-2023 Dianne Skoll # Copyright (C) 1992-2024 Dianne Skoll
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
REM [easterdate($Uy)-46] MSG Ash Wednesday REM [easterdate($Uy)-46] MSG Ash Wednesday

53
include/lang/ca.rem Normal file
View File

@@ -0,0 +1,53 @@
# Support for the Catalan language.
# This file is part of REMIND.
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file was created by Eloi Torrents <eloitor@disroot.org>
SET $Monday "dilluns"
SET $Tuesday "dimarts"
SET $Wednesday "dimecres"
SET $Thursday "dijous"
SET $Friday "divendres"
SET $Saturday "dissabte"
SET $Sunday "diumenge"
SET $January "gener"
SET $February "febrer"
SET $March "març"
SET $April "abril"
SET $May "maig"
SET $June "juny"
SET $July "juliol"
SET $August "agost"
SET $September "setembre"
SET $October "octubre"
SET $November "novembre"
SET $December "desembre"
SET $Today "avui"
SET $Tomorrow "demà"
FSET subst_bx(a,d,t) iif(d==today()+2, "demà passat", "d'aquí " + (d-today()) + " dies")
# 1 d'abril vs 1 de maig.
FSET subst_sx(a,d,t) iif(isany(substr(mon(d), 1, 1), "a", "o") , "d'", "de")
FSET subst_ordinal(d) ""
BANNER Agenda pel %w, %d %s %m de %y%o:
SET $Am "am"
SET $Pm "pm"
SET $Ago "fa"
SET $Fromnow "des d'avui"
SET $On "el dia"
SET $Now "ara"
SET $At "a les"
SET $Minute "minut"
SET $Mplu "s"
SET $Hour "hora"
FSET subst_hours(h) iif(h==1, "1 hora", h + " hores")
SET $Is "és"
SET $Was "va ser"
SET $And "i"

View File

@@ -1,6 +1,6 @@
# Support for the Danish language. # Support for the Danish language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Mogens Lynnerup. # This file is derived from a translation by Mogens Lynnerup.
SET $Sunday "Søndag" SET $Sunday "Søndag"

View File

@@ -1,6 +1,6 @@
# Support for the German language. # Support for the German language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Wolfgang Thronicke # This file is derived from a translation by Wolfgang Thronicke
# Day names # Day names

View File

@@ -1,4 +1,4 @@
# Support for the English language. # Support for the English language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# Nothing to do for English since it is the default. # Nothing to do for English since it is the default.

View File

@@ -1,6 +1,6 @@
# Support for the Spanish language. # Support for the Spanish language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Rafa Couto <rafacouto@biogate.com> # This file is derived from a translation by Rafa Couto <rafacouto@biogate.com>
SET $Sunday "Domingo" SET $Sunday "Domingo"

View File

@@ -1,6 +1,6 @@
# Support for the Finnish language. # Support for the Finnish language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Mikko Silvonen # This file is derived from a translation by Mikko Silvonen
SET $Sunday "sunnuntai" SET $Sunday "sunnuntai"

View File

@@ -1,6 +1,6 @@
# Support for the French language. # Support for the French language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Laurent Duperval # This file is derived from a translation by Laurent Duperval
SET $Sunday "dimanche" SET $Sunday "dimanche"

View File

@@ -1,6 +1,6 @@
# Support for the Hellenic (Greek) language. # Support for the Hellenic (Greek) language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by jarlaxl lamat (jarlaxl@freemail.gr) # This file is derived from a translation by jarlaxl lamat (jarlaxl@freemail.gr)
SET $Sunday "Κυριακή" SET $Sunday "Κυριακή"

View File

@@ -1,6 +1,6 @@
# Support for the Icelanding language. # Support for the Icelanding language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Björn Davíðsson (bjossi@snerpa.is) # This file is derived from a translation by Björn Davíðsson (bjossi@snerpa.is)
SET $Sunday "sunnudagur" SET $Sunday "sunnudagur"

View File

@@ -1,6 +1,6 @@
# Support for the Italian language. # Support for the Italian language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Valerio Aimale # This file is derived from a translation by Valerio Aimale
SET $Sunday "Domenica" SET $Sunday "Domenica"

View File

@@ -1,6 +1,6 @@
# Support for the Dutch language. # Support for the Dutch language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Willem Kasdorp and Erik-Jan Vens # This file is derived from a translation by Willem Kasdorp and Erik-Jan Vens
SET $Sunday "zondag" SET $Sunday "zondag"

View File

@@ -1,6 +1,6 @@
# Support for the Norwegian language. # Support for the Norwegian language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Trygve Randen # This file is derived from a translation by Trygve Randen
SET $Sunday "Søndag" SET $Sunday "Søndag"

View File

@@ -1,6 +1,6 @@
# Support for the Polish language. # Support for the Polish language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Jerzy Sobczyk # This file is derived from a translation by Jerzy Sobczyk
SET $Sunday "Niedziela" SET $Sunday "Niedziela"

View File

@@ -1,6 +1,6 @@
# Support for the (Brazilian) Portuguese language. # Support for the (Brazilian) Portuguese language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Marco Paganini # This file is derived from a translation by Marco Paganini
SET $Sunday "domingo" SET $Sunday "domingo"

View File

@@ -1,6 +1,6 @@
# Support for the Romanian language. # Support for the Romanian language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Liviu Daia # This file is derived from a translation by Liviu Daia
SET $Sunday "Duminică" SET $Sunday "Duminică"

View File

@@ -5,22 +5,25 @@ rem2ps \- draw a PostScript calendar from Remind output
.SH SYNOPSIS .SH SYNOPSIS
.B rem2ps [\fIoptions\fR] .B rem2ps [\fIoptions\fR]
.SH DESCRIPTION .SH DESCRIPTION
\fBRem2ps\fR reads the standard input, which should be the results of \fBrem2ps\fR reads the standard input, which should be the results of
running \fBRemind\fR with the \fB\-p\fR or \fB\-pp\fR option. It running \fBRemind\fR with the \fB\-p\fR or \fB\-pp\fR option. It
emits PostScript code (which draws a calendar) to the standard output. emits PostScript code (which draws a calendar) to the standard output.
.PP .PP
See the section "Rem2PS Input Format" for details about the \fB\-p\fR Although \fBrem2ps\fR will be maintained, no new features will be added
to it. Instead, all new development will continue on \fBrem2pdf\fR.
.PP
See the section "REM2PS INPUT FORMAT" for details about the \fB\-p\fR
data. This may be useful if you wish to create other \fBRemind\fR data. This may be useful if you wish to create other \fBRemind\fR
back-ends. back-ends.
.PP .PP
Note that \fBRem2PS\fR does not handle UTF-8 input. If you need to Note that \fBrem2ps\fR does not handle UTF-8 input. If you need to
render characters outside the ASCII character set, see render characters outside the ASCII character set, see
\fBrem2pdf\fR instead. \fBrem2pdf\fR instead.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-v .B \-v
Be more verbose. This causes \fBRem2ps\fR to print progress messages Be more verbose. This causes \fBrem2ps\fR to print progress messages
to the standard error stream. Normally, it is silent. to the standard error stream. Normally, it is silent.
.TP .TP
.B \-p file .B \-p file
@@ -133,7 +136,7 @@ numbers.
.PP .PP
Type "rem2ps \-m help" for a list of available media. Note that the media Type "rem2ps \-m help" for a list of available media. Note that the media
type (and all \fBRem2ps\fR options) are case-sensitive. If you don't use type (and all \fBrem2ps\fR options) are case-sensitive. If you don't use
the \fB\-m\fR option, the media defaults to a compiled-in default - this the \fB\-m\fR option, the media defaults to a compiled-in default - this
is usually Letter for North America and A4 for Europe. The "\-m help" is usually Letter for North America and A4 for Europe. The "\-m help"
option will display the compiled-in default. option will display the compiled-in default.
@@ -193,8 +196,8 @@ for good output:
rem2ps \-ol 72 \-sh 12 rem2ps \-ol 72 \-sh 12
.fi .fi
.SH USAGE .SH USAGE
To use \fBRem2ps\fR, you should pipe the output of \fBRemind\fR with the \fB\-p\fR To use \fBrem2ps\fR, you should pipe the output of \fBRemind\fR with the \fB\-p\fR
option to \fBRem2ps\fR, and then send the result to a printer. This is most easily option to \fBrem2ps\fR, and then send the result to a printer. This is most easily
illustrated with examples: illustrated with examples:
.PP .PP
.nf .nf
@@ -228,10 +231,10 @@ calendar entries. This border is normally blank space.
.TP .TP
BoxWidth and BoxHeight BoxWidth and BoxHeight
The width and height of the calendar box, from center-to-center of the The width and height of the calendar box, from center-to-center of the
black gridlines. black grid lines.
.TP .TP
InBoxHeight InBoxHeight
The height from the center of the bottom black gridline to the top The height from the center of the bottom black grid line to the top
of the regular calendar entry area. The space from here to the top of the regular calendar entry area. The space from here to the top
of the box is used only to draw the day number. of the box is used only to draw the day number.
.TP .TP
@@ -260,7 +263,7 @@ PostScript files. Always test your PostScript thoroughly with a PostScript
viewer before sending it to the printer. You should not use any document viewer before sending it to the printer. You should not use any document
structuring comments in your PostScript code. structuring comments in your PostScript code.
.PP .PP
In addition, prior to drawing a calendar page, \fBRem2ps\fR emits In addition, prior to drawing a calendar page, \fBrem2ps\fR emits
the following PostScript code: the following PostScript code:
.PP .PP
.nf .nf
@@ -320,8 +323,8 @@ For an example, create a file called "myprolog" whose contents are:
} bind def } bind def
.fi .fi
.PP .PP
Use that file with the \fBRem2ps\fR \fB\-p\fR option to create calendars Use that file with the \fBrem2ps\fR \fB\-p\fR option to create calendars
with the year and month in large grey letters in the background of the with the year and month in large gray letters in the background of the
calendar. calendar.
.PP .PP
.SH REM2PS INPUT FORMAT (-P OPTION) .SH REM2PS INPUT FORMAT (-P OPTION)
@@ -380,7 +383,7 @@ been set to "-". The consistent use of "/" is designed to ease parsing.
.PP .PP
\fIspecial\fR is a string used \fIspecial\fR is a string used
for "out-of-band" communication with back-ends. If the reminder for "out-of-band" communication with back-ends. If the reminder
is a normal reminder, \fIspecial\fR is "*". The \fBRem2PS\fR is a normal reminder, \fIspecial\fR is "*". The \fBrem2ps\fR
back-end understands the specials \fBPostScript\fR and \fBPSFile\fR. back-end understands the specials \fBPostScript\fR and \fBPSFile\fR.
Other back-ends may understand other specials. A back end should Other back-ends may understand other specials. A back end should
\fIsilently ignore\fR a reminder with a special it doesn't understand. \fIsilently ignore\fR a reminder with a special it doesn't understand.
@@ -458,7 +461,7 @@ JSON object. The keys that may be present in the JSON object are as
follows: follows:
.TP .TP
.B date \fIYYYY-MM-DD\fR .B date \fIYYYY-MM-DD\fR
The \fbdate\fR key will \fIalways\fR be present; it is the trigger date The \fBdate\fR key will \fIalways\fR be present; it is the trigger date
of the reminder expressed as a string in the format \fIYYYY-MM-DD\fR of the reminder expressed as a string in the format \fIYYYY-MM-DD\fR
.TP .TP
.B filename \fIf\fR .B filename \fIf\fR
@@ -674,17 +677,17 @@ is desired.
.SH AUTHOR .SH AUTHOR
Rem2PS was written by Dianne Skoll <dianne@skoll.ca> rem2ps was written by Dianne Skoll <dianne@skoll.ca>
.SH BUGS .SH BUGS
All \fBRem2ps\fR options are case-sensitive, unlike \fBRemind\fR. All \fBrem2ps\fR options are case-sensitive, unlike \fBRemind\fR.
Any time you supply Any time you supply
a font name or size, line thickness, or border width, it is treated as a a font name or size, line thickness, or border width, it is treated as a
string and sent straight to the PostScript interpreter. Thus, if you string and sent straight to the PostScript interpreter. Thus, if you
supply invalid fonts or sizes, \fBRem2ps\fR will not complain, but the supply invalid fonts or sizes, \fBrem2ps\fR will not complain, but the
resulting PostScript output will probably not work. resulting PostScript output will probably not work.
.PP .PP
You should ensure that the values you supply for margin widths are sensible. You should ensure that the values you supply for margin widths are sensible.
If they are too big for the media size, \fBRem2ps\fR will not complain, If they are too big for the media size, \fBrem2ps\fR will not complain,
but again, the PostScript output will probably not work. but again, the PostScript output will probably not work.
.SH HOME PAGE .SH HOME PAGE
https://dianne.skoll.ca/projects/remind/ https://dianne.skoll.ca/projects/remind/

View File

@@ -66,7 +66,7 @@ causes \fBRemind\fR to display reminders on the calendar on the
day they actually occur \fIas well as\fR on any preceding days day they actually occur \fIas well as\fR on any preceding days
specified by the reminder's \fIdelta\fR. This \fIalso\fR causes specified by the reminder's \fIdelta\fR. This \fIalso\fR causes
\fBRemind\fR to include text outside %"...%" sequences that would \fBRemind\fR to include text outside %"...%" sequences that would
otherwise be removed (though the actual %" markers themselves are removed.) otherwise be removed (though the actual %" markers themselves are removed.) \"" Add comment to avoid Emacs highlighting problems
.TP .TP
.B 'l' .B 'l'
causes \fBRemind\fR to use VT100 line-drawing characters to draw causes \fBRemind\fR to use VT100 line-drawing characters to draw
@@ -300,6 +300,11 @@ Echo lines when displaying error messages
.TP .TP
.B f .B f
Trace the reading of reminder files Trace the reading of reminder files
.TP
.B s
Upon exit, print the high-water mark of the operator and value stacks
used for expression-parsing. This is unlikely to be useful unless
you're intimately familiar with Remind's source code.
.RE .RE
.TP .TP
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]] \fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
@@ -368,9 +373,12 @@ defaults to 1, and can range from 1 to 60. Note that the use of the
\fB\-z\fR option also enables the \fB\-f\fR option. \fB\-z\fR option also enables the \fB\-f\fR option.
.PP .PP
.RS .RS
If you supply the option \fB\-z0\fR, \fBRemind\fR runs in a If you supply the option \fB\-zj\fR, \fBRemind\fR runs in a
special mode called \fBserver mode\fR. This is documented special mode called \fBserver mode\fR. This is documented
in the tkremind man page; see tkremind(1). in the tkremind man page; see tkremind(1). The older server mode
option \fB\-z0\fR still works, but is deprecated; it uses an ad-hoc
method to communicate with the client rather than using JSON to communicate
with the client.
.RE .RE
.TP .TP
\fB\-u\fR\fIname\fR \fB\-u\fR\fIname\fR
@@ -1138,7 +1146,7 @@ reminder is queued for later activation. When \fBRemind\fR has
finished processing the reminder file, it puts itself in the finished processing the reminder file, it puts itself in the
background, and activates timed reminders when the system time reached background, and activates timed reminders when the system time reached
the specified time. Note that if you use the \fBNOQUEUE\fR modifier the specified time. Note that if you use the \fBNOQUEUE\fR modifier
in the \fBREM\fR command, then this queueing and background activation in the \fBREM\fR command, then this queuing and background activation
is \fInot\fR performed. \fBNOQUEUE\fR is useful if you want a time is \fInot\fR performed. \fBNOQUEUE\fR is useful if you want a time
to be associated with a reminder (eg, in the calendar) but are not to be associated with a reminder (eg, in the calendar) but are not
interested in a popup reminder happening at the specified time. interested in a popup reminder happening at the specified time.
@@ -1210,7 +1218,9 @@ in the bodies of timed reminders, then when the timed reminders are
activated, the variables and functions have the definitions that were activated, the variables and functions have the definitions that were
in effect at the end of the reminder script. These definitions may in effect at the end of the reminder script. These definitions may
\fInot\fR necessarily be those that were in effect at the time the reminder \fInot\fR necessarily be those that were in effect at the time the reminder
was queued. was queued. In addition, the OMIT context is whatever was in effect at
the end of the reminder script, which may not necessarily be the same
as when the \fBREM\fR command was first processed.
.PP .PP
.B THE SCHED AND WARN KEYWORDS .B THE SCHED AND WARN KEYWORDS
.PP .PP
@@ -1704,6 +1714,11 @@ For example, the following sequences are equivalent:
OMIT 3 Jan 2011 THROUGH 5 Jan 2011 OMIT 3 Jan 2011 THROUGH 5 Jan 2011
.fi .fi
.PP .PP
Note that \fBRemind\fR has a compiled-in limit to the number of full
OMITs. If you omit a range of \fIN\fR fully-specified (ie, year
included) days, then \fIN\fR full OMITs are used up. Trying to omit a
very large range may result in the error "Too many full OMITs"
.PP
You can make a THROUGH \fBOMIT\fR do double-duty as a \fBREM\fR command as You can make a THROUGH \fBOMIT\fR do double-duty as a \fBREM\fR command as
long as both dates are fully specified long as both dates are fully specified
.PP .PP
@@ -2162,7 +2177,10 @@ Unary minus. Can be applied to an \fBINT\fR. Returns the negative
of the operand. of the operand.
.TP .TP
.B * .B *
Multiplication. Returns the product of two \fBINT\fRs. Multiplication. Returns the product of two \fBINT\fRs. Alternatively, if
one argument is a \fBSTRING\fR and the other an \fBINT\fR, returns a
\fBSTRING\fR consisting of the INT number of repeats of the original STRING.
In this case, the INT argument cannot be negative.
.TP .TP
.B / .B /
Integer division. Returns the quotient of two \fBINT\fRs, discarding the Integer division. Returns the quotient of two \fBINT\fRs, discarding the
@@ -2562,6 +2580,24 @@ updates \fB$LongDeg\fR, \fB$LongMin\fR and \fB$LongSec\fR. Similar
rules apply to \fB$Latitude\fR, \fB$LatDeg\fR, \fB$LatMin\fR and \fB$LatSec\fR. rules apply to \fB$Latitude\fR, \fB$LatDeg\fR, \fB$LatMin\fR and \fB$LatSec\fR.
.RE .RE
.TP .TP
.B $MaxLateMinutes
This variable controls how \fBRemind\fR reacts to a computer being suspended
and then woken. Normally, if a timed reminder is queued and then the
computer suspended, and then the computer is woken \fIafter\fR the
timed reminder's trigger time, \fBRemind\fR will triger the timer anyway,
despite the fact that the trigger time has already passed.
.RS
.PP
If you set \fB$MaxLateMinutes\fR to a non-zero integer between 1 and 1440,
then \fBRemind\fR will \fInot\fR trigger a timed reminder whose trigger
time is more than \fB$MaxLateMinutes\fR minutes in the past.
.PP
Note that \fBRemind\fR uses the value of \fB$MaxLateMinutes\fR that is in
effect when it has finished reading the reminder file and puts itself in
the background. Generally, you should set \fB$MaxLateMinutes\fR once
near the beginning of the file and not change it after that.
.RE
.TP
.B $MaxSatIter .B $MaxSatIter
The maximum number of iterations for the \fBSATISFY\fR clause The maximum number of iterations for the \fBSATISFY\fR clause
(described later.) Must be at least 10. (described later.) Must be at least 10.
@@ -2570,7 +2606,9 @@ The maximum number of iterations for the \fBSATISFY\fR clause
A limit on the longest string that \fBRemind\fR will allow you A limit on the longest string that \fBRemind\fR will allow you
to create. The default is 65535. If you set \fB$MaxStringLen\fR to 0 to create. The default is 65535. If you set \fB$MaxStringLen\fR to 0
or to -1, then \fBremind\fR will allow you to create arbitrarily-long or to -1, then \fBremind\fR will allow you to create arbitrarily-long
strings, at least until it runs out of memory. strings, at least until it runs out of memory. We do not recommend
setting \fB$MaxStringLen\fR to 0 or -1 because it is very easy to write
code that DOSes \fBRemind\fR in that case.
.TP .TP
.B $MinsFromUTC .B $MinsFromUTC
The number of minutes between Universal Time Coordinated and local time. If The number of minutes between Universal Time Coordinated and local time. If
@@ -2585,6 +2623,18 @@ must also set \fB$CalcUTC\fR to 0 with the \fB\-i\fR option.
.B $NextMode (read-only) .B $NextMode (read-only)
If non-zero, then the \fB\-n\fR option was supplied on the command line. If non-zero, then the \fB\-n\fR option was supplied on the command line.
.TP .TP
.B $MaxFullOmits (read-only)
The maximum number of full OMITs allowed (a compiled-in constant.)
.TP
.B $MaxPartialOmits (read-only)
The maximum number of partial OMITs allowed (a compiled-in constant.)
.TP
.B $NumFullOmits (read-only)
The number of full OMITs in the current OMIT context.
.TP
.B $NumPartialOmits (read-only)
The number of partial OMITs in the current OMIT context.
.TP
.B $NumQueued (read-only) .B $NumQueued (read-only)
Contains the number of reminders queued so far for background Contains the number of reminders queued so far for background
timed triggering. timed triggering.
@@ -3038,11 +3088,12 @@ will produce undefined results.
Returns the time of "civil twilight" on the specified \fIdate\fR. If Returns the time of "civil twilight" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR. \fIdate\fR is omitted, defaults to \fBtoday()\fR.
.TP .TP
.B easterdate(dqi_arg) .B easterdate([dqi_arg])
If \fIarg\fR is an \fBINT\fR, then returns the date of Easter Sunday If \fIarg\fR is an \fBINT\fR, then returns the date of Easter Sunday
for the specified year. If \fIarg\fR is a \fBDATE\fR or for the specified year. If \fIarg\fR is a \fBDATE\fR or
\fBDATETIME\fR, then returns the date of the next Easter Sunday on or \fBDATETIME\fR, then returns the date of the next Easter Sunday on or
after \fIarg\fR. (The time component of a datetime is ignored.) after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS .RS
.P .P
Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox
@@ -3397,11 +3448,12 @@ the actual time, or a time supplied on the command line.
Returns a string that is the ordinal number \fInum\fR. For example, Returns a string that is the ordinal number \fInum\fR. For example,
\fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th". \fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th".
.TP .TP
.B orthodoxeaster(dqi_arg) .B orthodoxeaster([dqi_arg])
If \fIarg\fR is an \fBINT\fR, then returns the date of Orthodox Easter Sunday If \fIarg\fR is an \fBINT\fR, then returns the date of Orthodox Easter Sunday
for the specified year. If \fIarg\fR is a \fBDATE\fR or for the specified year. If \fIarg\fR is a \fBDATE\fR or
\fBDATETIME\fR, then returns the date of the next Orthodox Easter Sunday on or \fBDATETIME\fR, then returns the date of the next Orthodox Easter Sunday on or
after \fIarg\fR. (The time component of a datetime is ignored.) after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS .RS
.P .P
Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western
@@ -3622,7 +3674,7 @@ not supplied, then it defaults to \fBtoday()\fR.
.PP .PP
The return value of \fBsoleq()\fR is a \fBDATETIME\fR object specifying The return value of \fBsoleq()\fR is a \fBDATETIME\fR object specifying
the date and time of the solstice or equinox in the local time zone. It the date and time of the solstice or equinox in the local time zone. It
should be accurate to within 3 minues or so in the worst case. should be accurate to within 3 minutes or so in the worst case.
.PP .PP
See the included file \fB$SysInclude/seasons.rem\fR for examples of how See the included file \fB$SysInclude/seasons.rem\fR for examples of how
to use \fBsoleq()\fR. to use \fBsoleq()\fR.
@@ -5213,7 +5265,7 @@ A number of system variables let you translate various phrases
to other languages. These system variables are: to other languages. These system variables are:
.PP .PP
.TP .TP
.B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday .B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday, $Sunday
Set each of these system variables to a string representing the corresponding Set each of these system variables to a string representing the corresponding
day's name in your language. Strings must be valid UTF-8 strings. day's name in your language. Strings must be valid UTF-8 strings.
.TP .TP
@@ -5519,13 +5571,13 @@ the anniversary of a death is. The following rules are used:
o o
If the death occurred on 30 Heshvan, and Heshvan in the year after the If the death occurred on 30 Heshvan, and Heshvan in the year after the
death is \fIchaser\fR, then the jahrzeit is observed on 29 Heshvan in years death is \fIchaser\fR, then the jahrzeit is observed on 29 Heshvan in years
when Heshvan is \fIchaser\fR. Otherwise, the yahrzeit is observed on 1 when Heshvan is \fIchaser\fR. Otherwise, the jahrzeit is observed on 1
Kislev when Heshvan is \fIchaser\fR. Kislev when Heshvan is \fIchaser\fR.
.TP .TP
o o
If the death occurred on 30 Kislev, and Kislev in the year after the If the death occurred on 30 Kislev, and Kislev in the year after the
death is \fIchaser\fR, then the jahrzeit is observed on 29 Kislev in years death is \fIchaser\fR, then the jahrzeit is observed on 29 Kislev in years
when Kislev is \fIchaser\fR. Otherwise, the yahrzeit is observed on 1 when Kislev is \fIchaser\fR. Otherwise, the jahrzeit is observed on 1
Tevet when Kislev is \fIchaser\fR. Tevet when Kislev is \fIchaser\fR.
.TP .TP
o o
@@ -5932,6 +5984,9 @@ Almanac Office, USNO.
.PP .PP
Richard Siegel and Michael and Sharon Strassfeld, \fIThe First Jewish Richard Siegel and Michael and Sharon Strassfeld, \fIThe First Jewish
Catalog\fR, Jewish Publication Society of America. Catalog\fR, Jewish Publication Society of America.
.PP
Jean Meeus, \fIAstronomical Algorithms, Second Edition\fR, Willmann-Bell, Inc.
.SH HOME PAGE .SH HOME PAGE
https://dianne.skoll.ca/projects/remind/ https://dianne.skoll.ca/projects/remind/
.SH MAILING LIST .SH MAILING LIST

View File

@@ -192,7 +192,7 @@ If you create "timed" reminders, \fBTkRemind\fR will queue them in
the background and pop up boxes as they are triggered. Additionally, the background and pop up boxes as they are triggered. Additionally,
if you created the reminder using \fBTkRemind\fR, you will be given the if you created the reminder using \fBTkRemind\fR, you will be given the
option of "turning off" the reminder for the rest of the day. option of "turning off" the reminder for the rest of the day.
\fBTkRemind\fR achieves queueing of background reminders by running \fBTkRemind\fR achieves queuing of background reminders by running
\fBRemind\fR in \fIserver mode\fR, described later. \fBRemind\fR in \fIserver mode\fR, described later.
.SH OPTIONS .SH OPTIONS
@@ -301,11 +301,11 @@ Today
.SH IMMEDIATE UPDATES .SH IMMEDIATE UPDATES
If you are running \fBTkRemind\fR on Linux and have the If you are running \fBTkRemind\fR on Linux and \fBRemind\fR has been
\fBinotifywait\fR program installed (part of the \fBinotify-tools\fR compiled with \fBinotify\fR(7) support, then \fBTkRemind\fR redraws
or similar package), then \fBTkRemind\fR redraws the calendar window the calendar window \fIimmediately\fR if \fB$HOME/.reminders\fR
\fIimmediately\fR if \fB$HOME/.reminders\fR changes (or, if it is a changes (or, if it is a directory, any files in that directory
directory, any files in that directory change.) change.)
.PP .PP
This lets \fBTkRemind\fR react immediately to hand-edited reminders or This lets \fBTkRemind\fR react immediately to hand-edited reminders or
to reminder files that are imported from another calendar system (for example, to reminder files that are imported from another calendar system (for example,
@@ -366,60 +366,110 @@ your hand-edited files in a separate \fB*.rem\fR file than \fBTkRemind\fR's
\fBRemind\fR has a special mode for interacting with programs like \fBRemind\fR has a special mode for interacting with programs like
\fBTkRemind\fR. This mode is called \fIserver mode\fR and is \fBTkRemind\fR. This mode is called \fIserver mode\fR and is
selected by supplying the \fB\-z0\fR option to \fBRemind\fR. selected by supplying the \fB\-zj\fR option to \fBRemind\fR.
In server mode, \fBRemind\fR operates similar to daemon mode, except In server mode, \fBRemind\fR operates similar to daemon mode, except
it reads commands (one per line) it reads commands (one per line) from standard input and writes status
from standard input and writes status lines to standard output. lines to standard output. Each status line is a JSON object.
The commands accepted in server mode are: The commands accepted in server mode are:
.TP .TP
EXIT EXIT
Terminate the \fBRemind\fR process. EOF on standard input does the Terminate the \fBRemind\fR process. EOF on standard input does the
same thing. same thing. \fBRemind\fR exits immediately without printing
a JSON status line.
.TP .TP
STATUS STATUS
Return the number of queued reminders. Return the number of queued reminders. The JSON object looks
something like this:
.nf
{"response":"queued","nqueued":n,"command":"STATUS"}
.fi
where \fIn\fR is the number of reminders queued.
.TP
QUEUE or JSONQUEUE
Returns the contents of the queue. The JSON object looks something
like this:
.nf
{"response":"queue","queue":[ ... ],"command":"QUEUE"}
.fi
The value of the \fBqueue\fR key is an array of JSON objects, each
representing a queued reminder.
.TP
DEL \fIqid\fR
Delete the reminder with queue-id \fIqid\fR from the queue.
.TP .TP
REREAD REREAD
Re-read the reminder file Re-read the reminder file. Returns the following status line:
.nf
{"response":"reread","command":"REREAD"}
.fi
.PP .PP
The status lines written are as follows: Additional status lines written are as follows:
.TP .TP
NOTE reminder \fItime\fR \fItag\fR .nf
Signifies the beginning of a timed reminder whose trigger time is
\fItime\fR with tag \fItag\fR. If the reminder has no tag, an
asterisk is supplied for \fItag\fR. All lines following this line
are the body of the reminder, until the line \fBNOTE endreminder\fR
is transmitted.
{"response":"reminder","ttime":tt,"now":now,"tags":tags,"qid":qid,"body":body}
.fi
In this line, \fItt\fR is the trigger time of the reminder (expressed
as a string), \fInow\fR is the current time, \fItags\fR (if present)
is the tag or tags associated with the reminder, and \fIbody\fR is the
body of the reminder. This response causes \fBTkRemind\fR to pop up a
reminder notification. \fIqid\fR is a unique identifier for this
reminder. You may delete it from the queue by sending a \fBDEL\fR
\fIqid\fR command to the server. Note that \fIqid\fRs are not stable
across re-reads; if \fBRemind\fR restarts itself to re-read the reminder
file, then the \fIqid\fR values are likely to change, and any reminder
deleted with a \fBDEL\fR \fIqid\fR command is likely to be re-queued.
.TP .TP
NOTE newdate .nf
{"response":"newdate"}
.fi
This line is emitted whenever \fBRemind\fR has detected a rollover of This line is emitted whenever \fBRemind\fR has detected a rollover of
the system date. The front-end program should redraw its calendar the system date. The front-end program should redraw its calendar
or take whatever other action is needed. or take whatever other action is needed.
.TP .TP
NOTE reread .nf
This line is emitted whenever the number of reminders in \fBRemind\fR's
queue changes because of a date rollover or a \fBREREAD\fR command.
The front-end should issue a \fBSTATUS\fR command in response to this
message.
.TP {"response":"reread","command":"inotify"}
NOTE queued \fIn\fR
This line is emitted in response to a \fBSTATUS\fR command. The number .fi
\fIn\fR is the number of reminders in the queue.
If \fBRemind\fR was compiled with support for \fBinotify\fR(7), then
if it detects a change to the top-level reminder file or directory,
it issues the above response. The front-end should redraw its
calendar since this response indicates that a change has been made
to the reminder file or directory.
.PP
Please note that \fBRemind\fR can write a status message \fIat any time\fR
and not just in response to a command sent to its standard input. Therefore,
a program that runs \fBRemind\fR in server mode must be prepared to handle
asynchronous status messages.
.SH AUTHOR .SH AUTHOR
TkRemind was written by Dianne Skoll <dianne@skoll.ca> TkRemind was written by Dianne Skoll <dianne@skoll.ca>
\fBTkRemind\fR is Copyright 1996-2023 by Dianne Skoll. \fBTkRemind\fR is Copyright 1996-2024 by Dianne Skoll.
.SH FILES .SH FILES

View File

@@ -6,10 +6,11 @@ use warnings;
use Getopt::Long; use Getopt::Long;
use JSON::MaybeXS; use JSON::MaybeXS;
use Encode;
my %Options; my %Options;
my $rem2html_version = '2.1'; my $rem2html_version = '@VERSION@';
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks, my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
@Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen); @Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);
@@ -44,6 +45,11 @@ Print usage information
Print version Print version
=item --utf8
Assume standard input is encoded in UTF-8; write UTF-8 data to standard
output.
=item --backurl I<url> =item --backurl I<url>
When producing the small calendar for the previous month, make the When producing the small calendar for the previous month, make the
@@ -154,6 +160,7 @@ Usage: remind -pp ... | rem2html [options]
Options: Options:
--help, -h Print usage information --help, -h Print usage information
--utf8 Assume UTF-8 input and write UTF-8 output
--man Show man page (requires "perldoc") --man Show man page (requires "perldoc")
--version Print version --version Print version
--backurl url Make the title on the previous month's small calendar --backurl url Make the title on the previous month's small calendar
@@ -197,6 +204,7 @@ sub parse_options
local $SIG{__WARN__} = sub { print STDERR "$TIDY_PROGNAME: $_[0]\n"; }; local $SIG{__WARN__} = sub { print STDERR "$TIDY_PROGNAME: $_[0]\n"; };
if (!GetOptions(\%Options, "help|h", if (!GetOptions(\%Options, "help|h",
"man", "man",
"utf8",
"pngs", "pngs",
"version", "version",
"stylesheet=s", "stylesheet=s",
@@ -216,13 +224,22 @@ sub parse_options
if ($stylesheet) { if ($stylesheet) {
$Options{stylesheet} = smoosh($Options{imgbase}, $stylesheet); $Options{stylesheet} = smoosh($Options{imgbase}, $stylesheet);
} }
if ($Options{utf8}) {
binmode(STDIN, ':encoding(UTF-8)');
binmode(STDOUT, ':encoding(UTF-8)');
}
} }
sub start_output sub start_output
{ {
return if ($Options{tableonly}); return if ($Options{tableonly});
print("<html>\n<head>\n<title>" . $Options{title} . "</title>\n"); print("<html>\n<head>\n");
if ($Options{utf8}) {
print '<meta charset="UTF-8">' . "\n";
}
print("<title>" . $Options{title} . "</title>\n");
if (!$Options{nostyle}) { if (!$Options{nostyle}) {
if ($Options{stylesheet}) { if ($Options{stylesheet}) {
print('<link rel="stylesheet" type="text/css" href="' . print('<link rel="stylesheet" type="text/css" href="' .
@@ -310,7 +327,12 @@ sub parse_input
($y, $m, $d, $special, $tag, $duration, $time, $body) = ($y, $m, $d, $special, $tag, $duration, $time, $body) =
($1, $2, $3, $4, $5, $6, $7, $8); ($1, $2, $3, $4, $5, $6, $7, $8);
} elsif (/\{/) { } elsif (/\{/) {
my $obj = decode_json($_); my $obj;
if ($Options{utf8}) {
$obj = decode_json(encode('UTF-8', $_, Encode::FB_DEFAULT));
} else {
$obj = decode_json($_);
}
next unless ($obj->{date} =~ /^(\d+)-(\d+)-(\d+)$/); next unless ($obj->{date} =~ /^(\d+)-(\d+)-(\d+)$/);
$y = $1; $y = $1;
$m = $2; $m = $2;

15
resources/tkremind.desktop Executable file
View File

@@ -0,0 +1,15 @@
[Desktop Entry]
Type=Application
Exec=tkremind
StartupNotify=true
Icon=tkremind
Terminal=false
Name=tkremind
Comment=TkRemind Calendar Program
Categories=Office;Calendar;
Keywords=Calendar;remind;
Keywords[ca]=Calendari;remind;
Keywords[de]=Kalender;remind;
Keywords[en_GB]=Calendar;remind;
Keywords[es]=Calendario;remind;

BIN
resources/tkremind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -8,7 +8,7 @@
# A cheesy graphical front/back end for Remind using Tcl/Tk # A cheesy graphical front/back end for Remind using Tcl/Tk
# #
# This file is part of REMIND. # This file is part of REMIND.
# Copyright (C) 1992-2023 Dianne Skoll # Copyright (C) 1992-2024 Dianne Skoll
# #
#-------------------------------------------------------------- #--------------------------------------------------------------
@@ -29,69 +29,92 @@ set Hostname [exec hostname]
# Our icon photo # Our icon photo
catch { catch {
image create photo rpicon -data { image create photo rpicon -data {
iVBORw0KGgoAAAANSUhEUgAAAEAAAABbCAYAAADDeIOGAAAACXBIWXMAAAu6AAALugFBTNueAAAA iVBORw0KGgoAAAANSUhEUgAAAEAAAABbCAYAAADDeIOGAAAACXBIWXMAAAtEAAALRAHk62/EAAAA
GXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAADANJREFUeJzdnGtsFNcVx38Xr19r GXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAD5RJREFUeJzdXHtQFNea/80DZ2SQ
4/BawjPGjcGY2MY8Q+1g05iXKwoihvAQoCJEQoiU0CofWgmpUhIJNVKpSGnUSGmKLEEihcZFQeBW l8CMkEUQH0QQAUWXiKKiUVGjxmvFupqUGpMQU1nLSmpTtbkbTWW33GTzWG8iZbIxlUqMsLnxZkwG
gRJeKQkQisFAsWDxGsdAawjgGGF7Tz/MrrvGuzN3ZmchzV8aeT17zj3n/u+5d87cxyoR4fsMpVQx klosFbM+koDGOL5AUECYAeU1w2Ngmv72j6GpRqZ7unsG2b2/qlMz5/T5zuN3Xt/5zulWERH+hhED
MBkYC+QDQ4DBQDtQ7XmEviUESqnBwAKgFHgaGAP0iRC5AWwDtojI1e8FAUqp4cBzwExgBpAVRexb YAuAxQBSAUQDGA9AA4AF8Jvqb5CApwHsBJAOIBQA7ty5g8rKSrS0tMBut0OlUiE9PR3r1q2zg4j+
4E/AJhG52n1XRP4vL2Ag8DOgFmgDJMYVBPYCT0ct51FXJEbl+gLpMb77IbAZuGpS6fBVD6wys6W+ vzstEb1ARBVE5GZZls6dO0dvv/02bdy4kaZOnUparZYAEACaNWsWFRYWktvtJiJyj3Xh/XFbiegy
C4OgUiodeBGYC4zHaN1OoAU4CxzGaOVFwHR69ulo+Bb4HfCmiNw1lfwOtPY84AzWral7HQfKtO0/ ETEMw5DFYqGXX36ZkpOThyrLd8nJyfyKExG5iOg/xroS3twkIsonoj8S0Rwi0vOeRRLRYSLqcrvd
4sqvA+66VPFO4PeA15YPj7Dyz4VC1Y3KtwIvOvLjEVW+CPi3TuUyMzOtZC4DJY59eQSVTwL26VR+ ZLFY6KWXXqKpU6d6rTQACg8Pp9dee40cDsewig/2HIx1ZTlnJKLviaiLvOM+EV0lIra2tpb27dsn
48aNcu/ePXn++edjyVwAiuPy5xEQ8Gvdlr97966IiLz99tvRZPzA2Hj9sXqcuAql1CLgVR3Ze/fu 2NKcU6vVtHbtWrJarVwaLBEVEdE4ft7/FybBfwewp6KiQmOxWHD9+nW0t7cjJCQEcXFxyMrKwqpV
ce7cOUSE/fv39/oaeFVEzsft1ENs+TwggI3BbcSIEVJcXBztu39iEFkBpGra7xtN9qEkQqFE568Y q3D+/HkcPXoUJSUl6OrqEk1w9uzZeOutt7B27Vou6CqAdQBqRkQe45Y/WVNTQ1u2bKGgoCDB1hw/
SYzbuAU0YHQJP0aGeBODKICRQAHQCPxCHqzwQ2r93+BeomM3NzgCVMXyLeERoJSaDewC0hJqyEAb frxoa3NOp9PR7t27yel0cq3eQURPiZVhLCv/3z/99BMlJSVJqpwvl5SURKWlpfxhU0xEal/lGKvK
xpPhLHAa+LuInDBTSCgBSql+wCGMEEwULgN/AfYDB0Xkti3tBIf+H0hQeCulBKgG+n4nH4NKqXnA /9ulS5coLi4uIJVfvHgx1dbWchV3EFGe1LKMReWT29vbB1JTUwNS+eeff576+vq4yl8momA55XnY
mkSUnZmZyaZNmxgyZMhgEbkTV2EJavlU4CQWrejz+SQjI0O71VNTU2XBggXyxRdfiIhIZWXlN0BK lVcTkf3FF1+UNJ4PHTpEZrOZIiIiRjxXqVS0Z88eYlmWq/zHSsr0sAn4saioSHTC43drDkuXLh3x
PL4makpsIzDBSmjz5s3Mnz+fQ4cOceHCBQKBAM3Nzdy+fZs7d+6glGLAgAH4fD6Ki4uZNWsWeXl5 /NVXXyUe3lRapodZ+X+4ceMGmUwmSV07IyODGIah+vp6iomJGfZs5cqVxDAMV3k7Ef2JiBbRoHLz
3fqTJk3K2rt372xgt2NPE9D6+RhvZ6atmZubK/fv35d48MknnwiwOR5/EzEGvAn0txKqqqoiOTk5 gNMT0T8RUfpYEpDW3d098Nhjj8ka31lZWTRlypRhYRqNhl555RX69ttv6d69e+QFveSZCDsH/5PT
LkPPPvss/fv3/1E8ZbhKgFJqDjBfR7a0tDRue16vl/z8/LGhx60juEaAUkoBvwTrcSUzM5NnnnnG 6SSGYTpouFYJooejCKkBNBcUFER9/PHHAU1Yp9Nh6tSpSEhIwJQpUzB58mRERkYCADQaDe7evQur
Fbv5+flpQLlTfTcj4KdoOpKXl0f//pa9RAsFBQUAE53qu0KAUioF2KArP2DAADfMAvDkk0+CMZPs 1Ypp06bhzTffBIALALKHJfIQWv9oUVERaTSagMz6Up1Wq6Xc3Fw6fvw41zNYInr1YfeA3KamplML
CG5FwAsY01xaSE9Pd8ksFBUV4fF4xjrVj5sApVQSsNqOjsfjXvqRnZ3NsGHDRimlHPUpNyJgNTb7 FixQ3blzZzTzQXh4OGbOnImUlBTMnj0bubm5SE1N5R63A9gBwDxCcBRbXktEHZs3bx61VjYajbR7
YEtLiwtm/4fs7OxUYIoT3bgICI38tvN9v98fTppcwdChQwFynejGGwFVwDS7Si0tLWzdupWvv/46 926yWCzkcrm8zQWXyGMrGJNV4C+fffYZqVSqUan8jBkzyGaz8bt3PREdI6IC8uwuJZVT62/XE0Ca
TvMGfD4fwAgnuvESsNKJUjAY5JVXXmHMmDEsXLiQHTt2EAwGHTsxePBggKGOlOPI+Qsxdlm48n4/ zWbb+MYbbyDQQ0yr1WL79u0IDg5Gd3c3F/wGgH9RlOAotf7tnTt3+mzFtLQ0mjt3LoWEhPiMq9Pp
depU+fjjjx29E7zzzjsC1DqqRxwEbHGr8uHL4/HI+vXrpb293RYB27dvF+DUQyMAYweGzvq8o2vR aM2aNWSxWIiIqKioiD766COuB1QqLetoTIIvl5eX/3nFihXo6+sTjWixWLB69Wo4nU78/PPPaGho
okXS0dGhTUBNTY0ATU7q4nQMWAYMc6hriZ07d/LGG29oy2dkZAD0VUrZTjCcEqD1xhcPtm7dyqVL QFNTExwOB1iWBQBMnDgRCQkJyMnJwSOPPDIke//+fRQUFOCbb74BAAZAkKLSBrjl9QzDuLKzs322
l7RkQwRkAo/ZtWObAKVULhDXO7gOWltb2blzp5as1+sFoy62nwROImA54F4yH4GsrJ6buw4fPqyl aHp6Ol+PV4T8/Hz+RujvlZRZrYg1YXzy4Ycf6s6fP+8z4rp166BSqfzKLCEhAWfOnOG8W5SkEUgC
16dPdzUG2bXpJCmf50DHEsOHD+f+/fs97ukmShEE2J5ishUBSqnxaEx2OsG4ceO4detWj3t37ujN Ih0Oxx8PHjwoKfLcuXP9zjA1NRWVlZWcd5GSNAJJQPF7772nuXXrls+IBoMBCxYs8DvDtLQ0XLp0
eEcQoOzatdsFFuAsaizh8/no6Ojoca+wsFBLNyJykuzatUvATLsGdPH444/3ujdtmt5rRkSk2B7T ifPOUJRIgMZ+UmNjI2s0GiWt4SkpKX6NfQ5ut5syMjL4QZLX/0DPAZ+9++67qubmZkmRDQZDQDLV
tBWUUqOBqXYN6CAjI4ORI0f2uJeWlkZlZaWWfnt7u2Pbdhj7McaKj+soKirq1f8rKysZN26cln4E arXQarWor6/ngvLlphEIAiY1Nzcv/OqrryQL6HS6AGTrweTJk/Hbb79x3hy58oEg4PCBAwdULS0t
Affs2rZDQIndwnUxceJEmpqaetxbvHixtn4EAbZDQYuAUIqZMAImTJhAY2Njj3vNzc3a+q2treGP kgV86QdykJCQAN68M0uuvL8EhLe1ta344osvZAnV1dUNKTr+Ij4+nk9Aglx5fwn4c2FhobqxsVGW
5ttio0A3AmbicMLBCklJSZSUlBAIBHrcf++997h586ZWGRHd5xu79nUJcGcVIwrGjx9PVlYWly9f UEtLC27evOln1h7ExMTg9u3bnDdcrrxfBDAM84cjR47IliMi5OXlYevWrSguLsbAwIDiMsTExMBm
7nH//PnzvPbaa1plhIj6Frhu174uAZPsFBp6OdFCWVkZx44d65UFAmzbto13333XsoxQF7glIu6P s3HeIMitkx9L367PP/88YFvb999/n6/WSkZtbS0ZjUa+Wj1dTj38IaBu5cqVAd3jZ2VlUWVlpSwC
AUqpZGxmf6E5Oi2Ul5dTX18f9btgMMgHH3xgWUYoZb5lJRcNOhEwA+idpZggNEenJTd37lzOnTsX +vv7KSwsjG8bED0LDBQBMysrKyXZ9+W6iRMnUllZmSwSjEYjlZeXc969cuqidA741yNHjsDtdisU
UyY7O9uynKtXr4KxNc42dAiwFf4Ajz2m91peXl5OWloaZ86ciSnzYIL0IILBYPiJ8R8bLnZDh4Bi F0ZrayuefvppVFVVSZYxGAzo7OzkvEY5+SkigGGYFSUlJUpEJcFut2Pv3r2S4xsMBr51KFJOXkoI
u4XqLn3NmDGDixcvmkbAE088YVpGY2NjeAxwtNqiQ8BTtgrs00dr8TM1NZXKykoOHDjQ6yUoEqNG WHfs2LHxclpICcxmM+rq6iTF1ev1cDqdnDdCTj5KCHj9+++/VyAmDq1WC71eP+R3uVwoLS2VJKtS
jTItp66uLjyl7miRwZQApdQQYLSdAlNTU7UImDx5Mjk5OXz55ZcxZbxeLxMnmq+6RUybuU8Axgkt qeBwODhvqJx85RKg7ujoyPjhhx9kivlGYmIi+vv7h4U1NDRIlufJytpoyCVgi9ls1ra1tckU8w2j
W/l/V1eX1mOwoqICwJSAwsJCBg0yn+SJyB+u6foYCSsCbIU/GNPsVk+B5ORkqqqqOH36tOkAOGmS 0ThCPeZNbKJQq9WeJW3QKydfuQQUnDhxQqaINEyaNGlE2IwZ0mwcLMvytUlZdjZZBLhcrjknT56U
9fgbigABHG2dtyIgz+L7Xujs7CQzM9NUpqSkhKKiInbv3k1nZ2dMueJi6/H34sWLYGSA/7LnqQEr IyIZcXFxw/wqlQq5ubmSZHt7e/m9RyMnXzkEPG6xWHQ8vTtgMBqNiIqKGhaWmZmJ2bNnS5J3uVyK
An5gt0ARYeDAgabdYN48Y1px3759MWWSkpKYPt18d73f76ehoQHgiog4WlyMSYBSqg8OCAAjCmK9 85ZDwHPl5eWKMxLDnDlz0NTUNCxs8+bNkuV7e3v5K4gsNuQQsPDs2bNy0paM9PT0YWt+WFgYtm3b
y2dlZbF06VIaGxv5/PPPY5ZRUFDA2LHmGz+OHj0ajiC/Ez/BPALGYTMDDOPatWuMHx992878+fMZ JkmWZVl0d3ePOgEh169fj7ly5YqctCUjIyMDd+/eHfI7HA5IzevevXtwOBx8M1uvnLylErDzxIkT
MWIENTU1pjM5s2bNsrQTMX40OHATMCcgHwezrGAMTLEIWLZsGQA1NTWmZcyePdvSTl1dXfjjBTv+ KoZh5KQtCQaDAdnZ2XzDJogI77zzDn9pE0RdXR0GBgb4PaBHTv5SCdgg5bRHCebNm4fa2lq0t7cP
9YDJAujPcbi4WVVVJUeOHOl1f/LkyRIMBuX48ePi8Xhi6o8ePVq6urpMF0SDwaCMGjVKME6HFzpZ C//xxx+xf/9+n/Ktra0APMNmEPfl5C+VgFkVFRWSEw0LC4NGI201ys3NhdVq9fps//798DXxckpZ
GLVaHHW8+NnQ0MC0adN6bGwGWLVqFUopPvzwQ9PRv6KiInKuPyrq6+u5cuUKQBPG2WNnMImA7TiM RMTQFqBJMLIXSCFAb7Vaw2tqRl60FoLJZJJs5lqyZAmuX7/u9VlXVxf/5McruNXDZDJxQXcFI3uB
gJSUFAkEAvLyyy9338vJyZG2tjbp6OiQ3NxcU/29e/daLom///77Yfm/OW19qwhwtuUEY6HiwIED FAI2nTt3TiXHihsTEyMp3vTp07Fw4ULcuHFDME58fLxoGo2NjRg3bhz/6Fz6BgLSCNhw8eJFOWki
PQaylStX4vV6qa6uDj+6omLChAnMmTPH0kbEABjf2UGTCDhHHJscNmzYIB0dHZKdnS3Dhg2T69ev ODhYUrzc3FwwDIPff/9dMM706dNF02hqaoLJZEJISAgXdFliMQFIIyD98mVZaUo++Vm0aBFOnjwJ
i4hIeXm5qd7rr79u2foiImVlZWGd9fFEQNRlrtAskOMIAOMZ7fF4mDlzJkOHDsXn81FbW8uhQ4di oSO1yMhIJCcni6bR2NjIb30CIMtG75MAl8sVK9ZFvSE2NtZnnNDQUKxevRq//PKLYJz4+HhotcLX
6ni9XpYuXWpZ9vXr1zlxovsw2Nl4/IzV+k/hwn6furo6qa2tlRs3boiIyOLFi011Fi5cqNX61dXV mIgId+7c4RMgawkEfBMQdfbsWV1HR4esRI1Go88D0JycHERERODXX38VjMO75+cV1dXVsNlsfALa
YZ2baJ4cjRnpMQj4SbwEAPLWW291O338+HFJS0szla+pqdEiYN26dWGdA/FU3oyAF9wgoKKiotvp xeJ7gy8C/nDt2jW5aUKlUg1dWRXC6tWr4XK5RHtAZmamaBpXr14FEfF3kvKOqOCbgMVCS5QYWJbF
JUuWmMpOmTJFgsGgFgFFRUVhvd8mioBfuUFAcnKynDx5Ug4fPiwpKSmmslu2bNGqfH19vSQlJYX1 xIkTBZ9HRUVh8+bNsFgsguMf8GySxMAdifF6wG3ByALwRcCjUm58PIjOzk7+ujwC+fn5iIyMhJht
VsZLQKy1fsdHUCLR0dHBRx99xNmzZ6PO+4cxcuRIVq/W23D+6aef0tXVBXAf41RqXIhFQLRfYnKE ITY21uctEk6B4i2Vsjcrvgj4OzkKEIfW1lbR8fvkk0+CZVmIWZcyMzN9apNWqxV6vZ6fl3R1dRCi
6upqy93hK1asoG/fvlrlHT16NPyxXkT8cTkHMbvATlzoAjpXv379xO/3a4V/V1dXOP8X4I/xhr+Y BNjt9lA5hkkOra2tSEtL8/psxowZWLt2LU6dOoXq6mrBNJYuXSqah8PhwI0bNxAfH8/XAYQnFAGI
ZIKuRYAVli9frrX4AcYEit/vD/970g37sQiwveHQCdLT03nppZe05ffs2RP+KMA/XHEiRheIKw3W ERB38eJFjZLTn/r6emRnZ3tdwtavXw+1Wg2zeeTFbQ46nQ5r1qwRzePMmTNwOp18PaEfgLzlCuIE
vZYvX64V+mEUFhaGdS8ROvof7xWLgOZEVz4pKUk+++wz7cofPHgwfGRegB1uVD7qGKCUSuUhdIG5 5PK3qHJw+/ZtmEwmJCUlDQsPCgrCU089hb6+Pnz33XeC8vPnz8e0adNE87h69SoA8AmwKymrGAFp
c+dSVlamLb9nz55w44Bb4U/0McBHgrbCRmLt2rW25CNmkIPAQdcciQj7EmATxs9RJDT8S0tLtdNe fCOFHPT09KCiomLEXcAVK1YgIyMDX3/9NcTIzcvL85kHZzDhESB/uYI4AdPtdkWkAvAUcNmyZcPC
EZFTp05FTqHVuxX+IoJHKTUIeBtYgsuHqWNh7dq1GAfO9LBr167IKbTYc+kO8WceUtIDSEFBga3T tm/fDgCi3T8oKAjr16/3mT7XA3iG0/9RVFCRo+NfV61apfiYe9u2bdTe3j70zt+8efOIZVmqqqoi
ICIi06dPjyzjBVcjANiKsQBiex+AE6xZsybmydG2tja++uorrly5QlNTE4FAgEAgwLFjx8IiHRg/ g8EgKLds2TKfx+EtLS2k1+vJZDLx3x5LU3LUL9YDTP6YwC9evIjw8HAsWbIEALBr1y6oVCocOnSI
xOYalIiEDz+vBVZgbIdNSFfIyclh9+7dNDU1dVcw8q/f76etrc2siBMiMtlNn3r9jpBSqhSYjTEo f5I7Ahs2bPCZ9unTp+FyuZCcnMwtlSwA4S2lGISYYVnW+eD7enKcVqslq9VKhYWFlJmZSQzDUFtb
5mNMjztaIHkQKSkpdHZ2Wp0RvI2x2NkS+tsc+nsNqBMRV8cA0x9SCh2NHYtxRnA0MBwYgJEnhC+z G8XGxgrKREdHU1tbm88esGfPHgJABQUFXNA9Ja1PJPLGiM1mG3//vizz2jAwDIPS0lJs3boVBoMB
17h2jP27d0Kf2zH2893G2NNzM3S1YvzAYgDj9Ffsd2eX8V/DpporbFKohAAAAABJRU5ErkJggg== } Go0Ghw8fHmH/5+OJJ57gm7YEwdkneROgfG2NgwAzIWVlZX5fd1m1atVQq/X19VFKSopojzl//rzP
1m9oaCC9Xk8A+NdiPlDaA4QeLPnyyy/9JmDChAnU2NhIREQHDhwQjZufn++z8kREn376KQGeN8Z4
H0TJUkqA0CSYKvXisxicTieKi4vR09ODQ4cOicaVehJ04cIFAJ69wqCmyQAQNir4gNAcEPegnV4p
zGYzGIYRtPwCnm3vxo0bJaXHjX+erUCZtjYIIQJipF5O8IVz587B147ymWeegVrt2zx5+fLlISJ5
BFzwp3xCuUYFioCBgQHRmX/y5MnYsWOHpLROnToFlmVhMBj4dweO+VM+IQIiA0WAL2zZsoW/nRXF
6dOnAXhelRm0OBEA4V2VBAgREMa7dTVqCAsLwwsvvCApbnt7+9AxGa/7t8KzDVYMIQJCHgYBmzZt
8nnyw8FsNoOzTvMIUKb+8iBIwGgPAZ1Oh127dkmOX1ZWBgAYN24c31r0V78L4kU5mE5EvdHR0QG/
CM13mzZtkqT4EBH19PQQ90ZaVlYW/1GIUgXImyK0HB67+s2uri79aA4BlUqF5557TnL848ePD5nP
582bxwXfByD+UTEJ4PSAvW63e98nn3yCkpIS3Lx5M6AvNj2IvLw8LF++XHJ8vvU4O3voEyCyDaBe
QURzmpub2UWLFo1ql+e74uJiyd2fYRhKTEwkABQcHEx2u517tNXf7k+Dm6H7e/fufWiVnzNnDg0M
DEiqPMuydPTo0SHZnJycoUfk/ZtBiuwBf9q3b99HRqNR88EHH4ja6gOBHTt2DFN7+/v7UVVVhdra
WtTX16OhoWHYL/+NtPnz53N/bfBsgvzHIBPJRGTv6uqigwcP0uOPP07BwcEBb/0JEybQ66+/Ts8+
+ywtX76cHn30UVH7IOdCQ0Np2rRpQ1+PIKL/CkTrE438gsRLAP4ZgNFms6GsrAzXrl3DrVu3UF1d
jZqaGlF7nhyo1WpEREQgIiICkZGRiIiIQFRUFKKiohAbG4vo6GiYTCYkJiYiKSkJQUHDPhCxBkBA
XlkR+oRGGoB/BLAEgAmDClN3dzeuXLkCh8OBjo4OdHZ2orOzc8Q9fz5CQ0Oh1+sxfvx4GAwGhIaG
Ijg4GImJiTAaRV/vGYDnwkMHgHsA6uExfd0E8J/yq+odUr8hkgTPm9nZAGbC84amAZ6vNI+D8LYa
8HTjAQBueMatG57bnF3wrOX34DnVqYOngjXwfAPULx1fKv4X5zAnLNolSeQAAAAASUVORK5CYII=}
wm iconphoto . -default rpicon wm iconphoto . -default rpicon
} }
proc die_with_error { msg } {
tk_messageBox -message "Fatal Error" -detail $msg -icon error -type ok
exit 1
}
proc show_error { msg } {
tk_messageBox -message "Error" -detail $msg -icon error -type ok
}
proc missing_tcllib { pkg } { proc missing_tcllib { pkg } {
catch { puts stderr "Could not find the '$pkg' package -- you must install tcllib.\nPlease see http://tcllib.sourceforge.net/" } catch { puts stderr "Could not find the '$pkg' package -- you must install tcllib.\nPlease see http://tcllib.sourceforge.net/" }
tk_dialog .err "Error: tcllib not installed" "Could not find the '$pkg' package -- you must install tcllib. Please see http://tcllib.sourceforge.net/" error 0 OK tk_messageBox -message "Error: tcllib not installed" -detail "Could not find the '$pkg' package -- you must install tcllib. Please see http://tcllib.sourceforge.net/" -icon error -type ok
exit 1 exit 1
} }
if {[catch {package require mime}]} { if {[catch {package require mime}]} {
@@ -106,10 +129,23 @@ if {[catch {package require json}]} {
} }
if {$tcl_platform(platform) == "windows"} { if {$tcl_platform(platform) == "windows"} {
tk_dialog .error Error "Please do not port Remind to Windows" error 0 OK tk_messageBox -message "Please do not port Remind to Windows" -icon error -type ok
exit 1 exit 1
} }
#---------------------------------------------------------------------------
# GetRemindVersion
# Arguments:
# none
# Returns:
# The version of Remind
#---------------------------------------------------------------------------
proc GetRemindVersion {} {
global Remind
set ver [exec sh -c "(echo \"banner %\"; echo \"msg \[version()\]%\") | $Remind -"]
return $ver
}
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# GLOBAL VARIABLES # GLOBAL VARIABLES
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@@ -203,9 +239,6 @@ set ConfigFile ""
set EditorPid -1 set EditorPid -1
# Inotify file
set InotifyFP ""
# Errors from last remind run # Errors from last remind run
set RemindErrors "" set RemindErrors ""
@@ -289,11 +322,19 @@ set Option(PrintSmallCalendars) 1
set OptDescr(PrintFormat) "Print format: pdf or ps" set OptDescr(PrintFormat) "Print format: pdf or ps"
set Option(PrintFormat) ps set Option(PrintFormat) ps
set WarningHeaders [list "# Lines staring with REM TAG TKTAGnnn ... were created by tkremind" "# Do not edit them by hand or results may be unpredictable."] set WarningHeaders [list "# Lines starting with REM TAG TKTAGnnn ... were created by tkremind" "# Do not edit them by hand or results may be unpredictable."]
# Highest tag seen so far. Array of tags is stored in ReminderTags() # Highest tag seen so far.
set HighestTagSoFar 0 set HighestTagSoFar 0
# Check Remind version
set ver [GetRemindVersion]
if {"$ver" < "04.03.03"} {
tk_messageBox -message "This version of TkRemind requires Remind version 04.03.03 or newer; you have version $ver" -icon error -type ok
exit 1
}
proc get_weekday { yyyymmdd } { proc get_weekday { yyyymmdd } {
global EnglishDayNames global EnglishDayNames
return [lindex $EnglishDayNames [clock format [clock scan $yyyymmdd] -format %w -locale C]] return [lindex $EnglishDayNames [clock format [clock scan $yyyymmdd] -format %w -locale C]]
@@ -312,10 +353,29 @@ proc is_warning_header { line } {
if {"$line" == "$h"} { if {"$line" == "$h"} {
return 1 return 1
} }
# Ignore prior typo line too
if {"$line" == "# Lines staring with REM TAG TKTAGnnn ... were created by tkremind"} {
return 1
}
} }
return 0 return 0
} }
proc extract_tag { regex tag } {
if {[regexp $regex $tag extracted]} {
return $extracted
}
return "*"
}
proc extract_tktag { tag } {
extract_tag {TKTAG[0-9]+} $tag
}
proc extract_syntag { tag } {
extract_tag {__syn__[0-9a-f]+} $tag
}
#*********************************************************************** #***********************************************************************
# %PROCEDURE: Initialize # %PROCEDURE: Initialize
# %ARGUMENTS: # %ARGUMENTS:
@@ -382,8 +442,8 @@ proc Initialize {} {
# Check system sanity # Check system sanity
if {! [file readable $ReminderFile]} { if {! [file readable $ReminderFile]} {
set ans [tk_dialog .error "TkRemind: Warning" "Can't read reminder file `$ReminderFile'" warning 0 "Create it and continue" "Exit"] set ans [tk_messageBox -message "Can't read reminder file `$ReminderFile'. Create it and continue?" -type yesno -icon question]
if {$ans != 0} { if {$ans != "yes"} {
exit 1 exit 1
} }
catch { catch {
@@ -394,7 +454,7 @@ proc Initialize {} {
} }
} }
if {! [file readable $ReminderFile]} { if {! [file readable $ReminderFile]} {
tk_dialog .error "TkRemind: Error" "Could not create reminder file `$ReminderFile'" error 0 "Exit" die_with_error "Could not create reminder file `$ReminderFile'"
exit 1 exit 1
} }
@@ -404,12 +464,12 @@ proc Initialize {} {
write_warning_headers $out write_warning_headers $out
puts $out "" puts $out ""
close $out}]} { close $out}]} {
tk_dialog .error "Created File" "Created blank file `$AppendFile'" info 0 "OK" tk_messageBox -message "Created File" -detail "Created blank file `$AppendFile'" -icon info -type ok
} }
} }
if {! [file writable $AppendFile]} { if {! [file writable $AppendFile]} {
tk_dialog .error Error "Can't write reminder file `$AppendFile'" error 0 Ok die_with_error "Can't write reminder file `$AppendFile'"
exit 1 exit 1
} }
@@ -960,7 +1020,7 @@ proc WriteOptionsToFile {} {
global Option OptDescr global Option OptDescr
set problem [catch {set f [open "$ConfigFile.tmp" "w"]} err] set problem [catch {set f [open "$ConfigFile.tmp" "w"]} err]
if {$problem} { if {$problem} {
tk_dialog .error Error "Can't write $ConfigFile.tmp: $err" 0 OK show_error "Can't write $ConfigFile.tmp: $err"
return return
} }
@@ -1280,7 +1340,7 @@ proc Status { stuff } {
# None # None
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
proc DoPrint {} { proc DoPrint {} {
global Rem2PS Rem2PDF HaveRem2PDF PSCmd Option PrintStatus global Rem2PS Rem2PDF HaveRem2PDF PSCmd Option PrintStatus RemindErrors
global CurMonth CurYear MonthNames global CurMonth CurYear MonthNames
catch {destroy .p} catch {destroy .p}
@@ -1374,16 +1434,16 @@ proc DoPrint {} {
WriteOptionsToFile WriteOptionsToFile
if {$Option(PrintDest) == "file"} { if {$Option(PrintDest) == "file"} {
if {$fname == ""} { if {$fname == ""} {
tk_dialog .error Error "No filename specified" error 0 Ok show_error "No filename specified"
return return
} }
if {[file isdirectory $fname]} { if {[file isdirectory $fname]} {
tk_dialog .error Error "$fname is a directory" error 0 Ok show_error "$fname is a directory"
return return
} }
if {[file readable $fname]} { if {[file readable $fname]} {
set ans [tk_dialog .error Overwrite? "Overwrite $fname?" question 0 No Yes] set ans [tk_messageBox -message "Overwrite?" -detail "Overwrite $fname?" -icon question -type yesno]
if {$ans == 0} { if {$ans == no} {
return return
} }
} }
@@ -1466,7 +1526,8 @@ proc DoPrint {} {
append cmd " $fname" append cmd " $fname"
Status "Printing..." Status "Printing..."
if {[catch {eval "exec $cmd"} err]} { if {[catch {eval "exec $cmd"} err]} {
tk_dialog .error Error "Error during printing: $err" error 0 Ok set RemindErrors [unique_lines $err]
set_button_to_errors
} }
DisplayTime DisplayTime
} }
@@ -1523,9 +1584,7 @@ proc GotoDialog {} {
bind .g <KeyPress-Escape> ".g.b.cancel flash; .g.b.cancel invoke" bind .g <KeyPress-Escape> ".g.b.cancel flash; .g.b.cancel invoke"
CenterWindow .g . CenterWindow .g .
set oldFocus [focus] set oldFocus [focus]
grab .g
focus .g.y.e focus .g.y.e
tkwait window .g
catch {focus $oldFocus} catch {focus $oldFocus}
} }
@@ -1536,17 +1595,17 @@ proc DoGoto {} {
global CurYear CurMonth MonthNames global CurYear CurMonth MonthNames
set year [.g.y.e get] set year [.g.y.e get]
if { ! [regexp {^[0-9]+$} $year] } { if { ! [regexp {^[0-9]+$} $year] } {
tk_dialog .error Error {Illegal year specified (1990-5990)} error 0 Ok show_error {Illegal year specified (1990-5990)}
return return
} }
if { $year < 1990 || $year > 5990 } { if { $year < 1990 || $year > 5990 } {
tk_dialog .error Error {Illegal year specified (1990-5990)} error 0 Ok show_error {Illegal year specified (1990-5990)}
return return
} }
set month [lsearch -exact $MonthNames [.g.mon cget -text]] set month [lsearch -exact $MonthNames [.g.mon cget -text]]
set CurMonth $month set CurMonth $month
set CurYear $year set CurYear $year
destroy .g catch { destroy .g }
FillCalWindow FillCalWindow
} }
@@ -1555,19 +1614,15 @@ proc DoGoto {} {
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
proc Quit {} { proc Quit {} {
global Option global Option
global InotifyFP
if { !$Option(ConfirmQuit) } { if { !$Option(ConfirmQuit) } {
destroy . destroy .
StopBackgroundRemindDaemon StopBackgroundRemindDaemon
catch { exec kill [pid $InotifyFP] }
catch { close $InotifyFP }
exit 0 exit 0
} }
if { [tk_dialog .question "Confirm..." {Really quit?} question 0 No Yes] } { set ans [tk_messageBox -message "Really quit?" -icon question -type yesno]
if { $ans == "yes" } {
destroy . destroy .
StopBackgroundRemindDaemon StopBackgroundRemindDaemon
catch { exec kill [pid $InotifyFP] }
catch { close $InotifyFP }
exit 0 exit 0
} }
} }
@@ -2002,7 +2057,7 @@ proc CreateYearMenu {w {every 1}} {
# firstDay -- first weekday in month (0-6) # firstDay -- first weekday in month (0-6)
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
proc ModifyDay {d firstDay} { proc ModifyDay {d firstDay} {
global ModifyDialogResult AppendFile HighestTagSoFar ReminderTags global ModifyDialogResult AppendFile HighestTagSoFar
catch {destroy .mod} catch {destroy .mod}
toplevel .mod toplevel .mod
CreateModifyDialog .mod $d $firstDay "Cancel" "Add to reminder file" "Preview reminder" CreateModifyDialog .mod $d $firstDay "Cancel" "Add to reminder file" "Preview reminder"
@@ -2023,7 +2078,7 @@ proc ModifyDay {d firstDay} {
} }
set problem [catch {set rem [CreateReminder .mod]} err] set problem [catch {set rem [CreateReminder .mod]} err]
if {$problem} { if {$problem} {
tk_dialog .error Error "$err" error 0 Ok show_error $err
} else { } else {
if {$ModifyDialogResult == 3} { if {$ModifyDialogResult == 3} {
set rem [EditReminder $rem Cancel "Add reminder"] set rem [EditReminder $rem Cancel "Add reminder"]
@@ -2037,7 +2092,6 @@ proc ModifyDay {d firstDay} {
Status "Writing reminder..." Status "Writing reminder..."
set f [open $AppendFile a] set f [open $AppendFile a]
incr HighestTagSoFar incr HighestTagSoFar
set ReminderTags($HighestTagSoFar) 1
WriteReminder $f TKTAG$HighestTagSoFar $rem $opts WriteReminder $f TKTAG$HighestTagSoFar $rem $opts
close $f close $f
@@ -2050,7 +2104,6 @@ proc ModifyDay {d firstDay} {
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# CenterWindow -- center a window on the screen or over a parent. # CenterWindow -- center a window on the screen or over a parent.
# Stolen from tk_dialog code
# Arguments: # Arguments:
# w -- window to center # w -- window to center
# parent -- window over which to center. Defaults to screen if not supplied. # parent -- window over which to center. Defaults to screen if not supplied.
@@ -2566,7 +2619,7 @@ proc BrowseForFileRead {w {dir ""}} {
set dir [$w.cwd cget -text] set dir [$w.cwd cget -text]
} }
if {[catch "cd $dir" err]} { if {[catch "cd $dir" err]} {
tk_dialog .error Error "$err" error 0 Ok show_error "$err"
return return
} }
$w.cwd configure -text [pwd] $w.cwd configure -text [pwd]
@@ -2598,7 +2651,6 @@ proc BrowseForFileRead {w {dir ""}} {
cd $cwd cd $cwd
$w.entry delete 0 end $w.entry delete 0 end
} }
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# StartBackgroundRemindDaemon # StartBackgroundRemindDaemon
# Arguments: # Arguments:
@@ -2611,12 +2663,12 @@ proc BrowseForFileRead {w {dir ""}} {
proc StartBackgroundRemindDaemon {} { proc StartBackgroundRemindDaemon {} {
global Remind DaemonFile ReminderFile Option TwentyFourHourMode global Remind DaemonFile ReminderFile Option TwentyFourHourMode
if {$TwentyFourHourMode} { if {$TwentyFourHourMode} {
set problem [catch { set DaemonFile [open "|$Remind -b1 -z0 -itkremind=1 $Option(ExtraRemindArgs) $ReminderFile" "r+"] } err] set problem [catch { set DaemonFile [open "|$Remind -b1 -zj -y -itkremind=1 $Option(ExtraRemindArgs) $ReminderFile" "r+"] } err]
} else { } else {
set problem [catch { set DaemonFile [open "|$Remind -z0 -itkremind=1 $Option(ExtraRemindArgs) $ReminderFile" "r+"] } err] set problem [catch { set DaemonFile [open "|$Remind -zj -y -itkremind=1 $Option(ExtraRemindArgs) $ReminderFile" "r+"] } err]
} }
if {$problem} { if {$problem} {
tk_dialog .error Error "Can't start Remind daemon in background: $err" error 0 OK show_error "Can't start Remind daemon in background: $err"
} else { } else {
fileevent $DaemonFile readable "DaemonReadable $DaemonFile" fileevent $DaemonFile readable "DaemonReadable $DaemonFile"
puts $DaemonFile "STATUS" puts $DaemonFile "STATUS"
@@ -2669,19 +2721,19 @@ proc RestartBackgroundRemindDaemon {} {
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# ShowQueue # ShowQueue
# Arguments: # Arguments:
# file -- file channel that is readable # queue - the queue
# Returns: # Returns:
# nothing # nothing
# Description: # Description:
# Dumps the debugging queue listing # Dumps the debugging queue listing
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
proc ShowQueue { file } { proc ShowQueue { queue } {
set w .queuedbg set w .queuedbg
catch { destroy $w } catch { destroy $w }
toplevel $w toplevel $w
wm title $w "Queue (Debugging Output)" wm title $w "Queue (Debugging Output)"
wm iconname $w "Queue Dbg" wm iconname $w "Queue Dbg"
text $w.t -width 80 -height 30 -wrap word -yscrollcommand "$w.sb set" text $w.t -fg black -bg white -width 80 -height 30 -wrap word -yscrollcommand "$w.sb set"
scrollbar $w.sb -orient vertical -command "$w.text yview" scrollbar $w.sb -orient vertical -command "$w.text yview"
button $w.ok -text "OK" -command "destroy $w" button $w.ok -text "OK" -command "destroy $w"
grid $w.t -row 0 -column 0 -sticky nsew grid $w.t -row 0 -column 0 -sticky nsew
@@ -2692,26 +2744,34 @@ proc ShowQueue { file } {
grid rowconfigure $w 0 -weight 1 grid rowconfigure $w 0 -weight 1
grid rowconfigure $w 1 -weight 0 grid rowconfigure $w 1 -weight 0
CenterWindow $w . CenterWindow $w .
while (1) { set obj [lsort -command sort_q $queue]
# We should only get one line set did 0
gets $file line $w.t tag configure grey -background "#DDDDDD" -selectbackground "#999999"
if {$line == "NOTE ENDJSONQUEUE"} { set toggle 0
break foreach q $obj {
} if { $did > 0 } {
if {[catch {set obj [::json::json2dict $line]}]} { $w.t insert end "\n"
continue; }
} foreach r $q {
set obj [lsort -command sort_q $obj] if { $toggle != 0 } {
foreach q $obj { $w.t insert end "$r " grey
$w.t insert end "$q\n" } else {
} $w.t insert end "$r "
}
}
$w.t insert end "\n"
set toggle [expr 1 - $toggle]
set did 1
}
if { $did == 0 } {
$w.t insert end "(Queue is empty)\n"
} }
$w.t configure -state disabled $w.t configure -state disabled
} }
proc sort_q { a b } { proc sort_q { a b } {
set a_ttime [dict get $a nextttime] set a_ttime [dict get $a nexttime]
set b_ttime [dict get $b nextttime] set b_ttime [dict get $b nexttime]
if {$a_ttime < $b_ttime} { if {$a_ttime < $b_ttime} {
return -1 return -1
} }
@@ -2738,77 +2798,97 @@ proc DaemonReadable { file } {
catch { close $file } catch { close $file }
return return
} }
switch -glob -- $line { if {[catch {set obj [::json::json2dict $line]}]} {
"NOTE reminder*" { return;
scan $line "NOTE reminder %s %s %s" time now tag }
IssueBackgroundReminder $file $time $now $tag if {![dict exists $obj response]} {
} return;
"NOTE JSONQUEUE" { }
ShowQueue $file set response [dict get $obj response]
} switch -- $response {
"NOTE newdate" { "queued" {
# Date has rolled over -- clear "ignore" list set n [dict get $obj nqueued]
catch { unset Ignore}
Initialize
FillCalWindow
ShowTodaysReminders
}
"NOTE reread" {
puts $file "STATUS"
flush $file
}
"NOTE queued*" {
scan $line "NOTE queued %d" n
if {$n == 1} { if {$n == 1} {
.b.nqueued configure -text "1 reminder queued" .b.nqueued configure -text "1 reminder queued"
} else { } else {
.b.nqueued configure -text "$n reminders queued" .b.nqueued configure -text "$n reminders queued"
} }
} }
default { "reminder" {
puts stderr "Unknown message from daemon: $line\n" set time [dict get $obj ttime]
} set now [dict get $obj now]
set tag "*"
if {[dict exists $obj tags]} {
set tag [dict get $obj tags]
}
set body [dict get $obj body]
set qid "*"
if {[dict exists $obj qid]} {
set qid [dict get $obj qid]
}
IssueBackgroundReminder $body $time $now $tag $qid
}
"queue" {
set queue [dict get $obj queue]
ShowQueue $queue
}
"newdate" {
# Date has rolled over -- clear "ignore" list
catch { unset Ignore }
Initialize
FillCalWindow
ShowTodaysReminders
}
"reread" {
if {[dict exists $obj command]} {
set cmd [dict get $obj command]
if {"$cmd" == "inotify"} {
FillCalWindow
}
}
puts $file "STATUS"
flush $file
}
default {
puts stderr "Unknown message from daemon: $line\n"
}
} }
} }
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# IssueBackgroundReminder # IssueBackgroundReminder
# Arguments: # Arguments:
# file -- file channel that is readable # body -- body of reminder
# time -- time of reminder # time -- time of reminder
# now -- current time according to Remind daemon # now -- current time according to Remind daemon
# tag -- tag for reminder, or "*" if no tag # tag -- tag for reminder, or "*" if no tag
# qid -- Queue-ID for reminder, or "*" if no qid
# Returns: # Returns:
# nothing # nothing
# Description: # Description:
# Reads a background reminder from daemon and pops up window. # Reads a background reminder from daemon and pops up window.
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
proc IssueBackgroundReminder { file time now tag } { proc IssueBackgroundReminder { body time now tag qid } {
global BgCounter Option Ignore global BgCounter Option Ignore DaemonFile
if {$Option(Deiconify)} { if {$Option(Deiconify)} {
wm deiconify . wm deiconify .
} }
set msg ""
set line ""
while (1) {
gets $file line
if {$line == "NOTE endreminder"} {
break
}
if {$msg != ""} {
append msg "\n";
}
append msg $line
}
# Do nothing if it's blank -- was probably a RUN-type reminder. # Do nothing if it's blank -- was probably a RUN-type reminder.
if {$msg == ""} { if {$body == ""} {
return return
} }
# Do nothing if user told us to ignore this reminder # If we're ignoring it because of tag, ignore and delete
if {[info exists Ignore($tag)]} { set syntag [extract_syntag $tag]
return if {$syntag != "*"} {
if {[info exists Ignore($syntag)]} {
if {$qid != "*"} {
puts $DaemonFile "DEL $qid"
flush $DaemonFile
}
return
}
} }
incr BgCounter incr BgCounter
@@ -2817,24 +2897,30 @@ proc IssueBackgroundReminder { file time now tag } {
wm iconname $w "Reminder" wm iconname $w "Reminder"
wm title $w "Timed reminder ($time)" wm title $w "Timed reminder ($time)"
label $w.l -text "Reminder for $time issued at $now" label $w.l -text "Reminder for $time issued at $now"
message $w.msg -width 6i -text $msg message $w.msg -width 6i -text $body
frame $w.b frame $w.b
# Automatically shut down window after a minute if option says so # Automatically shut down window after a minute if option says so
set after_token [after 60000 [list ClosePopup $w "" $Option(MailAddr) $Option(AutoClose) "" $tag $msg $time]] set after_token [after 60000 [list ClosePopup $w "" $Option(MailAddr) $Option(AutoClose) "" $tag $body $time $qid]]
wm protocol $w WM_DELETE_WINDOW [list ClosePopup $w $after_token "" 1 "" $tag $msg $time] wm protocol $w WM_DELETE_WINDOW [list ClosePopup $w $after_token "" 1 "" $tag $body $time $qid]
button $w.ok -text "OK" -command [list ClosePopup $w $after_token "" 1 "" $tag $msg $time] button $w.ok -text "OK" -command [list ClosePopup $w $after_token "" 1 "" $tag $body $time $qid]
if {$tag != "*"} { set tktag [extract_tktag $tag]
button $w.nomore -text "Don't remind me again today" -command [list ClosePopup $w $after_token "" 1 "ignore" $tag $msg $time] if {$tktag != "*"} {
button $w.kill -text "Delete this reminder completely" -command [list ClosePopup $w $after_token "" 1 "kill" $tag $msg $time] button $w.kill -text "Delete this reminder completely" -command [list ClosePopup $w $after_token "" 1 "kill" $tag $body $time $qid]
}
if {$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.l -side top
pack $w.msg -side top -expand 1 -fill both pack $w.msg -side top -expand 1 -fill both
pack $w.b -side top pack $w.b -side top
pack $w.ok -in $w.b -side left pack $w.ok -in $w.b -side left
if {$tag != "*"} { if {$qid != "*"} {
pack $w.nomore $w.kill -in $w.b -side left pack $w.nomore -in $w.b -side left
}
if {$tktag != "*"} {
pack $w.kill -in $w.b -side left
} }
CenterWindow $w . CenterWindow $w .
@@ -2845,17 +2931,11 @@ proc IssueBackgroundReminder { file time now tag } {
} }
if {$Option(RunCmd) != ""} { if {$Option(RunCmd) != ""} {
if {$Option(FeedReminder)} { if {$Option(FeedReminder)} {
FeedReminderToCommand $Option(RunCmd) "$time: $msg" FeedReminderToCommand $Option(RunCmd) "$time: $body"
} else { } else {
exec "/bin/sh" "-c" $Option(RunCmd) "&" exec "/bin/sh" "-c" $Option(RunCmd) "&"
} }
} }
# reread status
if {$file != "stdin"} {
puts $file "STATUS"
flush $file
}
} }
#*********************************************************************** #***********************************************************************
@@ -2901,7 +2981,7 @@ proc main {} {
global AppendFile HighestTagSoFar DayNames global AppendFile HighestTagSoFar DayNames
catch { catch {
puts "\nTkRemind Copyright (C) 1996-2021 Dianne Skoll" puts "\nTkRemind Copyright (C) 1996-2024 Dianne Skoll"
} }
catch { SetFonts } catch { SetFonts }
Initialize Initialize
@@ -2920,7 +3000,6 @@ proc main {} {
CreateCalWindow $DayNames CreateCalWindow $DayNames
FillCalWindow FillCalWindow
StartBackgroundRemindDaemon StartBackgroundRemindDaemon
SetupInotify
DisplayTimeContinuously DisplayTimeContinuously
} }
@@ -2935,7 +3014,7 @@ proc main {} {
# the tag array. Also adjusts HighestTagSoFar # the tag array. Also adjusts HighestTagSoFar
#*********************************************************************** #***********************************************************************
proc ScanForTags { fname } { proc ScanForTags { fname } {
global HighestTagSoFar ReminderTags global HighestTagSoFar
if {[catch { set f [open $fname "r"]}]} { if {[catch { set f [open $fname "r"]}]} {
return return
} }
@@ -2946,7 +3025,6 @@ proc ScanForTags { fname } {
if {$tagno > $HighestTagSoFar} { if {$tagno > $HighestTagSoFar} {
set HighestTagSoFar $tagno set HighestTagSoFar $tagno
} }
set ReminderTags($tagno) 1
} }
} }
} }
@@ -3433,7 +3511,7 @@ proc EditTaggedReminder { w } {
} }
set problem [catch {set rem [CreateReminder .mod]} err] set problem [catch {set rem [CreateReminder .mod]} err]
if {$problem} { if {$problem} {
tk_dialog .error Error "$err" error 0 Ok show_error "$err"
continue continue
} }
if {$ModifyDialogResult == 4} { if {$ModifyDialogResult == 4} {
@@ -3453,7 +3531,7 @@ proc EditTaggedReminder { w } {
} }
} err] } err]
if {$problem} { if {$problem} {
tk_dialog .error Error "Error: $err" error 0 Ok show_error $err
return 1 return 1
} }
@@ -3516,7 +3594,7 @@ proc UniqueFileName { stem } {
#*********************************************************************** #***********************************************************************
proc DeleteTaggedReminder { tag } { proc DeleteTaggedReminder { tag } {
global AppendFile global AppendFile
global HighestTagSoFar global HighestTagSoFar Ignore
set tmpfile [UniqueFileName $AppendFile] set tmpfile [UniqueFileName $AppendFile]
set out [open $tmpfile "w"] set out [open $tmpfile "w"]
@@ -3525,12 +3603,14 @@ proc DeleteTaggedReminder { tag } {
set found 0 set found 0
set tagno 0 set tktag [extract_tktag $tag]
set syntag [extract_syntag $tag]
set h 0
while {[gets $in line] >= 0} { while {[gets $in line] >= 0} {
if {[is_warning_header $line]} { if {[is_warning_header $line]} {
continue continue
} }
if {[string match "REM TAG $tag *" $line]} { if {[string match "REM TAG $tktag *" $line]} {
set found 1 set found 1
continue continue
} }
@@ -3545,13 +3625,12 @@ proc DeleteTaggedReminder { tag } {
continue continue
} }
# Renumber tags if {[regexp {^REM TAG TKTAG([0-9]+)} $line all tagno]} {
if {[regexp {^REM TAG TKTAG([0-9]+) (.*)$} $line all oldtag rest]} { if {$tagno > $h} {
incr tagno set h $tagno
puts $out "REM TAG TKTAG$tagno $rest" }
} else { }
puts $out $line puts $out $line
}
} }
if {! $found } { if {! $found } {
@@ -3561,9 +3640,13 @@ proc DeleteTaggedReminder { tag } {
error "Did not find reminder with tag $tag" error "Did not find reminder with tag $tag"
} }
set HighestTagSoFar $tagno if {$syntag != "*"} {
catch { unset Ignore($syntag) }
}
close $in close $in
close $out close $out
set HighestTagSoFar $h
file rename -force -- $tmpfile $AppendFile file rename -force -- $tmpfile $AppendFile
} }
@@ -3740,7 +3823,7 @@ proc DoMoonSpecial { n stuff fntag day } {
# Displays current date and time in status window # Displays current date and time in status window
#*********************************************************************** #***********************************************************************
proc DisplayTime {} { proc DisplayTime {} {
global TwentyFourHourMode global TwentyFourHourMode DaemonFile
if {$TwentyFourHourMode} { if {$TwentyFourHourMode} {
set msg [clock format [clock seconds] -format "%e %b %Y %H:%M"] set msg [clock format [clock seconds] -format "%e %b %Y %H:%M"]
} else { } else {
@@ -3896,8 +3979,8 @@ proc ShowTodaysReminders {} {
# Prompts for confirmation; then deletes reminder # Prompts for confirmation; then deletes reminder
#*********************************************************************** #***********************************************************************
proc InteractiveDeleteReminder { tag } { proc InteractiveDeleteReminder { tag } {
set ans [tk_dialog .error "Really Delete" "Really delete reminder?" warning 0 No Yes] set ans [tk_messageBox -message "Really Delete" -detail "Really delete reminder?" -icon question -type yesno]
if {$ans == 1} { if {$ans == yes} {
DeleteTaggedReminder $tag DeleteTaggedReminder $tag
ScheduleUpdateForChanges ScheduleUpdateForChanges
} }
@@ -3919,8 +4002,8 @@ proc SendMail { recipient subject body } {
} }
} }
proc ClosePopup { w after_token mail_addr close_win ignore_or_kill tag reminder rem_time } { proc ClosePopup { w after_token mail_addr close_win ignore_or_kill tag reminder rem_time qid } {
global Ignore global DaemonFile Ignore
if {"$after_token" != ""} { if {"$after_token" != ""} {
catch { after cancel $after_token } catch { after cancel $after_token }
} }
@@ -3933,10 +4016,20 @@ proc ClosePopup { w after_token mail_addr close_win ignore_or_kill tag reminder
SendMail $mail_addr "Reminder for $rem_time" "Hello,\n\nThe following reminder is scheduled for $rem_time:\n\n$reminder\nRegards,\n\nTkRemind\n" SendMail $mail_addr "Reminder for $rem_time" "Hello,\n\nThe following reminder is scheduled for $rem_time:\n\n$reminder\nRegards,\n\nTkRemind\n"
} }
if {"$ignore_or_kill" == "ignore"} { if {"$ignore_or_kill" == "ignore"} {
set Ignore($tag) 1 if {$qid != "*"} {
set syntag [extract_syntag $tag]
if {$syntag != "*"} {
set Ignore($syntag) 1
}
puts $DaemonFile "DEL $qid"
flush $DaemonFile
}
} }
if {"$ignore_or_kill" == "kill"} { if {"$ignore_or_kill" == "kill"} {
InteractiveDeleteReminder $tag set tktag [extract_tktag $tag]
if {$tktag != "*"} {
InteractiveDeleteReminder $tag
}
} }
} }
@@ -3952,30 +4045,6 @@ proc SetFonts {} {
set SetFontsWorked 1 set SetFontsWorked 1
} }
# Set up inotify to watch for changes to reminder file/directory
proc SetupInotify {} {
global InotifyFP
global ReminderFile
set failed [catch {set InotifyFP [open "|inotifywait -r -q -m -e close_write -e move -e create -e delete $ReminderFile < /dev/null 2>/dev/null" "r"] } ]
if {$failed} {
# inotifywait probably not available... meh.
return
}
fileevent $InotifyFP readable [list InotifyReadable $InotifyFP]
}
# Called when inotifywait reports an event. Schedule a calendar update
# and daemon reload.
proc InotifyReadable { fp } {
catch { set num [gets $fp line] }
if {$num < 0} {
catch { exec kill [pid $fp] }
catch { close $fp }
return
}
ScheduleUpdateForChanges
}
### Balloon help ### Balloon help
set Balloon(HelpTime) 400 set Balloon(HelpTime) 400
set Balloon(StayTime) 3500 set Balloon(StayTime) 3500
@@ -4048,13 +4117,17 @@ bind Balloon <Destroy> {
catch { unset Balloon(helptext%W) } catch { unset Balloon(helptext%W) }
} }
proc balloon_add_help { w txt } { proc balloon_set_help { w txt } {
global Balloon global Balloon
if {"$txt" == ""} { if {"$txt" == ""} {
catch { unset Balloon(helptext$w) } catch { unset Balloon(helptext$w) }
return return
} }
set Balloon(helptext$w) $txt set Balloon(helptext$w) $txt
}
proc balloon_add_help { w txt } {
balloon_set_help $w $txt
bindtags $w "Balloon [bindtags $w]" bindtags $w "Balloon [bindtags $w]"
} }
@@ -4206,9 +4279,11 @@ proc update_color_buttons { w } {
} }
proc set_button_to_queue {} { proc set_button_to_queue {} {
balloon_set_help .b.queue "See the queue of pending reminders (debugging purposes only)"
.b.queue configure -text {Queue...} -command {DoQueue} .b.queue configure -text {Queue...} -command {DoQueue}
} }
proc set_button_to_errors {} { proc set_button_to_errors {} {
balloon_set_help .b.queue "See the list of errors from the most recent operation"
.b.queue configure -text {Errors...} -command {ShowErrors} .b.queue configure -text {Errors...} -command {ShowErrors}
} }

View File

@@ -63,6 +63,15 @@ install: all
done done
-mkdir -p $(DESTDIR)$(datarootdir)/remind || true -mkdir -p $(DESTDIR)$(datarootdir)/remind || true
cp -R ../include/* $(DESTDIR)$(datarootdir)/remind cp -R ../include/* $(DESTDIR)$(datarootdir)/remind
-mkdir -p $(DESTDIR)$(prefix)/share/pixmaps
-mkdir -p $(DESTDIR)$(prefix)/share/applications
$(INSTALL_DATA) $(srcdir)/../resources/tkremind.png $(DESTDIR)$(prefix)/share/pixmaps
$(INSTALL_PROGRAM) $(srcdir)/../resources/tkremind.desktop $(DESTDIR)$(prefix)/share/applications
-if test "$(DESTDIR)" = ""; then \
update-desktop-database < /dev/null > /dev/null 2>&1 ; \
xdg-icon-resource install --novendor --size 64 $(DESTDIR)$(prefix)/share/pixmaps/tkremind.png < /dev/null > /dev/null 2>&1; \
xdg-desktop-menu install --novendor $(DESTDIR)$(prefix)/share/applications/tkremind.desktop < /dev/null > /dev/null 2>&1 ; \
fi
install-stripped: install install-stripped: install
strip $(DESTDIR)$(bindir)/remind || true strip $(DESTDIR)$(bindir)/remind || true
@@ -71,9 +80,6 @@ install-stripped: install
clean: clean:
rm -f *.o *~ core *.bak $(PROGS) rm -f *.o *~ core *.bak $(PROGS)
cppcheck:
cppcheck --force --enable=all --suppress=variableScope --suppress=ConfigurationNotChecked *.c
clobber: clobber:
rm -f *.o *~ remind rem2ps test.out core *.bak rm -f *.o *~ remind rem2ps test.out core *.bak
@@ -83,6 +89,9 @@ depend:
# The next targets are not very useful to you. I use them to build # The next targets are not very useful to you. I use them to build
# distributions, etc. # distributions, etc.
cppcheck:
cppcheck -j`nproc` --force --enable=all --suppress=ConfigurationNotChecked --suppress=unmatchedSuppression --suppress=variableScope --inline-suppr .
# Build a tar file based on all files checked into git. # Build a tar file based on all files checked into git.
distro: distro:
cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)/ HEAD > src/remind-$(VERSION).tar cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)/ HEAD > src/remind-$(VERSION).tar

View File

@@ -5,7 +5,7 @@
/* The code for generating a calendar. */ /* The code for generating a calendar. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -475,7 +475,7 @@ void PrintJSONKeyPairTime(char const *name, int t)
} }
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
void PutWideChar(wchar_t const wc) void PutWideChar(wchar_t const wc, DynamicBuffer *output)
{ {
char buf[MB_CUR_MAX+1]; char buf[MB_CUR_MAX+1];
int len; int len;
@@ -483,7 +483,11 @@ void PutWideChar(wchar_t const wc)
len = wctomb(buf, wc); len = wctomb(buf, wc);
if (len > 0) { if (len > 0) {
buf[len] = 0; buf[len] = 0;
fputs(buf, stdout); if (output) {
DBufPuts(output, buf);
} else {
fputs(buf, stdout);
}
} }
} }
#endif #endif
@@ -556,11 +560,11 @@ static void goff(void)
static void static void
ClampColor(int *r, int *g, int *b) ClampColor(int *r, int *g, int *b)
{ {
if (TerminalBackground == TERMINAL_BACKGROUND_UNKNOWN) { if (GetTerminalBackground() == TERMINAL_BACKGROUND_UNKNOWN) {
/* No special clamping if terminal background is unknown */ /* No special clamping if terminal background is unknown */
return; return;
} }
if (TerminalBackground == TERMINAL_BACKGROUND_DARK) { if (GetTerminalBackground() == TERMINAL_BACKGROUND_DARK) {
if (*r <= 64 && *g <= 64 && *b <= 64) { if (*r <= 64 && *g <= 64 && *b <= 64) {
int max = *r; int max = *r;
double factor; double factor;
@@ -579,7 +583,7 @@ ClampColor(int *r, int *g, int *b)
} }
return; return;
} }
if (TerminalBackground == TERMINAL_BACKGROUND_LIGHT) { if (GetTerminalBackground() == TERMINAL_BACKGROUND_LIGHT) {
if (*r > 191 && *g > 191 && *b > 191) { if (*r > 191 && *g > 191 && *b > 191) {
int min = *r; int min = *r;
if (*g < min) min = *g; if (*g < min) min = *g;
@@ -667,11 +671,11 @@ Colorize(int r, int g, int b, int bg, int clamp)
if (b > 64) b = 1; if (b > 64) b = 1;
else b = 0; else b = 0;
if (clamp && TerminalBackground == TERMINAL_BACKGROUND_DARK && !bg) { if (clamp && GetTerminalBackground() == TERMINAL_BACKGROUND_DARK && !bg) {
/* Convert black-on-black to grey */ /* Convert black-on-black to grey */
if (!r && !g && !b) return VT100Colors[1][0][0][0]; if (!r && !g && !b) return VT100Colors[1][0][0][0];
} }
if (clamp && TerminalBackground == TERMINAL_BACKGROUND_LIGHT && !bg) { if (clamp && GetTerminalBackground() == TERMINAL_BACKGROUND_LIGHT && !bg) {
/* Convert white-on-white to grey */ /* Convert white-on-white to grey */
if (r && g && b) return VT100Colors[1][0][0][0]; if (r && g && b) return VT100Colors[1][0][0][0];
} }
@@ -1037,7 +1041,7 @@ static void DoCalendarOneMonth(void)
printf("\"entries\":[\n"); printf("\"entries\":[\n");
} }
} }
while (WriteCalendarRow()) continue; while (WriteCalendarRow()) /* continue */;
if (PsCal == PSCAL_LEVEL1) { if (PsCal == PSCAL_LEVEL1) {
printf("%s\n", PSEND); printf("%s\n", PSEND);
@@ -1218,7 +1222,7 @@ static void PrintLeft(char const *s, int width, char pad)
if (!buf) { if (!buf) {
/* Uh-oh... cannot recover */ /* Uh-oh... cannot recover */
fprintf(stderr, "%s\n", ErrMsg[E_NO_MEM]); fprintf(stderr, "%s\n", ErrMsg[E_NO_MEM]);
exit(1); exit(EXIT_FAILURE);
} }
} }
(void) mbstowcs(buf, s, len+1); (void) mbstowcs(buf, s, len+1);
@@ -1227,7 +1231,7 @@ static void PrintLeft(char const *s, int width, char pad)
ws = buf; ws = buf;
for (i=0; i<width;) { for (i=0; i<width;) {
if (*ws) { if (*ws) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
i+= wcwidth(*ws); i+= wcwidth(*ws);
} else { } else {
break; break;
@@ -1235,7 +1239,7 @@ static void PrintLeft(char const *s, int width, char pad)
} }
/* Mop up any potential combining characters */ /* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) { while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
} }
/* Possibly send lrm control sequence */ /* Possibly send lrm control sequence */
@@ -1297,7 +1301,7 @@ static void PrintCentered(char const *s, int width, char *pad)
if (!buf) { if (!buf) {
/* Uh-oh... cannot recover */ /* Uh-oh... cannot recover */
fprintf(stderr, "%s\n", ErrMsg[E_NO_MEM]); fprintf(stderr, "%s\n", ErrMsg[E_NO_MEM]);
exit(1); exit(EXIT_FAILURE);
} }
} }
(void) mbstowcs(buf, s, len+1); (void) mbstowcs(buf, s, len+1);
@@ -1308,7 +1312,7 @@ static void PrintCentered(char const *s, int width, char *pad)
for (i=0; i<d; i++) fputs(pad, stdout); for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) { for (i=0; i<width; i++) {
if (*ws) { if (*ws) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
if (wcwidth(*ws) == 0) { if (wcwidth(*ws) == 0) {
/* Don't count this character... it's zero-width */ /* Don't count this character... it's zero-width */
i--; i--;
@@ -1319,7 +1323,7 @@ static void PrintCentered(char const *s, int width, char *pad)
} }
/* Mop up any potential combining characters */ /* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) { while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
} }
/* Possibly send lrm control sequence */ /* Possibly send lrm control sequence */
send_lrm(); send_lrm();
@@ -1448,7 +1452,7 @@ static int WriteOneColLine(int col)
} }
numwritten += wcwidth(*ws); numwritten += wcwidth(*ws);
} }
PutWideChar(*ws); PutWideChar(*ws, NULL);
} }
} }
e->wc_pos = ws; e->wc_pos = ws;
@@ -1463,7 +1467,7 @@ static int WriteOneColLine(int col)
if (wcwidth(*ws) > 0) { if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws); numwritten += wcwidth(*ws);
} }
PutWideChar(*ws); PutWideChar(*ws, NULL);
} }
} }
} }
@@ -1603,7 +1607,7 @@ static void GenerateCalEntries(int col)
r=IncludeFile(InitialFile); r=IncludeFile(InitialFile);
if (r) { if (r) {
fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], InitialFile, ErrMsg[r]); fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], InitialFile, ErrMsg[r]);
exit(1); exit(EXIT_FAILURE);
} }
while(1) { while(1) {
@@ -1611,7 +1615,7 @@ static void GenerateCalEntries(int col)
if (r == E_EOF) return; if (r == E_EOF) return;
if (r) { if (r) {
Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]); Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]);
exit(1); exit(EXIT_FAILURE);
} }
s = FindInitialToken(&tok, CurLine); s = FindInitialToken(&tok, CurLine);
@@ -2211,10 +2215,114 @@ static void WriteSimpleEntryProtocol1(CalEntry *e)
printf("%s\n", e->text); printf("%s\n", e->text);
} }
void WriteJSONTimeTrigger(TimeTrig const *tt)
{
PrintJSONKeyPairTime("time", tt->ttime);
PrintJSONKeyPairTime("nexttime", tt->nexttime);
PrintJSONKeyPairInt("tdelta", tt->delta);
PrintJSONKeyPairInt("trep", tt->rep);
if (tt->duration != NO_TIME) {
PrintJSONKeyPairInt("duration", tt->duration);
}
}
void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
{
/* wd is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (t->wd != NO_WD) {
printf("\"wd\":[");
int done = 0;
int i;
for (i=0; i<7; i++) {
if (t->wd & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
if (t->d != NO_DAY) {
PrintJSONKeyPairInt("d", t->d);
}
if (t->m != NO_MON) {
PrintJSONKeyPairInt("m", t->m+1);
}
if (t->y != NO_YR) {
PrintJSONKeyPairInt("y", t->y);
}
if (t->back) {
PrintJSONKeyPairInt("back", t->back);
}
if (t->delta) {
PrintJSONKeyPairInt("delta", t->delta);
}
if (t->rep) {
PrintJSONKeyPairInt("rep", t->rep);
}
/* Local omit is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (t->localomit != NO_WD) {
printf("\"localomit\":[");
int done = 0;
int i;
for (i=0; i<7; i++) {
if (t->localomit & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
switch(t->skip) {
case SKIP_SKIP:
PrintJSONKeyPairString("skip", "SKIP");
break;
case BEFORE_SKIP:
PrintJSONKeyPairString("skip", "BEFORE");
break;
case AFTER_SKIP:
PrintJSONKeyPairString("skip", "AFTER");
break;
}
PrintJSONKeyPairDate("until", t->until);
if (t->once != NO_ONCE) {
PrintJSONKeyPairInt("once", t->once);
}
if (t->scanfrom != today) {
PrintJSONKeyPairDate("scanfrom", t->scanfrom);
}
PrintJSONKeyPairDate("from", t->from);
PrintJSONKeyPairInt("priority", t->priority);
PrintJSONKeyPairDateTime("eventstart", t->eventstart);
if (t->eventduration != NO_TIME) {
PrintJSONKeyPairInt("eventduration", t->eventduration);
}
if (t->maybe_uncomputable) {
PrintJSONKeyPairInt("maybe_uncomputable", 1);
}
if (t->noqueue) {
PrintJSONKeyPairInt("noqueue", 1);
}
PrintJSONKeyPairString("sched", t->sched);
PrintJSONKeyPairString("warn", t->warn);
PrintJSONKeyPairString("omitfunc", t->omitfunc);
if (t->addomit) {
PrintJSONKeyPairInt("addomit", 1);
}
if (include_tags) {
PrintJSONKeyPairString("tags", DBufValue(&(t->tags)));
}
}
static void WriteSimpleEntryProtocol2(CalEntry *e, int today) static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
{ {
int done = 0;
char const *s; char const *s;
if (DoPrefixLineNo) { if (DoPrefixLineNo) {
PrintJSONKeyPairString("filename", e->filename); PrintJSONKeyPairString("filename", e->filename);
@@ -2234,88 +2342,13 @@ static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
PrintJSONKeyPairInt("trep", e->tt.rep); PrintJSONKeyPairInt("trep", e->tt.rep);
} }
} }
if (e->trig.eventduration != NO_TIME) { WriteJSONTrigger(&e->trig, 0, today);
PrintJSONKeyPairInt("eventduration", e->trig.eventduration);
}
/* wd is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (e->trig.wd != NO_WD) {
printf("\"wd\":[");
done = 0;
int i;
for (i=0; i<7; i++) {
if (e->trig.wd & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
if (e->trig.d != NO_DAY) {
PrintJSONKeyPairInt("d", e->trig.d);
}
if (e->trig.m != NO_MON) {
PrintJSONKeyPairInt("m", e->trig.m+1);
}
if (e->trig.y != NO_YR) {
PrintJSONKeyPairInt("y", e->trig.y);
}
PrintJSONKeyPairDateTime("eventstart", e->trig.eventstart);
if (e->trig.back) {
PrintJSONKeyPairInt("back", e->trig.back);
}
if (e->trig.delta) {
PrintJSONKeyPairInt("delta", e->trig.delta);
}
if (e->trig.rep) {
PrintJSONKeyPairInt("rep", e->trig.rep);
}
if (e->nonconst_expr) { if (e->nonconst_expr) {
PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr); PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr);
} }
if (e->if_depth) { if (e->if_depth) {
PrintJSONKeyPairInt("if_depth", e->if_depth); PrintJSONKeyPairInt("if_depth", e->if_depth);
} }
switch(e->trig.skip) {
case SKIP_SKIP:
PrintJSONKeyPairString("skip", "SKIP");
break;
case BEFORE_SKIP:
PrintJSONKeyPairString("skip", "BEFORE");
break;
case AFTER_SKIP:
PrintJSONKeyPairString("skip", "AFTER");
break;
}
/* Local omit is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (e->trig.localomit != NO_WD) {
printf("\"localomit\":[");
done = 0;
int i;
for (i=0; i<7; i++) {
if (e->trig.localomit & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
PrintJSONKeyPairDate("until", e->trig.until);
if (e->trig.once != NO_ONCE) {
PrintJSONKeyPairInt("once", e->trig.once);
}
if (e->trig.scanfrom != today) {
PrintJSONKeyPairDate("scanfrom", e->trig.scanfrom);
}
PrintJSONKeyPairDate("from", e->trig.from);
PrintJSONKeyPairInt("priority", e->trig.priority);
if (e->is_color) { if (e->is_color) {
PrintJSONKeyPairInt("r", e->r); PrintJSONKeyPairInt("r", e->r);

View File

@@ -10,6 +10,9 @@
/* Define if you have the <sys/types.h> header file. */ /* Define if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H #undef HAVE_SYS_TYPES_H
/* Define if you have the <sys/inotify.h> header file. */
#undef HAVE_SYS_INOTIFY_H
/* Define if you have the <glob.h> header file */ /* Define if you have the <glob.h> header file */
#undef HAVE_GLOB_H #undef HAVE_GLOB_H
@@ -17,6 +20,8 @@
#undef HAVE_LOCALE_H #undef HAVE_LOCALE_H
#undef HAVE_INOTIFY_INIT1
#undef HAVE_LANGINFO_H #undef HAVE_LANGINFO_H
#undef HAVE_GLOB #undef HAVE_GLOB

View File

@@ -6,7 +6,7 @@
/* which you can customize. */ /* which you can customize. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -117,7 +117,7 @@
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */ /* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000 #define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */ /* INCLUDE_NEST: How many nested INCLUDES do we handle? */

View File

@@ -6,7 +6,7 @@
/* which you can customize. */ /* which you can customize. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -117,7 +117,7 @@
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */ /* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000 #define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */ /* INCLUDE_NEST: How many nested INCLUDES do we handle? */

View File

@@ -7,7 +7,7 @@
/* commands. */ /* commands. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -192,7 +192,7 @@ int DoRem(ParsePtr p)
r = OK; r = OK;
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) { if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
if ( (r=TriggerReminder(p, &trig, &tim, dse, 0)) ) { if ( (r=TriggerReminder(p, &trig, &tim, dse, 0, NULL)) ) {
FreeTrig(&trig); FreeTrig(&trig);
return r; return r;
} }
@@ -679,6 +679,9 @@ static int ParseLocalOmit(ParsePtr s, Trigger *t)
break; break;
default: default:
if (t->localomit == NO_WD) {
return E_EXPECTING_WEEKDAY;
}
PushToken(DBufValue(&buf), s); PushToken(DBufValue(&buf), s);
DBufFree(&buf); DBufFree(&buf);
return OK; return OK;
@@ -910,7 +913,7 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
/* Trigger the reminder if it's a RUN or MSG type. */ /* Trigger the reminder if it's a RUN or MSG type. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued) int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output)
{ {
int r, y, m, d; int r, y, m, d;
char PrioExpr[VAR_NAME_LEN+25]; char PrioExpr[VAR_NAME_LEN+25];
@@ -977,7 +980,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
} }
/* If it's a MSG-type reminder, and no -k option was used, issue the banner. */ /* If it's a MSG-type reminder, and no -k option was used, issue the banner. */
if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE) if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE)
&& !DidMsgReminder && !NextMode && !msg_command) { && !DidMsgReminder && !NextMode && !msg_command && !is_queued) {
DidMsgReminder = 1; DidMsgReminder = 1;
if (!DoSubstFromString(DBufValue(&Banner), &buf, if (!DoSubstFromString(DBufValue(&Banner), &buf,
DSEToday, NO_TIME) && DSEToday, NO_TIME) &&
@@ -1044,11 +1047,18 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
return E_NO_MEM; return E_NO_MEM;
} }
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf)); r = OK;
if (output) {
if (DBufPuts(output, DBufValue(&calRow)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&pre_buf)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&buf)) != OK) r = E_NO_MEM;
} else {
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf));
}
DBufFree(&buf); DBufFree(&buf);
DBufFree(&pre_buf); DBufFree(&pre_buf);
DBufFree(&calRow); DBufFree(&calRow);
return OK; return r;
} }
/* Correct colors */ /* Correct colors */
@@ -1144,18 +1154,27 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
case MSG_TYPE: case MSG_TYPE:
case PASSTHRU_TYPE: case PASSTHRU_TYPE:
if (msg_command) { if (msg_command) {
DoMsgCommand(msg_command, DBufValue(&buf)); DoMsgCommand(msg_command, DBufValue(&buf), is_queued);
} else { } else {
printf("%s", DBufValue(&buf)); if (output) {
DBufPuts(output, DBufValue(&buf));
} else {
/* Add a space before "NOTE endreminder" */
if (IsServerMode() && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) {
printf(" %s", DBufValue(&buf));
} else {
printf("%s", DBufValue(&buf));
}
}
} }
break; break;
case MSF_TYPE: case MSF_TYPE:
FillParagraph(DBufValue(&buf)); FillParagraph(DBufValue(&buf), output);
break; break;
case RUN_TYPE: case RUN_TYPE:
System(DBufValue(&buf)); System(DBufValue(&buf), is_queued);
break; break;
default: /* Unknown/illegal type? */ default: /* Unknown/illegal type? */
@@ -1194,7 +1213,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
if (DontIssueAts > 1) { if (DontIssueAts > 1) {
/* If two or more -a options, then *DO* issue ats that are in the /* If two or more -a options, then *DO* issue ats that are in the
future */ future */
if (tim->ttime < SystemTime(0) / 60) { if (tim->ttime < MinutesPastMidnight(0)) {
return 0; return 0;
} }
} else { } else {
@@ -1202,13 +1221,6 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
} }
} }
/* Don't trigger "old" timed reminders */
/*** REMOVED...
if (dse == DSEToday &&
tim->ttime != NO_TIME &&
tim->ttime < SystemTime(0) / 60) return 0;
*** ...UNTIL HERE */
/* If "infinite delta" option is chosen, always trigger future reminders */ /* If "infinite delta" option is chosen, always trigger future reminders */
if (InfiniteDelta || NextMode) return 1; if (InfiniteDelta || NextMode) return 1;
@@ -1389,7 +1401,7 @@ static int ParsePriority(ParsePtr s, Trigger *t)
/* Execute the '-k' command, escaping shell chars in message. */ /* Execute the '-k' command, escaping shell chars in message. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int DoMsgCommand(char const *cmd, char const *msg) int DoMsgCommand(char const *cmd, char const *msg, int is_queued)
{ {
int r; int r;
int i, l; int i, l;
@@ -1426,7 +1438,7 @@ int DoMsgCommand(char const *cmd, char const *msg)
} }
r = OK; r = OK;
System(DBufValue(&execBuffer)); System(DBufValue(&execBuffer), is_queued);
finished: finished:
DBufFree(&buf); DBufFree(&buf);

View File

@@ -6,7 +6,7 @@
/* reminders are triggered. */ /* reminders are triggered. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -46,11 +46,11 @@
int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode) int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode)
{ {
int diff = dse - DSEToday; int diff = dse - DSEToday;
int curtime = SystemTime(0) / 60; int curtime = MinutesPastMidnight(0);
int err, done; int err, done;
int c; int c;
int d, m, y; int d, m, y;
int tim = tt->ttime; int tim = NO_TIME;
int h, min, hh, ch, cmin, chh; int h, min, hh, ch, cmin, chh;
int i; int i;
char const *pm, *cpm; char const *pm, *cpm;
@@ -72,6 +72,9 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
FromDSE(dse, &y, &m, &d); FromDSE(dse, &y, &m, &d);
if (tt) {
tim = tt->ttime;
}
if (tim == NO_TIME) tim = curtime; if (tim == NO_TIME) tim = curtime;
tdiff = tim - curtime; tdiff = tim - curtime;
adiff = ABS(tdiff); adiff = ABS(tdiff);
@@ -894,7 +897,7 @@ int DoSubstFromString(char const *source, DynamicBuffer *dbuf,
int r; int r;
if (dse == NO_DATE) dse=DSEToday; if (dse == NO_DATE) dse=DSEToday;
if (tim == NO_TIME) tim=SystemTime(0)/60; if (tim == NO_TIME) tim=MinutesPastMidnight(0);
CreateParser(source, &tempP); CreateParser(source, &tempP);
tempP.allownested = 0; tempP.allownested = 0;
tempTrig.typ = MSG_TYPE; tempTrig.typ = MSG_TYPE;

View File

@@ -6,7 +6,7 @@
/* buffers. */ /* buffers. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Declaration of functions for manipulating dynamic buffers */ /* Declaration of functions for manipulating dynamic buffers */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Error definitions. */ /* Error definitions. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -121,6 +121,8 @@
#define E_STRING_TOO_LONG 101 #define E_STRING_TOO_LONG 101
#define E_TIME_TWICE 102 #define E_TIME_TWICE 102
#define E_DURATION_NO_AT 103 #define E_DURATION_NO_AT 103
#define E_EXPECTING_WEEKDAY 104
#ifdef MK_GLOBALS #ifdef MK_GLOBALS
#undef EXTERN #undef EXTERN
#define EXTERN #define EXTERN
@@ -129,6 +131,10 @@
#define EXTERN extern #define EXTERN extern
#endif #endif
#define STR(X) STR2(X)
#define STR2(X) #X
#ifndef L_ERR_OVERRIDE #ifndef L_ERR_OVERRIDE
EXTERN char *ErrMsg[] EXTERN char *ErrMsg[]
@@ -165,7 +171,7 @@ EXTERN char *ErrMsg[]
"Number too high", "Number too high",
"Number too low", "Number too low",
"Can't open file", "Can't open file",
"INCLUDE nested too deeply", "INCLUDE nested too deeply (max. " STR(INCLUDE_NEST) ")",
"Parse error", "Parse error",
"Can't compute trigger", "Can't compute trigger",
"Too many nested IFs", "Too many nested IFs",
@@ -189,8 +195,8 @@ EXTERN char *ErrMsg[]
"Day specified twice", "Day specified twice",
"Unknown token", "Unknown token",
"Must specify month in OMIT command", "Must specify month in OMIT command",
"Too many partial OMITs", "Too many partial OMITs (max. " STR(MAX_PARTIAL_OMITS) ")",
"Too many full OMITs", "Too many full OMITs (max. " STR(MAX_FULL_OMITS) ")",
"Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT", "Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT",
"Error reading", "Error reading",
"Expecting end-of-line", "Expecting end-of-line",
@@ -237,7 +243,8 @@ EXTERN char *ErrMsg[]
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Expecting weekday name"
} }
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
; ;

View File

@@ -5,7 +5,7 @@
/* This file contains routines to parse and evaluate */ /* This file contains routines to parse and evaluate */
/* expressions. */ /* expressions. */
/* */ /* */
/* Copyright 1992-2023 by Dianne Skoll */ /* Copyright 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -87,10 +87,11 @@ extern BuiltinFunc Func[];
static Operator OpStack[OP_STACK_SIZE]; static Operator OpStack[OP_STACK_SIZE];
static int OpStackPtr = 0; static int OpStackPtr = 0;
static int OpStackHiWater = 0;
/* ValStack can't be static - needed by funcs.c */ /* ValStack can't be static - needed by funcs.c */
Value ValStack[VAL_STACK_SIZE]; Value ValStack[VAL_STACK_SIZE];
int ValStackPtr = 0; int ValStackPtr = 0;
int ValStackHiWater = 0;
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -944,7 +945,8 @@ static int Subtract(void)
/***************************************************************/ /***************************************************************/
static int Multiply(void) static int Multiply(void)
{ {
Value v1, v2; Value v1, v2, v3;
char *ptr;
int r; int r;
PopValStack(v2); PopValStack(v2);
@@ -964,6 +966,61 @@ static int Multiply(void)
PushValStack(v1); PushValStack(v1);
return OK; return OK;
} }
/* String times int means repeat the string that many times */
if ((v1.type == INT_TYPE && v2.type == STR_TYPE) ||
(v1.type == STR_TYPE && v2.type == INT_TYPE)) {
int rep = (v1.type == INT_TYPE ? v1.v.val : v2.v.val);
char const *str = (v1.type == INT_TYPE ? v2.v.str : v1.v.str);
int l;
/* Can't multiply by a negative number */
if (rep < 0) {
return E_2LOW;
}
if (rep == 0 || !str || !*str) {
/* Empty string */
DestroyValue(v1); DestroyValue(v2);
v3.type = STR_TYPE;
v3.v.str = malloc(1);
if (!v3.v.str) {
return E_NO_MEM;
}
*v3.v.str = 0;
PushValStack(v3);
return OK;
}
/* Create the new value */
l = (int) strlen(str);
if (l * rep < 0) {
DestroyValue(v1); DestroyValue(v2);
return E_STRING_TOO_LONG;
}
if ((unsigned long) l * (unsigned long) rep >= (unsigned long) INT_MAX) {
DestroyValue(v1); DestroyValue(v2);
return E_STRING_TOO_LONG;
}
if (MaxStringLen > 0 && ((unsigned long) l * (unsigned long) rep) > (unsigned long)MaxStringLen) {
DestroyValue(v1); DestroyValue(v2);
return E_STRING_TOO_LONG;
}
v3.type = STR_TYPE;
v3.v.str = malloc(l * rep + 1);
if (!v3.v.str) {
DestroyValue(v1); DestroyValue(v2);
return E_NO_MEM;
}
*v3.v.str = 0;
ptr = v3.v.str;
for (int i=0; i<rep; i++) {
strcat(ptr, str);
ptr += l;
}
DestroyValue(v1); DestroyValue(v2);
PushValStack(v3);
return OK;
}
DestroyValue(v1); DestroyValue(v2); DestroyValue(v1); DestroyValue(v2);
return E_BAD_TYPE; return E_BAD_TYPE;
} }
@@ -1429,3 +1486,11 @@ int FnPopValStack(Value *val)
return OK; return OK;
} }
} }
void DebugExitFunc(void)
{
if (DebugFlag & DB_EXPR_STACKS) {
fprintf(stderr, "Operator stack high water: %d\n", OpStackHiWater);
fprintf(stderr, " Value stack high water: %d\n", ValStackHiWater);
}
}

View File

@@ -5,19 +5,20 @@
/* Contains a few definitions used by expression evaluator. */ /* Contains a few definitions used by expression evaluator. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
/* Define the types of values */ /* Define the types of values */
#define ERR_TYPE 0 #define ERR_TYPE 0
#define INT_TYPE 1 #define INT_TYPE 1
#define TIME_TYPE 2 #define TIME_TYPE 2
#define DATE_TYPE 3 #define DATE_TYPE 3
#define STR_TYPE 4 #define STR_TYPE 4
#define DATETIME_TYPE 5 #define DATETIME_TYPE 5
#define SPECIAL_TYPE 6 /* Only for system variables */ #define SPECIAL_TYPE 6 /* Only for system variables */
#define CONST_INT_TYPE 7 /* Only for system variables */
/* Define stuff for parsing expressions */ /* Define stuff for parsing expressions */
#define BEG_OF_EXPR '[' #define BEG_OF_EXPR '['
@@ -32,10 +33,8 @@
for speed. BEWARE: These macros invoke return if an error happens ! */ for speed. BEWARE: These macros invoke return if an error happens ! */
#define PushOpStack(op) \ #define PushOpStack(op) \
if (OpStackPtr >= OP_STACK_SIZE) \ do { if (OpStackPtr >= OP_STACK_SIZE) return E_OP_STK_OVER; \
return E_OP_STK_OVER; \ else { OpStack[OpStackPtr++] = (op); if (OpStackPtr > OpStackHiWater) OpStackHiWater = OpStackPtr; } } while(0)
else \
OpStack[OpStackPtr++] = (op)
#define PopOpStack(op) \ #define PopOpStack(op) \
if (OpStackPtr <= 0) \ if (OpStackPtr <= 0) \
@@ -44,10 +43,13 @@ else \
(op) = OpStack[--OpStackPtr] (op) = OpStack[--OpStackPtr]
#define PushValStack(val) \ #define PushValStack(val) \
if (ValStackPtr >= VAL_STACK_SIZE) \ do { if (ValStackPtr >= VAL_STACK_SIZE) { \
return E_VA_STK_OVER; \ DestroyValue(val); \
else \ return E_VA_STK_OVER; \
ValStack[ValStackPtr++] = (val) } else { \
ValStack[ValStackPtr++] = (val); \
if (ValStackPtr > ValStackHiWater) ValStackHiWater = ValStackPtr; \
} } while (0);
#define PopValStack(val) \ #define PopValStack(val) \
if (ValStackPtr <= 0) \ if (ValStackPtr <= 0) \

View File

@@ -7,7 +7,7 @@
/* files. */ /* files. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -15,7 +15,7 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <fcntl.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
@@ -42,8 +42,8 @@
/* Convenient macros for closing files */ /* Convenient macros for closing files */
#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL)) #define FCLOSE(fp) ((((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
#define PCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL)) #define PCLOSE(fp) ((((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
/* Define the structures needed by the file caching system */ /* Define the structures needed by the file caching system */
typedef struct cache { typedef struct cache {
@@ -100,6 +100,18 @@ static int CheckSafety (void);
static int CheckSafetyAux (struct stat *statbuf); static int CheckSafetyAux (struct stat *statbuf);
static int PopFile (void); static int PopFile (void);
static int IncludeCmd(char const *); static int IncludeCmd(char const *);
void set_cloexec(int fd)
{
int flags;
flags = fcntl(fd, F_GETFD);
if (flags >= 0) {
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
}
}
static void OpenPurgeFile(char const *fname, char const *mode) static void OpenPurgeFile(char const *fname, char const *mode)
{ {
DynamicBuffer fname_buf; DynamicBuffer fname_buf;
@@ -123,6 +135,7 @@ static void OpenPurgeFile(char const *fname, char const *mode)
if (!PurgeFP) { if (!PurgeFP) {
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno)); fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
} }
set_cloexec(fileno(PurgeFP));
DBufFree(&fname_buf); DBufFree(&fname_buf);
} }
@@ -327,6 +340,7 @@ int OpenFile(char const *fname)
} }
} else { } else {
fp = fopen(fname, "r"); fp = fopen(fname, "r");
if (fp) set_cloexec(fileno(fp));
if (DebugFlag & DB_TRACE_FILES) { if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname); fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
} }
@@ -346,6 +360,7 @@ int OpenFile(char const *fname)
if (strcmp(fname, "-")) { if (strcmp(fname, "-")) {
fp = fopen(fname, "r"); fp = fopen(fname, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN; if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fileno(fp));
if (PurgeMode) OpenPurgeFile(fname, "w"); if (PurgeMode) OpenPurgeFile(fname, "w");
} else { } else {
fp = stdin; fp = stdin;
@@ -542,6 +557,7 @@ static int PopFile(void)
if (strcmp(i->filename, "-")) { if (strcmp(i->filename, "-")) {
fp = fopen(i->filename, "r"); fp = fopen(i->filename, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN; if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fileno(fp));
if (PurgeMode) OpenPurgeFile(i->filename, "a"); if (PurgeMode) OpenPurgeFile(i->filename, "a");
} else { } else {
fp = stdin; fp = stdin;

View File

@@ -6,7 +6,7 @@
/* expressions. */ /* expressions. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -195,6 +195,7 @@ static int CacheHebYear, CacheHebMon, CacheHebDay;
/* We need access to the value stack */ /* We need access to the value stack */
extern Value ValStack[]; extern Value ValStack[];
extern int ValStackPtr; extern int ValStackPtr;
extern int ValStackHiWater;
/* Macro for accessing arguments from the value stack - args are numbered /* Macro for accessing arguments from the value stack - args are numbered
from 0 to (Nargs - 1) */ from 0 to (Nargs - 1) */
@@ -251,7 +252,7 @@ BuiltinFunc Func[] = {
{ "defined", 1, 1, 0, FDefined }, { "defined", 1, 1, 0, FDefined },
{ "dosubst", 1, 3, 0, FDosubst }, { "dosubst", 1, 3, 0, FDosubst },
{ "dusk", 0, 1, 0, FDusk }, { "dusk", 0, 1, 0, FDusk },
{ "easterdate", 1, 1, 0, FEasterdate }, { "easterdate", 0, 1, 0, FEasterdate },
{ "evaltrig", 1, 2, 0, FEvalTrig }, { "evaltrig", 1, 2, 0, FEvalTrig },
{ "filedate", 1, 1, 0, FFiledate }, { "filedate", 1, 1, 0, FFiledate },
{ "filedatetime", 1, 1, 0, FFiledatetime }, { "filedatetime", 1, 1, 0, FFiledatetime },
@@ -289,7 +290,7 @@ BuiltinFunc Func[] = {
{ "nonomitted", 2, NO_MAX, 0, FNonomitted }, { "nonomitted", 2, NO_MAX, 0, FNonomitted },
{ "now", 0, 0, 0, FNow }, { "now", 0, 0, 0, FNow },
{ "ord", 1, 1, 1, FOrd }, { "ord", 1, 1, 1, FOrd },
{ "orthodoxeaster",1, 1, 0, FOrthodoxeaster }, { "orthodoxeaster",0, 1, 0, FOrthodoxeaster },
{ "ostype", 0, 0, 1, FOstype }, { "ostype", 0, 0, 1, FOstype },
{ "pad", 3, 4, 1, FPad }, { "pad", 3, 4, 1, FPad },
{ "plural", 1, 3, 1, FPlural }, { "plural", 1, 3, 1, FPlural },
@@ -1421,28 +1422,28 @@ static int FRealtoday(func_info *info)
static int FNow(func_info *info) static int FNow(func_info *info)
{ {
RetVal.type = TIME_TYPE; RetVal.type = TIME_TYPE;
RETVAL = (int) ( SystemTime(0) / 60L ); RETVAL = MinutesPastMidnight(0);
return OK; return OK;
} }
static int FRealnow(func_info *info) static int FRealnow(func_info *info)
{ {
RetVal.type = TIME_TYPE; RetVal.type = TIME_TYPE;
RETVAL = (int) ( SystemTime(1) / 60L ); RETVAL = MinutesPastMidnight(1);
return OK; return OK;
} }
static int FCurrent(func_info *info) static int FCurrent(func_info *info)
{ {
RetVal.type = DATETIME_TYPE; RetVal.type = DATETIME_TYPE;
RETVAL = DSEToday * MINUTES_PER_DAY + (SystemTime(0) / 60); RETVAL = DSEToday * MINUTES_PER_DAY + MinutesPastMidnight(0);
return OK; return OK;
} }
static int FRealCurrent(func_info *info) static int FRealCurrent(func_info *info)
{ {
RetVal.type = DATETIME_TYPE; RetVal.type = DATETIME_TYPE;
RETVAL = RealToday * MINUTES_PER_DAY + (SystemTime(1) / 60); RETVAL = RealToday * MINUTES_PER_DAY + MinutesPastMidnight(1);
return OK; return OK;
} }
@@ -2364,13 +2365,17 @@ static int FEasterdate(func_info *info)
{ {
int y, m, d; int y, m, d;
int g, c, x, z, e, n; int g, c, x, z, e, n;
if (ARG(0).type == INT_TYPE) { if (Nargs == 0) {
y = ARGV(0); FromDSE(DSEToday, &y, &m, &d);
if (y < BASE) return E_2LOW; } else {
else if (y > BASE+YR_RANGE) return E_2HIGH; if (ARG(0).type == INT_TYPE) {
} else if (HASDATE(ARG(0))) { y = ARGV(0);
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */ if (y < BASE) return E_2LOW;
} else return E_BAD_TYPE; else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
do { do {
g = (y % 19) + 1; /* golden number */ g = (y % 19) + 1; /* golden number */
@@ -2409,13 +2414,17 @@ static int FOrthodoxeaster(func_info *info)
{ {
int y, m, d; int y, m, d;
int a, b, c, dd, e, f, dse; int a, b, c, dd, e, f, dse;
if (ARG(0).type == INT_TYPE) { if (Nargs == 0) {
y = ARGV(0); FromDSE(DSEToday, &y, &m, &d);
if (y < BASE) return E_2LOW; } else {
else if (y > BASE+YR_RANGE) return E_2HIGH; if (ARG(0).type == INT_TYPE) {
} else if (HASDATE(ARG(0))) { y = ARGV(0);
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */ if (y < BASE) return E_2LOW;
} else return E_BAD_TYPE; else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE;
}
do { do {
a = y % 4; a = y % 4;
@@ -2492,7 +2501,7 @@ static int FTimezone(func_info *info)
if (Nargs == 0) { if (Nargs == 0) {
dse = DSEToday; dse = DSEToday;
now = (SystemTime(0) / 60); now = MinutesPastMidnight(0);
} else { } else {
if (!HASDATE(ARG(0))) return E_BAD_TYPE; if (!HASDATE(ARG(0))) return E_BAD_TYPE;
dse = DATEPART(ARG(0)); dse = DATEPART(ARG(0));

View File

@@ -8,7 +8,7 @@
/* globals.h and err.h */ /* globals.h and err.h */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -39,7 +39,7 @@ EXTERN FILE *ErrFp;
#define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 )) #define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 ))
#define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y)) #define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y))
#define DestroyValue(x) (void) (((x).type == STR_TYPE && (x).v.str) ? (free((x).v.str),(x).v.str = NULL,(x).type = ERR_TYPE) : 0) #define DestroyValue(x) do { if ((x).type == STR_TYPE && (x).v.str) { free((x).v.str); (x).v.str = NULL; } (x).type = ERR_TYPE; } while (0)
EXTERN int DSEToday; EXTERN int DSEToday;
EXTERN int RealToday; EXTERN int RealToday;
@@ -50,6 +50,7 @@ EXTERN int LineNo;
EXTERN int FreshLine; EXTERN int FreshLine;
EXTERN uid_t TrustedUsers[MAX_TRUSTED_USERS]; EXTERN uid_t TrustedUsers[MAX_TRUSTED_USERS];
EXTERN INIT( int MaxLateMinutes, 0);
EXTERN INIT( int NumTrustedUsers, 0); EXTERN INIT( int NumTrustedUsers, 0);
EXTERN INIT( char const *MsgCommand, NULL); EXTERN INIT( char const *MsgCommand, NULL);
EXTERN INIT( char const *QueuedMsgCommand, NULL); EXTERN INIT( char const *QueuedMsgCommand, NULL);
@@ -78,7 +79,7 @@ EXTERN INIT( int SortByDate, 0);
EXTERN INIT( int SortByPrio, 0); EXTERN INIT( int SortByPrio, 0);
EXTERN INIT( int UntimedBeforeTimed, 0); EXTERN INIT( int UntimedBeforeTimed, 0);
EXTERN INIT( int DefaultPrio, NO_PRIORITY); EXTERN INIT( int DefaultPrio, NO_PRIORITY);
EXTERN INIT( long SysTime, -1L); EXTERN INIT( int SysTime, -1);
EXTERN INIT( int ParseUntriggered, 1); EXTERN INIT( int ParseUntriggered, 1);
EXTERN char const *InitialFile; EXTERN char const *InitialFile;
@@ -91,6 +92,7 @@ EXTERN INIT( int DontQueue, 0);
EXTERN INIT( int NumQueued, 0); EXTERN INIT( int NumQueued, 0);
EXTERN INIT( int DontIssueAts, 0); EXTERN INIT( int DontIssueAts, 0);
EXTERN INIT( int Daemon, 0); EXTERN INIT( int Daemon, 0);
EXTERN INIT( int DaemonJSON, 0);
EXTERN INIT( char DateSep, DATESEP); EXTERN INIT( char DateSep, DATESEP);
EXTERN INIT( char TimeSep, TIMESEP); EXTERN INIT( char TimeSep, TIMESEP);
EXTERN INIT( char DateTimeSep, DATETIMESEP); EXTERN INIT( char DateTimeSep, DATETIMESEP);
@@ -101,7 +103,7 @@ EXTERN INIT( int SynthesizeTags, 0);
EXTERN INIT( int ScFormat, SC_AMPM); EXTERN INIT( int ScFormat, SC_AMPM);
EXTERN INIT( int MaxSatIter, 1000); EXTERN INIT( int MaxSatIter, 1000);
EXTERN INIT( int MaxStringLen, MAX_STR_LEN); EXTERN INIT( int MaxStringLen, MAX_STR_LEN);
EXTERN INIT( char *FileName, NULL); EXTERN INIT( char *FileName, NULL);
EXTERN INIT( int UseStdin, 0); EXTERN INIT( int UseStdin, 0);
EXTERN INIT( int PurgeMode, 0); EXTERN INIT( int PurgeMode, 0);
EXTERN INIT( int PurgeIncludeDepth, 0); EXTERN INIT( int PurgeIncludeDepth, 0);
@@ -156,6 +158,9 @@ EXTERN INIT( char *EndSentIg, "\"')]}>");
EXTERN DynamicBuffer Banner; EXTERN DynamicBuffer Banner;
EXTERN DynamicBuffer LineBuffer; EXTERN DynamicBuffer LineBuffer;
EXTERN DynamicBuffer ExprBuf; EXTERN DynamicBuffer ExprBuf;
extern int NumFullOmits, NumPartialOmits;
/* List of months */ /* List of months */
EXTERN char *EnglishMonthName[] EXTERN char *EnglishMonthName[]
#ifdef MK_GLOBALS #ifdef MK_GLOBALS

View File

@@ -5,7 +5,7 @@
/* Support for the Hebrew calendar */ /* Support for the Hebrew calendar */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/* Derived from code written by Amos Shapir in 1978; revised */ /* Derived from code written by Amos Shapir in 1978; revised */

View File

@@ -7,7 +7,7 @@
/* in normal mode. */ /* in normal mode. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -78,6 +78,7 @@ static void ProcessLongOption(char const *arg);
* t = Display trigger dates * t = Display trigger dates
* v = Dump variables at end * v = Dump variables at end
* l = Display entire line in error messages * l = Display entire line in error messages
* s = Display expression-parsing stack usage before exit
* -e = Send messages normally sent to stderr to stdout instead * -e = Send messages normally sent to stderr to stdout instead
* -z[n] = Daemon mode waking up every n (def 1) minutes. * -z[n] = Daemon mode waking up every n (def 1) minutes.
* -bn = Time format for cal (0, 1, or 2) * -bn = Time format for cal (0, 1, or 2)
@@ -176,7 +177,6 @@ void InitRemind(int argc, char const *argv[])
int x; int x;
int dse; int dse;
int ttyfd; int ttyfd;
int r, g, b;
dse = NO_DATE; dse = NO_DATE;
@@ -229,7 +229,7 @@ void InitRemind(int argc, char const *argv[])
} }
} else { } else {
fprintf(stderr, "Invoked with a NULL argv[0]; bailing because that's just plain bizarre.\n"); fprintf(stderr, "Invoked with a NULL argv[0]; bailing because that's just plain bizarre.\n");
exit(1); exit(EXIT_FAILURE);
} }
/* Parse the command-line options */ /* Parse the command-line options */
@@ -426,7 +426,11 @@ void InitRemind(int argc, char const *argv[])
case 'z': case 'z':
case 'Z': case 'Z':
DontFork = 1; DontFork = 1;
if (*arg == '0') { if (*arg == 'j' || *arg == 'J') {
while (*arg) arg++;
Daemon = -1;
DaemonJSON = 1;
} else if (*arg == '0') {
PARSENUM(Daemon, arg); PARSENUM(Daemon, arg);
if (Daemon == 0) Daemon = -1; if (Daemon == 0) Daemon = -1;
else if (Daemon < 1) Daemon = 1; else if (Daemon < 1) Daemon = 1;
@@ -601,6 +605,7 @@ void InitRemind(int argc, char const *argv[])
case 'D': case 'D':
while (*arg) { while (*arg) {
switch(*arg++) { switch(*arg++) {
case 's': case 'S': DebugFlag |= DB_EXPR_STACKS; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break; case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break; case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break; case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
@@ -649,17 +654,6 @@ void InitRemind(int argc, char const *argv[])
} }
} }
if (should_guess_terminal_background) {
guess_terminal_background(&r, &g, &b);
if (r >= 0 && g >= 0 && b >= 0) {
if (r+g+b <= 85*3 && r <= 128 && g <= 128 && b <= 128) {
TerminalBackground = TERMINAL_BACKGROUND_DARK;
} else {
TerminalBackground = TERMINAL_BACKGROUND_LIGHT;
}
}
}
/* Get the filename. */ /* Get the filename. */
if (!InvokedAsRem) { if (!InvokedAsRem) {
if (i >= argc) { if (i >= argc) {
@@ -765,7 +759,7 @@ void InitRemind(int argc, char const *argv[])
/* Figure out the offset from UTC */ /* Figure out the offset from UTC */
if (CalculateUTC) if (CalculateUTC)
(void) CalcMinsFromUTC(DSEToday, SystemTime(0)/60, (void) CalcMinsFromUTC(DSEToday, MinutesPastMidnight(0),
&MinsFromUTC, NULL); &MinsFromUTC, NULL);
} }
@@ -779,7 +773,7 @@ void InitRemind(int argc, char const *argv[])
#ifndef L_USAGE_OVERRIDE #ifndef L_USAGE_OVERRIDE
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif #endif
@@ -1002,7 +996,7 @@ ProcessLongOption(char const *arg)
{ {
if (!strcmp(arg, "version")) { if (!strcmp(arg, "version")) {
printf("%s\n", VERSION); printf("%s\n", VERSION);
exit(0); exit(EXIT_SUCCESS);
} }
fprintf(ErrFp, "%s: Unknown long option --%s\n", ArgV[0], arg); fprintf(ErrFp, "%s: Unknown long option --%s\n", ArgV[0], arg);
} }
@@ -1012,7 +1006,7 @@ guess_terminal_background(int *r, int *g, int *b)
{ {
int ttyfd; int ttyfd;
struct pollfd p; struct pollfd p;
int rr, gg, bb; unsigned int rr, gg, bb;
char buf[128]; char buf[128];
int n; int n;
@@ -1042,7 +1036,12 @@ guess_terminal_background(int *r, int *g, int *b)
return; return;
} }
tty_raw(ttyfd); tty_raw(ttyfd);
write(ttyfd, "\033]11;?\033\\", 8); n = write(ttyfd, "\033]11;?\033\\", 8);
if (n != 8) {
/* write failed... WTF? Not much we can do */
return;
}
/* Wait up to 0.1s for terminal to respond */ /* Wait up to 0.1s for terminal to respond */
p.fd = ttyfd; p.fd = ttyfd;
@@ -1073,9 +1072,9 @@ guess_terminal_background(int *r, int *g, int *b)
/* Couldn't scan color codes */ /* Couldn't scan color codes */
return; return;
} }
*r = (rr >> 8) & 255; *r = (int) ((rr >> 8) & 255);
*g = (gg >> 8) & 255; *g = (int) ((gg >> 8) & 255);
*b = (bb >> 8) & 255; *b = (int) ((bb >> 8) & 255);
} }
static struct termios orig_termios; static struct termios orig_termios;
@@ -1110,3 +1109,21 @@ tty_reset(int fd)
{ {
tcsetattr(fd, TCSAFLUSH, &orig_termios); tcsetattr(fd, TCSAFLUSH, &orig_termios);
} }
int
GetTerminalBackground(void)
{
int r, g, b;
if (should_guess_terminal_background) {
guess_terminal_background(&r, &g, &b);
if (r >= 0 && g >= 0 && b >= 0) {
if (r+g+b <= 85*3 && r <= 128 && g <= 128 && b <= 128) {
TerminalBackground = TERMINAL_BACKGROUND_DARK;
} else {
TerminalBackground = TERMINAL_BACKGROUND_LIGHT;
}
}
should_guess_terminal_background = 0;
}
return TerminalBackground;
}

View File

@@ -5,7 +5,7 @@
/* Header file for language support for various languages. */ /* Header file for language support for various languages. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* */ /* */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* This file is Copyright (C) 1993 by Mogens Lynnerup. */ /* This file is Copyright (C) 1993 by Mogens Lynnerup. */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */

View File

@@ -11,7 +11,7 @@
/* Further corrections by Erik-Jan Vens */ /* Further corrections by Erik-Jan Vens */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Support for the English language. */ /* Support for the English language. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -11,7 +11,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* This file is Copyright (C) 1993-1998 by Mikko Silvonen. */ /* This file is Copyright (C) 1993-1998 by Mikko Silvonen. */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -245,7 +245,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT".
"Odotettu viikonpäivän nimi"
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -254,7 +255,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETAVERSIO <<<<\n"); fprintf(ErrFp, ">>>> BETAVERSIO <<<<\n");
#endif #endif

View File

@@ -8,7 +8,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* */ /* */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* This file is Copyright (C) 1993 by Laurent Duperval and */ /* This file is Copyright (C) 1993 by Laurent Duperval and */
/* Dianne Skoll. */ /* Dianne Skoll. */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
@@ -219,7 +219,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Nom du jour de la semaine attendu",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -228,7 +229,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif #endif

View File

@@ -9,7 +9,7 @@
/* I don't speak German. */ /* I don't speak German. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Support for the Icelandic language. */ /* Support for the Icelandic language. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* Translated by Björn Davíðsson (bjossi@snerpa.is) */ /* Translated by Björn Davíðsson (bjossi@snerpa.is) */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */

View File

@@ -7,7 +7,7 @@
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* It is Copyright (C) 1996 by Valerio Aimale */ /* It is Copyright (C) 1996 by Valerio Aimale */
/* */ /* */
/* Remind is copyright (C) 1992-2023 by Dianne Skoll */ /* Remind is copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -6,7 +6,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* This file is Copyright (C) 1993 by Trygve Randen. */ /* This file is Copyright (C) 1993 by Trygve Randen. */
/* Remind is Copyright (C) 1992-2023 by Dianne Skoll */ /* Remind is Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -9,7 +9,7 @@
/* Polish. */ /* Polish. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -235,7 +235,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Oczekiwana nazwa dnia tygodnia"
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -244,7 +245,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif #endif

View File

@@ -8,7 +8,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* */ /* */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* This file is Copyright (C) 1996 by Marco Paganini and */ /* This file is Copyright (C) 1996 by Marco Paganini and */
/* Dianne Skoll. */ /* Dianne Skoll. */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
@@ -244,7 +244,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -253,7 +254,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (versao %s) (C) 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (versao %s) (C) 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> VERSAO BETA <<<<\n"); fprintf(ErrFp, ">>>> VERSAO BETA <<<<\n");
#endif #endif

View File

@@ -8,7 +8,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* */ /* */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* This file is Copyright (C) 1996-1998 by Liviu Daia */ /* This file is Copyright (C) 1996-1998 by Liviu Daia */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */

View File

@@ -7,7 +7,7 @@
/* Author: Rafa Couto <rafacouto@biogate.com> */ /* Author: Rafa Couto <rafacouto@biogate.com> */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -6,7 +6,7 @@
/* routines, etc. */ /* routines, etc. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -14,6 +14,8 @@
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#include "config.h" #include "config.h"
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@@ -45,6 +47,9 @@
static void DoReminders(void); static void DoReminders(void);
/* Macro for simplifying common block so as not to litter code */
#define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0)
/***************************************************************/ /***************************************************************/
/***************************************************************/ /***************************************************************/
/** **/ /** **/
@@ -71,6 +76,8 @@ int main(int argc, char *argv[])
DBufInit(&(LastTrigger.tags)); DBufInit(&(LastTrigger.tags));
ClearLastTriggers(); ClearLastTriggers();
atexit(DebugExitFunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) { if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar(); ProduceCalendar();
return 0; return 0;
@@ -181,14 +188,14 @@ static void DoReminders(void)
if (FileAccessDate < 0) { if (FileAccessDate < 0) {
fprintf(ErrFp, "%s: `%s': %s.\n", ErrMsg[E_CANTACCESS], InitialFile, strerror(errno)); fprintf(ErrFp, "%s: `%s': %s.\n", ErrMsg[E_CANTACCESS], InitialFile, strerror(errno));
exit(1); exit(EXIT_FAILURE);
} }
r=IncludeFile(InitialFile); r=IncludeFile(InitialFile);
if (r) { if (r) {
fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING],
InitialFile, ErrMsg[r]); InitialFile, ErrMsg[r]);
exit(1); exit(EXIT_FAILURE);
} }
while(1) { while(1) {
@@ -196,7 +203,7 @@ static void DoReminders(void)
if (r == E_EOF) return; if (r == E_EOF) return;
if (r) { if (r) {
Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]); Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]);
exit(1); exit(EXIT_FAILURE);
} }
s = FindInitialToken(&tok, CurLine); s = FindInitialToken(&tok, CurLine);
@@ -585,8 +592,8 @@ int EvaluateExpr(ParsePtr p, Value *v)
int r; int r;
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */ if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */
while (isempty(*p->pos)) (p->pos)++;
if (!p->pos) return E_PARSE_ERR; /* Missing expression */ if (!p->pos) return E_PARSE_ERR; /* Missing expression */
while (isempty(*p->pos)) (p->pos)++;
if (*p->pos == BEG_OF_EXPR) { if (*p->pos == BEG_OF_EXPR) {
(p->pos)++; (p->pos)++;
bracketed = 1; bracketed = 1;
@@ -747,19 +754,32 @@ int PushToken(char const *tok, ParsePtr p)
/* Return the system time in seconds past midnight */ /* Return the system time in seconds past midnight */
/* */ /* */
/***************************************************************/ /***************************************************************/
long SystemTime(int realtime) int SystemTime(int realtime)
{ {
time_t tloc; time_t now;
struct tm *t; struct tm *t;
if (!realtime && (SysTime != -1L)) return SysTime; if (!realtime && (SysTime != -1)) return SysTime;
(void) time(&tloc); now = time(NULL);
t = localtime(&tloc); t = localtime(&now);
return (long) t->tm_hour * 3600L + (long) t->tm_min * 60L + return t->tm_hour * 3600L + t->tm_min * 60L +
(long) t->tm_sec; t->tm_sec;
} }
/***************************************************************/
/* */
/* MinutesPastMidnight */
/* */
/* Return the system time in minutes past midnight */
/* */
/***************************************************************/
int MinutesPastMidnight(int realtime)
{
return (SystemTime(realtime) / 60);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* SystemDate */ /* SystemDate */
@@ -771,11 +791,11 @@ long SystemTime(int realtime)
/***************************************************************/ /***************************************************************/
int SystemDate(int *y, int *m, int *d) int SystemDate(int *y, int *m, int *d)
{ {
time_t tloc; time_t now;
struct tm *t; struct tm *t;
(void) time(&tloc); now = time(NULL);
t = localtime(&tloc); t = localtime(&now);
*d = t->tm_mday; *d = t->tm_mday;
*m = t->tm_mon; *m = t->tm_mon;
@@ -998,6 +1018,12 @@ int DoDebug(ParsePtr p)
else DebugFlag &= ~DB_ECHO_LINE; else DebugFlag &= ~DB_ECHO_LINE;
break; break;
case 's':
case 'S':
if (val) DebugFlag |= DB_EXPR_STACKS;
else DebugFlag &= ~DB_EXPR_STACKS;
break;
case 'x': case 'x':
case 'X': case 'X':
if (val) DebugFlag |= DB_PRTEXPR; if (val) DebugFlag |= DB_PRTEXPR;
@@ -1238,19 +1264,19 @@ int CalcMinsFromUTC(int dse, int tim, int *mins, int *isdst)
return 0; return 0;
} }
static char const *OutputEscapeSequences(char const *s, int print) static char const *OutputEscapeSequences(char const *s, int print, DynamicBuffer *output)
{ {
while (*s == 0x1B && *(s+1) == '[') { while (*s == 0x1B && *(s+1) == '[') {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
while (*s && (*s < 0x40 || *s > 0x7E)) { while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
} }
if (*s) { if (*s) {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
} }
} }
@@ -1259,19 +1285,19 @@ static char const *OutputEscapeSequences(char const *s, int print)
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
#define ISWBLANK(c) (iswspace(c) && (c) != '\n') #define ISWBLANK(c) (iswspace(c) && (c) != '\n')
static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print) static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print, DynamicBuffer *output)
{ {
while (*s == 0x1B && *(s+1) == '[') { while (*s == 0x1B && *(s+1) == '[') {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
while (*s && (*s < 0x40 || *s > 0x7E)) { while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
} }
if (*s) { if (*s) {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
} }
} }
@@ -1280,7 +1306,7 @@ static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print)
static void static void
FillParagraphWCAux(wchar_t const *s) FillParagraphWCAux(wchar_t const *s, DynamicBuffer *output)
{ {
int line = 0; int line = 0;
int i, j; int i, j;
@@ -1295,7 +1321,7 @@ FillParagraphWCAux(wchar_t const *s)
/* If it's a carriage return, output it and start new paragraph */ /* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') { if (*s == '\n') {
putchar('\n'); OUTPUT('\n');
s++; s++;
line = 0; line = 0;
while(ISWBLANK(*s)) s++; while(ISWBLANK(*s)) s++;
@@ -1308,7 +1334,7 @@ FillParagraphWCAux(wchar_t const *s)
number of spaces */ number of spaces */
j = line ? SubsIndent : FirstIndent; j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) { for (i=0; i<j; i++) {
putchar(' '); OUTPUT(' ');
} }
/* Calculate the amount of room left on this line */ /* Calculate the amount of room left on this line */
@@ -1321,7 +1347,7 @@ FillParagraphWCAux(wchar_t const *s)
if (*s == '\n') break; if (*s == '\n') break;
while(1) { while(1) {
t = s; t = s;
s = OutputEscapeSequencesWS(s, 1); s = OutputEscapeSequencesWS(s, 1, output);
if (s == t) break; if (s == t) break;
while(ISWBLANK(*s)) s++; while(ISWBLANK(*s)) s++;
} }
@@ -1329,7 +1355,7 @@ FillParagraphWCAux(wchar_t const *s)
len = 0; len = 0;
while(*s && !iswspace(*s)) { while(*s && !iswspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') { if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequencesWS(s, 0); s = OutputEscapeSequencesWS(s, 0, output);
continue; continue;
} }
len += wcwidth(*s); len += wcwidth(*s);
@@ -1340,17 +1366,17 @@ FillParagraphWCAux(wchar_t const *s)
} }
if (!pendspace || len+pendspace <= roomleft) { if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) { for (i=0; i<pendspace; i++) {
putchar(' '); OUTPUT(' ');
} }
while(t < s) { while(t < s) {
PutWideChar(*t); PutWideChar(*t, output);
if (strchr(EndSent, *t)) doublespace = 2; if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1; else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++; t++;
} }
} else { } else {
s = t; s = t;
putchar('\n'); OUTPUT('\n');
line++; line++;
break; break;
} }
@@ -1361,7 +1387,7 @@ FillParagraphWCAux(wchar_t const *s)
} }
static int static int
FillParagraphWC(char const *s) FillParagraphWC(char const *s, DynamicBuffer *output)
{ {
size_t len; size_t len;
wchar_t *buf; wchar_t *buf;
@@ -1371,7 +1397,7 @@ FillParagraphWC(char const *s)
buf = calloc(len+1, sizeof(wchar_t)); buf = calloc(len+1, sizeof(wchar_t));
if (!buf) return E_NO_MEM; if (!buf) return E_NO_MEM;
(void) mbstowcs(buf, s, len+1); (void) mbstowcs(buf, s, len+1);
FillParagraphWCAux(buf); FillParagraphWCAux(buf, output);
free(buf); free(buf);
return OK; return OK;
} }
@@ -1391,7 +1417,7 @@ FillParagraphWC(char const *s)
/* A macro safe ONLY if used with arg with no side effects! */ /* A macro safe ONLY if used with arg with no side effects! */
#define ISBLANK(c) (isspace(c) && (c) != '\n') #define ISBLANK(c) (isspace(c) && (c) != '\n')
void FillParagraph(char const *s) void FillParagraph(char const *s, DynamicBuffer *output)
{ {
int line = 0; int line = 0;
@@ -1409,7 +1435,7 @@ void FillParagraph(char const *s)
if (!*s) return; if (!*s) return;
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
if (FillParagraphWC(s) == OK) { if (FillParagraphWC(s, output) == OK) {
return; return;
} }
#endif #endif
@@ -1419,7 +1445,7 @@ void FillParagraph(char const *s)
/* If it's a carriage return, output it and start new paragraph */ /* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') { if (*s == '\n') {
putchar('\n'); OUTPUT('\n');
s++; s++;
line = 0; line = 0;
while(ISBLANK(*s)) s++; while(ISBLANK(*s)) s++;
@@ -1432,7 +1458,7 @@ void FillParagraph(char const *s)
number of spaces */ number of spaces */
j = line ? SubsIndent : FirstIndent; j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) { for (i=0; i<j; i++) {
putchar(' '); OUTPUT(' ');
} }
/* Calculate the amount of room left on this line */ /* Calculate the amount of room left on this line */
@@ -1445,7 +1471,7 @@ void FillParagraph(char const *s)
if (*s == '\n') break; if (*s == '\n') break;
while(1) { while(1) {
t = s; t = s;
s = OutputEscapeSequences(s, 1); s = OutputEscapeSequences(s, 1, output);
if (s == t) break; if (s == t) break;
while(ISBLANK(*s)) s++; while(ISBLANK(*s)) s++;
} }
@@ -1453,7 +1479,7 @@ void FillParagraph(char const *s)
len = 0; len = 0;
while(*s && !isspace(*s)) { while(*s && !isspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') { if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequences(s, 0); s = OutputEscapeSequences(s, 0, output);
continue; continue;
} }
s++; s++;
@@ -1464,17 +1490,17 @@ void FillParagraph(char const *s)
} }
if (!pendspace || len+pendspace <= roomleft) { if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) { for (i=0; i<pendspace; i++) {
putchar(' '); OUTPUT(' ');
} }
while(t < s) { while(t < s) {
putchar(*t); OUTPUT(*t);
if (strchr(EndSent, *t)) doublespace = 2; if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1; else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++; t++;
} }
} else { } else {
s = t; s = t;
putchar('\n'); OUTPUT('\n');
line++; line++;
break; break;
} }
@@ -1638,12 +1664,51 @@ SaveLastTimeTrig(TimeTrig const *t)
memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig)); memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig));
} }
/* Wrapper to ignore warnings about ignoring return value of system() */ /* Wrapper to ignore warnings about ignoring return value of system()
Also redirects stdin and stdout to /dev/null for queued reminders */
void void
System(char const *cmd) System(char const *cmd, int is_queued)
{ {
int r; int r;
pid_t kid;
int fd;
int status;
int do_exit = 0;
if (is_queued && IsServerMode()) {
do_exit = 1;
/* Server mode... redirect stdin and stdout to /dev/null */
kid = fork();
if (kid == (pid_t) -1) {
/* Fork failed... nothing we can do */
return;
} else if (kid == 0) {
/* In the child */
(void) close(STDIN_FILENO);
(void) close(STDOUT_FILENO);
fd = open("/dev/null", O_RDONLY);
if (fd >= 0 && fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(STDIN_FILENO);
}
fd = open("/dev/null", O_WRONLY);
if (fd >= 0 && fd != STDOUT_FILENO) {
dup2(fd, STDOUT_FILENO);
close(STDOUT_FILENO);
}
} else {
/* In the parent */
while (waitpid(kid, &status, 0) != kid) /* continue */ ;
return;
}
}
/* This is the child process or original if we never forked */
r = system(cmd); r = system(cmd);
if (do_exit) {
/* In the child process, so exit! */
exit(0);
}
if (r == 0) { if (r == 0) {
return; return;
} }

View File

@@ -5,7 +5,7 @@
/* Calculations for figuring out moon phases. */ /* Calculations for figuring out moon phases. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -412,7 +412,7 @@ static double phase(double pdate,
Day = pdate - epoch; /* Date within epoch */ Day = pdate - epoch; /* Date within epoch */
N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */ N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */
M = fixangle(N + elonge - elongp); /* Convert from perigee M = fixangle(N + elonge - elongp); /* Convert from perigee
co-ordinates to epoch 1980.0 */ coordinates to epoch 1980.0 */
Ec = kepler(M, eccent); /* Solve equation of Kepler */ Ec = kepler(M, eccent); /* Solve equation of Kepler */
Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2); Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2);
Ec = 2 * todeg(atan(Ec)); /* 1 anomaly */ Ec = 2 * todeg(atan(Ec)); /* 1 anomaly */

View File

@@ -6,7 +6,7 @@
/* the data structures for OMITted dates. */ /* the data structures for OMITted dates. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -22,7 +22,7 @@
#include "err.h" #include "err.h"
#include "expr.h" #include "expr.h"
static int BexistsIntArray (int array[], int num, int key); static int BexistsIntArray (int const array[], int num, int key);
static void InsertIntoSortedArray (int *array, int num, int key); static void InsertIntoSortedArray (int *array, int num, int key);
/* Arrays for the global omits */ /* Arrays for the global omits */
@@ -32,7 +32,7 @@ static int PartialOmitArray[MAX_PARTIAL_OMITS];
/* WeekdayOmits is declared in global.h */ /* WeekdayOmits is declared in global.h */
/* How many of each omit types do we have? */ /* How many of each omit types do we have? */
static int NumFullOmits, NumPartialOmits; int NumFullOmits, NumPartialOmits;
/* The structure for saving and restoring OMIT contexts */ /* The structure for saving and restoring OMIT contexts */
typedef struct omitcontext { typedef struct omitcontext {
@@ -251,7 +251,7 @@ int IsOmitted(int dse, int localomit, char const *omitfunc, int *omit)
/* element is found, 0 otherwise. */ /* element is found, 0 otherwise. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int BexistsIntArray(int array[], int num, int key) static int BexistsIntArray(int const array[], int num, int key)
{ {
int top=num-1, bot=0, mid; int top=num-1, bot=0, mid;
@@ -445,6 +445,9 @@ int DoOmit(ParsePtr p)
if (!BexistsIntArray(PartialOmitArray, NumPartialOmits, syndrome)) { if (!BexistsIntArray(PartialOmitArray, NumPartialOmits, syndrome)) {
InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome); InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome);
NumPartialOmits++; NumPartialOmits++;
if (NumPartialOmits == 366) {
Wprint("You have OMITted everything! The space-time continuum is at risk.");
}
} }
if (mc == m[1] && dc == d[1]) { if (mc == m[1] && dc == d[1]) {
break; break;

View File

@@ -5,7 +5,7 @@
/* Function Prototypes. */ /* Function Prototypes. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -22,6 +22,9 @@
/* Characters to ignore */ /* Characters to ignore */
#define isempty(c) (isspace(c) || ((c) == '\\')) #define isempty(c) (isspace(c) || ((c) == '\\'))
#define IsServerMode() (Daemon < 0)
#define ShouldFork (!DontFork)
#include "dynbuf.h" #include "dynbuf.h"
#include <ctype.h> #include <ctype.h>
@@ -35,7 +38,7 @@ int DoRem (ParsePtr p);
int DoFlush (ParsePtr p); int DoFlush (ParsePtr p);
void DoExit (ParsePtr p); void DoExit (ParsePtr p);
int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals); int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals);
int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued); int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output);
int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err); int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err);
int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode); int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode);
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim); int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim);
@@ -71,7 +74,8 @@ void OutputLine (FILE *fp);
void CreateParser (char const *s, ParsePtr p); void CreateParser (char const *s, ParsePtr p);
void DestroyParser (ParsePtr p); void DestroyParser (ParsePtr p);
int PushToken (char const *tok, ParsePtr p); int PushToken (char const *tok, ParsePtr p);
long SystemTime (int realtime); int SystemTime (int realtime);
int MinutesPastMidnight (int realtime);
int SystemDate (int *y, int *m, int *d); int SystemDate (int *y, int *m, int *d);
int DoIf (ParsePtr p); int DoIf (ParsePtr p);
int DoElse (ParsePtr p); int DoElse (ParsePtr p);
@@ -104,7 +108,7 @@ char *StrDup (char const *s);
int StrCmpi (char const *s1, char const *s2); int StrCmpi (char const *s1, char const *s2);
Var *FindVar (char const *str, int create); Var *FindVar (char const *str, int create);
int DeleteVar (char const *str); int DeleteVar (char const *str);
int SetVar (char const *str, Value *val); int SetVar (char const *str, Value const *val);
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p); int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p);
int DoSet (Parser *p); int DoSet (Parser *p);
int DoUnset (Parser *p); int DoUnset (Parser *p);
@@ -114,7 +118,7 @@ void DestroyVars (int all);
int PreserveVar (char const *name); int PreserveVar (char const *name);
int DoPreserve (Parser *p); int DoPreserve (Parser *p);
int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p); int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p);
int DoMsgCommand (char const *cmd, char const *msg); int DoMsgCommand (char const *cmd, char const *msg, int is_queued);
int ParseNonSpaceChar (ParsePtr p, int *err, int peek); int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
unsigned int HashVal (char const *str); unsigned int HashVal (char const *str);
int DateOK (int y, int m, int d); int DateOK (int y, int m, int d);
@@ -138,7 +142,7 @@ int GetSysVar (char const *name, Value *val);
int SetSysVar (char const *name, Value *val); int SetSysVar (char const *name, Value *val);
void DumpSysVarByName (char const *name); void DumpSysVarByName (char const *name);
int CalcMinsFromUTC (int dse, int tim, int *mins, int *isdst); int CalcMinsFromUTC (int dse, int tim, int *mins, int *isdst);
void FillParagraph (char const *s); void FillParagraph (char const *s, DynamicBuffer *output);
void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime); void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime);
void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime); void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime);
int MoonPhase (int date, int time); int MoonPhase (int date, int time);
@@ -164,23 +168,30 @@ void PrintJSONKeyPairString(char const *name, char const *val);
void PrintJSONKeyPairDate(char const *name, int dse); void PrintJSONKeyPairDate(char const *name, int dse);
void PrintJSONKeyPairDateTime(char const *name, int dt); void PrintJSONKeyPairDateTime(char const *name, int dt);
void PrintJSONKeyPairTime(char const *name, int t); void PrintJSONKeyPairTime(char const *name, int t);
void System(char const *cmd); void System(char const *cmd, int queued);
int ShellEscape(char const *in, DynamicBuffer *out); int ShellEscape(char const *in, DynamicBuffer *out);
int AddGlobalOmit(int dse); int AddGlobalOmit(int dse);
void set_lat_and_long_from_components(void); void set_lat_and_long_from_components(void);
void set_components_from_lat_and_long(void); void set_components_from_lat_and_long(void);
void DebugExitFunc(void);
int GetTerminalBackground(void);
char const *get_day_name(int wkday); char const *get_day_name(int wkday);
char const *get_month_name(int mon); char const *get_month_name(int mon);
void set_cloexec(int fd);
int push_call(char const *filename, char const *func, int lineno); int push_call(char const *filename, char const *func, int lineno);
void clear_callstack(void); void clear_callstack(void);
int print_callstack(FILE *fp); int print_callstack(FILE *fp);
void pop_call(void); void pop_call(void);
void FixSpecialType(Trigger *trig); void FixSpecialType(Trigger *trig);
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);
void WriteJSONTimeTrigger(TimeTrig const *tt);
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#include <wctype.h> #include <wctype.h>
#include <wchar.h> #include <wchar.h>
void PutWideChar(wchar_t const wc); void PutWideChar(wchar_t const wc, DynamicBuffer *output);
#endif #endif

View File

@@ -5,7 +5,7 @@
/* Queue up reminders for subsequent execution. */ /* Queue up reminders for subsequent execution. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -24,16 +24,35 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h>
#include <sys/select.h> #include <sys/select.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "types.h" #include "types.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "protos.h" #include "protos.h"
#include "expr.h" #include "expr.h"
#undef USE_INOTIFY
#if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1)
#define USE_INOTIFY 1
#include <sys/inotify.h>
int watch_fd = -1;
static void consume_inotify_events(int fd);
static int setup_inotify_watch(void);
#endif
/* A list of filenames associated with queued reminders */
typedef struct queuedfname {
struct queuedfname *next;
char const *fname;
} QueuedFilename;
/* List structure for holding queued reminders */ /* List structure for holding queued reminders */
typedef struct queuedrem { typedef struct queuedrem {
struct queuedrem *next; struct queuedrem *next;
@@ -41,16 +60,18 @@ typedef struct queuedrem {
int RunDisabled; int RunDisabled;
int ntrig; int ntrig;
char const *text; char const *text;
char const *fname;
int lineno;
char passthru[PASSTHRU_LEN+1]; char passthru[PASSTHRU_LEN+1];
char sched[VAR_NAME_LEN+1]; char sched[VAR_NAME_LEN+1];
DynamicBuffer tags;
Trigger t; Trigger t;
TimeTrig tt; TimeTrig tt;
} QueuedRem; } QueuedRem;
/* Global variables */ /* Global variables */
static QueuedRem *QueueHead; static QueuedRem *QueueHead = NULL;
static QueuedFilename *Files = NULL;
static time_t FileModTime; static time_t FileModTime;
static struct stat StatBuf; static struct stat StatBuf;
@@ -61,6 +82,105 @@ static int CalculateNextTimeUsingSched (QueuedRem *q);
static void DaemonWait (struct timeval *sleep_tv); static void DaemonWait (struct timeval *sleep_tv);
static void reread (void); static void reread (void);
static void PrintQueue(void); static void PrintQueue(void);
static char const *QueueFilename(char const *fname);
static void chomp(DynamicBuffer *buf)
{
char *s = DBufValue(buf);
int l = DBufLen(buf);
while (l) {
if (s[l-1] == '\n') {
s[l-1] = 0;
DBufLen(buf)--;
l--;
} else {
break;
}
}
}
char const *SimpleTimeNoSpace(int tim)
{
char *s = (char *) SimpleTime(tim);
if (s && *s) {
size_t l = strlen(s);
if (l > 0 && s[l-1] == ' ') {
s[l-1] = 0;
}
}
return s;
}
/***************************************************************/
/* */
/* QueueFilename */
/* */
/* Add fname to the list of queued filenames if it's not */
/* already present. Either way, return a pointer to the */
/* filename. Returns NULL if out of memory */
/* */
/***************************************************************/
static QueuedFilename *last_file_found = NULL;
static char const *QueueFilename(char const *fname)
{
QueuedFilename *elem = Files;
/* Optimization: We are very likely in the same file as
before... */
if (last_file_found && !strcmp(fname, last_file_found->fname)) {
return last_file_found->fname;
}
/* No such luck; search the list */
while(elem) {
if (!strcmp(elem->fname, fname)) {
last_file_found = elem;
return elem->fname;
}
elem = elem->next;
}
/* Not found... queue it */
elem = NEW(QueuedFilename);
if (!elem) return NULL;
elem->fname = StrDup(fname);
if (!elem->fname) {
free(elem);
return NULL;
}
elem->next = Files;
Files = elem;
last_file_found = elem;
return elem->fname;
}
static void del_reminder(QueuedRem *qid)
{
QueuedRem *q = QueueHead;
QueuedRem *next;
if (!q) {
return;
}
if (q == qid) {
QueueHead = q->next;
if (q->text) free((void *) q->text);
free(q);
return;
}
while(q->next) {
next = q->next;
if (q->next == qid) {
q->next = q->next->next;
if (next->text) free((void *) next->text);
free(next);
return;
}
q = q->next;
}
}
static void del_reminder_ul(unsigned long qid) {
del_reminder((QueuedRem *) qid);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -71,7 +191,7 @@ static void PrintQueue(void);
/* */ /* */
/***************************************************************/ /***************************************************************/
int QueueReminder(ParsePtr p, Trigger *trig, int QueueReminder(ParsePtr p, Trigger *trig,
TimeTrig *tim, char const *sched) TimeTrig *tim, char const *sched)
{ {
QueuedRem *qelem; QueuedRem *qelem;
@@ -79,7 +199,7 @@ int QueueReminder(ParsePtr p, Trigger *trig,
trig->noqueue || trig->noqueue ||
tim->ttime == NO_TIME || tim->ttime == NO_TIME ||
trig->typ == CAL_TYPE || trig->typ == CAL_TYPE ||
tim->ttime < SystemTime(0) / 60 || tim->ttime < MinutesPastMidnight(0) ||
((trig->typ == RUN_TYPE) && RunDisabled)) return OK; ((trig->typ == RUN_TYPE) && RunDisabled)) return OK;
qelem = NEW(QueuedRem); qelem = NEW(QueuedRem);
@@ -91,25 +211,84 @@ int QueueReminder(ParsePtr p, Trigger *trig,
free(qelem); free(qelem);
return E_NO_MEM; return E_NO_MEM;
} }
qelem->fname = QueueFilename(FileName);
if (!qelem->fname) {
free((void *) qelem->text);
free(qelem);
return E_NO_MEM;
}
qelem->lineno = LineNo;
NumQueued++; NumQueued++;
qelem->typ = trig->typ; qelem->typ = trig->typ;
strcpy(qelem->passthru, trig->passthru); strcpy(qelem->passthru, trig->passthru);
qelem->tt = *tim; qelem->tt = *tim;
qelem->t = *trig; qelem->t = *trig;
DBufInit(&(qelem->t.tags)); DBufInit(&(qelem->t.tags));
DBufPuts(&(qelem->t.tags), DBufValue(&(trig->tags)));
if (SynthesizeTags) {
AppendTag(&(qelem->t.tags), SynthesizeTag());
}
qelem->next = QueueHead; qelem->next = QueueHead;
qelem->RunDisabled = RunDisabled; qelem->RunDisabled = RunDisabled;
qelem->ntrig = 0; qelem->ntrig = 0;
strcpy(qelem->sched, sched); strcpy(qelem->sched, sched);
DBufInit(&(qelem->tags));
DBufPuts(&(qelem->tags), DBufValue(&(trig->tags)));
if (SynthesizeTags) {
AppendTag(&(qelem->tags), SynthesizeTag());
}
QueueHead = qelem; QueueHead = qelem;
return OK; return OK;
} }
static void
maybe_close(int fd)
{
int new_fd;
/* Don't close descriptors connected to a TTY, except for stdin */
if (fd && isatty(fd)) return;
(void) close(fd);
if (fd != STDIN_FILENO) {
new_fd = open("/dev/null", O_WRONLY);
} else {
new_fd = open("/dev/null", O_RDONLY);
}
/* If the open failed... well... not much we can do */
if (new_fd < 0) return;
/* If we got back the same fd as what we just closed, aces! */
if (fd == new_fd) return;
(void) dup2(new_fd, fd);
(void) close(new_fd);
}
void
SigContHandler(int d)
{
UNUSED(d);
}
static void
print_num_queued(void)
{
int nqueued = 0;
QueuedRem *q = QueueHead;
while(q) {
if (q->tt.nexttime != NO_TIME) {
nqueued++;
}
q = q->next;
}
if (DaemonJSON) {
printf("{");
PrintJSONKeyPairString("response", "queued");
PrintJSONKeyPairInt("nqueued", nqueued);
printf("\"command\":\"STATUS\"}\n");
} else {
printf("NOTE queued %d\n", nqueued);
}
fflush(stdout);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* HandleQueuedReminders */ /* HandleQueuedReminders */
@@ -123,17 +302,20 @@ void HandleQueuedReminders(void)
int TimeToSleep; int TimeToSleep;
unsigned SleepTime; unsigned SleepTime;
Parser p; Parser p;
Trigger trig;
struct timeval tv; struct timeval tv;
struct timeval sleep_tv; struct timeval sleep_tv;
struct sigaction sa; struct sigaction sa;
char qid[64];
/* Suppress the BANNER from being issued */
DidMsgReminder = 1;
/* Turn off sorting -- otherwise, TriggerReminder has no effect! */ /* Turn off sorting -- otherwise, TriggerReminder has no effect! */
SortByDate = 0; SortByDate = 0;
/* Free FileName if necessary */
if (FileName) {
free(FileName);
FileName = NULL;
}
/* If we are not connected to a tty, then we must close the /* If we are not connected to a tty, then we must close the
* standard file descriptors. This is to prevent someone * standard file descriptors. This is to prevent someone
* doing: * doing:
@@ -143,9 +325,10 @@ void HandleQueuedReminders(void)
* processed correctly are RUN commands, provided they mail * processed correctly are RUN commands, provided they mail
* the result back or use their own resource (as a window). * the result back or use their own resource (as a window).
*/ */
if (!DontFork && (!isatty(1) || !isatty(2))) { if (ShouldFork) {
close(1); maybe_close(STDIN_FILENO);
close(2); maybe_close(STDOUT_FILENO);
maybe_close(STDERR_FILENO);
} }
/* If we're a daemon, get the mod time of initial file */ /* If we're a daemon, get the mod time of initial file */
@@ -160,17 +343,24 @@ void HandleQueuedReminders(void)
/* Initialize the queue - initialize all the entries time of issue */ /* Initialize the queue - initialize all the entries time of issue */
while (q) { while (q) {
q->tt.nexttime = (int) (SystemTime(1)/60 - 1); q->tt.nexttime = MinutesPastMidnight(1) - 1;
q->tt.nexttime = CalculateNextTime(q); q->tt.nexttime = CalculateNextTime(q);
q = q->next; q = q->next;
} }
if (!DontFork || Daemon) { if (ShouldFork || Daemon) {
sa.sa_handler = SigIntHandler; sa.sa_handler = SigIntHandler;
sa.sa_flags = 0; sa.sa_flags = 0;
(void) sigaction(SIGINT, &sa, NULL); (void) sigaction(SIGINT, &sa, NULL);
sa.sa_handler = SigContHandler;
(void) sigaction(SIGCONT, &sa, NULL);
} }
#ifdef USE_INOTIFY
if (IsServerMode()) {
watch_fd = setup_inotify_watch();
}
#endif
/* Sit in a loop, issuing reminders when necessary */ /* Sit in a loop, issuing reminders when necessary */
while(1) { while(1) {
q = FindNextReminder(); q = FindNextReminder();
@@ -179,7 +369,7 @@ void HandleQueuedReminders(void)
if (!q && !Daemon) break; if (!q && !Daemon) break;
if (Daemon && !q) { if (Daemon && !q) {
if (Daemon < 0) { if (IsServerMode()) {
/* Sleep until midnight */ /* Sleep until midnight */
TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1); TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1);
} else { } else {
@@ -198,7 +388,7 @@ void HandleQueuedReminders(void)
/* Wake up once a minute to recalibrate sleep time in /* Wake up once a minute to recalibrate sleep time in
case of laptop hibernation */ case of laptop hibernation */
if (Daemon < 0) { if (IsServerMode()) {
/* Wake up on the next exact minute */ /* Wake up on the next exact minute */
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
sleep_tv.tv_sec = 60 - (tv.tv_sec % 60); sleep_tv.tv_sec = 60 - (tv.tv_sec % 60);
@@ -209,6 +399,8 @@ void HandleQueuedReminders(void)
sleep_tv.tv_usec = 0; sleep_tv.tv_usec = 0;
} }
DaemonWait(&sleep_tv); DaemonWait(&sleep_tv);
/* A DEL command might have deleted our queued reminder! */
q = FindNextReminder();
} else { } else {
sleep(SleepTime); sleep(SleepTime);
} }
@@ -222,14 +414,14 @@ void HandleQueuedReminders(void)
if (!Daemon) { if (!Daemon) {
int y, m, d; int y, m, d;
if (RealToday != SystemDate(&y, &m, &d)) { if (RealToday != SystemDate(&y, &m, &d)) {
exit(0); exit(EXIT_SUCCESS);
} }
} }
if (Daemon > 0 && SleepTime) CheckInitialFile(); if (Daemon > 0 && SleepTime) CheckInitialFile();
if (Daemon && !q) { if (Daemon && !q) {
if (Daemon < 0) { if (IsServerMode()) {
/* Sleep until midnight */ /* Sleep until midnight */
TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1); TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1);
} else { } else {
@@ -244,31 +436,55 @@ void HandleQueuedReminders(void)
/* Do NOT trigger the reminder if tt.nexttime is more than a /* Do NOT trigger the reminder if tt.nexttime is more than a
minute in the past. This can happen if the clock is minute in the past. This can happen if the clock is
changed or a laptop awakes from hibernation. changed or a laptop awakes from hibernation.
However, DO trigger if tt.nexttime == tt.ttime so all However, DO trigger if tt.nexttime == tt.ttime and we're
within MaxLateTrigger minutes so all
queued reminders are triggered at least once. */ queued reminders are triggered at least once. */
if ((SystemTime(1) - (q->tt.nexttime * 60) <= 60) || if ((SystemTime(1) - (q->tt.nexttime * 60) <= 60) ||
(q->tt.nexttime == q->tt.ttime)) { (q->tt.nexttime == q->tt.ttime &&
(MaxLateMinutes == 0 || SystemTime(1) - (q->tt.nexttime * 60) <= 60 * MaxLateMinutes))) {
/* Trigger the reminder */ /* Trigger the reminder */
CreateParser(q->text, &p); CreateParser(q->text, &p);
trig.typ = q->typ;
strcpy(trig.passthru, q->passthru);
RunDisabled = q->RunDisabled; RunDisabled = q->RunDisabled;
if (Daemon < 0) { if (IsServerMode() && q->typ != RUN_TYPE) {
printf("NOTE reminder %s", if (DaemonJSON) {
SimpleTime(q->tt.ttime)); printf("{\"response\":\"reminder\",");
printf("%s", SimpleTime(SystemTime(1)/60)); snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
if (!*DBufValue(&q->tags)) { PrintJSONKeyPairString("qid", qid);
printf("*\n"); PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
} else { PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
printf("%s\n", DBufValue(&(q->tags))); PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
} } else {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(MinutesPastMidnight(1)));
if (!*DBufValue(&q->t.tags)) {
printf("*\n");
} else {
printf("%s\n", DBufValue(&(q->t.tags)));
}
}
} }
/* Set up global variables so some functions like trigdate() /* Set up global variables so some functions like trigdate()
and trigtime() work correctly */ and trigtime() work correctly */
SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1); SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1);
(void) TriggerReminder(&p, &trig, &q->tt, DSEToday, 1); FileName = (char *) q->fname;
if (Daemon < 0) { if (DaemonJSON) {
DynamicBuffer out;
DBufInit(&out);
(void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1, &out);
if (q->typ != RUN_TYPE) {
printf("\"body\":\"");
chomp(&out);
PrintJSONString(DBufValue(&out));
printf("\"}\n");
}
DBufFree(&out);
} else {
(void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1, NULL);
}
FileName = NULL;
if (IsServerMode() && !DaemonJSON && q->typ != RUN_TYPE) {
printf("NOTE endreminder\n"); printf("NOTE endreminder\n");
} }
fflush(stdout); fflush(stdout);
@@ -277,8 +493,26 @@ void HandleQueuedReminders(void)
/* Calculate the next trigger time */ /* Calculate the next trigger time */
q->tt.nexttime = CalculateNextTime(q); q->tt.nexttime = CalculateNextTime(q);
if (q->tt.nexttime != NO_TIME) {
/* If trigger time is way in the past because computer has been
suspended or hibernated, remove from queue */
if (q->tt.ttime < MinutesPastMidnight(1) - MaxLateMinutes &&
q->tt.nexttime < MinutesPastMidnight(1) - MaxLateMinutes) {
q->tt.nexttime = NO_TIME;
}
}
/* If queued reminder has expired, actually remove it from queue
and update status */
if (q->tt.nexttime == NO_TIME) {
del_reminder(q);
if (IsServerMode()) {
print_num_queued();
}
}
} }
exit(0); exit(EXIT_SUCCESS);
} }
@@ -474,7 +708,11 @@ static void
json_queue(QueuedRem const *q) json_queue(QueuedRem const *q)
{ {
int done = 0; int done = 0;
if (DaemonJSON) {
printf("{\"response\":\"queue\",\"queue\":");
}
printf("["); printf("[");
char idbuf[64];
while(q) { while(q) {
if (q->tt.nexttime == NO_TIME) { if (q->tt.nexttime == NO_TIME) {
q = q->next; q = q->next;
@@ -485,6 +723,14 @@ json_queue(QueuedRem const *q)
} }
done = 1; done = 1;
printf("{"); printf("{");
WriteJSONTrigger(&(q->t), 1, DSEToday);
WriteJSONTimeTrigger(&(q->tt));
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);
switch(q->typ) { switch(q->typ) {
case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break; case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break;
case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break; case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break;
@@ -497,26 +743,6 @@ json_queue(QueuedRem const *q)
case PASSTHRU_TYPE: PrintJSONKeyPairString("type", "PASSTHRU_TYPE"); break; case PASSTHRU_TYPE: PrintJSONKeyPairString("type", "PASSTHRU_TYPE"); break;
default: PrintJSONKeyPairString("type", "?"); break; default: PrintJSONKeyPairString("type", "?"); break;
} }
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairTime("ttime", q->tt.ttime);
PrintJSONKeyPairTime("nextttime", q->tt.nexttime);
PrintJSONKeyPairInt("delta", q->tt.delta);
if (q->tt.rep != NO_TIME) {
PrintJSONKeyPairInt("rep", q->tt.rep);
}
if (q->tt.duration != NO_TIME) {
PrintJSONKeyPairInt("duration", q->tt.duration);
}
if (q->passthru[0]) {
PrintJSONKeyPairString("passthru", q->passthru);
}
if (q->sched[0]) {
PrintJSONKeyPairString("sched", q->sched);
}
if (DBufLen(&(q->tags))) {
PrintJSONKeyPairString("tags", DBufValue(&(q->tags)));
}
/* Last one is a special case - no trailing comma */ /* Last one is a special case - no trailing comma */
printf("\""); printf("\"");
@@ -530,7 +756,12 @@ json_queue(QueuedRem const *q)
printf("\"}"); printf("\"}");
q = q->next; q = q->next;
} }
printf("]\n"); printf("]");
if (DaemonJSON) {
printf(",\"command\":\"QUEUE\"}\n");
} else {
printf("\n");
}
} }
/***************************************************************/ /***************************************************************/
@@ -545,15 +776,28 @@ static void DaemonWait(struct timeval *sleep_tv)
fd_set readSet; fd_set readSet;
int retval; int retval;
int y, m, d; int y, m, d;
int max = 1;
char cmdLine[256]; char cmdLine[256];
FD_ZERO(&readSet); FD_ZERO(&readSet);
FD_SET(0, &readSet); FD_SET(0, &readSet);
retval = select(1, &readSet, NULL, NULL, sleep_tv);
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
FD_SET(watch_fd, &readSet);
if (watch_fd > max-1)
max = watch_fd+1;
}
#endif
retval = select(max, &readSet, NULL, NULL, sleep_tv);
/* If date has rolled around, restart */ /* If date has rolled around, restart */
if (RealToday != SystemDate(&y, &m, &d)) { if (RealToday != SystemDate(&y, &m, &d)) {
printf("NOTE newdate\nNOTE reread\n"); if (DaemonJSON) {
printf("{\"response\":\"newdate\"}\n{\"response\":\"reread\",\"command\":\"newdate\"}\n");
} else {
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout); fflush(stdout);
reread(); reread();
} }
@@ -561,70 +805,108 @@ static void DaemonWait(struct timeval *sleep_tv)
/* If nothing readable or interrupted system call, return */ /* If nothing readable or interrupted system call, return */
if (retval <= 0) return; if (retval <= 0) return;
/* If inotify watch descriptor is readable, handle it */
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
if (FD_ISSET(watch_fd, &readSet)) {
consume_inotify_events(watch_fd);
if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"inotify\"}\n");
} else {
/* In deprecated server mode, we need to spit out
a NOTE newdate to force the front-end to redraw
the calendar */
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout);
reread();
}
}
#endif
/* If stdin not readable, return */ /* If stdin not readable, return */
if (!FD_ISSET(0, &readSet)) return; if (!FD_ISSET(0, &readSet)) return;
/* If EOF on stdin, exit */ /* If EOF on stdin, exit */
if (feof(stdin)) { if (feof(stdin)) {
exit(0); exit(EXIT_SUCCESS);
} }
/* Read a line from stdin and interpret it */ /* Read a line from stdin and interpret it */
if (!fgets(cmdLine, sizeof(cmdLine), stdin)) { if (!fgets(cmdLine, sizeof(cmdLine), stdin)) {
exit(0); exit(EXIT_SUCCESS);
} }
if (!strcmp(cmdLine, "EXIT\n")) { if (!strcmp(cmdLine, "EXIT\n")) {
exit(0); exit(EXIT_SUCCESS);
} else if (!strcmp(cmdLine, "STATUS\n")) { } else if (!strcmp(cmdLine, "STATUS\n")) {
int nqueued = 0; print_num_queued();
QueuedRem *q = QueueHead;
while(q) {
if (q->tt.nexttime != NO_TIME) {
nqueued++;
}
q = q->next;
}
printf("NOTE queued %d\n", nqueued);
fflush(stdout);
} else if (!strcmp(cmdLine, "QUEUE\n")) { } else if (!strcmp(cmdLine, "QUEUE\n")) {
printf("NOTE queue\n"); if (DaemonJSON) {
QueuedRem *q = QueueHead; json_queue(QueueHead);
while (q) { } else {
if (q->tt.nexttime != NO_TIME) { printf("NOTE queue\n");
switch (q->typ) { QueuedRem *q = QueueHead;
case NO_TYPE: printf("NO_TYPE "); break; while (q) {
case MSG_TYPE: printf("MSG_TYPE "); break; if (q->tt.nexttime != NO_TIME) {
case RUN_TYPE: printf("RUN_TYPE "); break; switch (q->typ) {
case CAL_TYPE: printf("CAL_TYPE "); break; case NO_TYPE: printf("NO_TYPE"); break;
case SAT_TYPE: printf("SAT_TYPE "); break; case MSG_TYPE: printf("MSG_TYPE"); break;
case PS_TYPE: printf("PS_TYPE "); break; case RUN_TYPE: printf("RUN_TYPE"); break;
case PSF_TYPE: printf("PSF_TYPE "); break; case CAL_TYPE: printf("CAL_TYPE"); break;
case MSF_TYPE: printf("MSF_TYPE "); break; case SAT_TYPE: printf("SAT_TYPE"); break;
case PASSTHRU_TYPE: printf("PASSTHRU_TYPE "); break; case PS_TYPE: printf("PS_TYPE"); break;
default: printf("? "); break; case PSF_TYPE: printf("PSF_TYPE"); break;
} case MSF_TYPE: printf("MSF_TYPE"); break;
printf("RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1)); case PASSTHRU_TYPE: printf("PASSTHRU_TYPE"); break;
printf("%s %s %s\n", default: printf("?"); break;
(q->passthru[0] ? q->passthru : "*"), }
(q->sched[0] ? q->sched : "*"), printf(" RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1));
q->text ? q->text : "NULL"); printf("%s %s %s\n",
(q->passthru[0] ? q->passthru : "*"),
(q->sched[0] ? q->sched : "*"),
q->text ? q->text : "NULL");
}
q = q->next;
} }
q = q->next; printf("NOTE endqueue\n");
} }
printf("NOTE endqueue\n");
fflush(stdout); fflush(stdout);
} else if (!strcmp(cmdLine, "JSONQUEUE\n")) { } else if (!strcmp(cmdLine, "JSONQUEUE\n")) {
printf("NOTE JSONQUEUE\n"); if (!DaemonJSON) {
printf("NOTE JSONQUEUE\n");
}
json_queue(QueueHead); json_queue(QueueHead);
printf("NOTE ENDJSONQUEUE\n"); if (!DaemonJSON) {
printf("NOTE ENDJSONQUEUE\n");
}
fflush(stdout); fflush(stdout);
} else if (!strcmp(cmdLine, "REREAD\n")) { } else if (!strcmp(cmdLine, "REREAD\n")) {
printf("NOTE reread\n"); if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"REREAD\"}\n");
} else {
printf("NOTE reread\n");
}
fflush(stdout); fflush(stdout);
reread(); reread();
} else if (!strncmp(cmdLine, "DEL ", 4)) {
unsigned long qid;
if (sscanf(cmdLine, "DEL %lx", &qid) == 1) {
del_reminder_ul(qid);
}
print_num_queued();
fflush(stdout);
} else { } else {
printf("ERR Invalid daemon command: %s", cmdLine); if (DaemonJSON) {
size_t l = strlen(cmdLine);
if (l && cmdLine[l-1] == '\n') {
cmdLine[l-1] = 0;
}
printf("{\"response\":\"error\",\"error\":\"Unknown command\",\"command\":\"");
PrintJSONString(cmdLine);
printf("\"}\n");
} else {
printf("ERR Invalid daemon command: %s", cmdLine);
}
fflush(stdout); fflush(stdout);
} }
} }
@@ -641,3 +923,56 @@ static void reread(void)
execvp(ArgV[0], (char **) ArgV); execvp(ArgV[0], (char **) ArgV);
} }
#ifdef USE_INOTIFY
static void consume_inotify_events(int fd)
{
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
int n;
struct timespec sleeptime;
int slept = 0;
/* Consume all the inotify events */
while(1) {
n = read(fd, buf, sizeof(buf));
if (n > 0) {
/* Something new since we slept */
slept = 0;
}
if (n < 0) {
if (errno == EINTR) continue;
if (slept) {
/* Nothing new since we slept */
return;
}
slept = 1;
/* HACK: sleep for 0.2 seconds to let multiple events queue up so we
only do a single reread */
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 200000000;
nanosleep(&sleeptime, NULL);
}
}
}
static int setup_inotify_watch(void)
{
int fd;
/* Don't inotify_watch stdin */
if (!strcmp(InitialFile, "-")) {
return -1;
}
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd < 0) {
return fd;
}
if (inotify_add_watch(fd, InitialFile, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) {
close(fd);
return -1;
}
return fd;
}
#endif

View File

@@ -5,7 +5,7 @@
/* Print a PostScript calendar. */ /* Print a PostScript calendar. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -186,19 +186,19 @@ JSONToCalEntry(DynamicBuffer *buf)
val = json_parse(DBufValue(buf), DBufLen(buf)); val = json_parse(DBufValue(buf), DBufLen(buf));
if (!val) { if (!val) {
fprintf(stderr, "Unable to parse JSON line `%s'\n", DBufValue(buf)); fprintf(stderr, "Unable to parse JSON line `%s'\n", DBufValue(buf));
exit(1); exit(EXIT_FAILURE);
} }
if (val->type != json_object) { if (val->type != json_object) {
fprintf(stderr, "Expecting JSON object; found `%s'\n", fprintf(stderr, "Expecting JSON object; found `%s'\n",
DBufValue(buf)); DBufValue(buf));
exit(1); exit(EXIT_FAILURE);
} }
c = NEW(CalEntry); c = NEW(CalEntry);
if (!c) { if (!c) {
fprintf(stderr, "malloc failed - aborting.\n"); fprintf(stderr, "malloc failed - aborting.\n");
exit(1); exit(EXIT_FAILURE);
} }
c->next = NULL; c->next = NULL;
c->special = SPECIAL_NORMAL; c->special = SPECIAL_NORMAL;
@@ -221,7 +221,7 @@ JSONToCalEntry(DynamicBuffer *buf)
c->entry = malloc(strlen(s)+1); c->entry = malloc(strlen(s)+1);
if (!c->entry) { if (!c->entry) {
fprintf(stderr, "malloc failed - aborting.\n"); fprintf(stderr, "malloc failed - aborting.\n");
exit(1); exit(EXIT_FAILURE);
} }
strcpy(c->entry, s); strcpy(c->entry, s);
got_body = 1; got_body = 1;
@@ -253,7 +253,7 @@ JSONToCalEntry(DynamicBuffer *buf)
if (!got_body || !got_date) { if (!got_body || !got_date) {
fprintf(stderr, "Could not parse line `%s'\n", DBufValue(buf)); fprintf(stderr, "Could not parse line `%s'\n", DBufValue(buf));
exit(1); exit(EXIT_FAILURE);
} }
return c; return c;
} }
@@ -272,7 +272,7 @@ TextToCalEntry(DynamicBuffer *buf)
CalEntry *c = NEW(CalEntry); CalEntry *c = NEW(CalEntry);
if (!c) { if (!c) {
fprintf(stderr, "malloc failed - aborting.\n"); fprintf(stderr, "malloc failed - aborting.\n");
exit(1); exit(EXIT_FAILURE);
} }
c->next = NULL; c->next = NULL;
c->special = SPECIAL_NORMAL; c->special = SPECIAL_NORMAL;
@@ -296,7 +296,7 @@ TextToCalEntry(DynamicBuffer *buf)
c->entry = malloc(strlen(startOfBody) + 1); c->entry = malloc(strlen(startOfBody) + 1);
if (!c->entry) { if (!c->entry) {
fprintf(stderr, "malloc failed - aborting.\n"); fprintf(stderr, "malloc failed - aborting.\n");
exit(1); exit(EXIT_FAILURE);
} }
strcpy(c->entry, startOfBody); strcpy(c->entry, startOfBody);
@@ -343,14 +343,14 @@ int main(int argc, char *argv[])
DBufGets(&buf, stdin); DBufGets(&buf, stdin);
if (first_line && (!strcmp(DBufValue(&buf), "["))) { 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.\n");
exit(1); exit(EXIT_FAILURE);
} }
first_line = 0; first_line = 0;
if (!strcmp(DBufValue(&buf), PSBEGIN) || if (!strcmp(DBufValue(&buf), PSBEGIN) ||
!strcmp(DBufValue(&buf), PSBEGIN2)) { !strcmp(DBufValue(&buf), PSBEGIN2)) {
if (!validfile) { if (!validfile) {
if (Verbose) { if (Verbose) {
fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2023 by Dianne Skoll\n\n", VERSION); fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2024 by Dianne Skoll\n\n", VERSION);
fprintf(stderr, "Generating PostScript calendar\n"); fprintf(stderr, "Generating PostScript calendar\n");
} }
} }
@@ -361,7 +361,7 @@ int main(int argc, char *argv[])
if (!validfile) { if (!validfile) {
fprintf(stderr, "Rem2PS: Couldn't find any calendar data - are you\n"); fprintf(stderr, "Rem2PS: Couldn't find any calendar data - are you\n");
fprintf(stderr, " sure you fed me input produced by remind -p ...?\n"); fprintf(stderr, " sure you fed me input produced by remind -p ...?\n");
exit(1); exit(EXIT_FAILURE);
} }
printf("%%%%Trailer\n"); printf("%%%%Trailer\n");
printf("%%%%Pages: %d\n", validfile); printf("%%%%Pages: %d\n", validfile);
@@ -486,7 +486,7 @@ void DoPsCal(void)
while(1) { while(1) {
if (feof(stdin)) { if (feof(stdin)) {
fprintf(stderr, "Input from REMIND is corrupt!\n"); fprintf(stderr, "Input from REMIND is corrupt!\n");
exit(1); exit(EXIT_FAILURE);
} }
DBufGets(&buf, stdin); DBufGets(&buf, stdin);
@@ -952,7 +952,7 @@ void Init(int argc, char *argv[])
fprintf(stderr, " WxHin Specify size in inches (W and H are decimal numbers)\n"); fprintf(stderr, " WxHin Specify size in inches (W and H are decimal numbers)\n");
fprintf(stderr, " WxHcm Specify size in centimetres (W and H are decimal numbers)\n"); fprintf(stderr, " WxHcm Specify size in centimetres (W and H are decimal numbers)\n");
fprintf(stderr, "Default media type is %s\n", DefaultPage[0].name); fprintf(stderr, "Default media type is %s\n", DefaultPage[0].name);
exit(1); exit(EXIT_FAILURE);
} }
break; break;
@@ -1033,7 +1033,7 @@ void Usage(char const *s)
fprintf(stderr, "-e Make calendar fill entire page\n"); fprintf(stderr, "-e Make calendar fill entire page\n");
fprintf(stderr, "-x Put day numbers on left instead of right\n"); fprintf(stderr, "-x Put day numbers on left instead of right\n");
fprintf(stderr, "-o[lrtb] marg Specify left, right, top and bottom margins\n"); fprintf(stderr, "-o[lrtb] marg Specify left, right, top and bottom margins\n");
exit(1); exit(EXIT_FAILURE);
} }
/***************************************************************/ /***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Define the PostScript prologue */ /* Define the PostScript prologue */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -14,7 +14,7 @@ char *PSProlog1[] =
{ {
"% This file was produced by Remind and Rem2PS, written by", "% This file was produced by Remind and Rem2PS, written by",
"% Dianne Skoll.", "% Dianne Skoll.",
"% Remind and Rem2PS are Copyright 1992-2023 Dianne Skoll.", "% Remind and Rem2PS are Copyright 1992-2024 Dianne Skoll.",
"/ISOLatin1Encoding where { pop save true }{ false } ifelse", "/ISOLatin1Encoding where { pop save true }{ false } ifelse",
" /ISOLatin1Encoding [ StandardEncoding 0 45 getinterval aload pop /minus", " /ISOLatin1Encoding [ StandardEncoding 0 45 getinterval aload pop /minus",
" StandardEncoding 46 98 getinterval aload pop /dotlessi /grave /acute", " StandardEncoding 46 98 getinterval aload pop /dotlessi /grave /acute",

View File

@@ -5,7 +5,7 @@
/* Routines for sorting reminders by trigger date */ /* Routines for sorting reminders by trigger date */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -136,7 +136,7 @@ void IssueSortedReminders(void)
switch(cur->typ) { switch(cur->typ) {
case MSG_TYPE: case MSG_TYPE:
if (MsgCommand && *MsgCommand) { if (MsgCommand && *MsgCommand) {
DoMsgCommand(MsgCommand, cur->text); DoMsgCommand(MsgCommand, cur->text, 0);
} else { } else {
if (cur->trigdate != olddate) { if (cur->trigdate != olddate) {
IssueSortBanner(cur->trigdate); IssueSortBanner(cur->trigdate);
@@ -151,11 +151,11 @@ void IssueSortedReminders(void)
IssueSortBanner(cur->trigdate); IssueSortBanner(cur->trigdate);
olddate = cur->trigdate; olddate = cur->trigdate;
} }
FillParagraph(cur->text); FillParagraph(cur->text, NULL);
break; break;
case RUN_TYPE: case RUN_TYPE:
System(cur->text); System(cur->text, 0);
break; break;
} }

View File

@@ -6,7 +6,7 @@
/* classifying the tokens parsed. */ /* classifying the tokens parsed. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Routines for figuring out the trigger date of a reminder */ /* Routines for figuring out the trigger date of a reminder */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -72,7 +72,10 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
m++; m++;
if (m == 12) { m = 0; y++; } if (m == 12) { m = 0; y++; }
} }
while (trig->d > DaysInMonth(m, trig->y)) m++; while (trig->d > DaysInMonth(m, y)) {
m++;
if (m == 12) { m = 0; y++; }
}
j = DSE(y, m, trig->d); j = DSE(y, m, trig->d);
return j; return j;
@@ -241,6 +244,7 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
case GOT_WD+GOT_MON+GOT_YR: case GOT_WD+GOT_MON+GOT_YR:
if (y > trig->y || (y == trig->y && m > trig->m)) return -1; if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
/* cppcheck-suppress knownConditionTrueFalse */
if (trig->y > y || (trig->y == y && trig->m > m)) { if (trig->y > y || (trig->y == y && trig->m > m)) {
j = DSE(trig->y, trig->m, 1); j = DSE(trig->y, trig->m, 1);
ADVANCE_TO_WD(j, trig->wd); ADVANCE_TO_WD(j, trig->wd);
@@ -324,8 +328,11 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
break; break;
} }
start--; start--;
if (start < 0) {
break;
}
} }
if (iter > MaxSatIter) { if (start < 0 || iter > MaxSatIter) {
/* omitfunc must have returned "true" too often */ /* omitfunc must have returned "true" too often */
*err = E_CANT_TRIG; *err = E_CANT_TRIG;
return -2; return -2;
@@ -388,6 +395,10 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
break; break;
} }
simple--; simple--;
if (simple < 0) {
*err = E_CANT_TRIG;
return -2;
}
} }
if (iter > MaxSatIter) { if (iter > MaxSatIter) {
*err = E_CANT_TRIG; *err = E_CANT_TRIG;

View File

@@ -5,7 +5,7 @@
/* Type definitions all dumped here. */ /* Type definitions all dumped here. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -150,6 +150,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_DUMP_VARS 8 #define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16 #define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32 #define DB_TRACE_FILES 32
#define DB_EXPR_STACKS 64
/* Enumeration of the tokens */ /* Enumeration of the tokens */
enum TokTypes enum TokTypes

View File

@@ -6,7 +6,7 @@
/* functions. */ /* functions. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -349,7 +349,9 @@ int CallUserFunc(char const *name, int nargs, ParsePtr p)
/* Skip the opening bracket, if there's one */ /* Skip the opening bracket, if there's one */
while (isempty(*s)) s++; while (isempty(*s)) s++;
if (*s == BEG_OF_EXPR) s++; if (*s == BEG_OF_EXPR) {
s++;
}
push_call(f->filename, f->name, f->lineno); push_call(f->filename, f->name, f->lineno);
h = Evaluate(&s, f->locals, p); h = Evaluate(&s, f->locals, p);
if (h == OK) { if (h == OK) {

View File

@@ -5,7 +5,7 @@
/* Useful utility functions. */ /* Useful utility functions. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -36,6 +36,11 @@ char *StrnCpy(char *dest, char const *source, int n)
{ {
char *odest = dest; char *odest = dest;
if (n <= 0) {
*dest = 0;
return dest;
}
while (n-- && (*dest++ = *source++)) ; while (n-- && (*dest++ = *source++)) ;
if (*(dest-1)) *dest = 0; if (*(dest-1)) *dest = 0;
return odest; return odest;
@@ -108,7 +113,7 @@ int DateOK(int y, int m, int d)
m > 11 || m > 11 ||
y > BASE + YR_RANGE || y > BASE + YR_RANGE ||
d > DaysInMonth(m, y) ) return 0; d > DaysInMonth(m, y) ) return 0;
else return 1; return 1;
} }
/* Functions designed to defeat gcc optimizer */ /* Functions designed to defeat gcc optimizer */

View File

@@ -6,7 +6,7 @@
/* user- and system-defined variables. */ /* user- and system-defined variables. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -167,6 +167,13 @@ static int latitude_func(int do_set, Value *val)
return latitude_longitude_func(do_set, val, &Latitude, -90.0, 90.0); return latitude_longitude_func(do_set, val, &Latitude, -90.0, 90.0);
} }
static int terminal_bg_func(int do_set, Value *val)
{
UNUSED(do_set);
val->type = INT_TYPE;
val->v.val = GetTerminalBackground();
return OK;
}
static int trig_date_func(int do_set, Value *val) static int trig_date_func(int do_set, Value *val)
{ {
@@ -473,7 +480,7 @@ int DeleteVar(char const *str)
/* Set the indicate variable to the specified value. */ /* Set the indicate variable to the specified value. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int SetVar(char const *str, Value *val) int SetVar(char const *str, Value const *val)
{ {
Var *v = FindVar(str, 1); Var *v = FindVar(str, 1);
@@ -525,7 +532,9 @@ int DoSet (Parser *p)
int r; int r;
DynamicBuffer buf; DynamicBuffer buf;
DynamicBuffer buf2;
DBufInit(&buf); DBufInit(&buf);
DBufInit(&buf2);
r = ParseIdentifier(p, &buf); r = ParseIdentifier(p, &buf);
if (r) return r; if (r) return r;
@@ -541,6 +550,13 @@ int DoSet (Parser *p)
return r; return r;
} }
r = ParseToken(p, &buf2);
if (r) return r;
if (DBufLen(&buf2)) {
DBufFree(&buf2);
return E_EXPECTING_EOL;
}
DBufFree(&buf2);
if (*DBufValue(&buf) == '$') r = SetSysVar(DBufValue(&buf)+1, &v); if (*DBufValue(&buf) == '$') r = SetSysVar(DBufValue(&buf)+1, &v);
else r = SetVar(DBufValue(&buf), &v); else r = SetVar(DBufValue(&buf), &v);
if (buf.len > VAR_NAME_LEN) { if (buf.len > VAR_NAME_LEN) {
@@ -762,10 +778,14 @@ typedef struct {
char modifiable; char modifiable;
int type; int type;
void *value; void *value;
int min; int min; /* Or const-value */
int max; int max;
} SysVar; } SysVar;
/* Macro to access "min" but as a constval. Just to make source more
readable */
#define constval min
/* If the type of a sys variable is STR_TYPE, then min is redefined /* If the type of a sys variable is STR_TYPE, then min is redefined
to be a flag indicating whether or not the value has been malloc'd. */ to be a flag indicating whether or not the value has been malloc'd. */
#define been_malloced min #define been_malloced min
@@ -825,6 +845,9 @@ static SysVar SysVarArr[] = {
{"LongMin", 1, SPECIAL_TYPE, longmin_func, 0, 0 }, {"LongMin", 1, SPECIAL_TYPE, longmin_func, 0, 0 },
{"LongSec", 1, SPECIAL_TYPE, longsec_func, 0, 0 }, {"LongSec", 1, SPECIAL_TYPE, longsec_func, 0, 0 },
{"March", 1, STR_TYPE, &DynamicMonthName[2], 0, 0 }, {"March", 1, STR_TYPE, &DynamicMonthName[2], 0, 0 },
{"MaxFullOmits", 0, CONST_INT_TYPE, NULL, MAX_FULL_OMITS, 0},
{"MaxLateMinutes", 1, INT_TYPE, &MaxLateMinutes, 0, 1440 },
{"MaxPartialOmits",0, CONST_INT_TYPE, NULL, MAX_PARTIAL_OMITS, 0},
{"MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY }, {"MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY },
{"MaxStringLen", 1, INT_TYPE, &MaxStringLen, -1, ANY }, {"MaxStringLen", 1, INT_TYPE, &MaxStringLen, -1, ANY },
{"May", 1, STR_TYPE, &DynamicMonthName[4], 0, 0 }, {"May", 1, STR_TYPE, &DynamicMonthName[4], 0, 0 },
@@ -835,6 +858,8 @@ static SysVar SysVarArr[] = {
{"NextMode", 0, INT_TYPE, &NextMode, 0, 0 }, {"NextMode", 0, INT_TYPE, &NextMode, 0, 0 },
{"November", 1, STR_TYPE, &DynamicMonthName[10],0, 0 }, {"November", 1, STR_TYPE, &DynamicMonthName[10],0, 0 },
{"Now", 1, STR_TYPE, &DynamicNow, 0, 0 }, {"Now", 1, STR_TYPE, &DynamicNow, 0, 0 },
{"NumFullOmits", 0, INT_TYPE, &NumFullOmits, 0, 0 },
{"NumPartialOmits",0, INT_TYPE, &NumPartialOmits, 0, 0 },
{"NumQueued", 0, INT_TYPE, &NumQueued, 0, 0 }, {"NumQueued", 0, INT_TYPE, &NumQueued, 0, 0 },
{"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 }, {"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 },
{"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0 }, {"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0 },
@@ -856,7 +881,7 @@ static SysVar SysVarArr[] = {
{"SysInclude", 0, STR_TYPE, &SysDir, 0, 0 }, {"SysInclude", 0, STR_TYPE, &SysDir, 0, 0 },
{"T", 0, SPECIAL_TYPE, trig_date_func, 0, 0 }, {"T", 0, SPECIAL_TYPE, trig_date_func, 0, 0 },
{"Td", 0, SPECIAL_TYPE, trig_day_func, 0, 0 }, {"Td", 0, SPECIAL_TYPE, trig_day_func, 0, 0 },
{"TerminalBackground", 0, INT_TYPE, &TerminalBackground, 0, 0 }, {"TerminalBackground", 0, SPECIAL_TYPE, terminal_bg_func, 0, 0 },
{"Thursday", 1, STR_TYPE, &DynamicDayName[3], 0, 0 }, {"Thursday", 1, STR_TYPE, &DynamicDayName[3], 0, 0 },
{"TimeSep", 1, SPECIAL_TYPE, time_sep_func, 0, 0 }, {"TimeSep", 1, SPECIAL_TYPE, time_sep_func, 0, 0 },
{"Tm", 0, SPECIAL_TYPE, trig_mon_func, 0, 0 }, {"Tm", 0, SPECIAL_TYPE, trig_mon_func, 0, 0 },
@@ -894,13 +919,13 @@ int SetSysVar(char const *name, Value *value)
int r; int r;
SysVar *v = FindSysVar(name); SysVar *v = FindSysVar(name);
if (!v) return E_NOSUCH_VAR; if (!v) return E_NOSUCH_VAR;
if (v->type != SPECIAL_TYPE &&
v->type != value->type) return E_BAD_TYPE;
if (!v->modifiable) { if (!v->modifiable) {
Eprint("%s: `$%s'", ErrMsg[E_CANT_MODIFY], name); Eprint("%s: `$%s'", ErrMsg[E_CANT_MODIFY], name);
return E_CANT_MODIFY; return E_CANT_MODIFY;
} }
if (v->type != SPECIAL_TYPE &&
v->type != value->type) return E_BAD_TYPE;
if (v->type == SPECIAL_TYPE) { if (v->type == SPECIAL_TYPE) {
SysVarFunc f = (SysVarFunc) v->value; SysVarFunc f = (SysVarFunc) v->value;
r = f(1, value); r = f(1, value);
@@ -941,6 +966,11 @@ int GetSysVar(char const *name, Value *val)
val->type = ERR_TYPE; val->type = ERR_TYPE;
if (!v) return E_NOSUCH_VAR; if (!v) return E_NOSUCH_VAR;
if (v->type == CONST_INT_TYPE) {
val->v.val = v->constval;
val->type = INT_TYPE;
return OK;
}
if (v->type == SPECIAL_TYPE) { if (v->type == SPECIAL_TYPE) {
SysVarFunc f = (SysVarFunc) v->value; SysVarFunc f = (SysVarFunc) v->value;
return f(0, val); return f(0, val);
@@ -1034,7 +1064,9 @@ static void DumpSysVar(char const *name, const SysVar *v)
if (name) strcat(buffer, name); else strcat(buffer, v->name); if (name) strcat(buffer, name); else strcat(buffer, v->name);
fprintf(ErrFp, "%16s ", buffer); fprintf(ErrFp, "%16s ", buffer);
if (v) { if (v) {
if (v->type == SPECIAL_TYPE) { if (v->type == CONST_INT_TYPE) {
fprintf(ErrFp, "%d\n", v->constval);
} else if (v->type == SPECIAL_TYPE) {
SysVarFunc f = (SysVarFunc) v->value; SysVarFunc f = (SysVarFunc) v->value;
f(0, &vtmp); f(0, &vtmp);
PrintValue(&vtmp, ErrFp); PrintValue(&vtmp, ErrFp);

View File

@@ -4,7 +4,7 @@ MSG UseVTColors is: [$UseVTColors]%
MSG Use256Colors is: [$Use256Colors]% MSG Use256Colors is: [$Use256Colors]%
MSG UseTrueColors is: [$UseTrueColors]% MSG UseTrueColors is: [$UseTrueColors]%
MSG UseBGVTColors is: [$UseBGVTColors]% MSG UseBGVTColors is: [$UseBGVTColors]%
set n ansicolor("")] set n ansicolor("")
MSG This is [ansicolor(0,255,0)]green[n], [ansicolor("255 0 0")]red[n] and [ansicolor("0 0 255")]blue[n] text.% MSG This is [ansicolor(0,255,0)]green[n], [ansicolor("255 0 0")]red[n] and [ansicolor("0 0 255")]blue[n] text.%
MSG This is [ansicolor(0,0,0)][ansicolor(0,255,0,1)]black text on a green background[n]% MSG This is [ansicolor(0,0,0)][ansicolor(0,255,0,1)]black text on a green background[n]%
MSG This is [ansicolor(0,0,0,0,1)]clamped black text[n]% MSG This is [ansicolor(0,0,0,0,1)]clamped black text[n]%

6
tests/queue1.rem Normal file
View File

@@ -0,0 +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 MSQ quux
DO queue2.rem

1
tests/queue2.rem Normal file
View File

@@ -0,0 +1 @@
REM at 23:59 PRIORITY 2 MSG XXXX

View File

@@ -7,7 +7,7 @@
# in the build directory. # in the build directory.
# #
# This file is part of REMIND. # This file is part of REMIND.
# Copyright (C) 1992-2023 Dianne Skoll # Copyright (C) 1992-2024 Dianne Skoll
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -31,6 +31,15 @@ fi
TZ=UTC TZ=UTC
export TZ 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 # If we're already in a utf-8 locale, do
# nothing; otherwise, set LC_ALL # nothing; otherwise, set LC_ALL
OK=0 OK=0
@@ -56,6 +65,8 @@ echo "Test 1" > ../tests/test.out
echo "" >> ../tests/test.out echo "" >> ../tests/test.out
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 >> ../tests/test.out 2>&1 ../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out echo "" >> ../tests/test.out
echo 'set a 1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+2*3))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))' | ../src/remind -ds - 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo "Test 2" >> ../tests/test.out echo "Test 2" >> ../tests/test.out
echo "" >> ../tests/test.out echo "" >> ../tests/test.out
../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1 ../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
@@ -440,6 +451,96 @@ rm -rf include_dir/ww
# Test --version long option # Test --version long option
../src/remind --version >> ../tests/test.out 2>&1 ../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
# Test for leap year bug that was fixed
../src/remind -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 1 Mar 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 28 Feb 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
../src/remind -dte - 1 Mar 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1
# Remove references to SysInclude, which is build-specific # Remove references to SysInclude, which is build-specific
grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out
cmp -s ../tests/test.out ../tests/test.cmp cmp -s ../tests/test.out ../tests/test.cmp

File diff suppressed because one or more lines are too long

View File

@@ -381,9 +381,11 @@ msg [a076]%
set a077 dosubst("%*Y %*Z", '1992/5/5') set a077 dosubst("%*Y %*Z", '1992/5/5')
msg [a077]% msg [a077]%
set a078 easterdate(today()) set a078 easterdate(today())
set a078 easterdate()
set a079 easterdate(1992) set a079 easterdate(1992)
set a080 easterdate(1995) set a080 easterdate(1995)
set a078 orthodoxeaster(today()) set a078 orthodoxeaster(today())
set a078 orthodoxeaster()
set a079 orthodoxeaster(1992) set a079 orthodoxeaster(1992)
set a080 orthodoxeaster(1995) set a080 orthodoxeaster(1995)
set a080 orthodoxeaster(2023) set a080 orthodoxeaster(2023)
@@ -882,11 +884,38 @@ set a htmlstriptags("this is > whut <b>foo</b>")
set a htmlstriptags("<img src=\"foo\">") set a htmlstriptags("<img src=\"foo\">")
# $ParseUntriggered # $ParseUntriggered
REM 2 Jan 1990 MSG ["bad_expr" * 2] REM 2 Jan 1990 MSG ["bad_expr" / 2]
SET $ParseUntriggered 0 SET $ParseUntriggered 0
REM 2 Jan 1990 MSG ["bad_expr" * 2] REM 2 Jan 1990 MSG ["bad_expr" / 2]
SET $ParseUntriggered 1 SET $ParseUntriggered 1
# String multiplication
set a "low" * (-1)
set a (-1) * "low"
set a "zero" * 0
set a 0 * "zero"
set a "" * 10000000
set a 10000000 * ""
# Too long for default limits
set a "wookie" * 1000000
set a 1000000 * "wookie"
set a "Cabbage! " * 7
set a 7 * "Cabbage! "
# Should result in errors
set pqxya 1+2)
# Should result in an error
REM Tue OMIT 2024-01-01 MSG Wookie
# No error
REM Tue OMIT Wed 2024-01-01 MSG Blort
# Don't want Remind to queue reminders # Don't want Remind to queue reminders
EXIT EXIT

View File

@@ -11,7 +11,7 @@
# Use the output to verify your translations. # Use the output to verify your translations.
# #
# This file is part of REMIND. # This file is part of REMIND.
# Copyright (C) 1992-2023 Dianne Skoll # Copyright (C) 1992-2024 Dianne Skoll
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# #
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@@ -1,341 +0,0 @@
<?php
class Remind
{
# For validating commands we send to popen
function is_valid_day($d) {
return (preg_match('/^\d+$/', $d)) &&
$d >= 1 && $d <= 31;
}
function is_valid_month($m) {
return
($m == 'January') ||
($m == 'February') ||
($m == 'March') ||
($m == 'April') ||
($m == 'May') ||
($m == 'June') ||
($m == 'July') ||
($m == 'August') ||
($m == 'September') ||
($m == 'October') ||
($m == 'November') ||
($m == 'December');
}
function is_valid_year($y) {
return preg_match('/^\d\d\d\d$/', $y) &&
$y >= 1900;
}
function get_el(&$array, $i)
{
if (!array_key_exists($i, $array)) return null;
return $array[$i];
}
function get_elem($array, $indexes)
{
foreach ($indexes as $i) {
if (!is_array($array)) return null;
if (!array_key_exists($i, $array)) return null;
$array = $array[$i];
}
return $array;
}
function munge_entry($day, &$results, &$specials, &$options, $str, &$e) {
return htmlspecialchars($str);
}
function format_entry($day, &$results, &$specials, &$options, &$e) {
$special = $this->get_el($e, 'special');
$body = $this->get_el($e, 'body');
if ($body === null) $body = '';
if ($special === null || $special == '*') {
return $this->munge_entry($day, $results, $specials, $options, $body, $e);
}
if ($special == 'COLOR' || $special == 'COLOUR') {
if (preg_match('/^(\d+)\s+(\d+)\s+(\d+)\s+(.*)/', $body, $matches)) {
return sprintf('<span style="color: #%02x%02x%02x">%s</span>',
$matches[1] % 255,
$matches[2] % 255,
$matches[3] % 255,
$this->munge_entry($day, $results, $specials, $options, $matches[4], $e));
}
return 'Bad COLOR spec: ' . htmlspecialchars($body);
}
# HTML is passed through un-munged.
if ($special == 'HTML') return $body;
# Ignore unknown specials
return '';
}
function format_entries($day, &$results, &$specials, &$options, &$entries) {
$html = '';
foreach ($entries as $e) {
$html .= '<div class="rem-entry">' . $this->format_entry($day, $results, $specials, $options, $e) . '</div>';
}
return $html;
}
function do_one_day($day, &$results, &$specials, &$options) {
$class = $this->get_elem($specials, array('HTMLCLASS', $day, 0, 'body'));
$shade = $this->get_elem($specials, array('SHADE', $day, 0, 'body'));
$moon = $this->get_elem($specials, array('MOON', $day, 0, 'body'));
if ($class === null) $class = 'rem-cell';
$bg = '';
if ($shade !== null) {
if (preg_match('/(\d+)\s+(\d+)\s+(\d+)/', $shade, $matches)) {
if ($matches[1] <= 255 && $matches[2] <= 255 && $matches[3] <= 255) {
$bg = sprintf(' style="background: #%02x%02x%02x"',
$matches[1], $matches[2], $matches[3]);
}
}
}
$html = "<td class=\"$class\"$bg>";
$week = $this->get_elem($specials, array('WEEK', $day, 0, 'body'));
if ($week === null) {
$week = '';
} else {
$week = ' ' . $week;
}
$moon_html = '';
if ($moon !== null) {
$phase = -1;
if (preg_match('/(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/', $moon, $matches)) {
$phase = $matches[1];
$moonsize = $matches[2];
$fontsize = $matches[3];
$msg = $matches[4];
} elseif (preg_match('/(\d+)/', $moon, $matches)) {
$phase = $matches[1];
$msg = '';
}
if ($phase >= 0) {
if ($phase == 0) {
$img = 'newmoon.png';
$title = 'New Moon';
$alt = 'new';
} elseif ($phase == 1) {
$img = 'firstquarter.png';
$title = 'First Quarter';
$alt = '1st';
} elseif ($phase == 2) {
$img = 'fullmoon.png';
$alt = 'full';
$title = 'Full Moon';
} else {
$img = 'lastquarter.png';
$alt = 'last';
$title = 'Last Quarter';
}
$base = rtrim($this->get_el($options, 'imgbase'), '/');
if ($base !== null) {
$img = $base . '/' . $img;
}
$moon_html = '<div class="rem-moon">' . "<img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">" . htmlspecialchars($msg) . '</div>';
}
}
# Day number
$html .= $moon_html . '<div class="rem-daynumber">' . $day . $week . '</div>';
# And the entries
$entries = $this->get_elem($results, array('entries', $day));
if (is_array($entries) && count($entries) > 0) {
$html .= '<div class="rem-entries">';
$html .= $this->format_entries($day, $results, $specials, $options, $entries);
$html .= '</div>';
}
$html .= "</td>\n";
return $html;
}
function small_calendar($results, $month, $monlen, $first_col, $which, &$options)
{
$monday_first = $results['monday_flag'];
if ($monday_first) {
$first_col--;
if ($first_col < 0) {
$first_col = 6;
}
}
$html = "<td class=\"rem-small-calendar\">\n<table class=\"rem-sc-table\">\n<caption class=\"rem-sc-caption\">";
# TODO: URL for small calendar
$html .= $month;
$html .= "</caption>\n";
}
function generate_html(&$results, &$specials, &$options)
{
$monday_first = $results['monday_flag'];
$first_col = $results['first_day'];
if ($monday_first) {
$first_col--;
if ($first_col < 0) $first_col = 6;
}
$last_col = ($first_col + $results['days_in_mon'] -1) % 7;
$html = '<table class="rem-cal"><caption class="rem-cal-caption">' .
htmlspecialchars($results['month']) . ' ' . htmlspecialchars($results['year']) .
"</caption>\n";
$html .= '<tr class="rem-cal-hdr-row">';
if (!$monday_first) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][0]) . '</th>';
for ($i=1; $i<7; $i++) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][$i]) . '</th>';
if ($monday_first) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][0]) . '</th>';
$html .= "</tr>\n";
# Do the leading empty columns
for ($col=0; $col < $first_col; $col++) {
if ($col == 0) $html .= '<tr class="rem-cal-body-row">';
$html .= '<td class="rem-empty">&nbsp;</td>';
}
for ($day=1; $day <= $results['days_in_mon']; $day++) {
if ($col == 0) $html .= '<tr class="rem-cal-body-row">';
$col++;
$html .= $this->do_one_day($day, $results, $specials, $options);
if ($col == 7) {
$html .= "</tr>\n";
$col = 0;
}
}
if ($col) {
while ($col++ < 7) {
$html .= '<td class="rem-empty">&nbsp;</td>';
}
}
$html .= "</tr>\n";
$html .= "</table>\n";
return $html;
}
function parse_remind_output ($fp)
{
while(1) {
$line = fgets($fp);
if ($line === false) break;
$line = trim($line);
if ($line == '# rem2ps begin') break;
}
if ($line === false) {
return array('success' => 0,
'error' => 'Could not find any Rem2PS data');
}
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($month, $year, $days_in_mon, $first_day, $monday_flag) = explode(' ', $line);
$retval = array('month' => $month,
'year' => $year,
'days_in_mon' => $days_in_mon,
'first_day' => $first_day,
'monday_flag' => $monday_flag);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
$retval['day_names'] = explode(' ', $line);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($m, $n) = explode(' ', $line);
$retval['prev'] = array('month' => $m, 'days' => $n);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($m, $n) = explode(' ', $line);
$retval['next'] = array('month' => $m, 'days' => $n);
$line_info = 0;
$entries = array();
$specials = array();
while (1) {
$line = fgets($fp);
if ($line === false) break;
$line = trim($line);
if ($line == '# rem2ps end') break;
if (strpos($line, '# fileinfo ') === 0) {
list($lno, $fname) = explode(' ', substr($line, 11), 2);
$lineinfo = array('file' => $fname, 'line' => $lno);
continue;
}
list($date, $special, $tags, $duration, $time, $body) = explode(' ', $line, 6);
list($y, $m, $d) = explode('/', $date);
$d = preg_replace('/^0(.)/', '$1', $d);
$m = preg_replace('/^0(.)/', '$1', $m);
$entry = array('day' => $d,
'month' => $m,
'year' => $y,
'special' => $special,
'tags' => $tags,
'duration' => $duration,
'time' => $time,
'body' => $body);
if (is_array($lineinfo)) {
$entry['line'] = $lineinfo['line'];
$entry['file'] = $lineinfo['file'];
$lineinfo = 0;
}
if ($special != '*' && $special != 'COLOR' && $special != 'COLOUR' && $special != 'HTML') {
if (!array_key_exists($special, $specials)) {
$specials[$special] = array();
}
if (!array_key_exists($d, $specials[$special])) {
$specials[$special][$d] = array();
}
$specials[$special][$d][] = $entry;
} else {
if (!array_key_exists($d, $entries)) {
$entries[$d] = array();
}
$entries[$d][] = $entry;
}
}
$retval['entries'] = $entries;
return array('success' => 1, 'results' => $retval, 'specials' => $specials);
}
}
$fp = popen('rem -p -l', 'r');
$r = new Remind;
$ans = $r->parse_remind_output($fp);
pclose($fp);
print_r($ans);
$options = array();
#print $r->generate_html($ans['results'], $ans['specials'], $options);
?>