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
pm_to_blib
rem2html/Makefile
rem2html/rem2html
rem2html/rem2html.1
rem2pdf/Makefile.PL
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
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.
3. DISTRIBUTION AND USE
@@ -16,7 +16,7 @@ individual files.
Version 2, June 1991
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
of this license document, but changing it is not allowed.

View File

@@ -44,7 +44,7 @@ test:
@$(MAKE) -C src -s test
distclean: clean
rm -f config.cache config.log config.status src/Makefile src/config.h tests/test.out www/Makefile rem2pdf/Makefile.top rem2pdf/Makefile.old rem2pdf/Makefile rem2pdf/Makefile.PL rem2pdf/bin/rem2pdf
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
./configure

24
build.tk Normal file → Executable file
View File

@@ -27,10 +27,10 @@ exec wish "$0" "$@"
proc SetConfigDefaults {} {
global Config
set Config(LAT_DEG) 45
set Config(LAT_MIN) 24
set Config(LAT_MIN) 25
set Config(LAT_SEC) 14
set Config(LON_DEG) 75
set Config(LON_MIN) 39
set Config(LON_MIN) 41
set Config(LON_SEC) 23
set Config(LOCATION) "Ottawa"
set Config(DEFAULT_PAGE) "Letter"
@@ -190,8 +190,8 @@ proc CreateLocationDialog { w } {
grid $w.north $w.west
grid $w.south $w.east
grid $w.loclab -sticky e
grid $w.location -sticky nsew -row 6 -column 1
grid $w.loclab -sticky e
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
CreateCustomH
.msgs insert end ">>> Calling `./configure'...\n\n" green
.msgs insert end "\n>>> Calling `./configure'...\n\n" green
CallConfigure
.msgs insert end ">>> Calling `make'...\n\n" green
.msgs insert end "\n>>> Calling `make'...\n\n" green
CallMake
.msgs insert end "\n----------------------------------------------\n\n"
.msgs insert end "Remind" red
@@ -447,12 +447,12 @@ proc CreateCustomH {} {
"#define DEFAULT_LATITUDE *" {
set lat [expr $LAT_DEG + ($LAT_MIN/60.0) + ($LAT_SEC/3600.0)];
puts $out "#define DEFAULT_LATITUDE $lat"
.msgs insert end "#define DEFAULT_LATITUDE $lat"
.msgs insert end "#define DEFAULT_LATITUDE $lat\n"
}
"#define DEFAULT_LONGITUDE *" {
set lon [expr -1.0 * ($LON_DEG + ($LON_MIN/60.0) + ($LON_SEC/3600.0))]
puts $out "#define DEFAULT_LONGITUDE $lon"
.msgs insert end "#define DEFAULT_LONGITUDE $lon"
.msgs insert end "#define DEFAULT_LONGITUDE $lon\n"
}
"#define LOCATION *" {
puts $out "#define LOCATION \"$Config(LOCATION)\""
@@ -506,7 +506,13 @@ proc CallMake {} {
"Icelandic" { set lang ICELANDIC }
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.
AC_INIT(src/queue.c)
AC_INIT
AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF'
@@ -12,7 +13,7 @@ cat <<'EOF'
EOF
AC_CONFIG_HEADER(src/config.h)
AC_CONFIG_HEADERS([src/config.h])
AC_ARG_ENABLE(perl-build-artifacts,
[ --disable-perl-build-artifacts
@@ -29,32 +30,32 @@ AC_PATH_PROG([PERL], [perl])
dnl Checks for libraries.
AC_CHECK_LIB(m, sqrt)
AC_CHECK_HEADERS_ONCE([sys/time.h])
dnl Integer sizes
AC_CHECK_SIZEOF(unsigned int)
AC_CHECK_SIZEOF(unsigned long)
AC_CHECK_SIZEOF(time_t)
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.
AC_STRUCT_TM
dnl Checks for library functions.
AC_FUNC_UTIME_NULL
AC_HEADER_TIME
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes"
# Check for link-time optimization support
f=-flto=auto
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])
CFLAGS="$CFLAGS $f"
f=-ffat-lto-objects
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])
CFLAGS="$CFLAGS $f"
else
@@ -65,6 +66,12 @@ if test "$GCC" = yes; then
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
PERLARTIFACTS=
else
@@ -79,11 +86,13 @@ if test "$?" != 0 ; then
echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!"
exit 1
fi
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups)
VERSION=04.02.05
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1)
VERSION=04.03.00
AC_SUBST(VERSION)
AC_SUBST(PERL)
AC_SUBST(PERLARTIFACTS)
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

View File

@@ -19,8 +19,7 @@
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
;;; 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-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 'fill-column) '100);cause I was having problems with autofill.
(set (make-local-variable 'indent-line-function) 'remind-indent-line)

View File

@@ -1,5 +1,181 @@
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
- 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
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 [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
# it: light.
bg_dark=1
# Set your latitude and longitude correctly for Sunrise/Sunset/Equinox/Solstice
#
# The values below are for Ottawa, Ontario, Canada
latitude="45.420556"
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
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)].%_
IF bg_dark
IF $TerminalBackground == 0
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%_
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
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
BANNER %
IF bg_dark
IF $TerminalBackground == 0
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(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)
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(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)
@@ -51,7 +50,7 @@ EOF
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
BANNER %

View File

@@ -16,7 +16,7 @@
# "#PSSTUFF" for nifty PostScript examples #
# #
# 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
# #
#############################################################################

View File

@@ -2,7 +2,7 @@
# Not all sequences are supported by all terminals.
# 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
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
# 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
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.
# 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.
SET $Sunday "Søndag"

View File

@@ -1,6 +1,6 @@
# Support for the German language.
# 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
# Day names

View File

@@ -1,4 +1,4 @@
# Support for the English language.
# 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.

View File

@@ -1,6 +1,6 @@
# Support for the Spanish language.
# 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>
SET $Sunday "Domingo"

View File

@@ -1,6 +1,6 @@
# Support for the Finnish language.
# 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
SET $Sunday "sunnuntai"

View File

@@ -1,6 +1,6 @@
# Support for the French language.
# 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
SET $Sunday "dimanche"

View File

@@ -1,6 +1,6 @@
# Support for the Hellenic (Greek) language.
# 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)
SET $Sunday "Κυριακή"

View File

@@ -1,6 +1,6 @@
# Support for the Icelanding language.
# 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)
SET $Sunday "sunnudagur"

View File

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

View File

@@ -1,6 +1,6 @@
# Support for the Dutch language.
# 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
SET $Sunday "zondag"

View File

@@ -1,6 +1,6 @@
# Support for the Norwegian language.
# 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
SET $Sunday "Søndag"

View File

@@ -1,6 +1,6 @@
# Support for the Polish language.
# 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
SET $Sunday "Niedziela"

View File

@@ -1,6 +1,6 @@
# Support for the (Brazilian) Portuguese language.
# 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
SET $Sunday "domingo"

View File

@@ -1,6 +1,6 @@
# Support for the Romanian language.
# 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
SET $Sunday "Duminică"

View File

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

View File

