Compare commits

..

170 Commits

Author SHA1 Message Date
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
Dianne Skoll
d32edbbb1f Fix release date. 2023-10-09 10:21:33 -04:00
Dianne Skoll
eae48a5538 Update release notes. 2023-10-08 18:17:28 -04:00
Dianne Skoll
63eba104d9 Reinstate tests that only work with 64-bit time_t 2023-10-08 18:09:34 -04:00
Dianne Skoll
ae64961735 Try to use 64-bit time_t on 32-bit systems. 2023-10-08 18:07:14 -04:00
Dianne Skoll
f7bb91320c Return an error rather than (DATETIME) -1 if soleq fails 2023-10-08 11:58:28 -04:00
Dianne Skoll
c11071a859 Undo fixes - the problem is with mktime on 32-bit systems.
Remove tests that break on 32-bit systems.
2023-10-08 11:49:44 -04:00
Dianne Skoll
53cbcc22db Fix typo 2023-10-08 11:35:06 -04:00
Dianne Skoll
af9dcec3e9 Use "long long" for 32-bit systems. 2023-10-08 11:34:10 -04:00
Dianne Skoll
d5a4b0d235 Allow specification of terminal-background mode as 't'
This forces Remind to try to guess the terminal background
even if stdout is not a tty.
2023-10-08 11:19:17 -04:00
Dianne Skoll
3b870403d9 Don't use UTF-8 input. 2023-10-08 11:08:58 -04:00
Dianne Skoll
284d822884 Fix a number of typos. 2023-10-08 10:55:48 -04:00
Dianne Skoll
d881a26ad0 Don't attempt to guess terminal background if supplied in an -@,n option. 2023-10-05 09:52:43 -04:00
Dianne Skoll
8519edde29 Fix logic error. 2023-10-04 10:11:49 -04:00
Dianne Skoll
a30c467c48 Revert "Guess terminal background even if stdout is not a tty"
This reverts commit 887cd83ebe.
2023-10-04 09:36:25 -04:00
Dianne Skoll
887cd83ebe Guess terminal background even if stdout is not a tty 2023-10-04 09:33:54 -04:00
Dianne Skoll
242d787ca2 Update release notes 2023-10-04 09:32:44 -04:00
Dianne Skoll
5dd2cf7356 Update version to 04.02.07 2023-10-04 09:32:38 -04:00
Dianne Skoll
5efb70909d Make -w0 obtain width from STDOUT_FILENO 2023-10-04 09:32:06 -04:00
Dianne Skoll
a60d466774 Document that Remind attempts to figure out terminal background lightness. 2023-10-03 18:59:51 -04:00
Dianne Skoll
1c01f36271 Fix typo 2023-10-03 09:25:45 -04:00
Dianne Skoll
3718632551 Let Remind guess if terminal is dark or light. 2023-10-03 00:09:59 -04:00
Dianne Skoll
e8f3d5ff9f Tweak the terminal background-color guessing code. 2023-10-03 00:04:01 -04:00
Dianne Skoll
d77e27942d Try to guess the terminal background color. 2023-10-03 00:00:59 -04:00
Dianne Skoll
734cc61489 Better logic for checking if we should close TTY fd. 2023-10-02 23:07:34 -04:00
Dianne Skoll
44d489d3d2 Make -w0 behave the same as -wt instead of causing an infinite loop. 2023-10-02 23:03:30 -04:00
Dianne Skoll
3e36ffa9ff Rename function. 2023-10-01 13:56:24 -04:00
Dianne Skoll
12104a96b1 Improve icon. 2023-09-29 18:06:18 -04:00
Dianne Skoll
8ab8d65a15 Document $DeltaOverride 2023-09-27 11:01:28 -04:00
Dianne Skoll
f7a8122cef Document --version 2023-09-25 12:41:57 -04:00
Dianne Skoll
77d9bbb7d6 Add FILES section to man page. 2023-09-25 09:43:06 -04:00
Dianne Skoll
623def52fd Add MAILING LIST section. 2023-09-25 09:38:07 -04:00
Dianne Skoll
d088e35142 Add bug-reporting email address. 2023-09-25 09:37:12 -04:00
Dianne Skoll
5821e55eb8 Add (1) to remind(1) in SEE ALSO section. 2023-09-25 08:28:24 -04:00
Dianne Skoll
1ee989c65d Add example for null date specification. 2023-09-25 08:27:22 -04:00
Dianne Skoll
62388fb21f Add "--version" long option. 2023-09-25 08:23:28 -04:00
Dianne Skoll
13571f84af Fix typo in man page source (found by Dan Jacobson) 2023-09-25 08:15:14 -04:00
Dianne Skoll
03fdc06b65 Make AT optional: If we encounter a TIME, then implicitly start an AT clause. 2023-09-18 12:59:00 -04:00
Dianne Skoll
4bce675ae6 Update remind-conf-mode.el per Bill Benedetto 2023-09-13 18:40:13 -04:00
Jochen Sprickerhof
7b64623115 Fix manpage formatting 2023-09-12 21:08:44 +02:00
Dianne Skoll
e268bbf31d Bump version to 04.02.06 2023-09-12 14:38:14 -04:00
Dianne Skoll
5863404de6 Document that MAYBE-UNCOMPUTABLE can be abbreviated to MAYBE. 2023-09-07 16:03:12 -04:00
Dianne Skoll
2de5996f4e Update man page. 2023-08-17 09:35:55 -04:00
Dianne Skoll
695e79602a Add Irish holidays in include/holidays/ie.rem
Courtesy of Amy de Buitléir.
2023-07-26 16:02:21 -04:00
Dianne Skoll
cf0d958da5 Add optional "step" parameter to slide() to match nonomitted(). 2023-07-23 14:43:13 -04:00
Dianne Skoll
85b0348fa7 Add $ParseUntriggered system variable. 2023-07-20 09:21:37 -04:00
Dianne Skoll
15a5d9a876 Simplify since() example. 2023-07-19 10:19:55 -04:00
Dianne Skoll
1baa6dab0c Updates to nomomitted:
o Add optional "step" argument
  o If start > end, swap the first two arguments
  o Update man page and tests
2023-07-15 13:04:47 -04:00
Dianne Skoll
34bb250ba3 Fix documentation accuracy. 2023-06-27 09:46:40 -04:00
Dianne Skoll
598b1b7464 Better documentation of %"...%" interaction with "-ca". 2023-06-11 14:08:22 -04:00
Dianne Skoll
e63d4be4e8 Make "-tn" explicitly set a delta of ++n for *all* REM statements.
Also change the name of $DeltaOffset to $DeltaOverride.
2023-06-03 13:36:58 -04:00
Dianne Skoll
65561e7f34 Add "-tz" option to suppress all deltas. Document -tn better. 2023-05-21 20:14:07 -04:00
Dianne Skoll
da31dadb71 Correct Italian localizatio; patch courtesy of Emanuele Torre 2023-04-18 16:07:10 -04:00
92 changed files with 5811 additions and 3101 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

24
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"
@@ -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\""
}
} }

3095
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_INIT(src/queue.c) AC_INIT
AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF' cat <<'EOF'
@@ -12,7 +13,7 @@ cat <<'EOF'
EOF EOF
AC_CONFIG_HEADER(src/config.h) AC_CONFIG_HEADERS([src/config.h])
AC_ARG_ENABLE(perl-build-artifacts, AC_ARG_ENABLE(perl-build-artifacts,
[ --disable-perl-build-artifacts [ --disable-perl-build-artifacts
@@ -29,32 +30,32 @@ AC_PATH_PROG([PERL], [perl])
dnl Checks for libraries. dnl Checks for libraries.
AC_CHECK_LIB(m, sqrt) AC_CHECK_LIB(m, sqrt)
AC_CHECK_HEADERS_ONCE([sys/time.h])
dnl Integer sizes dnl Integer sizes
AC_CHECK_SIZEOF(unsigned int) AC_CHECK_SIZEOF(unsigned int)
AC_CHECK_SIZEOF(unsigned long) AC_CHECK_SIZEOF(unsigned long)
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
dnl Checks for library functions. dnl Checks for library functions.
AC_FUNC_UTIME_NULL AC_FUNC_UTIME_NULL
AC_HEADER_TIME
if test "$GCC" = yes; then if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes" CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes"
# 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
@@ -65,6 +66,12 @@ if test "$GCC" = yes; then
fi fi
fi fi
dnl If sizeof(time_t) is 4, try to get 64-bit time_t
if test "$ac_cv_sizeof_time_t" = "4" ; then
AC_MSG_NOTICE([time_t is 32-bits on this system; attempting to use 64-bit time_t])
CFLAGS="$CFLAGS -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64"
fi
if test "$ac_cv_perlartifacts" = "yes" ; then if test "$ac_cv_perlartifacts" = "yes" ; then
PERLARTIFACTS= PERLARTIFACTS=
else else
@@ -79,11 +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.05
VERSION=04.03.00
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_OUTPUT(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
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:
@@ -514,6 +513,7 @@ Acts on the region or places point where it needs to be."
(set (make-local-variable 'comment-start) ";") (set (make-local-variable 'comment-start) ";")
(set (make-local-variable 'comment-start) "#") (set (make-local-variable 'comment-start) "#")
(set (make-local-variable 'comment-end) "\n") (set (make-local-variable 'comment-end) "\n")
(set (make-local-variable 'comment-end-skip) "[ \t]*\\(\\s>\\||#\\)")
(set (make-local-variable 'skeleton-end-hook) nil) ; so the skeletons will not automatically go to a new line. (set (make-local-variable 'skeleton-end-hook) nil) ; so the skeletons will not automatically go to a new line.
(set (make-local-variable 'fill-column) '100);cause I was having problems with autofill. (set (make-local-variable 'fill-column) '100);cause I was having problems with autofill.
(set (make-local-variable 'indent-line-function) 'remind-indent-line) (set (make-local-variable 'indent-line-function) 'remind-indent-line)

View File

@@ -1,5 +1,181 @@
CHANGES TO REMIND CHANGES TO REMIND
* 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
- IMPROVEMENT: remind: On 32-bit systems, attempt to use a 64-bit time_t
if the C library supports that. This lets Remind work properly with
dates after 2038 in the few cases it has to call mktime() internally.
- MINOR NEW FEATURE: remind: Attempt to obtain the terminal background
color using an OSC sequence. This normally only happens if standard
output is a terminal, but can be forced with the '-@..,t' option.
- MINOR NEW FEATURE: remind: Add "--version" long option to print out
Remind's version and exit.
- MINOR IMPROVEMENT: tkremind: Use a higher-resolution PNG image for
the icon.
- MINOR IMPROVEMENT: remind-conf-mode.el: Update highlighting rules
courtesy of Bill Benedetto
- MINOR CHANGE: Make AT optional. If we encounter a TIME in a REM
command, implicitly begin an AT clause.
- DOCUMENTATION: Many minor fixes and improvements courtesy of Dan Jacobson.
- BUG FIX: Make "-w0" set the calendar width based on standard output
rather than setting it to zero and causing an infinite loop.
* VERSION 4.2 Patch 6 - 2023-09-12
- NEW FEATURE: remind: The "nonomitted()" function takes an optional
extra INT argument called "step". See man page for details. Also
allows the "start" argument to be greater than the "end" argument,
in which case they are effectively swapped.
- NEW FEATURE: remind: The "slide()" function takes an optional extra
INT argument called "step", similar to "nonomitted()". See man page
for details.
- NEW FEATURE: remind: Added the $ParseUntriggered system variable;
see the man page for details. You almost certainly will never need
to use this.
- NEW FILE: holidays/ie.rem: Added Irish holidays, courtesy of
Amy de Buitléir.
- CHANGE: remind: The "-tn" option sets all REM statement deltas to
++n rather than adding n to any existing REM statement's delta.
Additionally, the corresponding system variable $DeltaOffset has
been renamed to $DeltaOverride.
- NEW OPTION: remind: Add the "-tz" option to explicitly set all
REM statement deltas to zero.
- DOCUMENTATION FIX: remind: various documentation improvements.
- BUG FIX: Correct some errors in Italian localization, courtesy of
Emanuele Torre
* VERSION 4.2 Patch 5 - 2023-04-11 * VERSION 4.2 Patch 5 - 2023-04-11
- MINOR IMPROVEMENT: remind: If someone uses OMIT yyyy-mm-dd UNTIL yyyy-mm-dd - MINOR IMPROVEMENT: remind: If someone uses OMIT yyyy-mm-dd UNTIL yyyy-mm-dd

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

@@ -8,15 +8,13 @@
# Set this variable to 1 if your terminal has a dark background or 0 if # Set this variable to 1 if your terminal has a dark background or 0 if
# it: light. # it: light.
bg_dark=1
# Set your latitude and longitude correctly for Sunrise/Sunset/Equinox/Solstice # Set your latitude and longitude correctly for Sunrise/Sunset/Equinox/Solstice
# #
# The values below are for Ottawa, Ontario, Canada # The values below are for Ottawa, Ontario, Canada
latitude="45.420556" latitude="45.420556"
longitude="-75.689722" longitude="-75.689722"
remind -g -ibg_dark="$bg_dark" "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF' remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF'
SET $AddBlankLines 0 SET $AddBlankLines 0
BANNER % BANNER %
@@ -24,24 +22,25 @@ INCLUDE [$SysInclude]/ansitext.rem
MSG Today is [ansi_bold][$T][ansi_normal], being the [ord($T-date(year($T),1,1)+1)] day of [year($T)].%_ MSG Today is [ansi_bold][$T][ansi_normal], being the [ord($T-date(year($T),1,1)+1)] day of [year($T)].%_
IF bg_dark IF $TerminalBackground == 0
SPECIAL COLOR 255 255 0 Sunrise: 🌅 [sunrise()] today and [sunrise($T+1)] tomorrow SPECIAL COLOR 255 255 0 Sunrise: 🌅 [sunrise()] today and [sunrise($T+1)] tomorrow
SPECIAL COLOR 255 128 0 Sunset: 🌇 [sunset()] today and [sunset($T+1)] tomorrow%_ SPECIAL COLOR 255 128 0 Sunset: 🌇 [sunset()] today and [sunset($T+1)] tomorrow%_
ELSE
SPECIAL COLOR 128 128 0 Sunrise: 🌅 [sunrise()] today and [sunrise($T+1)] tomorrow
SPECIAL COLOR 128 32 0 Sunset: 🌇 [sunset()] today and [sunset($T+1)] tomorrow%_
ENDIF ENDIF
EOF EOF
remind -g -ibg_dark="$bg_dark" "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF' remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF'
SET $AddBlankLines 0 SET $AddBlankLines 0
BANNER % BANNER %
IF bg_dark IF $TerminalBackground == 0
REM [moondatetime(0)] +60 SPECIAL COLOR 255 255 0 New moon: 🌑 [$T] %3 (%b) REM [moondatetime(0)] +60 SPECIAL COLOR 255 255 0 New moon: 🌑 [$T] %3 (%b)
REM [moondatetime(1)] +60 SPECIAL COLOR 255 255 128 First Quarter: 🌓 [$T] %3 (%b) REM [moondatetime(1)] +60 SPECIAL COLOR 255 255 128 First Quarter: 🌓 [$T] %3 (%b)
REM [moondatetime(2)] +60 SPECIAL COLOR 255 255 255 Full moon: 🌕 [$T] %3 (%b) REM [moondatetime(2)] +60 SPECIAL COLOR 255 255 255 Full moon: 🌕 [$T] %3 (%b)
REM [moondatetime(3)] +60 SPECIAL COLOR 255 255 128 Last Quarter: 🌗 [$T] %3 (%b) REM [moondatetime(3)] +60 SPECIAL COLOR 255 255 128 Last Quarter: 🌗 [$T] %3 (%b)
ELSE ELSE
SPECIAL COLOR 128 128 0 Sunrise: 🌅 [sunrise()] today and [sunrise($T+1)] tomorrow
SPECIAL COLOR 128 32 0 Sunset: 🌇 [sunset()] today and [sunset($T+1)] tomorrow%_
REM [moondatetime(0)] +60 SPECIAL COLOR 128 128 0 New moon: 🌑 [$T] %3 (%b) REM [moondatetime(0)] +60 SPECIAL COLOR 128 128 0 New moon: 🌑 [$T] %3 (%b)
REM [moondatetime(1)] +60 SPECIAL COLOR 128 128 64 First Quarter: 🌓 [$T] %3 (%b) REM [moondatetime(1)] +60 SPECIAL COLOR 128 128 64 First Quarter: 🌓 [$T] %3 (%b)
REM [moondatetime(2)] +60 SPECIAL COLOR 0 0 0 Full moon: 🌕 [$T] %3 (%b) REM [moondatetime(2)] +60 SPECIAL COLOR 0 0 0 Full moon: 🌕 [$T] %3 (%b)
@@ -51,7 +50,7 @@ EOF
echo "" echo ""
remind -g -ibg_dark="$bg_dark" "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF' remind -g "-i\$Latitude=\"$latitude\"" "-i\$Longitude=\"$longitude\"" -q -@2 - "$@" <<'EOF'
SET $AddBlankLines 0 SET $AddBlankLines 0
BANNER % BANNER %

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")

42
include/holidays/ie.rem Normal file
View File

@@ -0,0 +1,42 @@
;
; Irish Holidays
;
; The dates for the Public ("bank") holidays are taken from the following site:
; https://www.citizensinformation.ie/en/employment/employment-rights-and-conditions/leave-and-holidays/public-holidays/
;
; This file was derived from:
; https://github.com/mhwombat/dotWombat/blob/master/.config/remind/IrishHolidays.rem
; by Amy de Buitléir.
; fixed dates
OMIT 31 December MSG New Year's Eve (Oíche Chinn Bliana) Public Holiday
OMIT 1 January MSG New Year's Day (Lá Caille, Lá Bliana Nua) Public Holiday
OMIT 17 March MSG Saint Patrick's Day (Lá Fhéile Pádraig) Public Holiday
OMIT 24 December MSG Christmas Eve (Oíche Nollag) Public Holiday
OMIT 25 December MSG Christmas Day (Lá Nollag) Public Holiday
OMIT 26 December MSG Saint Stephen's Day, Wren Day (Lá Fhéile Stiofáin, Lá an Dreoilín) Public Holiday
; moving dates
; First Monday in May
REM Monday 1 May SCANFROM -7 ADDOMIT MSG May Day (Lá Bealtaine) Public Holiday
; First Monday in June
REM Monday 1 June SCANFROM -7 ADDOMIT MSG June Public Holiday
; First Monday in August
REM Monday 1 August SCANFROM -7 ADDOMIT MSG August Public Holiday
; Last Monday in October
REM Monday 1 -7 November SCANFROM -7 ADDOMIT MSG October Public Holiday
; Easter
SET easter easterdate(today())
REM [TRIGGER(easter-2)] MSG Good Friday (Aoine an Chéasta)
REM [TRIGGER(easter)] MSG Easter Sunday (Domhnach Cásca)
OMIT [TRIGGER(easter+1)] MSG Easter Monday (Luan Cásca) Public Holiday
; St. Brigid's Day
REM 1 February MSG Saint Brigid's Day (Lá Fhéile Bríde or Imbolc)
; The public holiday is the first Monday in February, or 1 February if the date falls on a Friday
REM February SCANFROM -7 ADDOMIT SATISFY [($Td==1 && $Tw==5) || ($Td<8 && $Tw==1 && $Td!=4)] MSG Public Holiday

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,14 +1,14 @@
# 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"
SET $Monday "Lunedí" SET $Monday "Lunedì"
SET $Tuesday "Martedí" SET $Tuesday "Martedì"
SET $Wednesday "Mercoledí" SET $Wednesday "Mercoledì"
SET $Thursday "Giovedí" SET $Thursday "Giovedì"
SET $Friday "Venerdí" SET $Friday "Venerdì"
SET $Saturday "Sabato" SET $Saturday "Sabato"
SET $January "Gennaio" SET $January "Gennaio"
@@ -40,7 +40,7 @@ SET $Now "ora"
SET $At "alle" SET $At "alle"
SET $Minute "minuto" SET $Minute "minuto"
SET $Hour "ora" SET $Hour "ora"
SET $Is "é" SET $Is "è"
SET $Was "era" SET $Was "era"
SET $And "e" SET $And "e"
SET $Hplu "a" SET $Hplu "a"

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

@@ -19,4 +19,4 @@ Remind was written by Dianne Skoll <dianne@skoll.ca>
.SH HOME PAGE .SH HOME PAGE
https://dianne.skoll.ca/projects/remind/ https://dianne.skoll.ca/projects/remind/
.SH SEE ALSO .SH SEE ALSO
\fBremind\fR \fBremind\fR(1)

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,14 +323,14 @@ 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)
The \fB\-p\fR option is an older, simpler interchange format used by The \fB\-p\fR option is an older, simpler interchange format used by
\fBRemind\fR to communicate with back-ends. New back-ends are \fBRemind\fR to communicate with back-ends. New back-ends are
encoraged to support the new \fB\-pp\fR format preferably, though they encouraged to support the new \fB\-pp\fR format preferably, though they
are encouraged to support the older \fB\-p\fR format as well if the are encouraged to support the older \fB\-p\fR format as well if the
older format contains enough information for them to work properly. older format contains enough information for them to work properly.
.PP .PP
@@ -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

@@ -28,6 +28,10 @@ Anything after the __EOF__ marker is completely ignored.
\fBRemind\fR has a slew of options. If you're new to the program, \fBRemind\fR has a slew of options. If you're new to the program,
ignore them for now and skip to the section "REMINDER FILES". ignore them for now and skip to the section "REMINDER FILES".
.TP .TP
.B \-\-version
The \fB\-\-version\fR option causes \fBRemind\fR to print its version number
to standard output and then exit.
.TP
.B \-n .B \-n
The \fB\-n\fR option causes \fBRemind\fR to print the \fBnext\fR occurrence The \fB\-n\fR option causes \fBRemind\fR to print the \fBnext\fR occurrence
of each reminder in a simple calendar format. You can sort this by of each reminder in a simple calendar format. You can sort this by
@@ -60,7 +64,9 @@ weeks to be produced.
.B 'a' .B 'a'
causes \fBRemind\fR to display reminders on the calendar on the 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. specified by the reminder's \fIdelta\fR. This \fIalso\fR causes
\fBRemind\fR to include text outside %"...%" sequences that would
otherwise be removed (though the actual %" markers themselves are removed.)
.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
@@ -103,11 +109,18 @@ If the optional \fIm\fR parameter is supplied following a comma, then
\fIm\fR=0 tells \fBRemind\fR that the terminal background is dark, and \fIm\fR=0 tells \fBRemind\fR that the terminal background is dark, and
\fBRemind\fR will brighten up dark colors to make them visible. If \fBRemind\fR will brighten up dark colors to make them visible. If
\fIm\fR=1, then \fBRemind\fR assumes the terminal background is light \fIm\fR=1, then \fBRemind\fR assumes the terminal background is light
and it will darken bright colors to make them visible. If no \fIm\fR and it will darken bright colors to make them visible. If \fIm\fR is
is supplied, or it is supplied as \fIm\fR=2, then \fBRemind\fR does specified as 2, then \fBRemind\fR does not perform any adjustments,
not perform any adjustments, and some reminders may be hard or and some reminders may be hard or impossible to see if the color is
impossible to see if the color is too close to the terminal background too close to the terminal background color. If you supply the letter
color. \fBt\fR rather than a number, then Remind attempts to guess the background
color of the terminal, \fIeven if\fR stdout is not a terminal.
.PP
On startup, if the standard output is a terminal, \fBRemind\fR
attempts to determine if the terminal background is dark or light by
sending a special escape sequence to determine the background color.
The \fIm\fR parameter can override this check (or force it if
\fIm\fR is given as \fBt\fR.)
.PP .PP
If the optional \fIb\fR parameter is supplied following a comma, then If the optional \fIb\fR parameter is supplied following a comma, then
\fIb=0\fR tells \fBRemind\fR to ignore SPECIAL SHADE reminders (the \fIb=0\fR tells \fBRemind\fR to ignore SPECIAL SHADE reminders (the
@@ -222,8 +235,14 @@ regardless of the \fIdelta\fR supplied for each reminder.
.TP .TP
.B \-t\fR\fIn\fR .B \-t\fR\fIn\fR
If you supply a number \fIn\fR after the \fB\-t\fR option, then If you supply a number \fIn\fR after the \fB\-t\fR option, then
\fBRemind\fR pretends that each non-expired reminder has a \fIdelta\fR \fBRemind\fR pretends that echo \fBREM\fR command has a delta
of \fIn\fR days and triggers reminders accordingly. of \+\+\fIn\fR, regardless of any existing delta.
.TP
.B \-tz\fR
If you supply the letter \fBz\fR after the \fB\-t\fR option, then
\fBRemind\fR sets all REM statements' deltas to zero, regardless of the
value supplied in the REM statement itself. In effect, this disables
all deltas of the form \fB\+\fIn\fR and \fB\+\+\fIn\fR.
.TP .TP
.B \-tt\fR[\fIn\fR] .B \-tt\fR[\fIn\fR]
The \fB-tt\fR option causes \fBRemind\fR to assume a default delta of The \fB-tt\fR option causes \fBRemind\fR to assume a default delta of
@@ -349,9 +368,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
@@ -602,6 +624,11 @@ The following examples show how date specifications are interpreted.
.PP .PP
1. Null date specification - the reminder is triggered every day. 1. Null date specification - the reminder is triggered every day.
The trigger date for a specific run is simply the current system date. The trigger date for a specific run is simply the current system date.
For example:
.PP
.nf
REM MSG This is triggered every time Remind runs
.fi
.PP .PP
2. Only 2. Only
.I day .I day
@@ -1114,7 +1141,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.
@@ -1186,7 +1213,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
@@ -1680,6 +1709,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
@@ -1782,7 +1816,7 @@ will begin reading from standard input.
.PP .PP
If you specify a \fIdirectory\fR as the argument to \fBINCLUDE\fR, then If you specify a \fIdirectory\fR as the argument to \fBINCLUDE\fR, then
\fBRemind\fR will process all files in that directory that match the shell \fBRemind\fR will process all files in that directory that match the shell
patterm "*.rem". The files are processed in sorted order; the sort order pattern "*.rem". The files are processed in sorted order; the sort order
matches that used by the shell when it expands "*.rem". matches that used by the shell when it expands "*.rem".
.PP .PP
Note that the file specified by an \fBINCLUDE\fR command is interpreted Note that the file specified by an \fBINCLUDE\fR command is interpreted
@@ -2138,7 +2172,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
@@ -2390,6 +2427,10 @@ is normally 0, but can be set with the \fB\-tt\fR option or explicitly
set in your script. If \fB$DefaultDelta\fR is non-zero, you can use an set in your script. If \fB$DefaultDelta\fR is non-zero, you can use an
explicit delta of +0 in an AT clause to countermand the default delta. explicit delta of +0 in an AT clause to countermand the default delta.
.TP .TP
.B $DeltaOverride (read-only)
If non-zero, corresponds to the \fIn\fR argument given to a
\fB\-t\fR\fIn\fR command-line option.
.TP
.B $DontFork (read-only) .B $DontFork (read-only)
If non-zero, then the \fB\-c\fR option was supplied on the command line. If non-zero, then the \fB\-c\fR option was supplied on the command line.
.TP .TP
@@ -2456,7 +2497,8 @@ or a date different from today's true date was supplied. If non-zero,
then \fBONCE\fR directives will be ignored. then \fBONCE\fR directives will be ignored.
.TP .TP
.B $InfDelta (read-only) .B $InfDelta (read-only)
If non-zero, then the \fB\-t\fR option was supplied on the command line. If non-zero, then the \fB\-t\fR option was supplied on the command line,
with no \fIn\fR argument.
.TP .TP
.B $IntMax (read-only) .B $IntMax (read-only)
The largest representable \fBINT\fR. On a machine with 32-bit signed integers The largest representable \fBINT\fR. On a machine with 32-bit signed integers
@@ -2533,6 +2575,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.
@@ -2541,7 +2601,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
@@ -2556,6 +2618,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.
@@ -2577,6 +2651,28 @@ by \fBREM\fR commands; triggers in \fBIFTRIG\fR commands do
not affect it. not affect it.
.RE .RE
.TP .TP
.B $ParseUntriggered
A flag indicating whether or not \fBRemind\fR should fully parse \fBREM\fR
statements that are not triggered. 0 means to skip parsing them and 1
(the default) means to parse them.
.PP
.RS
For example, if we have the following \fBREM\fR statement:
.PP
.nf
REM 2020-01-01 MSG ["bad_expression" * 2]
.fi
.PP
Then by default, \fBRemind\fR will fully parse the line and issue
a "Type mismatch" error even if the reminder is not triggered. However,
if \fB$ParseUntriggered\fR is set to 0, then \fBRemind\fR will not
issue the error except on 2020-01-01, when the reminder is triggered.
.PP
Setting \fB$ParseUntriggered\fR to 0 may in some cases slightly
improve performance, at the risk of not catching errors until a
reminder is triggered.
.RE
.TP
.B $PrefixLineNo (read-only) .B $PrefixLineNo (read-only)
If non-zero, then the \fB\-l\fR option was supplied on the command line. If non-zero, then the \fB\-l\fR option was supplied on the command line.
.TP .TP
@@ -2678,9 +2774,13 @@ Set to 1 if the \fB\-@1\fR option was used; 0 otherwise.
Set to 1 if the \fB\-@2\fR option was used; 0 otherwise. Set to 1 if the \fB\-@2\fR option was used; 0 otherwise.
.TP .TP
.B $TerminalBackground (read-only) .B $TerminalBackground (read-only)
Returns -1 if the terminal background color was not specified, Returns -1 if the terminal background color could not be determined, 0
0 if it was specified as dark with the \fB\-@,0\fR option or if it was found to be dark (or was specified as dark with the
1 if it was specified as light with the \fB\-@,1\fR option. \fB\-@,0\fR option) or 1 if it was found to be light (or specified as
light with the \fB\-@,1\fR option.) The terminal background is considered
to be "dark" if the average of the red, green and blue components is
at most 85 out of 255, and if the maximum of any component is at most
128 out of 255.
.PP .PP
Note: If any of the calendar modes are in effect, then the Note: If any of the calendar modes are in effect, then the
values of $Daemon, $DontFork, $DontTrigAts, $DontQueue, $HushMode, values of $Daemon, $DontFork, $DontTrigAts, $DontQueue, $HushMode,
@@ -2983,11 +3083,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
@@ -3258,14 +3359,31 @@ is supplied, only the date component is used.
Returns the time of "nautical twilight" on the specified \fIdate\fR. If Returns the time of "nautical 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 nonomitted(dq_start, dq_end [,s_wkday...]) .B nonomitted(dq_start, dq_end [, i_step] [,s_wkday...])
This function returns the number of \fInon-\fRomitted days between This function returns the number of \fInon-\fRomitted days between
\fIstart\fR and \fIend\fR. If \fIstart\fR is non-omitted, then it is \fIstart\fR and \fIend\fR. If \fIstart\fR is non-omitted, then it is
counted. \fIend\fR is never counted. counted. \fIend\fR is never counted.
.RS .RS
.PP .PP
Note that \fIend\fR must be greater than or equal to \fIstart\fR or an Note that if \fIend\fR is less than \fIstart\fR, the arguments
error is reported. In addition to using the global OMIT context, you are effectively swapped, so counting always begins from the older
date.
.PP
If the third argument to \fBnonomitted\fR is an \fBINT\fR, then it must
be greater than zero, and is consider to be the \fIstep\fR by which
\fBnonomitted\fR counts. For example the following expression:
.PP
.nf
nonomitted('2023-07-01', '2023-07-29', 7)
.fi
.PP
returns the number of non-omitted Saturdays from 2023-07-01 up to
(but not including) 2023-07-29. (Both 2023-07-01 and 2023-07-29 are
Saturdays.)
.PP
If no \fIstep\fR argument is supplied, then a step of 1 is used.
.PP
In addition to using the global OMIT context, you
can supply additional arguments that are names of weekdays to be can supply additional arguments that are names of weekdays to be
omitted. However, in a \fBREM\fR command, any local \fBOMITFUNC\fR omitted. However, in a \fBREM\fR command, any local \fBOMITFUNC\fR
clause is \fInot\fR taken into account by this function. clause is \fInot\fR taken into account by this function.
@@ -3292,7 +3410,7 @@ reminder will label day numbers in a calendar:
.fi .fi
.PP .PP
Obviously, the answer you get from \fBnonomitted\fR depends on the global Obviously, the answer you get from \fBnonomitted\fR depends on the global
OMIT context. If you use moveable OMITs, you may get inconsistent results. OMIT context. If you use movable OMITs, you may get inconsistent results.
.PP .PP
Here is a more complex use for \fBnonomitted\fR. My garbage collection Here is a more complex use for \fBnonomitted\fR. My garbage collection
follows two interleaved 14-day cycles: One Friday, garbage and paper follows two interleaved 14-day cycles: One Friday, garbage and paper
@@ -3325,11 +3443,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
@@ -3486,10 +3605,13 @@ will set \fBa\fR to:
.fi .fi
.TP .TP
.B slide(d_start, i_amt [,s_wkday...]) .B slide(d_start, i_amt [, i_step] [,s_wkday...])
This function is the inverse of \fBnonomitted\fR. It adds \fIamt\fR This function is the inverse of \fBnonomitted\fR. It adds \fIamt\fR
days (which can be negative) to \fIstart\fR, \fInot counting omitted days\fR. (which can be negative) chunks of \fIstep\fR days to \fIstart\fR,
The optional \fIwkday\fR arguments are additional weekday names to omit. \fInot counting omitted days\fR. If \fIstep\fR is not supplied, then
it is assumed to be 1. Note that only every \fIstep\fRth day is
tested to see if it is omitted. The optional \fIwkday\fR arguments
are additional weekday names to omit.
.RS .RS
.PP .PP
Consider this example: Consider this example:
@@ -3509,6 +3631,26 @@ May 16 and 17. You can go backwards, too, so:
.fi .fi
.PP .PP
takes \fIa\fR back to 2009-05-13. takes \fIa\fR back to 2009-05-13.
.PP
Now consider this example:
.PP
.nf
OMIT 14 May 2009
SET a slide('2009-05-07', 2, 7)
.fi
.PP
This sets \fIa\fR to '2009-05-28' because we skip ahead two weeks, not
counting a week where the day we land on happens to be omitted. Contrast with
this:
.PP
.nf
OMIT 13 May 2009
SET a slide('2009-05-07', 2, 7)
.fi
.PP
which sets \fIa\fR to '2009-05-21'. Although 2009-05-13 is omitted, we
don't "land" on it as we step forward in chunks of 7 days, so we never
see that it is omitted.
.RE .RE
.TP .TP
.B soleq(i_which [, dqi_start]) .B soleq(i_which [, dqi_start])
@@ -3527,7 +3669,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.
@@ -4216,7 +4358,7 @@ you define a function taking no parameters. Here are some examples:
.nf .nf
FSET double(x) 2*x FSET double(x) 2*x
FSET yeardiff(date1, date2) year(date1) - year(date2) FSET yeardiff(date1, date2) year(date1) - year(date2)
FSET since(x) ord(year(trigdate())\-x) FSET since(x) ord($Ty \- x)
.fi .fi
.PP .PP
The last function is useful in birthday reminders. For example: The last function is useful in birthday reminders. For example:
@@ -4231,6 +4373,12 @@ Dean was born in 1984. The above example, on 1 November 1992, would print:
Dean's 8th birthday is today. Dean's 8th birthday is today.
.fi .fi
.PP .PP
Similarly, the function is useful in anniversary reminders. For example:
.PP
.nf
REM 4 June MSG [since(1989)] anniversary of the Tienanmen Square massacre
.fi
.PP
Notes: Notes:
.TP .TP
o o
@@ -5112,7 +5260,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
@@ -5418,13 +5566,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
@@ -5449,7 +5597,7 @@ The \fBSPECIAL\fR keyword is used to transmit "out-of-band" information
to \fBRemind\fR backends, such as \fBtkremind\fR or \fBRem2PS\fR. to \fBRemind\fR backends, such as \fBtkremind\fR or \fBRem2PS\fR.
They are used only when piping data from a \fBremind \-p\fR line. They are used only when piping data from a \fBremind \-p\fR line.
(Note that the COLOR special is an exception; it downgrades to the (Note that the COLOR special is an exception; it downgrades to the
equivalent of MSG in \fBremind's\fR normal mode of operation.) equivalent of MSG in \fBRemind's\fR normal mode of operation.)
.PP .PP
The various \fBSPECIAL\fRs recognized are particular for each The various \fBSPECIAL\fRs recognized are particular for each
backend; however, there are four \fBSPECIAL\fRs that all backends backend; however, there are four \fBSPECIAL\fRs that all backends
@@ -5538,7 +5686,7 @@ after the WEEK keyword.
.PP .PP
.SH MISCELLANEOUS .SH MISCELLANEOUS
.PP .PP
.B COMMAND ABBREVIATIONS .B COMMAND AND KEYWORD ABBREVIATIONS
.PP .PP
The following tokens can be abbreviated: The following tokens can be abbreviated:
.TP .TP
@@ -5565,6 +5713,9 @@ o
\fBINCLUDE\fR --> \fBINC\fR \fBINCLUDE\fR --> \fBINC\fR
.TP .TP
o o
\fBMAYBE-UNCOMPUTABLE\fR --> \fBMAYBE\fR
.TP
o
\fBSCANFROM\fR --> \fBSCAN\fR \fBSCANFROM\fR --> \fBSCAN\fR
.PP .PP
.B NIFTY EXAMPLES .B NIFTY EXAMPLES
@@ -5737,6 +5888,25 @@ components, then \fBRemind\fR will correctly compute a trigger date, even
if it happens to be before the start of scanning. if it happens to be before the start of scanning.
Note that this behaviour is not true for Note that this behaviour is not true for
versions of \fBRemind\fR prior to 03.00.01. versions of \fBRemind\fR prior to 03.00.01.
.SH FILES
.PP
The traditional location of your reminders file or directory is:
.PP
.nf
$HOME/.reminders
.fi
.PP
where \fB$HOME\fR is your home directory.
.PP
Remind ships with some preinstalled files for holidays and language
packs. These are located in the following directory:
.PP
.nf
@prefix@/share/remind/
.fi
.PP
Do not hard-code the above directory in your reminder files. Instead,
use the value of the $SysInclude system variable.
.SH AUTHOR .SH AUTHOR
.PP .PP
Dianne Skoll <dianne@skoll.ca> wrote \fBRemind\fR. The moon code Dianne Skoll <dianne@skoll.ca> wrote \fBRemind\fR. The moon code
@@ -5784,9 +5954,11 @@ Liviu Daia
Rafa Couto Rafa Couto
.PP .PP
\fBIcelandic\fR -- \fBIcelandic\fR --
Bj\(:orn Dav\('i\[Sd]sson Bj\[:o]rn Daví\[Sd]sson
.SH BUGS .SH BUGS
.PP .PP
If you find a bug in Remind, please report it to: dianne@skoll.ca
.PP
There's no good reason why read-only system variables are not There's no good reason why read-only system variables are not
implemented as functions, or why functions like \fBversion()\fR, etc. implemented as functions, or why functions like \fBversion()\fR, etc.
are not implemented as read-only system variables. are not implemented as read-only system variables.
@@ -5807,8 +5979,13 @@ 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
https://dianne.skoll.ca/mailman/listinfo/remind-fans
.SH SEE ALSO .SH SEE ALSO
.PP .PP
\fBrem\fR(1), \fBrem2ps\fR(1), \fBrem2pdf\fR(1), \fBtkremind\fR(1), \fBrem2html\fR(1) \fBrem\fR(1), \fBrem2ps\fR(1), \fBrem2pdf\fR(1), \fBtkremind\fR(1), \fBrem2html\fR(1)

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,102 @@ 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 .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 {"response":"reminder","ttime":tt,"now":now,"tags":tags,"body":body}
asterisk is supplied for \fItag\fR. All lines following this line
are the body of the reminder, until the line \fBNOTE endreminder\fR .fi
is transmitted. 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.
.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: 3.1 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,31 +29,62 @@ set Hostname [exec hostname]
# Our icon photo # Our icon photo
catch { catch {
image create photo rpicon -data { image create photo rpicon -data {
R0lGODlhFwAgAOecABUTERYTERYUERcVEhgWExkXFBkXFRoXFRsZFhwZFxwa iVBORw0KGgoAAAANSUhEUgAAAEAAAABbCAYAAADDeIOGAAAACXBIWXMAAAu6AAALugFBTNueAAAA
GB0bGR4cGR4cGh8dGiAeHCEfHCEfHSIgHSIgHiQiHyYkISknJCooJispJywq GXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAADANJREFUeJzdnGtsFNcVx38Xr19r
Jy4sKTIwLjUzMDUzMTo4Njs5Nzs5ODw7ODw7OT07OT48OkE/PUJAPkNBP0RC 4/BawjPGjcGY2MY8Q+1g05iXKwoihvAQoCJEQoiU0CofWgmpUhIJNVKpSGnUSGmKLEEihcZFQeBW
QEVDQUVEQkdFQ0lIRkpJR01LSU5MSlBPTVFQTlNSUFRSUFRSUVVTUlVUUllY gRJeKQkQisFAsWDxGsdAawjgGGF7Tz/MrrvGuzN3ZmchzV8aeT17zj3n/u+5d87cxyoR4fsMpVQx
VltZV1xaWF1cWmBfXmJgX2RiYGZlY2dmZGppZ2tqaG1ram9tbHFwb3Jwb3Rz MBkYC+QDQ4DBQDtQ7XmEviUESqnBwAKgFHgaGAP0iRC5AWwDtojI1e8FAUqp4cBzwExgBpAVRexb
cXV0c3Z0c3Z1c3Z1dHd1dHh2dXh3dnt5eHx7eXx7en18en59e4B/foGAf4KB 4E/AJhG52n1XRP4vL2Ag8DOgFmgDJMYVBPYCT0ct51FXJEbl+gLpMb77IbAZuGpS6fBVD6wys6W+
f4SDgYWEgoWEg4eGhIiHhouKiI2Mio6Ni46NjJCQj5KRkJSTkZeWlpiXlpmY C4OgUiodeBGYC4zHaN1OoAU4CxzGaOVFwHR69ulo+Bb4HfCmiNw1lfwOtPY84AzWral7HQfKtO0/
l5qZmJybmp6dnKCfnqGgoKKhoKOioaSjoqinp6qpqKurqq+urbCvrrCwr7Gw 4sqvA+66VPFO4PeA15YPj7Dyz4VC1Y3KtwIvOvLjEVW+CPi3TuUyMzOtZC4DJY59eQSVTwL26VR+
r7OysbW1tLi3tri3t7u6ur28vMTDw8TEw8XFxMbFxcfGxsfHxsrJycrKyczM 48aNcu/ePXn++edjyVwAiuPy5xEQ8Gvdlr97966IiLz99tvRZPzA2Hj9sXqcuAql1CLgVR3Ze/fu
y83My83MzM3NzdDQz9LR0dPS0tPT09fX19jY19ra2dvb29zc29zc3Ojn5+jo ce7cOUSE/fv39/oaeFVEzsft1ENs+TwggI3BbcSIEVJcXBztu39iEFkBpGra7xtN9qEkQqFE568Y
6Orq6uzs7O/v7/T09PX19fb29vf39/r6+vv7+/7+/v////////////////// SYzbuAU0YHQJP0aGeBODKICRQAHQCPxCHqzwQ2r93+BeomM3NzgCVMXyLeERoJSaDewC0hJqyEAb
//////////////////////////////////////////////////////////// xpPhLHAa+LuInDBTSCgBSql+wCGMEEwULgN/AfYDB0Xkti3tBIf+H0hQeCulBKgG+n4nH4NKqXnA
//////////////////////////////////////////////////////////// mkSUnZmZyaZNmxgyZMhgEbkTV2EJavlU4CQWrejz+SQjI0O71VNTU2XBggXyxRdfiIhIZWXlN0BK
//////////////////////////////////////////////////////////// PL4makpsIzDBSmjz5s3Mnz+fQ4cOceHCBQKBAM3Nzdy+fZs7d+6glGLAgAH4fD6Ki4uZNWsWeXl5
//////////////////////////////////////////////////////////// 3fqTJk3K2rt372xgt2NPE9D6+RhvZ6atmZubK/fv35d48MknnwiwOR5/EzEGvAn0txKqqqoiOTk5
//////////////////////////////////////////////////////////// LkPPPvss/fv3/1E8ZbhKgFJqDjBfR7a0tDRue16vl/z8/LGhx60juEaAUkoBvwTrcSUzM5NnnnnG
//////////////////////////////////////////////////////////// Fbv5+flpQLlTfTcj4KdoOpKXl0f//pa9RAsFBQUAE53qu0KAUioF2KArP2DAADfMAvDkk0+CMZPs
/////////////////////yH5BAEKAP8ALAAAAAAXACAAAAj+AP8JHEiwoMGD CG5FwAsY01xaSE9Pd8ksFBUV4fF4xjrVj5sApVQSsNqOjsfjXvqRnZ3NsGHDRimlHPUpNyJgNTb7
CAcusRAAQEKDBQIcEBAAwUODAQJAsBGAwsWCBzJuUBLgI0ENGVM2dACg5UWV YEtLiwtm/4fs7OxUYIoT3bgICI38tvN9v98fTppcwdChQwFynejGGwFVwDS7Si0tLWzdupWvv/46
KU+Y/JfRQBknPoq8ATQz4wxOQIFa6vMx5ZSgQetczJDSClKgcF6mFDEnE9I2 TvMGfD4fwAgnuvESsNKJUjAY5JVXXmHMmDEsXLiQHTt2EAwGHTsxePBggKGOlOPI+Qsxdlm48n4/
D0fADOChUdA1D7dmTBEUTditDQRQAnomIQaxICpoAmomoUoAGS2YIBIUDEIu depU+fjjjx29E7zzzjsC1DqqRxwEbHGr8uHL4/HI+vXrpb293RYB27dvF+DUQyMAYweGzvq8o2vR
YndI8FAJaBaEMlIuSEkloxugUBBOSLkh44AvGfkAPYJQpYqMLIQEILB205DO okXS0dGhTUBNTY0ATU7q4nQMWAYMc6hriZ07d/LGG29oy2dkZAD0VUrZTjCcEqD1xhcPtm7dyqVL
KW9kJHMhQAmgkaKgzsgjggM5GbEAxaNmdoAPOoz8CCAgEVAtg3wPEPMnQQAU l7RkQwRkAo/ZtWObAKVULhDXO7gOWltb2blzp5as1+sFoy62nwROImA54F4yH4GsrJ6buw4fPqyl
QWsg5AAzDZSMbIBeaoHwAUwSDAI2XMAENA8ThAPEBvAStEkc3yonrOW0aUMk 16dPdzUG2bXpJCmf50DHEsOHD+f+/fs97ukmShEE2J5ishUBSqnxaEx2OsG4ceO4detWj3t37ujN
+BkBVAlaKATC8Fsp8Igid5ABgxMHtaTgggy6ZFBAADs= } eEcQoOzatdsFFuAsaizh8/no6Ojoca+wsFBLNyJykuzatUvATLsGdPH444/3ujdtmt5rRkSk2B7T
tBWUUqOBqXYN6CAjI4ORI0f2uJeWlkZlZaWWfnt7u2Pbdhj7McaKj+soKirq1f8rKysZN26cln4E
Affs2rZDQIndwnUxceJEmpqaetxbvHixtn4EAbZDQYuAUIqZMAImTJhAY2Njj3vNzc3a+q2treGP
5ttio0A3AmbicMLBCklJSZSUlBAIBHrcf++997h586ZWGRHd5xu79nUJcGcVIwrGjx9PVlYWly9f
7nH//PnzvPbaa1plhIj6Frhu174uAZPsFBp6OdFCWVkZx44d65UFAmzbto13333XsoxQF7glIu6P
AUqpZGxmf6E5Oi2Ul5dTX18f9btgMMgHH3xgWUYoZb5lJRcNOhEwA+idpZggNEenJTd37lzOnTsX
UyY7O9uynKtXr4KxNc42dAiwFf4Ajz2m91peXl5OWloaZ86ciSnzYIL0IILBYPiJ8R8bLnZDh4Bi
u4XqLn3NmDGDixcvmkbAE088YVpGY2NjeAxwtNqiQ8BTtgrs00dr8TM1NZXKykoOHDjQ6yUoEqNG
jTItp66uLjyl7miRwZQApdQQYLSdAlNTU7UImDx5Mjk5OXz55ZcxZbxeLxMnmq+6RUybuU8Axgkt
W/l/V1eX1mOwoqICwJSAwsJCBg0yn+SJyB+u6foYCSsCbIU/GNPsVk+B5ORkqqqqOH36tOkAOGmS
9fgbigABHG2dtyIgz+L7Xujs7CQzM9NUpqSkhKKiInbv3k1nZ2dMueJi6/H34sWLYGSA/7LnqQEr
An5gt0ARYeDAgabdYN48Y1px3759MWWSkpKYPt18d73f76ehoQHgiog4WlyMSYBSqg8OCAAjCmK9
y2dlZbF06VIaGxv5/PPPY5ZRUFDA2LHmGz+OHj0ajiC/Ez/BPALGYTMDDOPatWuMHx992878+fMZ
MWIENTU1pjM5s2bNsrQTMX40OHATMCcgHwezrGAMTLEIWLZsGQA1NTWmZcyePdvSTl1dXfjjBTv+
9YDJAujPcbi4WVVVJUeOHOl1f/LkyRIMBuX48ePi8Xhi6o8ePVq6urpMF0SDwaCMGjVKME6HFzpZ
GLVaHHW8+NnQ0MC0adN6bGwGWLVqFUopPvzwQ9PRv6KiInKuPyrq6+u5cuUKQBPG2WNnMImA7TiM
gJSUFAkEAvLyyy9338vJyZG2tjbp6OiQ3NxcU/29e/daLom///77Yfm/OW19qwhwtuUEY6HiwIED
PQaylStX4vV6qa6uDj+6omLChAnMmTPH0kbEABjf2UGTCDhHHJscNmzYIB0dHZKdnS3Dhg2T69ev
i4hIeXm5qd7rr79u2foiImVlZWGd9fFEQNRlrtAskOMIAOMZ7fF4mDlzJkOHDsXn81FbW8uhQ4di
6ni9XpYuXWpZ9vXr1zlxovsw2Nl4/IzV+k/hwn6furo6qa2tlRs3boiIyOLFi011Fi5cqNX61dXV
YZ2baJ4cjRnpMQj4SbwEAPLWW291O338+HFJS0szla+pqdEiYN26dWGdA/FU3oyAF9wgoKKiotvp
JUuWmMpOmTJFgsGgFgFFRUVhvd8mioBfuUFAcnKynDx5Ug4fPiwpKSmmslu2bNGqfH19vSQlJYX1
VsZLQKy1fsdHUCLR0dHBRx99xNmzZ6PO+4cxcuRIVq/W23D+6aef0tXVBXAf41RqXIhFQLRfYnKE
6upqy93hK1asoG/fvlrlHT16NPyxXkT8cTkHMbvATlzoAjpXv379xO/3a4V/V1dXOP8X4I/xhr+Y
ZIKuRYAVli9frrX4AcYEit/vD/970g37sQiwveHQCdLT03nppZe05ffs2RP+KMA/XHEiRheIKw3W
vZYvX64V+mEUFhaGdS8ROvof7xWLgOZEVz4pKUk+++wz7cofPHgwfGRegB1uVD7qGKCUSuUhdIG5
c+dSVlamLb9nz55w44Bb4U/0McBHgrbCRmLt2rW25CNmkIPAQdcciQj7EmATxs9RJDT8S0tLtdNe
EZFTp05FTqHVuxX+IoJHKTUIeBtYgsuHqWNh7dq1GAfO9LBr167IKbTYc+kO8WceUtIDSEFBga3T
ICIi06dPjyzjBVcjANiKsQBiex+AE6xZsybmydG2tja++uorrly5QlNTE4FAgEAgwLFjx8IiHRg/
xOYalIiEDz+vBVZgbIdNSFfIyclh9+7dNDU1dVcw8q/f76etrc2siBMiMtlNn3r9jpBSqhSYjTEo
5mNMjztaIHkQKSkpdHZ2Wp0RvI2x2NkS+tsc+nsNqBMRV8cA0x9SCh2NHYtxRnA0MBwYgJEnhC+z
17h2jP27d0Kf2zH2893G2NNzM3S1YvzAYgDj9Ffsd2eX8V/DpporbFKohAAAAABJRU5ErkJggg== }
wm iconphoto . -default rpicon wm iconphoto . -default rpicon
} }
@@ -79,6 +110,19 @@ if {$tcl_platform(platform) == "windows"} {
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
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@@ -172,9 +216,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 ""
@@ -258,11 +299,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. Array of tags is stored in ReminderTags()
set HighestTagSoFar 0 set HighestTagSoFar 0
# Check Remind version
set ver [GetRemindVersion]
if {"$ver" < "04.03.00"} {
tk_dialog .error Error "This version of TkRemind requires Remind version 04.03.00 or newer; you have version $ver" error 0 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]]
@@ -281,6 +330,10 @@ 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
} }
@@ -1249,7 +1302,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}
@@ -1435,7 +1488,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
} }
@@ -1492,9 +1546,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}
} }
@@ -1515,7 +1567,7 @@ proc DoGoto {} {
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
} }
@@ -1524,19 +1576,14 @@ 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] } { if { [tk_dialog .question "Confirm..." {Really quit?} question 0 No Yes] } {
destroy . destroy .
StopBackgroundRemindDaemon StopBackgroundRemindDaemon
catch { exec kill [pid $InotifyFP] }
catch { close $InotifyFP }
exit 0 exit 0
} }
} }
@@ -2567,7 +2614,6 @@ proc BrowseForFileRead {w {dir ""}} {
cd $cwd cd $cwd
$w.entry delete 0 end $w.entry delete 0 end
} }
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# StartBackgroundRemindDaemon # StartBackgroundRemindDaemon
# Arguments: # Arguments:
@@ -2580,9 +2626,9 @@ 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 -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 -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 tk_dialog .error Error "Can't start Remind daemon in background: $err" error 0 OK
@@ -2638,19 +2684,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
@@ -2661,26 +2707,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
} }
@@ -2707,43 +2761,63 @@ 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]
IssueBackgroundReminder $body $time $now $tag
}
"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
@@ -2752,26 +2826,14 @@ proc DaemonReadable { file } {
# 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 } {
global BgCounter Option Ignore global BgCounter Option Ignore
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
} }
@@ -2786,17 +2848,17 @@ 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]]
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]
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]
if {$tag != "*"} { if {$tag != "*"} {
button $w.nomore -text "Don't remind me again today" -command [list ClosePopup $w $after_token "" 1 "ignore" $tag $msg $time] button $w.nomore -text "Don't remind me again today" -command [list ClosePopup $w $after_token "" 1 "ignore" $tag $body $time]
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]
} }
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
@@ -2814,17 +2876,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
}
} }
#*********************************************************************** #***********************************************************************
@@ -2870,7 +2926,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
@@ -2889,7 +2945,6 @@ proc main {} {
CreateCalWindow $DayNames CreateCalWindow $DayNames
FillCalWindow FillCalWindow
StartBackgroundRemindDaemon StartBackgroundRemindDaemon
SetupInotify
DisplayTimeContinuously DisplayTimeContinuously
} }
@@ -3709,7 +3764,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 {
@@ -3921,30 +3976,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
@@ -4017,13 +4048,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]"
} }
@@ -4175,9 +4210,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)/icons
-mkdir -p $(DESTDIR)$(prefix)/applications
$(INSTALL_DATA) $(srcdir)/../resources/tkremind.png $(DESTDIR)$(prefix)/icons
$(INSTALL_PROGRAM) $(srcdir)/../resources/tkremind.desktop $(DESTDIR)$(prefix)/applications
-if test "$(DESTDIR)" = ""; then \
update-desktop-database < /dev/null > /dev/null 2>&1 ; \
xdg-icon-resource install --novendor --size 64 $(DESTDIR)$(prefix)/icons/tkremind.png < /dev/null > /dev/null 2>&1; \
xdg-desktop-menu install --novendor $(DESTDIR)$(prefix)/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

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];
} }
@@ -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 */
/* */ /* */
/***************************************************************/ /***************************************************************/

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 */
/* */ /* */
/***************************************************************/ /***************************************************************/

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,16 +192,18 @@ 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;
} }
} else { } else {
/* Parse the rest of the line to catch any potential /* Parse the rest of the line to catch any potential
expression-pasting errors */ expression-pasting errors */
while (ParseChar(p, &r, 0)) { if (ParseUntriggered) {
if (r != 0) { while (ParseChar(p, &r, 0)) {
break; if (r != 0) {
break;
}
} }
} }
} }
@@ -348,6 +350,16 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
if (r) return r; if (r) return r;
break; break;
/* A time implicitly introduces an AT if AT is not explicit */
case T_Time:
DBufFree(&buf);
if (tim->ttime != NO_TIME) return E_TIME_TWICE;
tim->ttime = tok.val;
r = ParseTimeTrig(s, tim, save_in_globals);
if (r) return r;
trig->duration_days = ComputeTrigDuration(tim);
break;
case T_At: case T_At:
DBufFree(&buf); DBufFree(&buf);
r=ParseTimeTrig(s, tim, save_in_globals); r=ParseTimeTrig(s, tim, save_in_globals);
@@ -898,7 +910,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];
@@ -965,7 +977,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) &&
@@ -1032,11 +1044,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 */
@@ -1132,18 +1151,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? */
@@ -1182,7 +1210,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 {
@@ -1190,29 +1218,31 @@ 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;
/* If there's a "warn" function, it overrides any deltas */ /* If there's a "warn" function, it overrides any deltas except
* DeltaOverride*/
if (t->warn[0] != 0) { if (t->warn[0] != 0) {
if (DeltaOffset) { if (DeltaOverride > 0) {
if (dse <= DSEToday + DeltaOffset) { if (dse <= DSEToday + DeltaOverride) {
return 1; return 1;
} }
} }
return ShouldTriggerBasedOnWarn(t, dse, err); return ShouldTriggerBasedOnWarn(t, dse, err);
} }
/* Zero delta */
if (DeltaOverride < 0) {
return dse == DSEToday;
}
/* Move back by delta days, if any */ /* Move back by delta days, if any */
if (t->delta != NO_DELTA) { if (DeltaOverride) {
if (t->delta < 0) /* A positive DeltaOverride takes precedence over everything */
dse = dse - DeltaOverride;
} else if (t->delta != NO_DELTA) {
if (t->delta < 0)
dse = dse + t->delta; dse = dse + t->delta;
else { else {
int iter = 0; int iter = 0;
@@ -1237,7 +1267,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
} }
/* Should we trigger the reminder? */ /* Should we trigger the reminder? */
return (dse <= DSEToday + DeltaOffset); return (dse <= DSEToday);
} }
/***************************************************************/ /***************************************************************/
@@ -1368,7 +1398,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;
@@ -1405,7 +1435,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,7 +46,7 @@
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;
@@ -894,7 +894,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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -129,6 +129,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 +169,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 +193,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",

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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -944,7 +944,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 +965,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;
} }

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 '['

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>
@@ -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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -251,7 +251,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 +289,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 +1421,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 +2364,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 +2413,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 +2500,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));
@@ -3290,6 +3298,8 @@ FSlide(func_info *info)
{ {
int r, omit, d, i, localomit, amt; int r, omit, d, i, localomit, amt;
Token tok; Token tok;
int step = 1;
int localargs = 2;
if (!HASDATE(ARG(0))) return E_BAD_TYPE; if (!HASDATE(ARG(0))) return E_BAD_TYPE;
ASSERT_TYPE(1, INT_TYPE); ASSERT_TYPE(1, INT_TYPE);
@@ -3299,8 +3309,13 @@ FSlide(func_info *info)
if (amt > 1000000) return E_2HIGH; if (amt > 1000000) return E_2HIGH;
if (amt < -1000000) return E_2LOW; if (amt < -1000000) return E_2LOW;
if (Nargs > 2 && ARG(2).type == INT_TYPE) {
step = ARGV(2);
if (step < 1) return E_2LOW;
localargs++;
}
localomit = 0; localomit = 0;
for (i=2; i<Nargs; i++) { for (i=localargs; i<Nargs; i++) {
if (ARG(i).type != STR_TYPE) return E_BAD_TYPE; if (ARG(i).type != STR_TYPE) return E_BAD_TYPE;
FindToken(ARG(i).v.str, &tok); FindToken(ARG(i).v.str, &tok);
if (tok.type != T_WkDay) return E_UNKNOWN_TOKEN; if (tok.type != T_WkDay) return E_UNKNOWN_TOKEN;
@@ -3311,14 +3326,14 @@ FSlide(func_info *info)
if ((WeekdayOmits | localomit) == 0x7F && amt != 0) return E_2MANY_LOCALOMIT; if ((WeekdayOmits | localomit) == 0x7F && amt != 0) return E_2MANY_LOCALOMIT;
if (amt > 0) { if (amt > 0) {
while(amt) { while(amt) {
d++; d += step;
r = IsOmitted(d, localomit, NULL, &omit); r = IsOmitted(d, localomit, NULL, &omit);
if (r) return r; if (r) return r;
if (!omit) amt--; if (!omit) amt--;
} }
} else { } else {
while(amt) { while(amt) {
d--; d -= step;
if (d < 0) return E_DATE_OVER; if (d < 0) return E_DATE_OVER;
r = IsOmitted(d, localomit, NULL, &omit); r = IsOmitted(d, localomit, NULL, &omit);
if (r) return r; if (r) return r;
@@ -3335,6 +3350,8 @@ FNonomitted(func_info *info)
{ {
int d1, d2, ans, localomit, i; int d1, d2, ans, localomit, i;
int omit, r; int omit, r;
int step = 1;
int localargs = 2;
Token tok; Token tok;
if (!HASDATE(ARG(0)) || if (!HASDATE(ARG(0)) ||
@@ -3343,10 +3360,20 @@ FNonomitted(func_info *info)
} }
d1 = DATEPART(ARG(0)); d1 = DATEPART(ARG(0));
d2 = DATEPART(ARG(1)); d2 = DATEPART(ARG(1));
if (d2 < d1) return E_2LOW; if (d2 < d1) {
i = d1;
d1 = d2;
d2 = i;
}
/* Check for a "step" argument - it's an INT */
if (Nargs > 2 && ARG(2).type == INT_TYPE) {
step = ARGV(2);
if (step < 1) return E_2LOW;
localargs++;
}
localomit = 0; localomit = 0;
for (i=2; i<Nargs; i++) { for (i=localargs; i<Nargs; i++) {
if (ARG(i).type != STR_TYPE) return E_BAD_TYPE; if (ARG(i).type != STR_TYPE) return E_BAD_TYPE;
FindToken(ARG(i).v.str, &tok); FindToken(ARG(i).v.str, &tok);
if (tok.type != T_WkDay) return E_UNKNOWN_TOKEN; if (tok.type != T_WkDay) return E_UNKNOWN_TOKEN;
@@ -3355,11 +3382,12 @@ FNonomitted(func_info *info)
ans = 0; ans = 0;
while (d1 < d2) { while (d1 < d2) {
r = IsOmitted(d1++, localomit, NULL, &omit); r = IsOmitted(d1, localomit, NULL, &omit);
if (r) return r; if (r) return r;
if (!omit) { if (!omit) {
ans++; ans++;
} }
d1 += step;
} }
RetVal.type = INT_TYPE; RetVal.type = INT_TYPE;
RETVAL = ans; RETVAL = ans;
@@ -3554,6 +3582,8 @@ rows_or_cols(func_info *info, int want_rows)
struct winsize w; struct winsize w;
int fd = STDOUT_FILENO; int fd = STDOUT_FILENO;
int opened = 0;
RetVal.type = INT_TYPE; RetVal.type = INT_TYPE;
if (!isatty(fd)) { if (!isatty(fd)) {
fd = open("/dev/tty", O_RDONLY); fd = open("/dev/tty", O_RDONLY);
@@ -3561,6 +3591,7 @@ rows_or_cols(func_info *info, int want_rows)
RETVAL = -1; RETVAL = -1;
return OK; return OK;
} }
opened = 1;
} }
if (ioctl(fd, TIOCGWINSZ, &w) == 0) { if (ioctl(fd, TIOCGWINSZ, &w) == 0) {
if (want_rows) RETVAL = w.ws_row; if (want_rows) RETVAL = w.ws_row;
@@ -3568,7 +3599,7 @@ rows_or_cols(func_info *info, int want_rows)
} else { } else {
RETVAL = -1; RETVAL = -1;
} }
if (fd != STDOUT_FILENO) { if (opened) {
close(fd); close(fd);
} }
return OK; return OK;
@@ -3737,7 +3768,7 @@ solstice_equinox_for_year(int y, int which)
j -= 2447892.50000; /* This is the Julian date of midnight, 1 Jan 1990 UTC */ j -= 2447892.50000; /* This is the Julian date of midnight, 1 Jan 1990 UTC */
int dse = (int) j; int dse = (int) j;
int min = floor((j - (double) dse) * MINUTES_PER_DAY); int min = (int) floor((j - (double) dse) * MINUTES_PER_DAY);
int ret; int ret;
/* Convert from UTC to local time */ /* Convert from UTC to local time */
@@ -3785,8 +3816,10 @@ FSoleq(func_info *info)
} }
ret = solstice_equinox_for_year(y, which); ret = solstice_equinox_for_year(y, which);
if (ret < 0) return E_MKTIME_PROBLEM;
if (dse != NO_DATE && (ret / MINUTES_PER_DAY) < dse) { if (dse != NO_DATE && (ret / MINUTES_PER_DAY) < dse) {
ret = solstice_equinox_for_year(y+1, which); ret = solstice_equinox_for_year(y+1, which);
if (ret < 0) return E_MKTIME_PROBLEM;
} }
RetVal.type = DATETIME_TYPE; RetVal.type = DATETIME_TYPE;
RETVAL = ret; RETVAL = ret;

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

@@ -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);
@@ -70,7 +71,7 @@ EXTERN INIT( int Hush, 0);
EXTERN INIT( int NextMode, 0); EXTERN INIT( int NextMode, 0);
EXTERN INIT( int InfiniteDelta, 0); EXTERN INIT( int InfiniteDelta, 0);
EXTERN INIT( int DefaultTDelta, 0); EXTERN INIT( int DefaultTDelta, 0);
EXTERN INIT( int DeltaOffset, 0); EXTERN INIT( int DeltaOverride, 0);
EXTERN INIT( int RunDisabled, 0); EXTERN INIT( int RunDisabled, 0);
EXTERN INIT( int IgnoreOnce, 0); EXTERN INIT( int IgnoreOnce, 0);
EXTERN INIT( int SortByTime, 0); EXTERN INIT( int SortByTime, 0);
@@ -78,7 +79,8 @@ 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 char const *InitialFile; EXTERN char const *InitialFile;
EXTERN int FileAccessDate; EXTERN int FileAccessDate;
@@ -90,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);
@@ -100,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);
@@ -155,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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -25,10 +25,11 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <poll.h>
#include <pwd.h> #include <pwd.h>
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <termios.h>
#ifdef HAVE_INITGROUPS #ifdef HAVE_INITGROUPS
#include <grp.h> #include <grp.h>
@@ -40,6 +41,14 @@
#include "expr.h" #include "expr.h"
#include "err.h" #include "err.h"
static int should_guess_terminal_background = 1;
static void guess_terminal_background(int *r, int *g, int *b);
static int tty_init(int fd);
static void tty_raw(int fd);
static void tty_reset(int fd);
static void ProcessLongOption(char const *arg);
/*************************************************************** /***************************************************************
* *
* Command line options recognized: * Command line options recognized:
@@ -219,7 +228,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 */
@@ -243,6 +252,11 @@ void InitRemind(int argc, char const *argv[])
while(*arg) arg++; while(*arg) arg++;
break; break;
case '-':
ProcessLongOption(arg);
while(*arg) arg++;
break;
case '@': case '@':
UseVTColors = 1; UseVTColors = 1;
if (*arg) { if (*arg) {
@@ -259,16 +273,24 @@ void InitRemind(int argc, char const *argv[])
if (*arg == ',') { if (*arg == ',') {
arg++; arg++;
if (*arg != ',') { if (*arg != ',') {
PARSENUM(x, arg); if (*arg == 't') {
if (x == 0) { arg++;
TerminalBackground = TERMINAL_BACKGROUND_DARK; should_guess_terminal_background = 2;
} else if (x == 1) {
TerminalBackground = TERMINAL_BACKGROUND_LIGHT;
} else if (x == 2) {
TerminalBackground = TERMINAL_BACKGROUND_UNKNOWN;
} else { } else {
fprintf(ErrFp, "%s: -@n,m,b: m must be 0, 1 or 2 (assuming 2)\n", PARSENUM(x, arg);
argv[0]); if (x == 0) {
should_guess_terminal_background = 0;
TerminalBackground = TERMINAL_BACKGROUND_DARK;
} else if (x == 1) {
should_guess_terminal_background = 0;
TerminalBackground = TERMINAL_BACKGROUND_LIGHT;
} else if (x == 2) {
should_guess_terminal_background = 0;
TerminalBackground = TERMINAL_BACKGROUND_UNKNOWN;
} else {
fprintf(ErrFp, "%s: -@n,m,b: m must be t, 0, 1 or 2 (assuming 2)\n",
argv[0]);
}
} }
} }
} }
@@ -341,10 +363,15 @@ void InitRemind(int argc, char const *argv[])
} else if (!*arg) { } else if (!*arg) {
InfiniteDelta = 1; InfiniteDelta = 1;
} else { } else {
PARSENUM(DeltaOffset, arg); if (*arg == 'z') {
if (DeltaOffset < 0) { DeltaOverride = -1;
DeltaOffset = 0; arg++;
} } else {
PARSENUM(DeltaOverride, arg);
if (DeltaOverride < 0) {
DeltaOverride = 0;
}
}
} }
break; break;
case 'e': case 'e':
@@ -398,7 +425,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;
@@ -532,7 +563,7 @@ void InitRemind(int argc, char const *argv[])
arg++; arg++;
/* -wt means get width from /dev/tty */ /* -wt means get width from /dev/tty */
ttyfd = open("/dev/tty", O_RDONLY); ttyfd = open("/dev/tty", O_RDONLY);
if (!ttyfd) { if (ttyfd < 0) {
fprintf(stderr, "%s: `-wt': Cannot open /dev/tty: %s\n", fprintf(stderr, "%s: `-wt': Cannot open /dev/tty: %s\n",
argv[0], strerror(errno)); argv[0], strerror(errno));
} else { } else {
@@ -543,7 +574,12 @@ void InitRemind(int argc, char const *argv[])
PARSENUM(CalWidth, arg); PARSENUM(CalWidth, arg);
if (CalWidth != 0 && CalWidth < 71) CalWidth = 71; if (CalWidth != 0 && CalWidth < 71) CalWidth = 71;
if (CalWidth == 0) { if (CalWidth == 0) {
CalWidth = -1; /* Cal width of 0 means obtain from stdout */
if (isatty(STDOUT_FILENO)) {
InitCalWidthAndFormWidth(STDOUT_FILENO);
} else {
CalWidth = 80;
}
} }
FormWidth = CalWidth - 8; FormWidth = CalWidth - 8;
if (FormWidth < 20) FormWidth = 20; if (FormWidth < 20) FormWidth = 20;
@@ -721,7 +757,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);
} }
@@ -735,7 +771,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
@@ -953,3 +989,139 @@ AddTrustedUser(char const *username)
NumTrustedUsers++; NumTrustedUsers++;
} }
static void
ProcessLongOption(char const *arg)
{
if (!strcmp(arg, "version")) {
printf("%s\n", VERSION);
exit(EXIT_SUCCESS);
}
fprintf(ErrFp, "%s: Unknown long option --%s\n", ArgV[0], arg);
}
static void
guess_terminal_background(int *r, int *g, int *b)
{
int ttyfd;
struct pollfd p;
int rr, gg, bb;
char buf[128];
int n;
*r = -1;
*g = -1;
*b = -1;
/* Don't guess if stdout not a terminal unless asked to by @,t */
if (should_guess_terminal_background != 2) {
if (!isatty(STDOUT_FILENO)) {
return;
}
}
ttyfd = open("/dev/tty", O_RDWR);
if (ttyfd < 0) {
return;
}
if (!isatty(ttyfd)) {
/* Not a TTY: Can't guess the color */
close(ttyfd);
return;
}
if (!tty_init(ttyfd)) {
return;
}
tty_raw(ttyfd);
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 */
p.fd = ttyfd;
p.events = POLLIN;
if (poll(&p, 1, 100) < 0) {
tty_reset(ttyfd);
close(ttyfd);
return;
}
if (!(p.revents & POLLIN)) {
tty_reset(ttyfd);
close(ttyfd);
return;
}
n = read(ttyfd, buf, 127);
if (n <= 0) {
tty_reset(ttyfd);
close(ttyfd);
return;
}
tty_reset(ttyfd);
buf[n+1] = 0;
if (n < 25) {
/* Too short */
return;
}
if (sscanf(buf+5, "rgb:%x/%x/%x", &rr, &gg, &bb) != 3) {
/* Couldn't scan color codes */
return;
}
*r = (rr >> 8) & 255;
*g = (gg >> 8) & 255;
*b = (bb >> 8) & 255;
}
static struct termios orig_termios;
static int
tty_init(int fd)
{
if (tcgetattr(fd, &orig_termios) < 0) {
return 0;
}
return 1;
}
static void
tty_raw(int fd)
{
struct termios raw;
raw = orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* put terminal in raw mode after flushing */
tcsetattr(fd,TCSAFLUSH,&raw);
}
static void
tty_reset(int fd)
{
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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -254,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 (%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 */
@@ -228,7 +228,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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -17,11 +17,11 @@
/* Day names */ /* Day names */
#define L_SUNDAY "Domenica" #define L_SUNDAY "Domenica"
#define L_MONDAY "Lunedí" #define L_MONDAY "Lunedì"
#define L_TUESDAY "Martedí" #define L_TUESDAY "Martedì"
#define L_WEDNESDAY "Mercoledí" #define L_WEDNESDAY "Mercoledì"
#define L_THURSDAY "Giovedí" #define L_THURSDAY "Giovedì"
#define L_FRIDAY "Venerdí" #define L_FRIDAY "Venerdì"
#define L_SATURDAY "Sabato" #define L_SATURDAY "Sabato"
/* Month names */ /* Month names */
@@ -68,7 +68,7 @@
#define L_AT "alle" #define L_AT "alle"
#define L_MINUTE "minut" #define L_MINUTE "minut"
#define L_HOUR "or" #define L_HOUR "or"
#define L_IS "é" #define L_IS "è"
#define L_WAS "era" #define L_WAS "era"
#define L_AND "e" #define L_AND "e"
/* What to add to make "hour" plural */ /* What to add to make "hour" plural */

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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -244,7 +244,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 */
@@ -253,7 +253,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>
@@ -26,16 +28,10 @@
#endif #endif
#include <ctype.h> #include <ctype.h>
#ifdef TIME_WITH_SYS_TIME #if defined(HAVE_SYS_TIME_H)
#include <time.h>
#include <sys/time.h> #include <sys/time.h>
#else #endif
#if defined(HAVE_SYS_TIME_H) || defined (TIME_WITH_SYS_TIME)
#include <sys/time.h>
#else
#include <time.h> #include <time.h>
#endif
#endif
#include <sys/types.h> #include <sys/types.h>
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
@@ -51,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)
/***************************************************************/ /***************************************************************/
/***************************************************************/ /***************************************************************/
/** **/ /** **/
@@ -187,14 +186,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) {
@@ -202,7 +201,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);
@@ -753,19 +752,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 */
@@ -777,11 +789,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;
@@ -1244,19 +1256,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++;
} }
} }
@@ -1265,19 +1277,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++;
} }
} }
@@ -1286,7 +1298,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;
@@ -1301,7 +1313,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++;
@@ -1314,7 +1326,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 */
@@ -1327,7 +1339,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++;
} }
@@ -1335,7 +1347,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);
@@ -1346,17 +1358,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;
} }
@@ -1367,7 +1379,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;
@@ -1377,7 +1389,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;
} }
@@ -1397,7 +1409,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;
@@ -1415,7 +1427,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
@@ -1425,7 +1437,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++;
@@ -1438,7 +1450,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 */
@@ -1451,7 +1463,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++;
} }
@@ -1459,7 +1471,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++;
@@ -1470,17 +1482,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;
} }
@@ -1644,11 +1656,45 @@ 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;
if (is_queued && IsServerMode()) {
/* 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 (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 */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -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 {
@@ -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);
@@ -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,28 @@ 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);
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,77 @@ 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;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -71,7 +163,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 +171,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 +183,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 +274,19 @@ 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;
/* 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 +296,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 +314,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 +340,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 +359,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);
@@ -222,14 +383,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 +405,53 @@ 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)); PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
if (!*DBufValue(&q->tags)) { PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
printf("*\n"); PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
} else { } else {
printf("%s\n", DBufValue(&(q->tags))); 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 +460,23 @@ void HandleQueuedReminders(void)
/* Calculate the next trigger time */ /* Calculate the next trigger time */
q->tt.nexttime = CalculateNextTime(q); q->tt.nexttime = CalculateNextTime(q);
/* If it's dequeued, update num_queued */
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 we have dequeued a reminder, update controlling process */
if (q->tt.nexttime == NO_TIME && IsServerMode()) {
print_num_queued();
}
} }
exit(0); exit(EXIT_SUCCESS);
} }
@@ -474,6 +672,9 @@ 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("[");
while(q) { while(q) {
if (q->tt.nexttime == NO_TIME) { if (q->tt.nexttime == NO_TIME) {
@@ -485,6 +686,12 @@ json_queue(QueuedRem const *q)
} }
done = 1; done = 1;
printf("{"); printf("{");
WriteJSONTrigger(&(q->t), 1, DSEToday);
WriteJSONTimeTrigger(&(q->tt));
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 +704,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 +717,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 +737,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 +766,101 @@ 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 { } 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 +877,47 @@ 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;
/* 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);
/* Consume all the inotify events */
while(1) {
n = read(fd, buf, sizeof(buf));
if (n < 0) {
if (errno == EINTR) continue;
return;
}
}
}
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;
@@ -324,8 +327,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 +394,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 */
/* */ /* */
/***************************************************************/ /***************************************************************/

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)
{ {
@@ -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
@@ -792,7 +812,7 @@ static SysVar SysVarArr[] = {
{"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0 }, {"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0 },
{"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 }, {"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 },
{"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440 }, {"DefaultTDelta", 1, INT_TYPE, &DefaultTDelta, 0, 1440 },
{"DeltaOffset", 0, INT_TYPE, &DeltaOffset, 0, 0 }, {"DeltaOverride", 0, INT_TYPE, &DeltaOverride, 0, 0 },
{"DontFork", 0, INT_TYPE, &DontFork, 0, 0 }, {"DontFork", 0, INT_TYPE, &DontFork, 0, 0 },
{"DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 }, {"DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 },
{"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 }, {"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 },
@@ -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,10 +858,13 @@ 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 },
{"On", 1, STR_TYPE, &DynamicOn, 0, 0 }, {"On", 1, STR_TYPE, &DynamicOn, 0, 0 },
{"ParseUntriggered", 1, INT_TYPE, &ParseUntriggered, 0, 1 },
{"Pm", 1, STR_TYPE, &DynamicPm, 0, 0 }, {"Pm", 1, STR_TYPE, &DynamicPm, 0, 0 },
{"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 }, {"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 },
{"PSCal", 0, INT_TYPE, &PsCal, 0, 0 }, {"PSCal", 0, INT_TYPE, &PsCal, 0, 0 },
@@ -855,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 },
@@ -893,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);
@@ -940,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);
@@ -1033,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
@@ -408,6 +417,18 @@ TZ=Europe/Berlin ../src/remind -dxe ../tests/tz.rem >> ../tests/test.out 2>&1
# Test that banner is printed on every iteration # Test that banner is printed on every iteration
echo "MSG Should be three banners." | ../src/remind - 2022-10-20 '*3' >> ../tests/test.out 2>&1 echo "MSG Should be three banners." | ../src/remind - 2022-10-20 '*3' >> ../tests/test.out 2>&1
# Test the -tn option
echo "REM May 23 +10 MSG Orange %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 +10 MSG Quux %b" | ../src/remind -t1 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 +10 MSG Cabbage %b" | ../src/remind -t2 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Banana %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Carrot %b" | ../src/remind -t1 - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 23 MSG Apple %b" | ../src/remind -t2 - 2023-05-21 >> ../tests/test.out 2>&1
# Test the -tz option
echo "REM May 22 +10 MSG Foo %b" | ../src/remind - 2023-05-21 >> ../tests/test.out 2>&1
echo "REM May 22 +10 MSG Bar %b" | ../src/remind -tz - 2023-05-21 >> ../tests/test.out 2>&1
# World-writable file # World-writable file
rm -rf include_dir/ww rm -rf include_dir/ww
touch include_dir/ww touch include_dir/ww
@@ -425,6 +446,99 @@ rm -rf include_dir/ww
# This segfaulted in 04.02.03 # This segfaulted in 04.02.03
../src/remind -h '-imsgprefix(x)="foo"' /dev/null >> ../tests/test.out 2>&1 ../src/remind -h '-imsgprefix(x)="foo"' /dev/null >> ../tests/test.out 2>&1
# Test --version long option
../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' >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind -zj ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/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 - >> ../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)
@@ -482,7 +484,7 @@ msg [$December]%
msg [$DefaultColor]% msg [$DefaultColor]%
msg [$DefaultPrio]% msg [$DefaultPrio]%
msg [$DefaultTDelta]% msg [$DefaultTDelta]%
msg [$DeltaOffset]% msg [$DeltaOverride]%
msg [$DontFork]% msg [$DontFork]%
msg [$DontQueue]% msg [$DontQueue]%
msg [$DontTrigAts]% msg [$DontTrigAts]%
@@ -881,6 +883,33 @@ set a htmlstriptags("This is <unclosed whut?")
set a htmlstriptags("this is > whut <b>foo</b>") set a htmlstriptags("this is > whut <b>foo</b>")
set a htmlstriptags("<img src=\"foo\">") set a htmlstriptags("<img src=\"foo\">")
# $ParseUntriggered
REM 2 Jan 1990 MSG ["bad_expr" / 2]
SET $ParseUntriggered 0
REM 2 Jan 1990 MSG ["bad_expr" / 2]
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)
# Don't want Remind to queue reminders # Don't want Remind to queue reminders
EXIT EXIT