@@ -5,22 +5,25 @@ rem2ps \- draw a PostScript calendar from Remind output
.SH SYNOPSIS
.B rem2ps [\fIoptions\fR]
.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
emits PostScript code (which draws a calendar) to the standard output.
.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
back-ends.
.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
\fBrem2pdf\fR instead.
.SH OPTIONS
.TP
.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.
.TP
.B \-p file
@@ -133,7 +136,7 @@ numbers.
.PP
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
is usually Letter for North America and A4 for Europe. The "\-m help"
option will display the compiled-in default.
@@ -193,8 +196,8 @@ for good output:
rem2ps \-ol 72 \-sh 12
.fi
.SH USAGE
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
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
illustrated with examples:
.PP
.nf
@@ -228,10 +231,10 @@ calendar entries. This border is normally blank space.
.TP
BoxWidth and BoxHeight
The width and height of the calendar box, from center-to-center of the
black gridlines.
black grid lines.
.TP
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 box is used only to draw the day number.
.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
structuring comments in your PostScript code.
.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:
.PP
.nf
@@ -320,14 +323,14 @@ For an example, create a file called "myprolog" whose contents are:
} bind def
.fi
.PP
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
Use that file with the \fBrem2ps\fR \fB\-p\fR option to create calendars
with the year and month in large gray letters in the background of the
calendar.
.PP
.SH REM2PS INPUT FORMAT (-P OPTION)
The \fB\-p\fR option is an older, simpler interchange format used by
\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
older format contains enough information for them to work properly.
.PP
@@ -380,7 +383,7 @@ been set to "-". The consistent use of "/" is designed to ease parsing.
.PP
\fIspecial\fR is a string used
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.
Other back-ends may understand other specials. A back end should
\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:
.TP
.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
.TP
.B filename \fIf\fR
@@ -674,17 +677,17 @@ is desired.
.SH AUTHOR
Rem2PS was written by Dianne Skoll <dianne@skoll.ca>
rem2ps was written by Dianne Skoll <dianne@skoll.ca>
.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
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
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.
.PP
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.
.SH HOME PAGE
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,
ignore them for now and skip to the section "REMINDER FILES".
.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
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
@@ -60,7 +64,9 @@ weeks to be produced.
.B 'a'
causes \fBRemind\fR to display reminders on the calendar on the
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
.B 'l'
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
\fBRemind\fR will brighten up dark colors to make them visible. If
\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
is supplied, or it is supplied as \fIm\fR=2, then \fBRemind\fR does
not perform any adjustments, and some reminders may be hard or
impossible to see if the color is too close to the terminal background
color.
and it will darken bright colors to make them visible. If \fIm\fR is
specified as 2, then \fBRemind\fR does not perform any adjustments,
and some reminders may be hard or impossible to see if the color is
too close to the terminal background color. If you supply the letter
\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
If the optional \fIb\fR parameter is supplied following a comma, then
\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
.B \-t\fR\fIn\fR
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
of \fIn\fR days and triggers reminders accordingly.
\fBRemind\fR pretends that echo \fBREM\fR command has a delta
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
.B \-tt\fR[\fIn\fR]
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.
.PP
.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
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
.TP
\fB\-u\fR\fIname\fR
@@ -602,6 +624,11 @@ The following examples show how date specifications are interpreted.
.PP
1. Null date specification - the reminder is triggered every day.
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
2. Only
.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
background, and activates timed reminders when the system time reached
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
to be associated with a reminder (eg, in the calendar) but are not
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
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
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
.B THE SCHED AND WARN KEYWORDS
.PP
@@ -1680,6 +1709,11 @@ For example, the following sequences are equivalent:
OMIT 3 Jan 2011 THROUGH 5 Jan 2011
.fi
.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
long as both dates are fully specified
.PP
@@ -1782,7 +1816,7 @@ will begin reading from standard input.
.PP
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
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".
.PP
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.
.TP
.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
.B /
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
explicit delta of +0 in an AT clause to countermand the default delta.
.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)
If non-zero, then the \fB\-c\fR option was supplied on the command line.
.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.
.TP
.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
.B $IntMax (read-only)
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.
.RE
.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
The maximum number of iterations for the \fBSATISFY\fR clause
(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
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
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
.B $MinsFromUTC
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)
If non-zero, then the \fB\-n\fR option was supplied on the command line.
.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)
Contains the number of reminders queued so far for background
timed triggering.
@@ -2577,6 +2651,28 @@ by \fBREM\fR commands; triggers in \fBIFTRIG\fR commands do
not affect it.
.RE
.TP
.B $ParseUntriggered
A flag indicating whether or not \fBRemind\fR should fully parse \fBREM\fR
statements that are not triggered. 0 means to skip parsing them and 1
(the default) means to parse them.
.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)
If non-zero, then the \fB\-l\fR option was supplied on the command line.
.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.
.TP
.B $TerminalBackground (read-only)
Returns -1 if the terminal background color was not specified,
0 if it was specified as dark with the \fB\-@,0\fR option or
1 if it was specified as light with the \fB\-@,1\fR option.
Returns -1 if the terminal background color could not be determined, 0
if it was found to be dark (or was specified as dark with the
\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
Note: If any of the calendar modes are in effect, then the
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
\fIdate\fR is omitted, defaults to \fBtoday()\fR.
.TP
.B easterdate(dqi_arg)
.B easterdate([dqi_arg])
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
\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
.P
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
\fIdate\fR is omitted, defaults to \fBtoday()\fR.
.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
\fIstart\fR and \fIend\fR. If \fIstart\fR is non-omitted, then it is
counted. \fIend\fR is never counted.
.RS
.PP
Note that \fIend\fR must be greater than or equal to \fIstart\fR or an
error is reported. In addition to using the global OMIT context, you
Note that if \fIend\fR is less than \fIstart\fR, the arguments
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
omitted. However, in a \fBREM\fR command, any local \fBOMITFUNC\fR
clause is \fInot\fR taken into account by this function.
@@ -3292,7 +3410,7 @@ reminder will label day numbers in a calendar:
.fi
.PP
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
Here is a more complex use for \fBnonomitted\fR. My garbage collection
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,
\fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th".
.TP
.B orthodoxeaster(dqi_arg)
.B orthodoxeaster([dqi_arg])
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
\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
.P
Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western
@@ -3486,10 +3605,13 @@ will set \fBa\fR to:
.fi
.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
days (which can be negative) to \fIstart\fR, \fInot counting omitted days\fR.
The optional \fIwkday\fR arguments are additional weekday names to omit.
(which can be negative) chunks of \fIstep\fR days to \fIstart\fR,
\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
.PP
Consider this example:
@@ -3509,6 +3631,26 @@ May 16 and 17. You can go backwards, too, so:
.fi
.PP
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
.TP
.B soleq(i_which [, dqi_start])
@@ -3527,7 +3669,7 @@ not supplied, then it defaults to \fBtoday()\fR.
.PP
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
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
See the included file \fB$SysInclude/seasons.rem\fR for examples of how
to use \fBsoleq()\fR.
@@ -4216,7 +4358,7 @@ you define a function taking no parameters. Here are some examples:
.nf
FSET double(x) 2*x
FSET yeardiff(date1, date2) year(date1) - year(date2)
FSET since(x) ord(year(trigdate())\-x)
FSET since(x) ord($Ty \- x)
.fi
.PP
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.
.fi
.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:
.TP
o
@@ -5112,7 +5260,7 @@ A number of system variables let you translate various phrases
to other languages. These system variables are:
.PP
.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
day's name in your language. Strings must be valid UTF-8 strings.
.TP
@@ -5418,13 +5566,13 @@ the anniversary of a death is. The following rules are used:
o
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
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.
.TP
o
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
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.
.TP
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.
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
equivalent of MSG in \fBremind's\fR normal mode of operation.)
equivalent of MSG in \fBRemind's\fR normal mode of operation.)
.PP
The various \fBSPECIAL\fRs recognized are particular for each
backend; however, there are four \fBSPECIAL\fRs that all backends
@@ -5538,7 +5686,7 @@ after the WEEK keyword.
.PP
.SH MISCELLANEOUS
.PP
.B COMMAND ABBREVIATIONS
.B COMMAND AND KEYWORD ABBREVIATIONS
.PP
The following tokens can be abbreviated:
.TP
@@ -5565,6 +5713,9 @@ o
\fBINCLUDE\fR --> \fBINC\fR
.TP
o
\fBMAYBE-UNCOMPUTABLE\fR --> \fBMAYBE\fR
.TP
o
\fBSCANFROM\fR --> \fBSCAN\fR
.PP
.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.
Note that this behaviour is not true for
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
.PP
Dianne Skoll <dianne@skoll.ca> wrote \fBRemind\fR. The moon code
@@ -5784,9 +5954,11 @@ Liviu Daia
Rafa Couto
.PP
\fBIcelandic\fR --
Bj\(:orn Dav\('i\[Sd]sson
Bj\[:o]rn Daví\[Sd]sson
.SH BUGS
.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
implemented as functions, or why functions like \fBversion()\fR, etc.
are not implemented as read-only system variables.
@@ -5807,8 +5979,13 @@ Almanac Office, USNO.
.PP
Richard Siegel and Michael and Sharon Strassfeld, \fIThe First Jewish
Catalog\fR, Jewish Publication Society of America.
.PP
Jean Meeus, \fIAstronomical Algorithms, Second Edition\fR, Willmann-Bell, Inc.
.SH HOME PAGE
https://dianne.skoll.ca/projects/remind/
.SH MAILING LIST
https://dianne.skoll.ca/mailman/listinfo/remind-fans
.SH SEE ALSO
.PP
\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,
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.
\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.
.SH OPTIONS
@@ -301,11 +301,11 @@ Today
.SH IMMEDIATE UPDATES
If you are running \fBTkRemind\fR on Linux and have the
\fBinotifywait\fR program installed (part of the \fBinotify-tools\fR
or similar package), then \fBTkRemind\fR redraws the calendar window
\fIimmediately\fR if \fB$HOME/.reminders\fR changes (or, if it is a
directory, any files in that directory change.)
If you are running \fBTkRemind\fR on Linux and \fBRemind\fR has been
compiled with \fBinotify\fR(7) support, then \fBTkRemind\fR redraws
the calendar window \fIimmediately\fR if \fB$HOME/.reminders\fR
changes (or, if it is a directory, any files in that directory
change.)
.PP
This lets \fBTkRemind\fR react immediately to hand-edited reminders or
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
\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
it reads commands (one per line)
from standard input and writes status lines to standard output.
it reads commands (one per line) from standard input and writes status
lines to standard output. Each status line is a JSON object.
The commands accepted in server mode are:
.TP
EXIT
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
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
REREAD
Re-read the reminder file
Re-read the reminder file. Returns the following status line:
.nf
{"response":"reread","command":"REREAD"}
.fi
.PP
The status lines written are as follows:
Additional status lines written are as follows:
.TP
NOTE reminder \fItime\fR \fItag\fR
Signifies the beginning of a timed reminder whose trigger time is
\fItime\fR with tag \fItag\fR. If the reminder has no tag, an
asterisk is supplied for \fItag\fR. All lines following this line
are the body of the reminder, until the line \fBNOTE endreminder\fR
is transmitted.
.nf
{"response":"reminder","ttime":tt,"now":now,"tags":tags,"body":body}
.fi
In this line, \fItt\fR is the trigger time of the reminder (expressed
as a string), \fInow\fR is the current time, \fItags\fR (if present)
is the tag or tags associated with the reminder, and \fIbody\fR is
the body of the reminder. This response causes \fBTkRemind\fR to
pop up a reminder notification.
.TP
NOTE newdate
.nf
{"response":"newdate"}
.fi
This line is emitted whenever \fBRemind\fR has detected a rollover of
the system date. The front-end program should redraw its calendar
or take whatever other action is needed.
.TP
NOTE reread
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.
.nf
.TP
NOTE queued \fIn\fR
This line is emitted in response to a \fBSTATUS\fR command. The number
\fIn\fR is the number of reminders in the queue.
{"response":"reread","command":"inotify"}
.fi
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
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

View File

@@ -6,10 +6,11 @@ use warnings;
use Getopt::Long;
use JSON::MaybeXS;
use Encode;
my %Options;
my $rem2html_version = '2.1';
my $rem2html_version = '@VERSION@';
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
@Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);
@@ -44,6 +45,11 @@ Print usage information
Print version
=item --utf8
Assume standard input is encoded in UTF-8; write UTF-8 data to standard
output.
=item --backurl I<url>
When producing the small calendar for the previous month, make the
@@ -154,6 +160,7 @@ Usage: remind -pp ... | rem2html [options]
Options:
--help, -h Print usage information
--utf8 Assume UTF-8 input and write UTF-8 output
--man Show man page (requires "perldoc")
--version Print version
--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"; };
if (!GetOptions(\%Options, "help|h",
"man",
"utf8",
"pngs",
"version",
"stylesheet=s",
@@ -216,13 +224,22 @@ sub parse_options
if ($stylesheet) {
$Options{stylesheet} = smoosh($Options{imgbase}, $stylesheet);
}
if ($Options{utf8}) {
binmode(STDIN, ':encoding(UTF-8)');
binmode(STDOUT, ':encoding(UTF-8)');
}
}
sub start_output
{
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{stylesheet}) {
print('<link rel="stylesheet" type="text/css" href="' .
@@ -310,7 +327,12 @@ sub parse_input
($y, $m, $d, $special, $tag, $duration, $time, $body) =
($1, $2, $3, $4, $5, $6, $7, $8);
} 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+)$/);
$y = $1;
$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
#
# 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
catch {
image create photo rpicon -data {
R0lGODlhFwAgAOecABUTERYTERYUERcVEhgWExkXFBkXFRoXFRsZFhwZFxwa
GB0bGR4cGR4cGh8dGiAeHCEfHCEfHSIgHSIgHiQiHyYkISknJCooJispJywq
Jy4sKTIwLjUzMDUzMTo4Njs5Nzs5ODw7ODw7OT07OT48OkE/PUJAPkNBP0RC
QEVDQUVEQkdFQ0lIRkpJR01LSU5MSlBPTVFQTlNSUFRSUFRSUVVTUlVUUllY
VltZV1xaWF1cWmBfXmJgX2RiYGZlY2dmZGppZ2tqaG1ram9tbHFwb3Jwb3Rz
cXV0c3Z0c3Z1c3Z1dHd1dHh2dXh3dnt5eHx7eXx7en18en59e4B/foGAf4KB
f4SDgYWEgoWEg4eGhIiHhouKiI2Mio6Ni46NjJCQj5KRkJSTkZeWlpiXlpmY
l5qZmJybmp6dnKCfnqGgoKKhoKOioaSjoqinp6qpqKurqq+urbCvrrCwr7Gw
r7OysbW1tLi3tri3t7u6ur28vMTDw8TEw8XFxMbFxcfGxsfHxsrJycrKyczM
y83My83MzM3NzdDQz9LR0dPS0tPT09fX19jY19ra2dvb29zc29zc3Ojn5+jo
6Orq6uzs7O/v7/T09PX19fb29vf39/r6+vv7+/7+/v//////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////////////yH5BAEKAP8ALAAAAAAXACAAAAj+AP8JHEiwoMGD
CAcusRAAQEKDBQIcEBAAwUODAQJAsBGAwsWCBzJuUBLgI0ENGVM2dACg5UWV
KU+Y/JfRQBknPoq8ATQz4wxOQIFa6vMx5ZSgQetczJDSClKgcF6mFDEnE9I2
D0fADOChUdA1D7dmTBEUTditDQRQAnomIQaxICpoAmomoUoAGS2YIBIUDEIu
YndI8FAJaBaEMlIuSEkloxugUBBOSLkh44AvGfkAPYJQpYqMLIQEILB205DO
KW9kJHMhQAmgkaKgzsgjggM5GbEAxaNmdoAPOoz8CCAgEVAtg3wPEPMnQQAU
QWsg5AAzDZSMbIBeaoHwAUwSDAI2XMAENA8ThAPEBvAStEkc3yonrOW0aUMk
+BkBVAlaKATC8Fsp8Igid5ABgxMHtaTgggy6ZFBAADs= }
iVBORw0KGgoAAAANSUhEUgAAAEAAAABbCAYAAADDeIOGAAAACXBIWXMAAAu6AAALugFBTNueAAAA
GXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAADANJREFUeJzdnGtsFNcVx38Xr19r
4/BawjPGjcGY2MY8Q+1g05iXKwoihvAQoCJEQoiU0CofWgmpUhIJNVKpSGnUSGmKLEEihcZFQeBW
gRJeKQkQisFAsWDxGsdAawjgGGF7Tz/MrrvGuzN3ZmchzV8aeT17zj3n/u+5d87cxyoR4fsMpVQx
MBkYC+QDQ4DBQDtQ7XmEviUESqnBwAKgFHgaGAP0iRC5AWwDtojI1e8FAUqp4cBzwExgBpAVRexb
4E/AJhG52n1XRP4vL2Ag8DOgFmgDJMYVBPYCT0ct51FXJEbl+gLpMb77IbAZuGpS6fBVD6wys6W+
C4OgUiodeBGYC4zHaN1OoAU4CxzGaOVFwHR69ulo+Bb4HfCmiNw1lfwOtPY84AzWral7HQfKtO0/
4sqvA+66VPFO4PeA15YPj7Dyz4VC1Y3KtwIvOvLjEVW+CPi3TuUyMzOtZC4DJY59eQSVTwL26VR+
48aNcu/ePXn++edjyVwAiuPy5xEQ8Gvdlr97966IiLz99tvRZPzA2Hj9sXqcuAql1CLgVR3Ze/fu
ce7cOUSE/fv39/oaeFVEzsft1ENs+TwggI3BbcSIEVJcXBztu39iEFkBpGra7xtN9qEkQqFE568Y
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
CG5FwAsY01xaSE9Pd8ksFBUV4fF4xjrVj5sApVQSsNqOjsfjXvqRnZ3NsGHDRimlHPUpNyJgNTb7
YEtLiwtm/4fs7OxUYIoT3bgICI38tvN9v98fTppcwdChQwFynejGGwFVwDS7Si0tLWzdupWvv/46
TvMGfD4fwAgnuvESsNKJUjAY5JVXXmHMmDEsXLiQHTt2EAwGHTsxePBggKGOlOPI+Qsxdlm48n4/
depU+fjjjx29E7zzzjsC1DqqRxwEbHGr8uHL4/HI+vXrpb293RYB27dvF+DUQyMAYweGzvq8o2vR
okXS0dGhTUBNTY0ATU7q4nQMWAYMc6hriZ07d/LGG29oy2dkZAD0VUrZTjCcEqD1xhcPtm7dyqVL
l7RkQwRkAo/ZtWObAKVULhDXO7gOWltb2blzp5as1+sFoy62nwROImA54F4yH4GsrJ6buw4fPqyl
16dPdzUG2bXpJCmf50DHEsOHD+f+/fs97ukmShEE2J5ishUBSqnxaEx2OsG4ceO4detWj3t37ujN
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
}
@@ -79,6 +110,19 @@ if {$tcl_platform(platform) == "windows"} {
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
#---------------------------------------------------------------------------
@@ -172,9 +216,6 @@ set ConfigFile ""
set EditorPid -1
# Inotify file
set InotifyFP ""
# Errors from last remind run
set RemindErrors ""
@@ -258,11 +299,19 @@ set Option(PrintSmallCalendars) 1
set OptDescr(PrintFormat) "Print format: pdf or 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()
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 } {
global EnglishDayNames
return [lindex $EnglishDayNames [clock format [clock scan $yyyymmdd] -format %w -locale C]]
@@ -281,6 +330,10 @@ proc is_warning_header { line } {
if {"$line" == "$h"} {
return 1
}
# Ignore prior typo line too
if {"$line" == "# Lines staring with REM TAG TKTAGnnn ... were created by tkremind"} {
return 1
}
}
return 0
}
@@ -1249,7 +1302,7 @@ proc Status { stuff } {
# None
#---------------------------------------------------------------------------
proc DoPrint {} {
global Rem2PS Rem2PDF HaveRem2PDF PSCmd Option PrintStatus
global Rem2PS Rem2PDF HaveRem2PDF PSCmd Option PrintStatus RemindErrors
global CurMonth CurYear MonthNames
catch {destroy .p}
@@ -1435,7 +1488,8 @@ proc DoPrint {} {
append cmd " $fname"
Status "Printing..."
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
}
@@ -1492,9 +1546,7 @@ proc GotoDialog {} {
bind .g <KeyPress-Escape> ".g.b.cancel flash; .g.b.cancel invoke"
CenterWindow .g .
set oldFocus [focus]
grab .g
focus .g.y.e
tkwait window .g
catch {focus $oldFocus}
}
@@ -1515,7 +1567,7 @@ proc DoGoto {} {
set month [lsearch -exact $MonthNames [.g.mon cget -text]]
set CurMonth $month
set CurYear $year
destroy .g
catch { destroy .g }
FillCalWindow
}
@@ -1524,19 +1576,14 @@ proc DoGoto {} {
#---------------------------------------------------------------------------
proc Quit {} {
global Option
global InotifyFP
if { !$Option(ConfirmQuit) } {
destroy .
StopBackgroundRemindDaemon
catch { exec kill [pid $InotifyFP] }
catch { close $InotifyFP }
exit 0
}
if { [tk_dialog .question "Confirm..." {Really quit?} question 0 No Yes] } {
destroy .
StopBackgroundRemindDaemon
catch { exec kill [pid $InotifyFP] }
catch { close $InotifyFP }
exit 0
}
}
@@ -2567,7 +2614,6 @@ proc BrowseForFileRead {w {dir ""}} {
cd $cwd
$w.entry delete 0 end
}
#---------------------------------------------------------------------------
# StartBackgroundRemindDaemon
# Arguments:
@@ -2580,9 +2626,9 @@ proc BrowseForFileRead {w {dir ""}} {
proc StartBackgroundRemindDaemon {} {
global Remind DaemonFile ReminderFile Option 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 {
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} {
tk_dialog .error Error "Can't start Remind daemon in background: $err" error 0 OK
@@ -2638,19 +2684,19 @@ proc RestartBackgroundRemindDaemon {} {
#---------------------------------------------------------------------------
# ShowQueue
# Arguments:
# file -- file channel that is readable
# queue - the queue
# Returns:
# nothing
# Description:
# Dumps the debugging queue listing
#---------------------------------------------------------------------------
proc ShowQueue { file } {
proc ShowQueue { queue } {
set w .queuedbg
catch { destroy $w }
toplevel $w
wm title $w "Queue (Debugging Output)"
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"
button $w.ok -text "OK" -command "destroy $w"
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 1 -weight 0
CenterWindow $w .
while (1) {
# We should only get one line
gets $file line
if {$line == "NOTE ENDJSONQUEUE"} {
break
}
if {[catch {set obj [::json::json2dict $line]}]} {
continue;
}
set obj [lsort -command sort_q $obj]
foreach q $obj {
$w.t insert end "$q\n"
}
set obj [lsort -command sort_q $queue]
set did 0
$w.t tag configure grey -background "#DDDDDD" -selectbackground "#999999"
set toggle 0
foreach q $obj {
if { $did > 0 } {
$w.t insert end "\n"
}
foreach r $q {
if { $toggle != 0 } {
$w.t insert end "$r " grey
} 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
}
proc sort_q { a b } {
set a_ttime [dict get $a nextttime]
set b_ttime [dict get $b nextttime]
set a_ttime [dict get $a nexttime]
set b_ttime [dict get $b nexttime]
if {$a_ttime < $b_ttime} {
return -1
}
@@ -2707,43 +2761,63 @@ proc DaemonReadable { file } {
catch { close $file }
return
}
switch -glob -- $line {
"NOTE reminder*" {
scan $line "NOTE reminder %s %s %s" time now tag
IssueBackgroundReminder $file $time $now $tag
}
"NOTE JSONQUEUE" {
ShowQueue $file
}
"NOTE newdate" {
# Date has rolled over -- clear "ignore" list
catch { unset Ignore}
Initialize
FillCalWindow
ShowTodaysReminders
}
"NOTE reread" {
puts $file "STATUS"
flush $file
}
"NOTE queued*" {
scan $line "NOTE queued %d" n
if {[catch {set obj [::json::json2dict $line]}]} {
return;
}
if (![dict exists $obj response]) {
return;
}
set response [dict get $obj response]
switch -- $response {
"queued" {
set n [dict get $obj nqueued]
if {$n == 1} {
.b.nqueued configure -text "1 reminder queued"
} else {
.b.nqueued configure -text "$n reminders queued"
}
}
default {
puts stderr "Unknown message from daemon: $line\n"
}
}
"reminder" {
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
# Arguments:
# file -- file channel that is readable
# body -- body of reminder
# time -- time of reminder
# now -- current time according to Remind daemon
# tag -- tag for reminder, or "*" if no tag
@@ -2752,26 +2826,14 @@ proc DaemonReadable { file } {
# Description:
# 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
if {$Option(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.
if {$msg == ""} {
if {$body == ""} {
return
}
@@ -2786,17 +2848,17 @@ proc IssueBackgroundReminder { file time now tag } {
wm iconname $w "Reminder"
wm title $w "Timed reminder ($time)"
label $w.l -text "Reminder for $time issued at $now"
message $w.msg -width 6i -text $msg
message $w.msg -width 6i -text $body
frame $w.b
# 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]
button $w.ok -text "OK" -command [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 $body $time]
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.kill -text "Delete this reminder completely" -command [list ClosePopup $w $after_token "" 1 "kill" $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 $body $time]
}
pack $w.l -side top
pack $w.msg -side top -expand 1 -fill both
@@ -2814,17 +2876,11 @@ proc IssueBackgroundReminder { file time now tag } {
}
if {$Option(RunCmd) != ""} {
if {$Option(FeedReminder)} {
FeedReminderToCommand $Option(RunCmd) "$time: $msg"
FeedReminderToCommand $Option(RunCmd) "$time: $body"
} else {
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
catch {
puts "\nTkRemind Copyright (C) 1996-2021 Dianne Skoll"
puts "\nTkRemind Copyright (C) 1996-2024 Dianne Skoll"
}
catch { SetFonts }
Initialize
@@ -2889,7 +2945,6 @@ proc main {} {
CreateCalWindow $DayNames
FillCalWindow
StartBackgroundRemindDaemon
SetupInotify
DisplayTimeContinuously
}
@@ -3709,7 +3764,7 @@ proc DoMoonSpecial { n stuff fntag day } {
# Displays current date and time in status window
#***********************************************************************
proc DisplayTime {} {
global TwentyFourHourMode
global TwentyFourHourMode DaemonFile
if {$TwentyFourHourMode} {
set msg [clock format [clock seconds] -format "%e %b %Y %H:%M"]
} else {
@@ -3921,30 +3976,6 @@ proc SetFonts {} {
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
set Balloon(HelpTime) 400
set Balloon(StayTime) 3500
@@ -4017,13 +4048,17 @@ bind Balloon <Destroy> {
catch { unset Balloon(helptext%W) }
}
proc balloon_add_help { w txt } {
proc balloon_set_help { w txt } {
global Balloon
if {"$txt" == ""} {
catch { unset Balloon(helptext$w) }
return
}
set Balloon(helptext$w) $txt
}
proc balloon_add_help { w txt } {
balloon_set_help $w $txt
bindtags $w "Balloon [bindtags $w]"
}
@@ -4175,9 +4210,11 @@ proc update_color_buttons { w } {
}
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}
}
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}
}

View File

@@ -63,6 +63,15 @@ install: all
done
-mkdir -p $(DESTDIR)$(datarootdir)/remind || true
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
strip $(DESTDIR)$(bindir)/remind || true

View File

@@ -5,7 +5,7 @@
/* The code for generating a calendar. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -475,7 +475,7 @@ void PrintJSONKeyPairTime(char const *name, int t)
}
#ifdef REM_USE_WCHAR
void PutWideChar(wchar_t const wc)
void PutWideChar(wchar_t const wc, DynamicBuffer *output)
{
char buf[MB_CUR_MAX+1];
int len;
@@ -483,7 +483,11 @@ void PutWideChar(wchar_t const wc)
len = wctomb(buf, wc);
if (len > 0) {
buf[len] = 0;
fputs(buf, stdout);
if (output) {
DBufPuts(output, buf);
} else {
fputs(buf, stdout);
}
}
}
#endif
@@ -556,11 +560,11 @@ static void goff(void)
static void
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 */
return;
}
if (TerminalBackground == TERMINAL_BACKGROUND_DARK) {
if (GetTerminalBackground() == TERMINAL_BACKGROUND_DARK) {
if (*r <= 64 && *g <= 64 && *b <= 64) {
int max = *r;
double factor;
@@ -579,7 +583,7 @@ ClampColor(int *r, int *g, int *b)
}
return;
}
if (TerminalBackground == TERMINAL_BACKGROUND_LIGHT) {
if (GetTerminalBackground() == TERMINAL_BACKGROUND_LIGHT) {
if (*r > 191 && *g > 191 && *b > 191) {
int min = *r;
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;
else b = 0;
if (clamp && TerminalBackground == TERMINAL_BACKGROUND_DARK && !bg) {
if (clamp && GetTerminalBackground() == TERMINAL_BACKGROUND_DARK && !bg) {
/* Convert black-on-black to grey */
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 */
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) {
/* Uh-oh... cannot recover */
fprintf(stderr, "%s\n", ErrMsg[E_NO_MEM]);
exit(1);
exit(EXIT_FAILURE);
}
}
(void) mbstowcs(buf, s, len+1);
@@ -1227,7 +1231,7 @@ static void PrintLeft(char const *s, int width, char pad)
ws = buf;
for (i=0; i<width;) {
if (*ws) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
i+= wcwidth(*ws);
} else {
break;
@@ -1235,7 +1239,7 @@ static void PrintLeft(char const *s, int width, char pad)
}
/* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
}
/* Possibly send lrm control sequence */
@@ -1297,7 +1301,7 @@ static void PrintCentered(char const *s, int width, char *pad)
if (!buf) {
/* Uh-oh... cannot recover */
fprintf(stderr, "%s\n", ErrMsg[E_NO_MEM]);
exit(1);
exit(EXIT_FAILURE);
}
}
(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<width; i++) {
if (*ws) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
if (wcwidth(*ws) == 0) {
/* Don't count this character... it's zero-width */
i--;
@@ -1319,7 +1323,7 @@ static void PrintCentered(char const *s, int width, char *pad)
}
/* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++);
PutWideChar(*ws++, NULL);
}
/* Possibly send lrm control sequence */
send_lrm();
@@ -1448,7 +1452,7 @@ static int WriteOneColLine(int col)
}
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
PutWideChar(*ws, NULL);
}
}
e->wc_pos = ws;
@@ -1463,7 +1467,7 @@ static int WriteOneColLine(int col)
if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
PutWideChar(*ws, NULL);
}
}
}
@@ -1603,7 +1607,7 @@ static void GenerateCalEntries(int col)
r=IncludeFile(InitialFile);
if (r) {
fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], InitialFile, ErrMsg[r]);
exit(1);
exit(EXIT_FAILURE);
}
while(1) {
@@ -1611,7 +1615,7 @@ static void GenerateCalEntries(int col)
if (r == E_EOF) return;
if (r) {
Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]);
exit(1);
exit(EXIT_FAILURE);
}
s = FindInitialToken(&tok, CurLine);
@@ -2211,10 +2215,114 @@ static void WriteSimpleEntryProtocol1(CalEntry *e)
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)
{
int done = 0;
char const *s;
if (DoPrefixLineNo) {
PrintJSONKeyPairString("filename", e->filename);
@@ -2234,88 +2342,13 @@ static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
PrintJSONKeyPairInt("trep", e->tt.rep);
}
}
if (e->trig.eventduration != NO_TIME) {
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);
}
WriteJSONTrigger(&e->trig, 0, today);
if (e->nonconst_expr) {
PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr);
}
if (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) {
PrintJSONKeyPairInt("r", e->r);

View File

@@ -10,6 +10,9 @@
/* Define if you have the <sys/types.h> header file. */
#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 */
#undef HAVE_GLOB_H
@@ -17,6 +20,8 @@
#undef HAVE_LOCALE_H
#undef HAVE_INOTIFY_INIT1
#undef HAVE_LANGINFO_H
#undef HAVE_GLOB

View File

@@ -6,7 +6,7 @@
/* which you can customize. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -6,7 +6,7 @@
/* which you can customize. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -7,7 +7,7 @@
/* commands. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -192,16 +192,18 @@ int DoRem(ParsePtr p)
r = OK;
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);
return r;
}
} else {
/* Parse the rest of the line to catch any potential
expression-pasting errors */
while (ParseChar(p, &r, 0)) {
if (r != 0) {
break;
if (ParseUntriggered) {
while (ParseChar(p, &r, 0)) {
if (r != 0) {
break;
}
}
}
}
@@ -348,6 +350,16 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
if (r) return r;
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:
DBufFree(&buf);
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. */
/* */
/***************************************************************/
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;
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 ((t->typ == MSG_TYPE || t->typ == MSF_TYPE)
&& !DidMsgReminder && !NextMode && !msg_command) {
&& !DidMsgReminder && !NextMode && !msg_command && !is_queued) {
DidMsgReminder = 1;
if (!DoSubstFromString(DBufValue(&Banner), &buf,
DSEToday, NO_TIME) &&
@@ -1032,11 +1044,18 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
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(&pre_buf);
DBufFree(&calRow);
return OK;
return r;
}
/* Correct colors */
@@ -1132,18 +1151,27 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
case MSG_TYPE:
case PASSTHRU_TYPE:
if (msg_command) {
DoMsgCommand(msg_command, DBufValue(&buf));
DoMsgCommand(msg_command, DBufValue(&buf), is_queued);
} 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;
case MSF_TYPE:
FillParagraph(DBufValue(&buf));
FillParagraph(DBufValue(&buf), output);
break;
case RUN_TYPE:
System(DBufValue(&buf));
System(DBufValue(&buf), is_queued);
break;
default: /* Unknown/illegal type? */
@@ -1182,7 +1210,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
if (DontIssueAts > 1) {
/* If two or more -a options, then *DO* issue ats that are in the
future */
if (tim->ttime < SystemTime(0) / 60) {
if (tim->ttime < MinutesPastMidnight(0)) {
return 0;
}
} 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 (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 (DeltaOffset) {
if (dse <= DSEToday + DeltaOffset) {
if (DeltaOverride > 0) {
if (dse <= DSEToday + DeltaOverride) {
return 1;
}
}
return ShouldTriggerBasedOnWarn(t, dse, err);
}
/* Zero delta */
if (DeltaOverride < 0) {
return dse == DSEToday;
}
/* Move back by delta days, if any */
if (t->delta != NO_DELTA) {
if (t->delta < 0)
if (DeltaOverride) {
/* A positive DeltaOverride takes precedence over everything */
dse = dse - DeltaOverride;
} else if (t->delta != NO_DELTA) {
if (t->delta < 0)
dse = dse + t->delta;
else {
int iter = 0;
@@ -1237,7 +1267,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
}
/* 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. */
/* */
/***************************************************************/
int DoMsgCommand(char const *cmd, char const *msg)
int DoMsgCommand(char const *cmd, char const *msg, int is_queued)
{
int r;
int i, l;
@@ -1405,7 +1435,7 @@ int DoMsgCommand(char const *cmd, char const *msg)
}
r = OK;
System(DBufValue(&execBuffer));
System(DBufValue(&execBuffer), is_queued);
finished:
DBufFree(&buf);

View File

@@ -6,7 +6,7 @@
/* reminders are triggered. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -46,7 +46,7 @@
int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode)
{
int diff = dse - DSEToday;
int curtime = SystemTime(0) / 60;
int curtime = MinutesPastMidnight(0);
int err, done;
int c;
int d, m, y;
@@ -894,7 +894,7 @@ int DoSubstFromString(char const *source, DynamicBuffer *dbuf,
int r;
if (dse == NO_DATE) dse=DSEToday;
if (tim == NO_TIME) tim=SystemTime(0)/60;
if (tim == NO_TIME) tim=MinutesPastMidnight(0);
CreateParser(source, &tempP);
tempP.allownested = 0;
tempTrig.typ = MSG_TYPE;

View File

@@ -6,7 +6,7 @@
/* buffers. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Declaration of functions for manipulating dynamic buffers */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Error definitions. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -129,6 +129,10 @@
#define EXTERN extern
#endif
#define STR(X) STR2(X)
#define STR2(X) #X
#ifndef L_ERR_OVERRIDE
EXTERN char *ErrMsg[]
@@ -165,7 +169,7 @@ EXTERN char *ErrMsg[]
"Number too high",
"Number too low",
"Can't open file",
"INCLUDE nested too deeply",
"INCLUDE nested too deeply (max. " STR(INCLUDE_NEST) ")",
"Parse error",
"Can't compute trigger",
"Too many nested IFs",
@@ -189,8 +193,8 @@ EXTERN char *ErrMsg[]
"Day specified twice",
"Unknown token",
"Must specify month in OMIT command",
"Too many partial OMITs",
"Too many full OMITs",
"Too many partial OMITs (max. " STR(MAX_PARTIAL_OMITS) ")",
"Too many full OMITs (max. " STR(MAX_FULL_OMITS) ")",
"Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT",
"Error reading",
"Expecting end-of-line",

View File

@@ -5,7 +5,7 @@
/* This file contains routines to parse and evaluate */
/* expressions. */
/* */
/* Copyright 1992-2023 by Dianne Skoll */
/* Copyright 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */
/***************************************************************/
@@ -944,7 +944,8 @@ static int Subtract(void)
/***************************************************************/
static int Multiply(void)
{
Value v1, v2;
Value v1, v2, v3;
char *ptr;
int r;
PopValStack(v2);
@@ -964,6 +965,61 @@ static int Multiply(void)
PushValStack(v1);
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);
return E_BAD_TYPE;
}

View File

@@ -5,19 +5,20 @@
/* Contains a few definitions used by expression evaluator. */
/* */
/* 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 */
/* */
/***************************************************************/
/* Define the types of values */
#define ERR_TYPE 0
#define INT_TYPE 1
#define TIME_TYPE 2
#define DATE_TYPE 3
#define STR_TYPE 4
#define DATETIME_TYPE 5
#define SPECIAL_TYPE 6 /* Only for system variables */
#define ERR_TYPE 0
#define INT_TYPE 1
#define TIME_TYPE 2
#define DATE_TYPE 3
#define STR_TYPE 4
#define DATETIME_TYPE 5
#define SPECIAL_TYPE 6 /* Only for system variables */
#define CONST_INT_TYPE 7 /* Only for system variables */
/* Define stuff for parsing expressions */
#define BEG_OF_EXPR '['

View File

@@ -7,7 +7,7 @@
/* files. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -15,7 +15,7 @@
#include "config.h"
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
@@ -100,6 +100,18 @@ static int CheckSafety (void);
static int CheckSafetyAux (struct stat *statbuf);
static int PopFile (void);
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)
{
DynamicBuffer fname_buf;
@@ -123,6 +135,7 @@ static void OpenPurgeFile(char const *fname, char const *mode)
if (!PurgeFP) {
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
}
set_cloexec(fileno(PurgeFP));
DBufFree(&fname_buf);
}
@@ -327,6 +340,7 @@ int OpenFile(char const *fname)
}
} else {
fp = fopen(fname, "r");
if (fp) set_cloexec(fileno(fp));
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
}
@@ -346,6 +360,7 @@ int OpenFile(char const *fname)
if (strcmp(fname, "-")) {
fp = fopen(fname, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fileno(fp));
if (PurgeMode) OpenPurgeFile(fname, "w");
} else {
fp = stdin;
@@ -542,6 +557,7 @@ static int PopFile(void)
if (strcmp(i->filename, "-")) {
fp = fopen(i->filename, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fileno(fp));
if (PurgeMode) OpenPurgeFile(i->filename, "a");
} else {
fp = stdin;

View File

@@ -6,7 +6,7 @@
/* expressions. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -251,7 +251,7 @@ BuiltinFunc Func[] = {
{ "defined", 1, 1, 0, FDefined },
{ "dosubst", 1, 3, 0, FDosubst },
{ "dusk", 0, 1, 0, FDusk },
{ "easterdate", 1, 1, 0, FEasterdate },
{ "easterdate", 0, 1, 0, FEasterdate },
{ "evaltrig", 1, 2, 0, FEvalTrig },
{ "filedate", 1, 1, 0, FFiledate },
{ "filedatetime", 1, 1, 0, FFiledatetime },
@@ -289,7 +289,7 @@ BuiltinFunc Func[] = {
{ "nonomitted", 2, NO_MAX, 0, FNonomitted },
{ "now", 0, 0, 0, FNow },
{ "ord", 1, 1, 1, FOrd },
{ "orthodoxeaster",1, 1, 0, FOrthodoxeaster },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster },
{ "ostype", 0, 0, 1, FOstype },
{ "pad", 3, 4, 1, FPad },
{ "plural", 1, 3, 1, FPlural },
@@ -1421,28 +1421,28 @@ static int FRealtoday(func_info *info)
static int FNow(func_info *info)
{
RetVal.type = TIME_TYPE;
RETVAL = (int) ( SystemTime(0) / 60L );
RETVAL = MinutesPastMidnight(0);
return OK;
}
static int FRealnow(func_info *info)
{
RetVal.type = TIME_TYPE;
RETVAL = (int) ( SystemTime(1) / 60L );
RETVAL = MinutesPastMidnight(1);
return OK;
}
static int FCurrent(func_info *info)
{
RetVal.type = DATETIME_TYPE;
RETVAL = DSEToday * MINUTES_PER_DAY + (SystemTime(0) / 60);
RETVAL = DSEToday * MINUTES_PER_DAY + MinutesPastMidnight(0);
return OK;
}
static int FRealCurrent(func_info *info)
{
RetVal.type = DATETIME_TYPE;
RETVAL = RealToday * MINUTES_PER_DAY + (SystemTime(1) / 60);
RETVAL = RealToday * MINUTES_PER_DAY + MinutesPastMidnight(1);
return OK;
}
@@ -2364,13 +2364,17 @@ static int FEasterdate(func_info *info)
{
int y, m, d;
int g, c, x, z, e, n;
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
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;
if (Nargs == 0) {
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
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 {
g = (y % 19) + 1; /* golden number */
@@ -2409,13 +2413,17 @@ static int FOrthodoxeaster(func_info *info)
{
int y, m, d;
int a, b, c, dd, e, f, dse;
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
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;
if (Nargs == 0) {
FromDSE(DSEToday, &y, &m, &d);
} else {
if (ARG(0).type == INT_TYPE) {
y = ARGV(0);
if (y < BASE) return E_2LOW;
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 {
a = y % 4;
@@ -2492,7 +2500,7 @@ static int FTimezone(func_info *info)
if (Nargs == 0) {
dse = DSEToday;
now = (SystemTime(0) / 60);
now = MinutesPastMidnight(0);
} else {
if (!HASDATE(ARG(0))) return E_BAD_TYPE;
dse = DATEPART(ARG(0));
@@ -3290,6 +3298,8 @@ FSlide(func_info *info)
{
int r, omit, d, i, localomit, amt;
Token tok;
int step = 1;
int localargs = 2;
if (!HASDATE(ARG(0))) return E_BAD_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_2LOW;
if (Nargs > 2 && ARG(2).type == INT_TYPE) {
step = ARGV(2);
if (step < 1) return E_2LOW;
localargs++;
}
localomit = 0;
for (i=2; i<Nargs; i++) {
for (i=localargs; i<Nargs; i++) {
if (ARG(i).type != STR_TYPE) return E_BAD_TYPE;
FindToken(ARG(i).v.str, &tok);
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 (amt > 0) {
while(amt) {
d++;
d += step;
r = IsOmitted(d, localomit, NULL, &omit);
if (r) return r;
if (!omit) amt--;
}
} else {
while(amt) {
d--;
d -= step;
if (d < 0) return E_DATE_OVER;
r = IsOmitted(d, localomit, NULL, &omit);
if (r) return r;
@@ -3335,6 +3350,8 @@ FNonomitted(func_info *info)
{
int d1, d2, ans, localomit, i;
int omit, r;
int step = 1;
int localargs = 2;
Token tok;
if (!HASDATE(ARG(0)) ||
@@ -3343,10 +3360,20 @@ FNonomitted(func_info *info)
}
d1 = DATEPART(ARG(0));
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;
for (i=2; i<Nargs; i++) {
for (i=localargs; i<Nargs; i++) {
if (ARG(i).type != STR_TYPE) return E_BAD_TYPE;
FindToken(ARG(i).v.str, &tok);
if (tok.type != T_WkDay) return E_UNKNOWN_TOKEN;
@@ -3355,11 +3382,12 @@ FNonomitted(func_info *info)
ans = 0;
while (d1 < d2) {
r = IsOmitted(d1++, localomit, NULL, &omit);
r = IsOmitted(d1, localomit, NULL, &omit);
if (r) return r;
if (!omit) {
ans++;
}
d1 += step;
}
RetVal.type = INT_TYPE;
RETVAL = ans;
@@ -3554,6 +3582,8 @@ rows_or_cols(func_info *info, int want_rows)
struct winsize w;
int fd = STDOUT_FILENO;
int opened = 0;
RetVal.type = INT_TYPE;
if (!isatty(fd)) {
fd = open("/dev/tty", O_RDONLY);
@@ -3561,6 +3591,7 @@ rows_or_cols(func_info *info, int want_rows)
RETVAL = -1;
return OK;
}
opened = 1;
}
if (ioctl(fd, TIOCGWINSZ, &w) == 0) {
if (want_rows) RETVAL = w.ws_row;
@@ -3568,7 +3599,7 @@ rows_or_cols(func_info *info, int want_rows)
} else {
RETVAL = -1;
}
if (fd != STDOUT_FILENO) {
if (opened) {
close(fd);
}
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 */
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;
/* Convert from UTC to local time */
@@ -3785,8 +3816,10 @@ FSoleq(func_info *info)
}
ret = solstice_equinox_for_year(y, which);
if (ret < 0) return E_MKTIME_PROBLEM;
if (dse != NO_DATE && (ret / MINUTES_PER_DAY) < dse) {
ret = solstice_equinox_for_year(y+1, which);
if (ret < 0) return E_MKTIME_PROBLEM;
}
RetVal.type = DATETIME_TYPE;
RETVAL = ret;

View File

@@ -8,7 +8,7 @@
/* globals.h and err.h */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -50,6 +50,7 @@ EXTERN int LineNo;
EXTERN int FreshLine;
EXTERN uid_t TrustedUsers[MAX_TRUSTED_USERS];
EXTERN INIT( int MaxLateMinutes, 0);
EXTERN INIT( int NumTrustedUsers, 0);
EXTERN INIT( char const *MsgCommand, NULL);
EXTERN INIT( char const *QueuedMsgCommand, NULL);
@@ -70,7 +71,7 @@ EXTERN INIT( int Hush, 0);
EXTERN INIT( int NextMode, 0);
EXTERN INIT( int InfiniteDelta, 0);
EXTERN INIT( int DefaultTDelta, 0);
EXTERN INIT( int DeltaOffset, 0);
EXTERN INIT( int DeltaOverride, 0);
EXTERN INIT( int RunDisabled, 0);
EXTERN INIT( int IgnoreOnce, 0);
EXTERN INIT( int SortByTime, 0);
@@ -78,7 +79,8 @@ EXTERN INIT( int SortByDate, 0);
EXTERN INIT( int SortByPrio, 0);
EXTERN INIT( int UntimedBeforeTimed, 0);
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 int FileAccessDate;
@@ -90,6 +92,7 @@ EXTERN INIT( int DontQueue, 0);
EXTERN INIT( int NumQueued, 0);
EXTERN INIT( int DontIssueAts, 0);
EXTERN INIT( int Daemon, 0);
EXTERN INIT( int DaemonJSON, 0);
EXTERN INIT( char DateSep, DATESEP);
EXTERN INIT( char TimeSep, TIMESEP);
EXTERN INIT( char DateTimeSep, DATETIMESEP);
@@ -100,7 +103,7 @@ EXTERN INIT( int SynthesizeTags, 0);
EXTERN INIT( int ScFormat, SC_AMPM);
EXTERN INIT( int MaxSatIter, 1000);
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 PurgeMode, 0);
EXTERN INIT( int PurgeIncludeDepth, 0);
@@ -155,6 +158,9 @@ EXTERN INIT( char *EndSentIg, "\"')]}>");
EXTERN DynamicBuffer Banner;
EXTERN DynamicBuffer LineBuffer;
EXTERN DynamicBuffer ExprBuf;
extern int NumFullOmits, NumPartialOmits;
/* List of months */
EXTERN char *EnglishMonthName[]
#ifdef MK_GLOBALS

View File

@@ -5,7 +5,7 @@
/* Support for the Hebrew calendar */
/* */
/* 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 */
/* */
/* Derived from code written by Amos Shapir in 1978; revised */

View File

@@ -7,7 +7,7 @@
/* in normal mode. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -25,10 +25,11 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#ifdef HAVE_INITGROUPS
#include <grp.h>
@@ -40,6 +41,14 @@
#include "expr.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:
@@ -219,7 +228,7 @@ void InitRemind(int argc, char const *argv[])
}
} else {
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 */
@@ -243,6 +252,11 @@ void InitRemind(int argc, char const *argv[])
while(*arg) arg++;
break;
case '-':
ProcessLongOption(arg);
while(*arg) arg++;
break;
case '@':
UseVTColors = 1;
if (*arg) {
@@ -259,16 +273,24 @@ void InitRemind(int argc, char const *argv[])
if (*arg == ',') {
arg++;
if (*arg != ',') {
PARSENUM(x, arg);
if (x == 0) {
TerminalBackground = TERMINAL_BACKGROUND_DARK;
} else if (x == 1) {
TerminalBackground = TERMINAL_BACKGROUND_LIGHT;
} else if (x == 2) {
TerminalBackground = TERMINAL_BACKGROUND_UNKNOWN;
if (*arg == 't') {
arg++;
should_guess_terminal_background = 2;
} else {
fprintf(ErrFp, "%s: -@n,m,b: m must be 0, 1 or 2 (assuming 2)\n",
argv[0]);
PARSENUM(x, arg);
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) {
InfiniteDelta = 1;
} else {
PARSENUM(DeltaOffset, arg);
if (DeltaOffset < 0) {
DeltaOffset = 0;
}
if (*arg == 'z') {
DeltaOverride = -1;
arg++;
} else {
PARSENUM(DeltaOverride, arg);
if (DeltaOverride < 0) {
DeltaOverride = 0;
}
}
}
break;
case 'e':
@@ -398,7 +425,11 @@ void InitRemind(int argc, char const *argv[])
case 'z':
case 'Z':
DontFork = 1;
if (*arg == '0') {
if (*arg == 'j' || *arg == 'J') {
while (*arg) arg++;
Daemon = -1;
DaemonJSON = 1;
} else if (*arg == '0') {
PARSENUM(Daemon, arg);
if (Daemon == 0) Daemon = -1;
else if (Daemon < 1) Daemon = 1;
@@ -532,7 +563,7 @@ void InitRemind(int argc, char const *argv[])
arg++;
/* -wt means get width from /dev/tty */
ttyfd = open("/dev/tty", O_RDONLY);
if (!ttyfd) {
if (ttyfd < 0) {
fprintf(stderr, "%s: `-wt': Cannot open /dev/tty: %s\n",
argv[0], strerror(errno));
} else {
@@ -543,7 +574,12 @@ void InitRemind(int argc, char const *argv[])
PARSENUM(CalWidth, arg);
if (CalWidth != 0 && CalWidth < 71) CalWidth = 71;
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;
if (FormWidth < 20) FormWidth = 20;
@@ -721,7 +757,7 @@ void InitRemind(int argc, char const *argv[])
/* Figure out the offset from UTC */
if (CalculateUTC)
(void) CalcMinsFromUTC(DSEToday, SystemTime(0)/60,
(void) CalcMinsFromUTC(DSEToday, MinutesPastMidnight(0),
&MinsFromUTC, NULL);
}
@@ -735,7 +771,7 @@ void InitRemind(int argc, char const *argv[])
#ifndef L_USAGE_OVERRIDE
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
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif
@@ -953,3 +989,139 @@ AddTrustedUser(char const *username)
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. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -6,7 +6,7 @@
/* */
/* 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. */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */

View File

@@ -11,7 +11,7 @@
/* Further corrections by Erik-Jan Vens */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Support for the English language. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -11,7 +11,7 @@
/* */
/* This file is part of REMIND. */
/* 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 */
/* */
/***************************************************************/
@@ -254,7 +254,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1
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
fprintf(ErrFp, ">>>> BETAVERSIO <<<<\n");
#endif

View File

@@ -8,7 +8,7 @@
/* */
/* 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 */
/* Dianne Skoll. */
/* SPDX-License-Identifier: GPL-2.0-only */
@@ -228,7 +228,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1
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
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif

View File

@@ -9,7 +9,7 @@
/* I don't speak German. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Support for the Icelandic language. */
/* */
/* 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) */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */

View File

@@ -7,7 +7,7 @@
/* This file is part of REMIND. */
/* 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 */
/* */
/***************************************************************/
@@ -17,11 +17,11 @@
/* Day names */
#define L_SUNDAY "Domenica"
#define L_MONDAY "Lunedí"
#define L_TUESDAY "Martedí"
#define L_WEDNESDAY "Mercoledí"
#define L_THURSDAY "Giovedí"
#define L_FRIDAY "Venerdí"
#define L_MONDAY "Lunedì"
#define L_TUESDAY "Martedì"
#define L_WEDNESDAY "Mercoledì"
#define L_THURSDAY "Giovedì"
#define L_FRIDAY "Venerdì"
#define L_SATURDAY "Sabato"
/* Month names */
@@ -68,7 +68,7 @@
#define L_AT "alle"
#define L_MINUTE "minut"
#define L_HOUR "or"
#define L_IS "é"
#define L_IS "è"
#define L_WAS "era"
#define L_AND "e"
/* What to add to make "hour" plural */

View File

@@ -6,7 +6,7 @@
/* */
/* This file is part of REMIND. */
/* 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 */
/* */
/***************************************************************/

View File

@@ -9,7 +9,7 @@
/* Polish. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -244,7 +244,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1
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
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif

View File

@@ -8,7 +8,7 @@
/* */
/* 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 */
/* Dianne Skoll. */
/* SPDX-License-Identifier: GPL-2.0-only */
@@ -253,7 +253,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1
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
fprintf(ErrFp, ">>>> VERSAO BETA <<<<\n");
#endif

View File

@@ -8,7 +8,7 @@
/* */
/* 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 */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */

View File

@@ -7,7 +7,7 @@
/* Author: Rafa Couto <rafacouto@biogate.com> */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -6,7 +6,7 @@
/* routines, etc. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -14,6 +14,8 @@
#define _XOPEN_SOURCE 600
#include "config.h"
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
@@ -26,16 +28,10 @@
#endif
#include <ctype.h>
#ifdef TIME_WITH_SYS_TIME
#include <time.h>
#if defined(HAVE_SYS_TIME_H)
#include <sys/time.h>
#else
#if defined(HAVE_SYS_TIME_H) || defined (TIME_WITH_SYS_TIME)
#include <sys/time.h>
#else
#endif
#include <time.h>
#endif
#endif
#include <sys/types.h>
#ifdef REM_USE_WCHAR
@@ -51,6 +47,9 @@
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) {
fprintf(ErrFp, "%s: `%s': %s.\n", ErrMsg[E_CANTACCESS], InitialFile, strerror(errno));
exit(1);
exit(EXIT_FAILURE);
}
r=IncludeFile(InitialFile);
if (r) {
fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING],
InitialFile, ErrMsg[r]);
exit(1);
exit(EXIT_FAILURE);
}
while(1) {
@@ -202,7 +201,7 @@ static void DoReminders(void)
if (r == E_EOF) return;
if (r) {
Eprint("%s: %s", ErrMsg[E_ERR_READING], ErrMsg[r]);
exit(1);
exit(EXIT_FAILURE);
}
s = FindInitialToken(&tok, CurLine);
@@ -753,19 +752,32 @@ int PushToken(char const *tok, ParsePtr p)
/* 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;
if (!realtime && (SysTime != -1L)) return SysTime;
if (!realtime && (SysTime != -1)) return SysTime;
(void) time(&tloc);
t = localtime(&tloc);
return (long) t->tm_hour * 3600L + (long) t->tm_min * 60L +
(long) t->tm_sec;
now = time(NULL);
t = localtime(&now);
return t->tm_hour * 3600L + t->tm_min * 60L +
t->tm_sec;
}
/***************************************************************/
/* */
/* MinutesPastMidnight */
/* */
/* Return the system time in minutes past midnight */
/* */
/***************************************************************/
int MinutesPastMidnight(int realtime)
{
return (SystemTime(realtime) / 60);
}
/***************************************************************/
/* */
/* SystemDate */
@@ -777,11 +789,11 @@ long SystemTime(int realtime)
/***************************************************************/
int SystemDate(int *y, int *m, int *d)
{
time_t tloc;
time_t now;
struct tm *t;
(void) time(&tloc);
t = localtime(&tloc);
now = time(NULL);
t = localtime(&now);
*d = t->tm_mday;
*m = t->tm_mon;
@@ -1244,19 +1256,19 @@ int CalcMinsFromUTC(int dse, int tim, int *mins, int *isdst)
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) == '[') {
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
}
if (*s) {
if (print) putchar(*s);
if (print) OUTPUT(*s);
s++;
}
}
@@ -1265,19 +1277,19 @@ static char const *OutputEscapeSequences(char const *s, int print)
#ifdef REM_USE_WCHAR
#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) == '[') {
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
}
if (*s) {
if (print) PutWideChar(*s);
if (print) PutWideChar(*s, output);
s++;
}
}
@@ -1286,7 +1298,7 @@ static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print)
static void
FillParagraphWCAux(wchar_t const *s)
FillParagraphWCAux(wchar_t const *s, DynamicBuffer *output)
{
int line = 0;
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 (*s == '\n') {
putchar('\n');
OUTPUT('\n');
s++;
line = 0;
while(ISWBLANK(*s)) s++;
@@ -1314,7 +1326,7 @@ FillParagraphWCAux(wchar_t const *s)
number of spaces */
j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) {
putchar(' ');
OUTPUT(' ');
}
/* Calculate the amount of room left on this line */
@@ -1327,7 +1339,7 @@ FillParagraphWCAux(wchar_t const *s)
if (*s == '\n') break;
while(1) {
t = s;
s = OutputEscapeSequencesWS(s, 1);
s = OutputEscapeSequencesWS(s, 1, output);
if (s == t) break;
while(ISWBLANK(*s)) s++;
}
@@ -1335,7 +1347,7 @@ FillParagraphWCAux(wchar_t const *s)
len = 0;
while(*s && !iswspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequencesWS(s, 0);
s = OutputEscapeSequencesWS(s, 0, output);
continue;
}
len += wcwidth(*s);
@@ -1346,17 +1358,17 @@ FillParagraphWCAux(wchar_t const *s)
}
if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) {
putchar(' ');
OUTPUT(' ');
}
while(t < s) {
PutWideChar(*t);
PutWideChar(*t, output);
if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++;
}
} else {
s = t;
putchar('\n');
OUTPUT('\n');
line++;
break;
}
@@ -1367,7 +1379,7 @@ FillParagraphWCAux(wchar_t const *s)
}
static int
FillParagraphWC(char const *s)
FillParagraphWC(char const *s, DynamicBuffer *output)
{
size_t len;
wchar_t *buf;
@@ -1377,7 +1389,7 @@ FillParagraphWC(char const *s)
buf = calloc(len+1, sizeof(wchar_t));
if (!buf) return E_NO_MEM;
(void) mbstowcs(buf, s, len+1);
FillParagraphWCAux(buf);
FillParagraphWCAux(buf, output);
free(buf);
return OK;
}
@@ -1397,7 +1409,7 @@ FillParagraphWC(char const *s)
/* A macro safe ONLY if used with arg with no side effects! */
#define ISBLANK(c) (isspace(c) && (c) != '\n')
void FillParagraph(char const *s)
void FillParagraph(char const *s, DynamicBuffer *output)
{
int line = 0;
@@ -1415,7 +1427,7 @@ void FillParagraph(char const *s)
if (!*s) return;
#ifdef REM_USE_WCHAR
if (FillParagraphWC(s) == OK) {
if (FillParagraphWC(s, output) == OK) {
return;
}
#endif
@@ -1425,7 +1437,7 @@ void FillParagraph(char const *s)
/* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') {
putchar('\n');
OUTPUT('\n');
s++;
line = 0;
while(ISBLANK(*s)) s++;
@@ -1438,7 +1450,7 @@ void FillParagraph(char const *s)
number of spaces */
j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) {
putchar(' ');
OUTPUT(' ');
}
/* Calculate the amount of room left on this line */
@@ -1451,7 +1463,7 @@ void FillParagraph(char const *s)
if (*s == '\n') break;
while(1) {
t = s;
s = OutputEscapeSequences(s, 1);
s = OutputEscapeSequences(s, 1, output);
if (s == t) break;
while(ISBLANK(*s)) s++;
}
@@ -1459,7 +1471,7 @@ void FillParagraph(char const *s)
len = 0;
while(*s && !isspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequences(s, 0);
s = OutputEscapeSequences(s, 0, output);
continue;
}
s++;
@@ -1470,17 +1482,17 @@ void FillParagraph(char const *s)
}
if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) {
putchar(' ');
OUTPUT(' ');
}
while(t < s) {
putchar(*t);
OUTPUT(*t);
if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++;
}
} else {
s = t;
putchar('\n');
OUTPUT('\n');
line++;
break;
}
@@ -1644,11 +1656,45 @@ SaveLastTimeTrig(TimeTrig const *t)
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
System(char const *cmd)
System(char const *cmd, int is_queued)
{
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);
if (r == 0) {
return;

View File

@@ -5,7 +5,7 @@
/* Calculations for figuring out moon phases. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -412,7 +412,7 @@ static double phase(double pdate,
Day = pdate - epoch; /* Date within epoch */
N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */
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 = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2);
Ec = 2 * todeg(atan(Ec)); /* 1 anomaly */

View File

@@ -6,7 +6,7 @@
/* the data structures for OMITted dates. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -32,7 +32,7 @@ static int PartialOmitArray[MAX_PARTIAL_OMITS];
/* WeekdayOmits is declared in global.h */
/* How many of each omit types do we have? */
static int NumFullOmits, NumPartialOmits;
int NumFullOmits, NumPartialOmits;
/* The structure for saving and restoring OMIT contexts */
typedef struct omitcontext {
@@ -445,6 +445,9 @@ int DoOmit(ParsePtr p)
if (!BexistsIntArray(PartialOmitArray, NumPartialOmits, syndrome)) {
InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome);
NumPartialOmits++;
if (NumPartialOmits == 366) {
Wprint("You have OMITted everything! The space-time continuum is at risk.");
}
}
if (mc == m[1] && dc == d[1]) {
break;

View File

@@ -5,7 +5,7 @@
/* Function Prototypes. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -22,6 +22,9 @@
/* Characters to ignore */
#define isempty(c) (isspace(c) || ((c) == '\\'))
#define IsServerMode() (Daemon < 0)
#define ShouldFork (!DontFork)
#include "dynbuf.h"
#include <ctype.h>
@@ -35,7 +38,7 @@ int DoRem (ParsePtr p);
int DoFlush (ParsePtr p);
void DoExit (ParsePtr p);
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 DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode);
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 DestroyParser (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 DoIf (ParsePtr p);
int DoElse (ParsePtr p);
@@ -114,7 +118,7 @@ void DestroyVars (int all);
int PreserveVar (char const *name);
int DoPreserve (Parser *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);
unsigned int HashVal (char const *str);
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);
void DumpSysVarByName (char const *name);
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 UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime);
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 PrintJSONKeyPairDateTime(char const *name, int dt);
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 AddGlobalOmit(int dse);
void set_lat_and_long_from_components(void);
void set_components_from_lat_and_long(void);
int GetTerminalBackground(void);
char const *get_day_name(int wkday);
char const *get_month_name(int mon);
void set_cloexec(int fd);
int push_call(char const *filename, char const *func, int lineno);
void clear_callstack(void);
int print_callstack(FILE *fp);
void pop_call(void);
void FixSpecialType(Trigger *trig);
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);
void WriteJSONTimeTrigger(TimeTrig const *tt);
#ifdef REM_USE_WCHAR
#define _XOPEN_SOURCE 600
#include <wctype.h>
#include <wchar.h>
void PutWideChar(wchar_t const wc);
void PutWideChar(wchar_t const wc, DynamicBuffer *output);
#endif

View File

@@ -5,7 +5,7 @@
/* Queue up reminders for subsequent execution. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -24,16 +24,35 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <sys/select.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "types.h"
#include "globals.h"
#include "err.h"
#include "protos.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 */
typedef struct queuedrem {
struct queuedrem *next;
@@ -41,16 +60,18 @@ typedef struct queuedrem {
int RunDisabled;
int ntrig;
char const *text;
char const *fname;
int lineno;
char passthru[PASSTHRU_LEN+1];
char sched[VAR_NAME_LEN+1];
DynamicBuffer tags;
Trigger t;
TimeTrig tt;
} QueuedRem;
/* Global variables */
static QueuedRem *QueueHead;
static QueuedRem *QueueHead = NULL;
static QueuedFilename *Files = NULL;
static time_t FileModTime;
static struct stat StatBuf;
@@ -61,6 +82,77 @@ static int CalculateNextTimeUsingSched (QueuedRem *q);
static void DaemonWait (struct timeval *sleep_tv);
static void reread (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,
TimeTrig *tim, char const *sched)
TimeTrig *tim, char const *sched)
{
QueuedRem *qelem;
@@ -79,7 +171,7 @@ int QueueReminder(ParsePtr p, Trigger *trig,
trig->noqueue ||
tim->ttime == NO_TIME ||
trig->typ == CAL_TYPE ||
tim->ttime < SystemTime(0) / 60 ||
tim->ttime < MinutesPastMidnight(0) ||
((trig->typ == RUN_TYPE) && RunDisabled)) return OK;
qelem = NEW(QueuedRem);
@@ -91,25 +183,84 @@ int QueueReminder(ParsePtr p, Trigger *trig,
free(qelem);
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++;
qelem->typ = trig->typ;
strcpy(qelem->passthru, trig->passthru);
qelem->tt = *tim;
qelem->t = *trig;
DBufInit(&(qelem->t.tags));
DBufPuts(&(qelem->t.tags), DBufValue(&(trig->tags)));
if (SynthesizeTags) {
AppendTag(&(qelem->t.tags), SynthesizeTag());
}
qelem->next = QueueHead;
qelem->RunDisabled = RunDisabled;
qelem->ntrig = 0;
strcpy(qelem->sched, sched);
DBufInit(&(qelem->tags));
DBufPuts(&(qelem->tags), DBufValue(&(trig->tags)));
if (SynthesizeTags) {
AppendTag(&(qelem->tags), SynthesizeTag());
}
QueueHead = qelem;
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 */
@@ -123,17 +274,19 @@ void HandleQueuedReminders(void)
int TimeToSleep;
unsigned SleepTime;
Parser p;
Trigger trig;
struct timeval tv;
struct timeval sleep_tv;
struct sigaction sa;
/* Suppress the BANNER from being issued */
DidMsgReminder = 1;
/* Turn off sorting -- otherwise, TriggerReminder has no effect! */
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
* standard file descriptors. This is to prevent someone
* doing:
@@ -143,9 +296,10 @@ void HandleQueuedReminders(void)
* processed correctly are RUN commands, provided they mail
* the result back or use their own resource (as a window).
*/
if (!DontFork && (!isatty(1) || !isatty(2))) {
close(1);
close(2);
if (ShouldFork) {
maybe_close(STDIN_FILENO);
maybe_close(STDOUT_FILENO);
maybe_close(STDERR_FILENO);
}
/* 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 */
while (q) {
q->tt.nexttime = (int) (SystemTime(1)/60 - 1);
q->tt.nexttime = MinutesPastMidnight(1) - 1;
q->tt.nexttime = CalculateNextTime(q);
q = q->next;
}
if (!DontFork || Daemon) {
if (ShouldFork || Daemon) {
sa.sa_handler = SigIntHandler;
sa.sa_flags = 0;
(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 */
while(1) {
q = FindNextReminder();
@@ -179,7 +340,7 @@ void HandleQueuedReminders(void)
if (!q && !Daemon) break;
if (Daemon && !q) {
if (Daemon < 0) {
if (IsServerMode()) {
/* Sleep until midnight */
TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1);
} else {
@@ -198,7 +359,7 @@ void HandleQueuedReminders(void)
/* Wake up once a minute to recalibrate sleep time in
case of laptop hibernation */
if (Daemon < 0) {
if (IsServerMode()) {
/* Wake up on the next exact minute */
gettimeofday(&tv, NULL);
sleep_tv.tv_sec = 60 - (tv.tv_sec % 60);
@@ -222,14 +383,14 @@ void HandleQueuedReminders(void)
if (!Daemon) {
int y, m, d;
if (RealToday != SystemDate(&y, &m, &d)) {
exit(0);
exit(EXIT_SUCCESS);
}
}
if (Daemon > 0 && SleepTime) CheckInitialFile();
if (Daemon && !q) {
if (Daemon < 0) {
if (IsServerMode()) {
/* Sleep until midnight */
TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1);
} else {
@@ -244,31 +405,53 @@ void HandleQueuedReminders(void)
/* Do NOT trigger the reminder if tt.nexttime is more than a
minute in the past. This can happen if the clock is
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. */
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 */
CreateParser(q->text, &p);
trig.typ = q->typ;
strcpy(trig.passthru, q->passthru);
RunDisabled = q->RunDisabled;
if (Daemon < 0) {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(SystemTime(1)/60));
if (!*DBufValue(&q->tags)) {
printf("*\n");
} else {
printf("%s\n", DBufValue(&(q->tags)));
}
if (IsServerMode() && q->typ != RUN_TYPE) {
if (DaemonJSON) {
printf("{\"response\":\"reminder\",");
PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
} else {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(MinutesPastMidnight(1)));
if (!*DBufValue(&q->t.tags)) {
printf("*\n");
} else {
printf("%s\n", DBufValue(&(q->t.tags)));
}
}
}
/* Set up global variables so some functions like trigdate()
and trigtime() work correctly */
SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1);
(void) TriggerReminder(&p, &trig, &q->tt, DSEToday, 1);
if (Daemon < 0) {
FileName = (char *) q->fname;
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");
}
fflush(stdout);
@@ -277,8 +460,23 @@ void HandleQueuedReminders(void)
/* Calculate the next trigger time */
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)
{
int done = 0;
if (DaemonJSON) {
printf("{\"response\":\"queue\",\"queue\":");
}
printf("[");
while(q) {
if (q->tt.nexttime == NO_TIME) {
@@ -485,6 +686,12 @@ json_queue(QueuedRem const *q)
}
done = 1;
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) {
case NO_TYPE: PrintJSONKeyPairString("type", "NO_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;
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 */
printf("\"");
@@ -530,7 +717,12 @@ json_queue(QueuedRem const *q)
printf("\"}");
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;
int retval;
int y, m, d;
int max = 1;
char cmdLine[256];
FD_ZERO(&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 (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);
reread();
}
@@ -561,70 +766,101 @@ static void DaemonWait(struct timeval *sleep_tv)
/* If nothing readable or interrupted system call, 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 (!FD_ISSET(0, &readSet)) return;
/* If EOF on stdin, exit */
if (feof(stdin)) {
exit(0);
exit(EXIT_SUCCESS);
}
/* Read a line from stdin and interpret it */
if (!fgets(cmdLine, sizeof(cmdLine), stdin)) {
exit(0);
exit(EXIT_SUCCESS);
}
if (!strcmp(cmdLine, "EXIT\n")) {
exit(0);
exit(EXIT_SUCCESS);
} else if (!strcmp(cmdLine, "STATUS\n")) {
int nqueued = 0;
QueuedRem *q = QueueHead;
while(q) {
if (q->tt.nexttime != NO_TIME) {
nqueued++;
}
q = q->next;
}
printf("NOTE queued %d\n", nqueued);
fflush(stdout);
print_num_queued();
} else if (!strcmp(cmdLine, "QUEUE\n")) {
printf("NOTE queue\n");
QueuedRem *q = QueueHead;
while (q) {
if (q->tt.nexttime != NO_TIME) {
switch (q->typ) {
case NO_TYPE: printf("NO_TYPE "); break;
case MSG_TYPE: printf("MSG_TYPE "); break;
case RUN_TYPE: printf("RUN_TYPE "); break;
case CAL_TYPE: printf("CAL_TYPE "); break;
case SAT_TYPE: printf("SAT_TYPE "); break;
case PS_TYPE: printf("PS_TYPE "); break;
case PSF_TYPE: printf("PSF_TYPE "); break;
case MSF_TYPE: printf("MSF_TYPE "); break;
case PASSTHRU_TYPE: printf("PASSTHRU_TYPE "); break;
default: printf("? "); 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));
printf("%s %s %s\n",
(q->passthru[0] ? q->passthru : "*"),
(q->sched[0] ? q->sched : "*"),
q->text ? q->text : "NULL");
if (DaemonJSON) {
json_queue(QueueHead);
} else {
printf("NOTE queue\n");
QueuedRem *q = QueueHead;
while (q) {
if (q->tt.nexttime != NO_TIME) {
switch (q->typ) {
case NO_TYPE: printf("NO_TYPE"); break;
case MSG_TYPE: printf("MSG_TYPE"); break;
case RUN_TYPE: printf("RUN_TYPE"); break;
case CAL_TYPE: printf("CAL_TYPE"); break;
case SAT_TYPE: printf("SAT_TYPE"); break;
case PS_TYPE: printf("PS_TYPE"); break;
case PSF_TYPE: printf("PSF_TYPE"); break;
case MSF_TYPE: printf("MSF_TYPE"); break;
case PASSTHRU_TYPE: printf("PASSTHRU_TYPE"); break;
default: printf("?"); 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));
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);
} else if (!strcmp(cmdLine, "JSONQUEUE\n")) {
printf("NOTE JSONQUEUE\n");
if (!DaemonJSON) {
printf("NOTE JSONQUEUE\n");
}
json_queue(QueueHead);
printf("NOTE ENDJSONQUEUE\n");
if (!DaemonJSON) {
printf("NOTE ENDJSONQUEUE\n");
}
fflush(stdout);
} 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);
reread();
} 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);
}
}
@@ -641,3 +877,47 @@ static void reread(void)
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. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -186,19 +186,19 @@ JSONToCalEntry(DynamicBuffer *buf)
val = json_parse(DBufValue(buf), DBufLen(buf));
if (!val) {
fprintf(stderr, "Unable to parse JSON line `%s'\n", DBufValue(buf));
exit(1);
exit(EXIT_FAILURE);
}
if (val->type != json_object) {
fprintf(stderr, "Expecting JSON object; found `%s'\n",
DBufValue(buf));
exit(1);
exit(EXIT_FAILURE);
}
c = NEW(CalEntry);
if (!c) {
fprintf(stderr, "malloc failed - aborting.\n");
exit(1);
exit(EXIT_FAILURE);
}
c->next = NULL;
c->special = SPECIAL_NORMAL;
@@ -221,7 +221,7 @@ JSONToCalEntry(DynamicBuffer *buf)
c->entry = malloc(strlen(s)+1);
if (!c->entry) {
fprintf(stderr, "malloc failed - aborting.\n");
exit(1);
exit(EXIT_FAILURE);
}
strcpy(c->entry, s);
got_body = 1;
@@ -253,7 +253,7 @@ JSONToCalEntry(DynamicBuffer *buf)
if (!got_body || !got_date) {
fprintf(stderr, "Could not parse line `%s'\n", DBufValue(buf));
exit(1);
exit(EXIT_FAILURE);
}
return c;
}
@@ -272,7 +272,7 @@ TextToCalEntry(DynamicBuffer *buf)
CalEntry *c = NEW(CalEntry);
if (!c) {
fprintf(stderr, "malloc failed - aborting.\n");
exit(1);
exit(EXIT_FAILURE);
}
c->next = NULL;
c->special = SPECIAL_NORMAL;
@@ -296,7 +296,7 @@ TextToCalEntry(DynamicBuffer *buf)
c->entry = malloc(strlen(startOfBody) + 1);
if (!c->entry) {
fprintf(stderr, "malloc failed - aborting.\n");
exit(1);
exit(EXIT_FAILURE);
}
strcpy(c->entry, startOfBody);
@@ -343,14 +343,14 @@ int main(int argc, char *argv[])
DBufGets(&buf, stdin);
if (first_line && (!strcmp(DBufValue(&buf), "["))) {
fprintf(stderr, "Rem2PS: It appears that you have invoked Remind with the -ppp option.\n Please use either -p or -pp, but not -ppp.\n");
exit(1);
exit(EXIT_FAILURE);
}
first_line = 0;
if (!strcmp(DBufValue(&buf), PSBEGIN) ||
!strcmp(DBufValue(&buf), PSBEGIN2)) {
if (!validfile) {
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");
}
}
@@ -361,7 +361,7 @@ int main(int argc, char *argv[])
if (!validfile) {
fprintf(stderr, "Rem2PS: Couldn't find any calendar data - are you\n");
fprintf(stderr, " sure you fed me input produced by remind -p ...?\n");
exit(1);
exit(EXIT_FAILURE);
}
printf("%%%%Trailer\n");
printf("%%%%Pages: %d\n", validfile);
@@ -486,7 +486,7 @@ void DoPsCal(void)
while(1) {
if (feof(stdin)) {
fprintf(stderr, "Input from REMIND is corrupt!\n");
exit(1);
exit(EXIT_FAILURE);
}
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, " WxHcm Specify size in centimetres (W and H are decimal numbers)\n");
fprintf(stderr, "Default media type is %s\n", DefaultPage[0].name);
exit(1);
exit(EXIT_FAILURE);
}
break;
@@ -1033,7 +1033,7 @@ void Usage(char const *s)
fprintf(stderr, "-e Make calendar fill entire page\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");
exit(1);
exit(EXIT_FAILURE);
}
/***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Define the PostScript prologue */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -14,7 +14,7 @@ char *PSProlog1[] =
{
"% This file was produced by Remind and Rem2PS, written by",
"% 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 [ StandardEncoding 0 45 getinterval aload pop /minus",
" StandardEncoding 46 98 getinterval aload pop /dotlessi /grave /acute",

View File

@@ -5,7 +5,7 @@
/* Routines for sorting reminders by trigger date */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -136,7 +136,7 @@ void IssueSortedReminders(void)
switch(cur->typ) {
case MSG_TYPE:
if (MsgCommand && *MsgCommand) {
DoMsgCommand(MsgCommand, cur->text);
DoMsgCommand(MsgCommand, cur->text, 0);
} else {
if (cur->trigdate != olddate) {
IssueSortBanner(cur->trigdate);
@@ -151,11 +151,11 @@ void IssueSortedReminders(void)
IssueSortBanner(cur->trigdate);
olddate = cur->trigdate;
}
FillParagraph(cur->text);
FillParagraph(cur->text, NULL);
break;
case RUN_TYPE:
System(cur->text);
System(cur->text, 0);
break;
}

View File

@@ -6,7 +6,7 @@
/* classifying the tokens parsed. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -5,7 +5,7 @@
/* Routines for figuring out the trigger date of a reminder */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -72,7 +72,10 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
m++;
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);
return j;
@@ -324,8 +327,11 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
break;
}
start--;
if (start < 0) {
break;
}
}
if (iter > MaxSatIter) {
if (start < 0 || iter > MaxSatIter) {
/* omitfunc must have returned "true" too often */
*err = E_CANT_TRIG;
return -2;
@@ -388,6 +394,10 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
break;
}
simple--;
if (simple < 0) {
*err = E_CANT_TRIG;
return -2;
}
}
if (iter > MaxSatIter) {
*err = E_CANT_TRIG;

View File

@@ -5,7 +5,7 @@
/* Type definitions all dumped here. */
/* */
/* 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 */
/* */
/***************************************************************/

View File

@@ -6,7 +6,7 @@
/* functions. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -349,7 +349,9 @@ int CallUserFunc(char const *name, int nargs, ParsePtr p)
/* Skip the opening bracket, if there's one */
while (isempty(*s)) s++;
if (*s == BEG_OF_EXPR) s++;
if (*s == BEG_OF_EXPR) {
s++;
}
push_call(f->filename, f->name, f->lineno);
h = Evaluate(&s, f->locals, p);
if (h == OK) {

View File

@@ -5,7 +5,7 @@
/* Useful utility functions. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -36,6 +36,11 @@ char *StrnCpy(char *dest, char const *source, int n)
{
char *odest = dest;
if (n <= 0) {
*dest = 0;
return dest;
}
while (n-- && (*dest++ = *source++)) ;
if (*(dest-1)) *dest = 0;
return odest;
@@ -108,7 +113,7 @@ int DateOK(int y, int m, int d)
m > 11 ||
y > BASE + YR_RANGE ||
d > DaysInMonth(m, y) ) return 0;
else return 1;
return 1;
}
/* Functions designed to defeat gcc optimizer */

View File

@@ -6,7 +6,7 @@
/* user- and system-defined variables. */
/* */
/* 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 */
/* */
/***************************************************************/
@@ -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);
}
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)
{
@@ -525,7 +532,9 @@ int DoSet (Parser *p)
int r;
DynamicBuffer buf;
DynamicBuffer buf2;
DBufInit(&buf);
DBufInit(&buf2);
r = ParseIdentifier(p, &buf);
if (r) return r;
@@ -541,6 +550,13 @@ int DoSet (Parser *p)
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);
else r = SetVar(DBufValue(&buf), &v);
if (buf.len > VAR_NAME_LEN) {
@@ -762,10 +778,14 @@ typedef struct {
char modifiable;
int type;
void *value;
int min;
int min; /* Or const-value */
int max;
} 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
to be a flag indicating whether or not the value has been malloc'd. */
#define been_malloced min
@@ -792,7 +812,7 @@ static SysVar SysVarArr[] = {
{"DefaultColor", 1, SPECIAL_TYPE, default_color_func, 0, 0 },
{"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 },
{"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 },
{"DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 },
{"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 },
@@ -825,6 +845,9 @@ static SysVar SysVarArr[] = {
{"LongMin", 1, SPECIAL_TYPE, longmin_func, 0, 0 },
{"LongSec", 1, SPECIAL_TYPE, longsec_func, 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 },
{"MaxStringLen", 1, INT_TYPE, &MaxStringLen, -1, ANY },
{"May", 1, STR_TYPE, &DynamicMonthName[4], 0, 0 },
@@ -835,10 +858,13 @@ static SysVar SysVarArr[] = {
{"NextMode", 0, INT_TYPE, &NextMode, 0, 0 },
{"November", 1, STR_TYPE, &DynamicMonthName[10],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 },
{"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 },
{"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0 },
{"On", 1, STR_TYPE, &DynamicOn, 0, 0 },
{"ParseUntriggered", 1, INT_TYPE, &ParseUntriggered, 0, 1 },
{"Pm", 1, STR_TYPE, &DynamicPm, 0, 0 },
{"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 },
{"PSCal", 0, INT_TYPE, &PsCal, 0, 0 },
@@ -855,7 +881,7 @@ static SysVar SysVarArr[] = {
{"SysInclude", 0, STR_TYPE, &SysDir, 0, 0 },
{"T", 0, SPECIAL_TYPE, trig_date_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 },
{"TimeSep", 1, SPECIAL_TYPE, time_sep_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;
SysVar *v = FindSysVar(name);
if (!v) return E_NOSUCH_VAR;
if (v->type != SPECIAL_TYPE &&
v->type != value->type) return E_BAD_TYPE;
if (!v->modifiable) {
Eprint("%s: `$%s'", ErrMsg[E_CANT_MODIFY], name);
return E_CANT_MODIFY;
}
if (v->type != SPECIAL_TYPE &&
v->type != value->type) return E_BAD_TYPE;
if (v->type == SPECIAL_TYPE) {
SysVarFunc f = (SysVarFunc) v->value;
r = f(1, value);
@@ -940,6 +966,11 @@ int GetSysVar(char const *name, Value *val)
val->type = ERR_TYPE;
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) {
SysVarFunc f = (SysVarFunc) v->value;
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);
fprintf(ErrFp, "%16s ", buffer);
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;
f(0, &vtmp);
PrintValue(&vtmp, ErrFp);

View File

@@ -4,7 +4,7 @@ MSG UseVTColors is: [$UseVTColors]%
MSG Use256Colors is: [$Use256Colors]%
MSG UseTrueColors is: [$UseTrueColors]%
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,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]%

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.
#
# 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
# ---------------------------------------------------------------------------
@@ -31,6 +31,15 @@ fi
TZ=UTC
export TZ
RESULT=`(echo 'BANNER %'; echo 'IF now() > 23:55'; echo 'MSG late%'; echo 'ENDIF') | ../src/remind -h -`
if test "$RESULT" = "late" ; then
echo ""
echo "*** Please do not run the test suite between 23:55 and 00:00 UTC; it will fail."
echo ""
exit 1
fi
# If we're already in a utf-8 locale, do
# nothing; otherwise, set LC_ALL
OK=0
@@ -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
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
rm -rf include_dir/ww
touch include_dir/ww
@@ -425,6 +446,99 @@ rm -rf include_dir/ww
# This segfaulted in 04.02.03
../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
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

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')
msg [a077]%
set a078 easterdate(today())
set a078 easterdate()
set a079 easterdate(1992)
set a080 easterdate(1995)
set a078 orthodoxeaster(today())
set a078 orthodoxeaster()
set a079 orthodoxeaster(1992)
set a080 orthodoxeaster(1995)
set a080 orthodoxeaster(2023)
@@ -482,7 +484,7 @@ msg [$December]%
msg [$DefaultColor]%
msg [$DefaultPrio]%
msg [$DefaultTDelta]%
msg [$DeltaOffset]%
msg [$DeltaOverride]%
msg [$DontFork]%
msg [$DontQueue]%
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("<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
EXIT

View File

@@ -26,6 +26,25 @@ REM 12 AUG SPECIAL MOON 0
# Test nonomitted
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', '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
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.
#
# 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
#
# ---------------------------------------------------------------------------

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