View File

@@ -26,6 +26,25 @@ REM 12 AUG SPECIAL MOON 0
# Test nonomitted # Test nonomitted
REM MSG [nonomitted('2007-08-01', today())] NonOmit-1 REM MSG [nonomitted('2007-08-01', today())] NonOmit-1
REM MSG [nonomitted('2007-08-01', today(), "Sat", "Sun")] NonOmit-2 REM MSG [nonomitted('2007-08-01', today(), "Sat", "Sun")] NonOmit-2
REM MSG [nonomitted('2007-08-01', '2007-08-30', 7)]
REM MSG [nonomitted('2007-08-01', '2007-08-29', 7)]
REM MSG [nonomitted('2007-08-28', '2007-08-01', 7)]
PUSH-OMIT-CONTEXT
OMIT 2007-08-15
REM MSG [nonomitted('2007-08-01', '2007-08-30', 7)]
REM MSG [nonomitted('2007-08-01', '2007-08-29', 7)]
REM MSG [nonomitted('2007-08-01', '2007-08-28', 7)]
POP-OMIT-CONTEXT
# Test slide
REM MSG [slide('2007-08-03', 4)]
REM MSG [slide('2007-08-03', 4, 7)]
PUSH-OMIT-CONTEXT
OMIT 2007-08-17
REM MSG [slide('2007-08-03', 4, 7)]
POP-OMIT-CONTEXT
# Test SPECIAL COLOR with an AT clause # Test SPECIAL COLOR with an AT clause
REM 20 AUG AT 13:45 SPECIAL COLOR 6 7 8 Mooo! REM 20 AUG AT 13:45 SPECIAL COLOR 6 7 8 Mooo!

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);
?>