Compare commits

..

289 Commits

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

See merge request dskoll/remind!5
2023-10-09 14:38:07 +00:00
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
Dianne Skoll
705adbb82a Update release notes for 04.02.05. 2023-04-11 08:25:54 -04:00
Dianne Skoll
269f9788b6 Bump version to 04.02.05. 2023-04-11 08:22:10 -04:00
Dianne Skoll
5e1c5ae384 Diagnose common error. 2023-04-10 08:51:25 -04:00
Dianne Skoll
562eb83bde *SIGH* Fix ADDOMIT/SATISFY bug.
This was fixed for normal mode in commit dedb9766c9
but was not fixed in calendar mode.
2023-04-09 10:44:34 -04:00
Dianne Skoll
a53db00243 Don't bother checking for sys/file.h because nothing includes it. 2023-03-19 09:47:12 -04:00
Dianne Skoll
11375729db Remove unnecessary #include. 2023-03-19 09:43:57 -04:00
Dianne Skoll
9fee354e6c Update release notes. 2023-03-15 18:46:30 -04:00
Dianne Skoll
ec76554d41 Treat a null -k option as no -k option 2023-03-15 09:22:44 -04:00
Dianne Skoll
ddb0817c99 Make an empty -k option the same as no -k option. 2023-03-15 09:17:02 -04:00
Dianne Skoll
3d6ecd1f72 Prep for 04.02.04 release. 2023-03-14 12:03:00 -04:00
Dianne Skoll
e3ec6565e9 Add support for -k: option --- applies command only to *queued* reminders. 2023-03-13 15:16:24 -04:00
Dianne Skoll
8ed49ead7f Document that SPECIAL [type] means the same thing as [type] for type in MSG, MSF, RUN, CAL, PS and PSFILE. 2023-03-12 18:51:41 -04:00
Dianne Skoll
49fbca416f Fix typo 2023-03-08 16:06:33 -05:00
Dianne Skoll
82cd438fff Use -y as shorthand for --wrap. 2023-03-08 14:27:43 -05:00
Dianne Skoll
946e1bca38 Update help text to include --wrap, -x option. 2023-03-08 14:14:53 -05:00
Dianne Skoll
e40c81b5bf Add the "--wrap, -x" command-line parameter to rem2pdf
This "wraps" calendars that would normally require 6 rows so they only
require 5.  It does this by putting the last day or two in the *first*
row rather than the last.
2023-03-08 13:39:49 -05:00
Dianne Skoll
f23418480d Set up explict (row, col) -> Day mapping
This will make it easier to eventually implement a calendar-wrapping
feature that will avoid ever having to use 6 rows to display the
calendar.
2023-03-08 10:42:15 -05:00
Dianne Skoll
bb4df39c50 Add note to defs.rem about US holidays. 2023-03-08 09:25:29 -05:00
Dianne Skoll
5fec775863 Better DST rules. 2023-03-07 18:34:53 -05:00
Dianne Skoll
a85980fec2 Add rules for moving US federal holidays if they fall on a weekend. 2023-03-07 18:31:58 -05:00
Dianne Skoll
f3ea2962e6 Remove unused function definition. 2023-03-07 16:40:53 -05:00
Dianne Skoll
3a5af23ab6 Fix Thanksgiving definition. 2023-03-07 16:37:17 -05:00
Dianne Skoll
f9656edc51 Make "SPECIAL MSG" the same as "MSG" and same for MSF, RUN, PS and PSFILE
This lets us use variables to set the type of a REM command:

     SET t "MSG"
     REM SPECIAL [t] A message
     SET t "CAL"
     REM SPECIAL [t] A calendar message
     SET t "RUN"
     REM SPECIAL [t] /bin/some_cmd
2023-03-03 11:53:45 -05:00
Dianne Skoll
5134b47d47 Oops! Fix up broken tests. 2023-03-02 11:42:54 -05:00
Dianne Skoll
d4a183f3bf Add htmlstriptags function. 2023-03-02 11:40:03 -05:00
Dianne Skoll
87e392de6c Check for E_NOMEM conditions. 2023-03-02 09:43:56 -05:00
Dianne Skoll
afc1667e64 Implement htmlescape() built-in function. 2023-03-02 09:39:14 -05:00
Dianne Skoll
8d25270c43 Fix syntax of TkRemind command-line per Ian! D. Allen. 2023-03-02 08:39:14 -05:00
Dianne Skoll
929866a770 Use ^A as the split character rather than \ 2023-02-27 12:13:19 -05:00
Dianne Skoll
395bad96a7 Don't barf if -underlinefg is not available; don't lose whitespace in MOON message. 2023-02-27 10:40:59 -05:00
Dianne Skoll
cd7be006c9 Set timezone for tests so moon phases show up on predictable days. 2023-02-23 08:32:24 -05:00
Dianne Skoll
f658ba7ee7 Fix typo, pointed out by @jochensp 2023-02-22 16:18:21 -05:00
Dianne Skoll
7416f4c035 Output a diff of test.out and test.cmp if tests fail, but limit to 200 lines. 2023-02-22 16:04:17 -05:00
Dianne Skoll
2860159ff7 Add test for a fixed bug. 2023-02-22 10:02:18 -05:00
Dianne Skoll
64fa71ab09 Avoid segfault if we define a function on the command-line with -i 2023-02-22 09:57:09 -05:00
Dianne Skoll
ffbba7d4d1 Update WHATSNEW for 04.02.03 release. 2023-02-10 12:57:53 -05:00
Dianne Skoll
fdcc2d8acf Bump version to 04.02.03. 2023-02-09 09:31:28 -05:00
Dianne Skoll
f1aa4d16af Test for v == INT_MIN on entry to FAbs. 2023-02-09 08:51:20 -05:00
Dianne Skoll
a55c5580f3 Silence Perl::Critic warning. 2023-02-07 13:45:38 -05:00
Dianne Skoll
569e315306 Suppress some Perl::Critic warnings. 2023-02-07 13:45:08 -05:00
Dianne Skoll
acd641845d Update WHATSNEW 2023-02-07 13:07:54 -05:00
Dianne Skoll
6b7e6f6788 Another cppcheck cleanup. 2023-02-07 11:20:09 -05:00
Dianne Skoll
4248b9c624 Add "cppcheck" Makefile target. 2023-02-07 11:16:29 -05:00
Dianne Skoll
6de98d1357 A few more cppcheck cleanups. 2023-02-07 11:05:16 -05:00
Dianne Skoll
18f21693af Clean up some warnings from cppcheck static analyzer. 2023-02-07 10:28:02 -05:00
Dianne Skoll
6fa500a860 Issue sort-banner correctly for MSF-type reminders. 2023-02-02 14:45:54 -05:00
Dianne Skoll
941c02582e More details on TIME type, courtesy of Ian! D. Allen 2023-01-20 12:58:28 -05:00
Dianne Skoll
e56e3924d9 Clarify DURATION. 2023-01-20 12:33:14 -05:00
Dianne Skoll
d1384a8f69 Add #include <fcntl.h> to funcs.c.
Reported by Zoltan Puskas; see https://bugs.gentoo.org/889318
2023-01-20 08:15:30 -05:00
Dianne Skoll
0488d689aa Update tax day in include/holidays/us.rem 2023-01-19 13:15:50 -05:00
Dianne Skoll
988a94e669 Update WHATSNEW. 2023-01-16 09:23:34 -05:00
Dianne Skoll
ac7e93ac1c Allow REMIND_LANG to override language. 2023-01-15 21:09:54 -05:00
Dianne Skoll
bb7e9ee676 Add Greek holidays and language pack, courtesy of JeiEl <jarlaxl@freemail.gr> 2023-01-15 19:40:39 -05:00
Dianne Skoll
ac949ce7bd Proper definition for US tax day. 2023-01-14 14:26:38 -05:00
Dianne Skoll
0fa8eed11f Correct the US Tax Day calculation, courtesy of Tavis Ormandy 2023-01-14 12:38:25 -05:00
Dianne Skoll
df8694a128 Add orthodoxeaster function. 2023-01-09 15:12:18 -05:00
Dianne Skoll
13df3d96e5 Add JulianToGregorianOffset function. 2023-01-09 14:18:10 -05:00
110 changed files with 7936 additions and 4007 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

26
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"
@@ -53,7 +53,7 @@ proc SetConfigDefaults {} {
# Pops up an error dialog; then calls exit.
#***********************************************************************
proc Bail { msg } {
tk_dialog .err "Remind Configuration Error" $msg error 0 "Bummer"
tk_messageBox -message "Remind Build Error" -detail $msg -icon error -type ok
exit 1
}
@@ -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 sys/file.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.02
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1)
VERSION=04.03.04
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

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

View File

@@ -1,5 +1,342 @@
CHANGES TO REMIND
* VERSION 4.3 Patch 4 - 2024-03-25
* NEW FEATURE: remind: Add the new "multitrig" function, which is how "trig"
should have worked in the first place. See man page for details.
* IMPROVEMENT: tkremind: Make errors in your reminders file stand out more
prominently.
* IMPROVEMENT: tkremind: If you click on an error in the "Errors..." popup,
tkremind will open a text editor on the offending file and line.
* IMPROVEMENT: tkremind: Format the "Queue..." output better and make queue
items clickable; clicking on a queue item opens a text editor on the
corresponding file and line.
* IMPROVEMENT: Add a standard [$SysInclude]/moonphases.rem file to display
moon phases on your calendar.
* IMPROVEMENT: Clean up the demo code in www/ and add PDF output.
* BUG FIX: tkremind: Correctly handle filenames containing spaces and other
characters that tend to confuse the shell.
* BUG FIX: tkremind: Raise dialog boxes after errors so that they
remain visible.
* CLEANUP: tkremind: Remove some dead code.
* VERSION 4.3 Patch 3 - 2024-03-18
* IMPROVEMENT: tkremind: Update icon to include a white border so it shows
up better on dark backgrounds.
* IMPROVEMENT: C code: Fix a number of cppcheck static-analysis warnings.
* IMPROVEMENT: remind: Update the "-zj" protocol to include a queue-id for
each queued reminder and add the DEL client command to delete a specific
item from the queue. Used by tkremind to implement "don't remind me about
this again today."
* MINOR NEW FEATURE: Add a "-ds" debugging flag to print out expression-parsing
stack high-water marks on exit. This esoteric feature is of no use to
anyone but the Remind author.
* IMPROVEMENT: tkremind: Stop using the deprecated tk_dialog command in favor
of the newer tk_messageBox command.
* IMPROVEMENT: remind: In server mode, try to minimize redraws by
consuming inotify events until at least 0.2s elapses without an
event appearing.
* BUG FIX: tkremind: The "Don't remind me about this again today" feature
was unreliable and only worked for reminders created with TkRemind itself.
It has been made more reliable and works with any reminder.
* BUG FIX: remind: Make it a syntax error if a local OMIT in a REM statement
is not followed by at least one weekday name.
* VERSION 4.3 Patch 2 - 2024-03-01
- BUG FIX: remind: Fix a logic error when implementing the RUN command in
server mode. As it turns out, the error is harmless, but it's best to do
things correctly.
- BUG FIX: The Makefile would install the tkremind.png and tkremind.desktop
files in the wrong location. This has been fixed.
* VERSION 4.3 Patch 1 - 2024-02-29
- BUG FIX: tests: "make test" could fail because of a bad test. This
has been fixed. There are no actual code changes to any of the programs
in Remind compared to 04.03.00.
* VERSION 4.3 Patch 0 - 2024-02-29
- IMPROVEMENT: remind: If Remind is compiled on a system that supports
inotify, then in server mode (-z0 or -zj) it monitors the reminders file
and restarts itself if it detects a change, and also notifies the client.
Moving inotify support directly into Remind means that tkremind no longer
has to invoke a separate inotifywait process.
- IMPROVEMENT: remind: Set the CLOEXEC flag on files we open so we
don't leak file descriptors to programs that we run. While I don't
think there's a security issue here (any program you run can do
anything as your userid anyway) it's best to be clean and tidy.
- IMPROVEMENT: remind: Add localization for the Catalan language, courtesy
of Eloi Torrents.
- IMPROVEMENT: tkremind: Add a .desktop file and icon so TkRemind can be
integrated into the desktop menu system, courtesy of Eloi Torrents.
- CHANGE: Add a new server mode with the "-zj" flag. This is just
like "-z0" except it uses JSON messages to communicate with the
client rather than an ad-hoc protocol. The "-z0" mode is still
supported, but is deprecated.
- CHANGE: In server mode (-z0 or -zj) any RUN-type reminders, or message
commands of the "-kcommand" type are run with standard input and standard
output connected to /dev/null. NOTE INCOMPATIBILITY: If you previously
relied on RUN-type reminders to pop up reminders in TkRemind, they no
longer do. If you want this, you'll have to get the command that you
run to pop up its own window with "xmessage" or something similar.
- IMPROVEMENT: tkremind: Make the "Go to date..." dialog non-modal.
- CHANGE: remind: Allow the argument to easterdate() and
orthodoxeaster() to be omitted, in which case it defaults to
today().
- BUG FIX: Miscellaneous man page fixes.
- BUG FIX: Fix a leap-year edge-case. The reminder: REM 29 MSG whatever
was not triggered on Feb 29 of leap years.
- BUG FIX: rem2html: Make the version of rem2html track the version of
Remind. Noted by Ian! D. Allen.
* VERSION 4.2 Patch 9 - 2024-02-04
- CHANGE: remind: Do not attempt to guess terminal background color on
startup. Only obtain it as needed. This can prevent mojibake from
appearing on terminals that don't support the color query escape
sequence.
- IMPROVEMENT: remind: Add new system variables $NumFullOmits,
$MaxFullOmits, $NumPartialOmits and $MaxPartialOmits.
- IMPROVEMENT: remind: Issue a warning if someone OMITs every possible date.
- IMPROVEMENT: remind: In several error messages complaining about limits
being exceeded, include the actual limit in the error message. Clarify
the man page regarding limits on the number of OMITs.
- MINOR NEW FEATURE: remind: The expression STRING * INT or INT * STRING
is now accepted and yields a string that is INT concatenations of the
original STRING. In this case, INT must be non-negative and the total
string length can't exceed $MaxStringLen.
- DOCUMENTATION: Add "Astronomical Algorithms" by Jean Meeus to bibliography.
- DOCUMENTATION FIX: Update address of the Free Software Foundation in the
license file.
- DOCUMENTATION: Note that rem2ps is deprecated and will not received any
new features. Further development will happen on rem2pdf.
- BUG FIX: Preserve the filename() and priority context for queued reminders.
Previously, the filename information was lost and the priority was
coming from uninitialized memory (yikes!). bug found by Alexander
Möller.
- BUG FIX: build.tk: Various minor improvements.
- BUG FIX: remind: In server mode, if we de-queue a reminder without
triggering it, issue a "NOTE queued %d" message to update the
client's notion of the queue size.
- BUG FIX: tkremind: Fix typo found by Lorenzo Bazzanini.
* VERSION 4.2 Patch 8 - 2023-12-14
- NEW FEATURE: Add the $MaxLateMinutes system variable. This suppresses
a queued time reminder if the current time is more than $MaxLateMinutes
past the trigger time. (This typically only occurs if the computer
has been suspended/hibernated and then resumed.)
- IMPROVEMENT: tkremind: If an error occurs during printing, catch it
and change the Queue... button to Errors... (the same way errors in
reminder files are handled.)
- IMPROVEMENT: rem2html: add the --utf8 flag to set the HTML charset to
UTF-8.
- MINOR IMPROVEMENTS: Refactor some of the C code; use symbolic exit
statuses and file descriptors for stdin/stdout/stderr where possible.
- BUG FIX: configure.in: Use better option detection so we don't use the
unsupported option -ffat-lto-objects if compiling with clang instead of gcc.
- BUG FIXES: Many fixes to man pages, some by Jochen Sprickerhof
- MINOR BUG FIX: If Remind puts itself in the background, only close
stdout/stderr if they are not associated with a terminal. If
we close a descriptor, dup /dev/null onto it.
- MINOR BUG FIX: Catch SIGCONT when running in daemon/background mode.
This forces the select() call to be interrupted so we can update the
sleep time. This really only matters if the computer or the background
Remind process is suspended and then resumed.
* VERSION 4.2 Patch 7 - 2023-10-09
- 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
give a better error message suggesting THROUGH instead of UNTIL.
- BUG FIX: remind: The fix for the combination of ADDOMIT and SATISFY that
appeared in version 04.02.00 was not complete; the bug has finally been
properly fixed.
- BUG FIX: remind: Remove an unnecessary #include <sys/file.h>.
Nothing needed that and it broke compilation on FreeBSD.
* VERSION 4.2 Patch 4 - 2023-03-15
- NEW FEATURE: Remind: Add "htmlescape" and "htmlstriptags" built-in
functions.
- NEW FEATURE: Rem2PDF: Add the "--wrap, -y" option to ensure that no
printed calendar takes up more than 5 rows. If a calendar would normally
require 6 rows, wrap it so the last day or two appear on the first
row instead of on a sixth row.
- NEW FEATURE: Remind: Improve the -k option to allow specification of
separate commands for immediately-issued vs. queued reminders. For
example:
remind '-kcmd1 %s' '-k:cmd2 %s' ...
will use "cmd1" for immediately-issued reminders and "cmd2" for queued
ones. If you only use '-k:cmd2 %s' then immediately-issued reminders
are simply printed as usual rather than being passed to a command.
- IMPROVEMENT: Remind: Make "SPECIAL MSG" the same as just "MSG" and
the same for MSF, RUN, PS and PSFILE. This effectively lets you use
expression-pasting to determine the type of a REM command; see the
remind(1) man page for details.
- MINOR IMPROVEMENT: If "make test" fails, output up to 200 lines of diff
so we can see immediately what failed.
- DOCUMENTATION FIX: Fix some typos; fix TkRemind syntax description.
- TEST FIX: Make tests run reliably regardless of local machine's time zone.
- BUG FIX: TkRemind: Don't crash if local installation of Tk lacks the
-underlinefg configuration option.
- BUG FIX: examples/defs.rem: Fix up US Thanksgiving example.
- BUG FIX: include/holidays/us.rem: Add logic for US holidays that are
observed on a Friday if the holiday is a Saturday, or on a Monday if the
holiday is a Sunday.
- BUG FIX: TkRemind: Don't cut off MOON text at the first white-space
character.
- BUG FIX: Remind: prevent functions defined on the command-line (as in
remind '-if(x)=whatever') from segfaulting.
* VERSION 4.2 Patch 3 - 2023-02-10
- NEW FEATURE: Remind: add the orthodoxeaster() function to return the
date of Orthodox Easter.
- IMPROVEMENT: Add Greek language support courtesy of JeiEl.
- IMPROVEMENT: Add Greek holiday file courtesy of JeiEl.
- IMPROVEMENT: Fix the Perl code (rem2pdf, rem2html) to silence Perl::Critic
warnings
- IMPROVEMENT: Many internal code tweaks to eliminate many cppcheck
static analysis warnings.
- DOCUMENTATION IMPROVEMENT: Clarify the distinction between a "time"
and a "duration" as suggested by Ian! D. Allen.
- BUG FIX: Remind: Fix incorrect interaction between sortbanner() and
MSF-type reminders. Bug found by Tim Chase.
- BUG FIX: examples/defs.rem: Fix the calculation of US Tax Day as per
Tavis Ormandy and Tim Chase. Also fixed in include/holidays/us.rem
- BUG FIX: Remind: Add missing #include <fcntl.h> to funcs.c
- BUG FIX: Remind: Fix undefined integer-overflow behavior in built-in abs()
function. Pointed out on IRC by "ubitux".
* VERSION 4.2 Patch 2 - 2023-01-01
- NEW FEATURE: Remind: Add the NOQUEUE modifier to the REM statement for

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
# #
#############################################################################
@@ -42,7 +42,6 @@ SET Week_1 1
SET Week_2 8
SET Week_3 15
SET Week_4 22
FSET _last(mo) "1 " + MON((mo%12)+1) + " --7"
#################################################################
# Function that removes a single leading zero from a string... #
@@ -126,6 +125,8 @@ REM Sat Sun SPECIAL SHADE 220
# The following holidays were provided by Dave Rickel #
# Modified by D. Skoll to give safe OMITs for moveable holidays #
# #
# NOTE: See include/holidays/us.rem for more up-to-date definitions #
# #
#############################################################################
SET SaveTrig $NumTrig
@@ -157,7 +158,24 @@ REM Last Sunday in October ++2 UNTIL 1 Jan 2007 MSG Daylight Saving Time - %"DST
REM First Sunday in November ++2 FROM 1 Jan 2007 MSG Daylight Saving Time - %"DST ends%" %b
REM Apr 1 MSG %"April Fool's%" Day
REM Mon Tue Wed Thu Fri Sat 15 Apr MSG %"Income tax%" due
# US Tax Day
PUSH-OMIT-CONTEXT
# Normal case: 16 April falls Mon-Fri
REM 16 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw >= 1 && $Tw <= 5] MSG Emancipation Day
# 16 April falls on Saturday: Observe on the 15th
REM 15 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 5] MSG Emancipation Day (observed)
# 16 April falls on Sunday: Observe on the 17th
REM 17 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 1] MSG Emancipation Day (observed)
# If you live in Maine or Massachussetts, uncomment the next line
# REM Third Monday in April SCANFROM -7 ADDOMIT MSG Patriots Day
REM Apr 15 OMIT Sat Sun AFTER MSG Tax Day
POP-OMIT-CONTEXT
REM May 5 MSG %"Cinco de Mayo%"
REM First Sat in May MSG %"Kentucky Derby%"
REM Second Sun in May MSG %"Mother's Day%"
@@ -172,7 +190,7 @@ REM Nov 11 MSG %"Veterans Day%"
REM Oct 30 MSG %"Mischief Night%"
REM Oct 31 MSG %"Halloween%"
REM Tue Nov 2 SCANFROM -7 SATISFY [($Ty % 4) == 0] MSG %"Election Day%"
REM Last Thu in Nov SCANFROM -7 ADDOMIT MSG %"Thanksgiving Day%"
REM Thu Nov [Week_4] SCANFROM -7 ADDOMIT MSG %"Thanksgiving Day%"
REM Fri Nov [Week_4+1] SCANFROM -7 ADDOMIT MSG %"Thanksgiving (cont.)%"
OMIT Dec 24 MSG %"Christmas Eve%"
OMIT Dec 25 MSG %"Christmas%" Day

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

39
include/holidays/gr.rem Normal file
View File

@@ -0,0 +1,39 @@
# Greek national holidays
# ΑΡΓΙΕΣ (για όλους)
# fixed
REM 1 Jan MSG ΠΡΩΤΟΧΡΟΝΙΑ
REM 6 Jan MSG ΤΑ ΦΩΤΑ/ ΘΕΟΦΑΝΕΙΑ
REM 25 Mar MSG η 25η Μαρτίου
REM 15 Aug MSG 15Αύγουστος
REM 28 Oct MSG ΟΧΙ
REM 25 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ
REM 26 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ2
REM [orthodoxeaster($Uy)+1] ΔΕΥΤΕΡΑ ΤΟΥ ΠΑΣΧΑ
# May first is a national holiday except if Sunday, day of great week (week before easter) or Monday after easter, then
# minister decides moving that holiday. Here is a likely assumption of how this day might be moved.
# Uncomment following lines to enable.
set PM date($Uy,5,1)
# IF PM>=orthodoxeaster($Uy)-7 && PM<=orthodoxeaster($Uy)+1
# IF PM<orthodoxeaster($Uy)-3
# REM orthodoxeaster($Uy) -8 MSG πιθανόν ΕΡΓΑΤΙΚΗ Πρωτομαγιά
# ELSE
# REM [plusfunc(2)] MSG πιθανόν ΕΡΓΑΤΙΚΗ Πρωτομαγιά
# ENDIF
# ENDIF
# REM [PM] OMIT Sun AFTER MSG Πρωτομαγιά
REM 1 May MSG Πρωτομαγιά
# end of May 1 speculations
# The following are main national holidays per custom (observed by most but not all)
REM [orthodoxeaster($Uy)] -48 MSG Καθαρά Δευτέρα
REM [orthodoxeaster($Uy)] -2 MSG Μεγάλη Παρασκευή
REM [orthodoxeaster($Uy)] -1 MSG Μεγάλο Σάββατο
REM [orthodoxeaster($Uy)+50] MSG Αγίου Πνεύματος

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
@@ -14,6 +14,9 @@ REM [easterdate($Uy)+49] MSG Pentecost
# which ones are omitted.
OMIT Jan 1 MSG New Year's Day
REM 31 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG New Year's Day (observed)
REM 2 Jan SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG New Year's Day (observed)
REM Third Monday in Jan MSG Martin Luther King - %"MLK Day%"
REM Feb 2 MSG Ground Hog Day
REM Feb 14 MSG Valentine's Day
@@ -21,11 +24,27 @@ REM Third Monday in Feb SCANFROM -7 ADDOMIT MSG President's Day
REM Mar 17 MSG St. Patrick's Day
# These are accurate for most places in North America
REM MAYBE-UNCOMPUTABLE Sun November SATISFY [isdst($T) != isdst($T+1)] MSG Daylight Saving Time Ends
REM MAYBE-UNCOMPUTABLE Sun March SATISFY [isdst($T) != isdst($T+1)] MSG Daylight Saving Time Starts
REM MAYBE-UNCOMPUTABLE Sun November SATISFY [isdst($T) && !isdst($T+1)] MSG Daylight Saving Time Ends
REM MAYBE-UNCOMPUTABLE Sun March SATISFY [!isdst($T) && isdst($T+1)] MSG Daylight Saving Time Starts
REM Apr 1 MSG %"April Fool's%" Day
REM Mon Tue Wed Thu Fri Sat 15 Apr MSG %"Income tax%" due
PUSH-OMIT-CONTEXT
# Normal case: 16 April falls Mon-Fri
REM 16 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw >= 1 && $Tw <= 5] MSG Emancipation Day
# 16 April falls on Saturday: Observe on the 15th
REM 15 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 5] MSG Emancipation Day (observed)
# 16 April falls on Sunday: Observe on the 17th
REM 17 Apr SCANFROM -7 ADDOMIT SATISFY [$Tw == 1] MSG Emancipation Day (observed)
# If you live in Maine or Massachussetts, uncomment the next line
# REM Third Monday in April SCANFROM -7 ADDOMIT MSG Patriots Day
REM Apr 15 OMIT Sat Sun AFTER MSG %"Income tax%" due
POP-OMIT-CONTEXT
REM May 5 MSG Cinco de Mayo
REM First Sat in May MSG Kentucky Derby
REM Second Sun in May MSG Mother's Day
@@ -33,14 +52,21 @@ REM Third Sat in May MSG Armed Forces Day
REM Last Monday in May SCANFROM -7 ADDOMIT MSG Memorial Day
REM Jun 14 MSG Flag Day
OMIT 19 June MSG Juneteenth
REM 18 June SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Juneteenth (observed)
REM 20 June SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Juneteenth (observed)
REM July 4 SCANFROM -7 ADDOMIT MSG Independence Day
REM July 3 SCANFROM -7 ADDOMIT SATISFY [$Tw == 5] MSG Independence Day (observed)
REM July 5 SCANFROM -7 ADDOMIT SATISFY [$Tw == 1] MSG Independence Day (observed)
REM July 3 SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Independence Day (observed)
REM July 5 SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Independence Day (observed)
REM Third Sun in June MSG Father's Day
REM First Mon in Sep SCANFROM -7 ADDOMIT MSG Labor Day
REM Second Mon in Oct MSG Columbus Day / Indigenous Peoples' Day
REM Nov 11 MSG Veterans Day
OMIT 11 Nov MSG Veterans Day
REM 10 Nov SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Veterans Day (observed)
REM 12 Nov SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Veterans Day (observed)
REM Oct 30 MSG Mischief Night
REM Oct 31 MSG Halloween
@@ -48,8 +74,9 @@ REM Oct 31 MSG Halloween
REM Tue Nov 2 SCANFROM -7 SATISFY [($Ty % 4) == 0] MSG Election Day
REM Thu 22 Nov SCANFROM -7 ADDOMIT MSG Thanksgiving Day
REM Fri 23 Nov SCANFROM -7 ADDOMIT MSG Thanksgiving (cont.)
REM Dec 24 MSG Christmas Eve
OMIT Dec 25 MSG %"Christmas%" Day
REM 24 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==5] MSG Christmas (observed)
REM 26 Dec SCANFROM -7 ADDOMIT SATISFY [$Tw==1] MSG Christmas (observed)

View File

@@ -1,5 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
SET autolang getenv("LC_ALL")
SET autolang getenv("REMIND_LANG")
IF autolang == ""
SET autolang getenv("LC_ALL")
ENDIF
IF autolang == ""
SET autolang getenv("LANGUAGE")
ENDIF

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"

83
include/lang/gr.rem Normal file
View File

@@ -0,0 +1,83 @@
# Support for the Hellenic (Greek) language.
# This file is part of REMIND.
# REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by jarlaxl lamat (jarlaxl@freemail.gr)
SET $Sunday "Κυριακή"
SET $Monday "Δευτέρα"
SET $Tuesday "Τρίτη"
SET $Wednesday "Τετάρτη"
SET $Thursday "Πέμπτη"
SET $Friday "Παρασκευή"
SET $Saturday "Σάββατο"
SET $January "Ιανουάρ."
SET $February "Φεβρουάρ."
SET $March "Μάρτ."
SET $April "Απρίλ."
SET $May "Μαι."
SET $June "Ιούν."
SET $July "Ιούλ."
SET $August "Αυγουστ."
SET $September "Σεπτέμβρ."
SET $October "Οκτώβρ."
SET $November "Νοέμβρ."
SET $December "Δεκέμβρ."
SET $Today "σήμερα"
SET $Tomorrow "αύριο"
BANNER Υπενθυμίσεις: %w, %d %m, %y%o:
SET $Am "πμ"
SET $Pm "μμ"
SET $Ago "πριν"
SET $Fromnow "από τώρα"
SET $On "την"
SET $Now "τώρα"
SET $At "στις"
SET $Minute "λεπτά"
SET $Hour "ώρες"
SET $Is "είναι"
SET $Was "ήταν"
SET $And "και"
SET $Hplu ""
SET $Mplu ""
FSET subst_bx(a, d, t) "σε " + (d - today()) + " ημέρες"
FSET subst_ordinal(d) "."
FSET subst_a_alt(d) wkday(d) + ", " + day(d) + ". " + mon(d) + " " + year(d)
FSET subst_ax(alt, d, t) iif(alt, subst_a_alt(d), $On + " " + subst_a_alt(d))
FSET subst_g_alt(d) wkday(d) + ", " + day(d) + ". " + mon(d)
FSET subst_gx(alt, d, t) iif(alt, subst_g_alt(d), $On + " " + subst_g_alt(d))
FSET subst_ux(alt, d, t) subst_ax(alt, d, t)
FSET subst_vx(alt, d, t) subst_gx(alt, d, t)
# Localization of various astronomical events
# Perihelion
SET earthseasons_Perihelion_str "Περιήλιον"
# Vernal equinox
SET earthseasons_EquinoxMar_str "Εαρινή ισημερία"
# Summer solstice
SET earthseasons_SolsticeJun_str "Θερινό ηλιοστάσιο"
# Aphelion
SET earthseasons_Aphelion_str "Αφήλιον"
# Autumnal Equinox
SET earthseasons_EquinoxSep_str "Φθινοπωρινή ισημερία"
# Winter Solstice
SET earthseasons_SolsticeDec_str "Χειμερινό ηλιοστάσιο"
# Daylight saving time starts
SET daylightST_starts_str "Έναρξη θέρους"
# Daylight saving time ends
SET daylightST_ends_str "Τέλος θέρους"

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

14
include/moonphases.rem Normal file
View File

@@ -0,0 +1,14 @@
# Moon phases
# SPDX-License-Identifier: GPL-2.0-only
IF $CalMode || $PsCal
REM [moondate(0)] SPECIAL MOON 0 -1 -1 [moontime(0)]
REM [moondate(1)] SPECIAL MOON 1 -1 -1 [moontime(1)]
REM [moondate(2)] SPECIAL MOON 2 -1 -1 [moontime(2)]
REM [moondate(3)] SPECIAL MOON 3 -1 -1 [moontime(3)]
ELSE
REM NOQUEUE [moondatetime(0)] MSG New Moon (%2)
REM NOQUEUE [moondatetime(1)] MSG First Quarter (%2)
REM NOQUEUE [moondatetime(2)] MSG Full Moon (%2)
REM NOQUEUE [moondatetime(3)] MSG Last Quarter (%2)
ENDIF

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.) \"" Add comment to avoid Emacs highlighting problems
.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
@@ -281,6 +300,11 @@ Echo lines when displaying error messages
.TP
.B f
Trace the reading of reminder files
.TP
.B s
Upon exit, print the high-water mark of the operator and value stacks
used for expression-parsing. This is unlikely to be useful unless
you're intimately familiar with Remind's source code.
.RE
.TP
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
@@ -328,6 +352,18 @@ processes with the above technique. So be very careful. Because all
shell and whitespace characters are escaped, the program you execute
with the \fB\-k\fR option must be prepared to handle the entire
message as a single argument.
.PP
If you follow the \fB\-k\fR option with a colon, then the command is applied
only to queued timed reminders. Normal reminders are handled as usual.
In the above example, if you want normal reminders to simply be displayed
as usual, but queued reminders to be sent to notify-send, you could use:
.PP
.nf
remind '\-k:notify-send %s &' ...
.fi
.PP
You use both \fB\-k\fR\fIcmd1\fR and \fB\-k:\fR\fIcmd2\fR to use different
commands for queued versus non-queued reminders.
.RE
.TP
\fB\-z\fR[\fIn\fR] Runs \fBRemind\fR in the daemon mode. If \fIn\fR
@@ -337,9 +373,12 @@ defaults to 1, and can range from 1 to 60. Note that the use of the
\fB\-z\fR option also enables the \fB\-f\fR option.
.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
@@ -590,6 +629,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
@@ -1006,7 +1050,7 @@ By comparison, if we had used "\-\-1", the reminder would be triggered on
the last day of the month, regardless of the \fBOMIT\fR.
.PP
If you locally omit weekdays but also have globally-omitted weekdays, then
the list of ommitted weekdays is the union of the two. Consider this
the list of omitted weekdays is the union of the two. Consider this
example:
.PP
.nf
@@ -1102,7 +1146,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.
@@ -1174,7 +1218,9 @@ in the bodies of timed reminders, then when the timed reminders are
activated, the variables and functions have the definitions that were
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
@@ -1215,11 +1261,25 @@ specifies the duration of an event. For example, if you have a
REM 5 March 2021 AT 1:00pm DURATION 90 MSG Meeting
.fi
.PP
Note that \fIduration\fR is specified either in hours and minutes as a
\fItime\fR, or in minutes as an \fIinteger\fR. If you specify a
duration of 00:00 or 0, then \fBRemind\fR behaves exactly as if no
\fBDURATION\fR at all had been present.
For long-duration reminders, it is convenient to use expressions
to simplify writing the DURATION. For example, if you are away
from 20 Feb 2023 through 23 Feb 2023 (a total of 4 days) you
could write:
.PP
.nf
REM 20 Feb AT 00:00 DURATION [4*24]:00 MSG away
REM 20 Feb AT 00:00 DURATION [4*24*60] MSG away
.fi
.PP
Note that \fIduration\fR is specified either as
\fIhours\fR:\fIminutes\fR or just as \fIminutes\fR specified as an
\fIinteger\fR.
.PP
If you specify a duration of 00:00 or 0, then \fBRemind\fR behaves
exactly as if no \fBDURATION\fR at all had been present. Although
durations specified as \fIhours\fR:\fIminutes\fR look superficially like a
time-of-day, they are not; the \fIhours\fR component is not limited
to the range 00-23.
.PP
.SH SYNTACTIC SUGAR FOR REM
@@ -1654,6 +1714,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
@@ -1756,7 +1821,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
@@ -1961,9 +2026,23 @@ somewhat comparable to a C character array, but more closely resembles
the string type in BASIC.
.TP
.B TIME
The \fBTIME\fR data type consists of times of the day. The \fBTIME\fR
data type is internally stored as an integer representing the number
of minutes since midnight.
The \fBTIME\fR data type is used for two different purposes: To represent
a time of day with one-minute precision or to represent a duration
with one-minute precision. The context of where a \fBTIME\fR is used
determines whether it is interpreted as a time of day or a duration.
.RS
.PP
In contexts where a \fBTIME\fR represents a time of day, it may range
from 00:00 to 23:59 and is stored internally as an integer from 0 to
1439 representing the number of minutes since midnight.
.PP
In contexts where a \fBTIME\fR represents a duration, there is no
upper limit on the hour component (beyond that imposed by the
restriction that a duration expressed in minutes must fit into the
signed integer type of your CPU architecture.) Internally, a duration
is stored as an integer number of minutes.
.RE
.TP
.B DATE
The \fBDATE\fR data type consists of dates (later than 1 January 1990.)
@@ -2006,6 +2085,15 @@ range from 1 to 12. Either a period or colon can be used to separate
the minutes from the hours. However, Remind will consistently output
times in 24-hour format using only one separator character. (The
output separator character is chosen at compile-time.)
.PP
If the \fBTIME\fR is used where \fBRemind\fR expects a time-of-day
(for example, in an \fBAT\fR clause), then it can be written in
24-hour format (ranging from 00:00 to 23:59) or 12-hour format
(ranging from 12:00am to 11:59pm). If the \fBTIME\fR is used where
\fBRemind\fR expects a duration, it must not have an \fIam\fR or
\fIpm\fR suffix and the hour can be as large as you want, so long
as the total number of minutes in the duration fits in a signed integer
variable.
.RE
.TP
.B DATE constants
@@ -2089,7 +2177,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
@@ -2341,6 +2432,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
@@ -2407,7 +2502,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
@@ -2484,6 +2580,24 @@ updates \fB$LongDeg\fR, \fB$LongMin\fR and \fB$LongSec\fR. Similar
rules apply to \fB$Latitude\fR, \fB$LatDeg\fR, \fB$LatMin\fR and \fB$LatSec\fR.
.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.
@@ -2492,7 +2606,9 @@ The maximum number of iterations for the \fBSATISFY\fR clause
A limit on the longest string that \fBRemind\fR will allow you
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
@@ -2507,6 +2623,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.
@@ -2528,6 +2656,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
@@ -2629,9 +2779,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,
@@ -2934,11 +3088,17 @@ 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
Easter date, see \fBorthodoxeaster\fR.
.RE
.TP
.B evaltrig(s_trigger [,dq_start])
Evaluates \fItrigger\fR as if it were a REM or IFTRIG trigger specification
@@ -3032,6 +3192,16 @@ Support for Hebrew dates - see the section "THE HEBREW CALENDAR"
.B hour(tq_time)
Returns the hour component of \fItime\fR.
.TP
.B htmlescape(s_str)
Returns a modified copy of \fIstr\fR where "<" is replaced with
"&lt;"; ">" is replaced with "&gt;" and "&" is replaced with "&amp;"
.TP
.B htmlstriptags(s_str)
Returns a modified copy of \fIstr\fR where HTML tags are stripped
out. The stripping algorithm is fairly naive; the function starts
stripping characters when it encounters a "<" and it stops stripping
when it encounters a ">".
.TP
.B iif(si_test1, x_arg1, [si_test2, x_arg2,...], x_default)
If \fItest1\fR is not zero or the null string, returns \fIarg1\fR.
Otherwise, if \fItest2\fR is not zero or the null string, returns
@@ -3185,6 +3355,62 @@ which default to \fBtoday()\fR and midnight, respectively. The returned
value is an integer from 0 to 359, representing the phase of the moon
in degrees. 0 is a new moon, 180 is a full moon, 90 is first-quarter, etc.
.TP
.B multitrig(s_trig1 [,s_trig2, [... s_trigN]])
\fBmultitrig\fR evaluates each string as a trigger, similar to \fBevaltrig\fR,
and returns the \fIearliest\fR trigger date that is on or after \fBtoday()\fR.
\fBmultitrig\fR is similar to \fBtrig\fR but has the following difference:
.RS
.PP
\fBtrig\fR returns the \fIfirst\fR trigger date that would have triggered today,
whereas \fBmultitrig\fR returns the \fIearliest\fR trigger date later than
today, regardless of whether it would have triggered today.
.PP
If no trigger can be computed that is later than \fBtoday()\fR, then
\fBmultitrig\fR returns 1990-01-01.
.PP
Consider the following examples, assuming that today is Sunday, 24 March 2024:
.PP
.nf
# Returns 1990-01-01 because neither would trigger today
SET a trig("Mon", "Wed")
# Returns 2024-03-25 because it's the earlier trigger date
SET a multitrig("Mon", "Wed")
# Returns 2024-03-27 because it's the first that would trigger today
SET a trig("Wed +3", "Mon +3")
# Returns 2024-03-25 because it's the earlier trigger date
SET a multitrig("Wed +3", "Mon +3")
# Returns 1990-01-01 because all triggers have expired
SET a multitrig("2000", "2022", "1998", "2023")
.fi
.PP
In general, \fBmultitrig\fR works better with the Remind algorithm than
\fBtrig\fR and should be used most of the time. As an example, this
reminder is issued at the end of each quarter:
.PP
.nf
REM [multitrig("Mar 31", "Jun 30", "Sep 30", "Dec 31")] +7 MSG \\
%"End of [ord($Tm/3)] quarter%" is %b.
.fi
.PP
If you want the last working day of each quarter, you could use:
.PP
.nf
PUSH-OMIT-CONTEXT
OMIT Sat Sun
REM [multitrig("Mar ~1", "Jun ~1", "Sep ~1", "Dec ~1")] +7 MSG \\
%"Last working day of [ord($Tm/3)] quarter%" is %b.
POP-OMIT-CONTEXT
.fi
.PP
Note that unlike \fBevaltrig\fR, \fBmultitrig\fR always returns a \fBDATE\fR
and never a \fBDATETIME\fR. Including an \fBAT\fR clause in a trigger
supplied to \fBmultitrig\fR will result in an error.
.RE
.TP
.B ndawn([dq_date])
Returns the time of "nautical dawn" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR. If a \fIdatetime\fR object
@@ -3194,14 +3420,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.
@@ -3228,7 +3471,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
@@ -3261,6 +3504,18 @@ 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])
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.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS
.P
Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western
Easter date, see \fBeasterdate\fR.
.RE
.TP
.B ostype()
Returns "UNIX". Remind used to run on OS/2 and MS-DOS, but does not
any longer.
@@ -3411,10 +3666,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:
@@ -3434,6 +3692,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])
@@ -3452,7 +3730,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.
@@ -4012,15 +4290,30 @@ You cannot use expression-pasting to determine the type (\fBMSG\fR,
\fBCAL\fR, etc.) of a \fBREM\fR command. You can paste expressions
before and after the \fBMSG\fR, etc. keywords, but cannot do something like
this:
.RS
.PP
.nf
REM ["12 Nov 1993 AT 13:05 " + "MSG" + " BOO!"]
REM ["12 Nov 1993 AT 13:05 " + "MSG" + " BOO!"]
.fi
.PP
.B COMMON PITFALLS IN EXPRESSION PASTING
However, as an escape hatch, the sequence \fBSPECIAL\fR \fItype\fR
means the same thing as just \fItype\fR where \fItype\fR is one
of MSG, MSF, RUN, CAL, PS and PSFILE. This lets you do something
like this:
.PP
Remember, when pasting in expressions, that extra spaces are not
inserted. Thus, something like:
.nf
SET type "MSG"
REM 12 Nov 2024 SPECIAL [type] Hello
.fi
.PP
You can use this to control the types of your reminders based on variables
you set, how Remind is invoked, etc.
.RE
.PP
.B COMMON PITFALLS WITH EXPRESSION PASTING
.PP
Remember that extra spaces are not inserted when an expression is
pasted. Thus, something like:
.PP
.nf
REM[expr]MSG[expr]
@@ -4126,7 +4419,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:
@@ -4141,6 +4434,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
@@ -5022,7 +5321,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
@@ -5125,7 +5424,7 @@ as:
.PP
You can define your own substitution sequences in addition to the built-in
ones as follows: If you define a function named \fBsubst_\fIname\fB(alt, date, time)\fR, then the sequence \fB%{name}\fR calls the function with \fBalt\fR
set to 0 and \fBdate\fR and \fRtime\fR to the trigger date and time,
set to 0 and \fBdate\fR and \fBtime\fR to the trigger date and time,
respectively. The \fB%{name}\fR sequence is replaced with whatever the
function returns. The sequence \fB%*{name}\fR is similar, but calls
the function with \fBalt\fR set to 1.
@@ -5328,13 +5627,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
@@ -5359,7 +5658,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
@@ -5448,7 +5747,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
@@ -5475,6 +5774,9 @@ o
\fBINCLUDE\fR --> \fBINC\fR
.TP
o
\fBMAYBE-UNCOMPUTABLE\fR --> \fBMAYBE\fR
.TP
o
\fBSCANFROM\fR --> \fBSCAN\fR
.PP
.B NIFTY EXAMPLES
@@ -5647,6 +5949,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
@@ -5694,9 +6015,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.
@@ -5717,8 +6040,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

@@ -3,7 +3,7 @@
.SH NAME
tkremind \- graphical front-end to Remind calendar program
.SH SYNOPSIS
.B tkremind \fR[\fIoptions\fR] [\fIread_file\fR] [\fIwrite_file\fR] [\fIconfig_file\fR]
.B tkremind \fR[\fIoptions\fR] [\fIread_file\fR [\fIwrite_file\fR [\fIconfig_file\fR]]]
.SH DESCRIPTION
\fBTkRemind\fR is a graphical front-end to the \fBRemind\fR program.
It provides a friendly graphical interface which allows you to view
@@ -184,7 +184,9 @@ on the reminder.
If there are any errors in your reminder file, the "Queue..." button
changes to "Errors...". Click on "Errors..." to see the Remind error
output. Click "OK" to close the error window; this makes the button
in the main TkRemind window to revert to "Queue..."
in the main TkRemind window to revert to "Queue..." You can click on
any error message to open an editor on the file and line number that
caused the error.
.SH BACKGROUND REMINDERS
@@ -192,7 +194,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 +303,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 +368,110 @@ your hand-edited files in a separate \fB*.rem\fR file than \fBTkRemind\fR's
\fBRemind\fR has a special mode for interacting with programs like
\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
DEL \fIqid\fR
Delete the reminder with queue-id \fIqid\fR from the queue.
.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,"qid":qid,"body":body}
.fi
In this line, \fItt\fR is the trigger time of the reminder (expressed
as a string), \fInow\fR is the current time, \fItags\fR (if present)
is the tag or tags associated with the reminder, and \fIbody\fR is the
body of the reminder. This response causes \fBTkRemind\fR to pop up a
reminder notification. \fIqid\fR is a unique identifier for this
reminder. You may delete it from the queue by sending a \fBDEL\fR
\fIqid\fR command to the server. Note that \fIqid\fRs are not stable
across re-reads; if \fBRemind\fR restarts itself to re-read the reminder
file, then the \fIqid\fR values are likely to change, and any reminder
deleted with a \fBDEL\fR \fIqid\fR command is likely to be re-queued.
.TP
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;
@@ -687,7 +709,7 @@ if ($Options{help}) {
exit(0);
}
if (-t STDIN) {
if (-t STDIN) { ## no critic
print STDERR "$TIDY_PROGNAME: Input should not come from a terminal.\n\n";
usage(1);
}

View File

@@ -37,7 +37,7 @@ my $settings = {
numbers_on_left => 0,
small_calendars => 0,
fill_entire_page => 0,
wrap_calendar => 0,
media => 'Letter',
width => 0,
height => 0,
@@ -86,6 +86,7 @@ Options:
--media=MEDIA, -mMEDIA Size for specified media
--width=W, -wW Specify media width in 1/72nds of an inch
--height=H, -hH Specify media height in 1/72nds of an inch
--wrap, -y Make calendar fit in at most 5 rows
--title-font=FONT Specify font for calendar title
--header-font=FONT Specify font for weekday names
--daynum-font=FONT Specify font for day numbers
@@ -114,6 +115,7 @@ my $ret = GetOptions('landscape|l' => \$settings->{landscape},
'fill-page|e' => \$settings->{fill_entire_page},
'media|m=s' => \$settings->{media},
'width|w=i' => \$settings->{width},
'wrap|y' => \$settings->{wrap_calendar},
'height|h=i' => \$settings->{height},
'title-font=s' => \$settings->{title_font},
'header-font=s' => \$settings->{header_font},
@@ -175,7 +177,7 @@ if ($settings->{landscape}) {
}
# Don't read from a terminal
if (-t STDIN) {
if (-t STDIN) { ## no critic
print STDERR "I can't read data from a terminal. Please run like this:\n";
print STDERR " remind -pp [options] filename | $me [options] > out.pdf\n";
exit(1);
@@ -249,20 +251,21 @@ sub set_media
sub set_media_from_file
{
my ($fn) = @_;
if (!open(IN, '<', $fn)) {
my $IN;
if (!open($IN, '<', $fn)) {
return 0;
}
while(<IN>) {
while(<$IN>) {
chomp;
s/^\s+//;
s/\s+$//;
next if ($_ eq '');
next if ($_ =~ /^#/);
my $m = $_;
close(IN);
close($IN);
return set_media($m);
}
close(IN);
close($IN);
return 0;
}
@@ -430,6 +433,14 @@ The default is 36.
The size of the margin at the right of the page in 1/72ths of an inch.
The default is 36.
=item --wrap, -y
Modify the calendar so that if it would normally require 6 rows to print,
then the last day (or last two days, as needed) are moved to the
first row of the calendar, and adjust the small calendar positions
as needed. This results in a calendar that only requires 5 rows, but
with the last day or two appearing in the I<first> row.
=item --verbose, -v
Print (on STDERR) the name of the month and year for each month that

View File

@@ -110,7 +110,6 @@ sub read_one_month
$self->{daysinnextmonth} = 0;
$self->{prevmonthyear} = 0;
$self->{nextmonthyear} = 0;
for (my $i=0; $i<=31; $i++) {
$self->{entries}->[$i] = [];
}
@@ -134,7 +133,7 @@ sub read_one_month
$line = $in->getline();
chomp($line);
if ($line =~ /^\S+ \S+ \S+ \S+ \S+ \S+ \S+$/) {
@{$self->{daynames}} = map { s/_/ /g; $_; } (split(/ /, $line));
@{$self->{daynames}} = map { s/_/ /g; $_; } (split(/ /, $line)); ## no critic
} else {
return (undef, "Cannot interpret line: $line");
}
@@ -213,7 +212,7 @@ hash keys found in the newer "remind -pp" JSON output.
sub parse_oldstyle_line
{
my ($self, $line) = @_;
return undef unless $line =~ m|^(\d+)/(\d+)/(\d+) (\S+) (\S+) (\S+) (\S+) (.*)$|;
return unless $line =~ m|^(\d+)/(\d+)/(\d+) (\S+) (\S+) (\S+) (\S+) (.*)$|;
my $hash = {
date => "$1-$2-$3",
@@ -244,6 +243,143 @@ sub parse_oldstyle_line
return $hash;
}
=head2 setup_daymap
Set up the array that maps ($row, $col) to day number (or -1
for rows/cols out of range.)
=cut
sub setup_daymap
{
my ($self, $settings) = @_;
# First column
my $first_col = $self->{firstwkday};
if ($self->{mondayfirst}) {
$first_col--;
if ($first_col < 0) {
$first_col = 6;
}
}
# Last column
my $last_col = ($first_col + $self->{daysinmonth} - 1) % 7;
# Number of rows
my $rows = 1;
my $last_day_on_row = 7 - $first_col;
while ($last_day_on_row < $self->{daysinmonth}) {
$last_day_on_row += 7;
$rows++;
}
# Add a row for small calendars if necessary
if (($settings->{small_calendars} != 0) && ($first_col == 0) && ($last_col == 6)) {
$rows++;
$self->{extra_row} = 1;
} else {
$self->{extra_row} = 0;
}
$self->{rows} = $rows;
$self->{daymap} = [];
$self->{first_col} = $first_col;
$self->{last_col} = $last_col;
for (my $row=0; $row<$rows; $row++) {
for (my $col=0; $col < 7; $col++) {
$self->{daymap}->[$row]->[$col] = -1;
}
}
$self->{nextcal_row} = -1;
$self->{prevcal_row} = -1;
$self->{nextcal_col} = 6;
$self->{prevcal_col} = 0;
# Figure out where to draw the small calendars
my $extra_row = $self->{extra_row};
if ($settings->{small_calendars} == 1) {
if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
$self->{prevcal_row} = $rows-1;
$self->{prevcal_col} = 5;
$self->{nextcal_row} = $rows-1;
$self->{nextcal_col} = 6;
} else {
$self->{prevcal_row} = 0;
$self->{prevcal_col} = 0;
$self->{nextcal_row} = 0;
$self->{nextcal_col} = 1;
}
} elsif ($settings->{small_calendars} == 2) {
if ($first_col >= 2) {
$self->{prevcal_row} = 0;
$self->{prevcal_col} = 0;
$self->{nextcal_row} = 0;
$self->{nextcal_col} = 1;
} else {
$self->{prevcal_row} = $rows-1;
$self->{prevcal_col} = 5;
$self->{nextcal_row} = $rows-1;
$self->{nextcal_col} = 6;
}
} elsif ($settings->{small_calendars} == 3) {
if ($first_col >= 1 && $last_col <= 5) {
$self->{prevcal_row} = 0;
$self->{prevcal_col} = 0;
$self->{nextcal_row} = $rows-1;
$self->{nextcal_col} = 6;
} else {
if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
$self->{prevcal_row} = $rows-1;
$self->{prevcal_col} = 5;
$self->{nextcal_row} = $rows-1;
$self->{nextcal_col} = 6;
} else {
$self->{prevcal_row} = 0;
$self->{prevcal_col} = 0;
$self->{nextcal_row} = 0;
$self->{nextcal_col} = 1;
}
}
}
my $col = $first_col;
my $row = 0;
my $day = 1;
while ($day <= $self->{daysinmonth}) {
$self->{daymap}->[$row]->[$col] = $day;
$day++;
$col++;
if ($col > 6) {
$row++;
$col = 0;
}
}
# Check if we should wrap the calendar
if ($self->{rows} == 6 && $settings->{wrap_calendar}) {
# Move everything in the last row to the first row
my $occupied_col = 0;
for (my $col=0; $col<7; $col++) {
if ($self->{daymap}->[5]->[$col] > 0) {
$self->{daymap}->[0]->[$col] = $self->{daymap}->[5]->[$col];
$occupied_col = $col;
} else {
last;
}
}
if ($settings->{small_calendars}) {
$self->{prevcal_row} = 0;
$self->{prevcal_col} = $occupied_col+1;
$self->{nextcal_row} = 0;
$self->{nextcal_col} = $occupied_col+2;
for (my $col = 6; $col > 0; $col--) {
if ($self->{daymap}->[0]->[$col] < 0) {
$self->{nextcal_col} = $col;
last;
}
}
}
$self->{rows} = 5;
}
}
=head2 read_one_month_pp($in, $specials_accepted)
This function reads one month's worth of data from the file handle
@@ -329,6 +465,7 @@ sub render
{
my ($self, $cr, $settings) = @_;
$self->setup_daymap($settings);
$self->{horiz_lines} = [];
$cr->set_line_cap('square');
my $so_far = $self->draw_title($cr, $settings);
@@ -347,111 +484,25 @@ sub render
$self->{remaining_space} = $settings->{height} - $settings->{margin_bottom} - $so_far;
$self->{minimum_row_height} = $self->{remaining_space} / 9;
# First column
my $first_col = $self->{firstwkday};
if ($self->{mondayfirst}) {
$first_col--;
if ($first_col < 0) {
$first_col = 6;
}
}
# Last column
my $last_col = ($first_col + $self->{daysinmonth} - 1) % 7;
# Number of rows
my $rows = 1;
my $last_day_on_row = 7 - $first_col;
while ($last_day_on_row < $self->{daysinmonth}) {
$last_day_on_row += 7;
$rows++;
}
my $extra_row = 0;
# Add a row for small calendars if necessary
if (($settings->{small_calendars} != 0) && ($first_col == 0) && ($last_col == 6)) {
$rows++;
$extra_row++;
}
# Figure out where to draw the small calendars
my $prevcal_top = 0;
my $nextcal_top = 0;
my $prevcal_bottom = 0;
my $nextcal_bottom = 0;
if ($settings->{small_calendars} == 1) {
if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
$prevcal_bottom = 1;
$nextcal_bottom = 1;
} else {
$prevcal_top = 1;
$nextcal_top = 1;
}
} elsif ($settings->{small_calendars} == 2) {
if ($first_col >= 2) {
$prevcal_top = 1;
$nextcal_top = 1;
} else {
$prevcal_bottom = 1;
$nextcal_bottom = 1;
}
} elsif ($settings->{small_calendars} == 3) {
if ($first_col >= 1 && $last_col <= 5) {
$prevcal_top = 1;
$nextcal_bottom = 1;
} else {
if ($last_col <= 4 || ($last_col == 6 && $extra_row)) {
$prevcal_bottom = 1;
$nextcal_bottom = 1;
} else {
$prevcal_top = 1;
$nextcal_top = 1;
}
}
}
# Row height if we are filling the page
$self->{row_height} = $self->{remaining_space} / $rows;
$self->{row_height} = $self->{remaining_space} / $self->{rows};
my ($start_col, $start_day);
for (my $row = 0; $row < $rows; $row++) {
if ($row == 0) {
$start_day = 1;
$start_col = $first_col;
} else {
$start_col = 0;
}
for (my $row = 0; $row < $self->{rows}; $row++) {
my $old_so_far = $so_far;
$so_far = $self->draw_row($cr, $settings, $so_far, $row, $start_day, $start_col);
$start_day += 7 - $start_col;
$so_far = $self->draw_row($cr, $settings, $so_far, $row);
push(@{$self->{horiz_lines}}, $so_far);
if ($row == 0) {
if ($prevcal_top) {
my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 0, $so_far - $old_so_far, $settings);
$self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size},
$x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
$settings, $self->{prevmonthname}, $self->{daysinprevmonth}, ($first_col + 35 - $self->{daysinprevmonth}) % 7);
}
if ($nextcal_top) {
my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 1, $so_far - $old_so_far, $settings);
$self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size},
$x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
$settings, $self->{nextmonthname}, $self->{daysinnextmonth}, ($last_col + 1) % 7);
}
} elsif ($row == $rows-1) {
if ($prevcal_bottom) {
my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 5, $so_far - $old_so_far, $settings);
$self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size},
$x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
$settings, $self->{prevmonthname}, $self->{daysinprevmonth}, ($first_col + 35 - $self->{daysinprevmonth}) % 7);
}
if ($nextcal_bottom) {
my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, 6, $so_far - $old_so_far, $settings);
$self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size},
$x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
$settings, $self->{nextmonthname}, $self->{daysinnextmonth}, ($last_col + 1) % 7);
}
if ($row == $self->{prevcal_row}) {
my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, $self->{prevcal_col}, $so_far - $old_so_far, $settings);
$self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size},
$x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
$settings, $self->{prevmonthname}, $self->{daysinprevmonth}, ($self->{first_col} + 35 - $self->{daysinprevmonth}) % 7);
}
if ($row == $self->{nextcal_row}) {
my ($x1, $y1, $x2, $y2) = $self->col_box_coordinates($old_so_far, $self->{nextcal_col}, $so_far - $old_so_far, $settings);
$self->draw_small_calendar($cr, $x1 + $settings->{border_size}, $y1 + $settings->{border_size},
$x2 - $x1 - 2*$settings->{border_size}, $y2 - $y1 - 2*$settings->{border_size},
$settings, $self->{nextmonthname}, $self->{daysinnextmonth}, ($self->{last_col} + 1) % 7);
}
}
@@ -495,23 +546,18 @@ calendar row.
=cut
sub draw_row
{
my ($self, $cr, $settings, $so_far, $row, $start_day, $start_col) = @_;
my ($self, $cr, $settings, $so_far, $row) = @_;
my $col = $start_col;
my $day = $start_day;
my $height = 0;
# Preview them to figure out the row height...
if (!$settings->{fill_entire_page}) {
while ($col < 7) {
for (my $col=0; $col<7; $col++) {
my $day = $self->{daymap}->[$row]->[$col];
next if ($day < 1);
my $h = $self->draw_day($cr, $settings, $so_far, $day, $col, 0);
$height = $h if ($h > $height);
$day++;
$col++;
last if ($day > $self->{daysinmonth});
}
$col = $start_col;
$day = $start_day;
} else {
$height = $self->{row_height} - $settings->{border_size} * 2;
}
@@ -520,10 +566,10 @@ sub draw_row
$height = $self->{minimum_row_height};
}
# Now draw for real
while ($col < 7 && $day <= $self->{daysinmonth}) {
for (my $col=0; $col<7; $col++) {
my $day = $self->{daymap}->[$row]->[$col];
next if ($day < 1);
$self->draw_day($cr, $settings, $so_far, $day, $col, $height);
$day++;
$col++;
}
return $so_far + $height + $settings->{border_size};
@@ -908,7 +954,7 @@ sub create_from_stream
return(undef, 'Unable to parse JSON stream');
}
=head2 Remind::PDF::Multi->create_from_stream($json, $specials_accepted)
=head2 Remind::PDF::Multi->create_from_json($json, $specials_accepted)
This method takes data from a JSON string <$json>. C<$specials_accepted>
is a hashref of SPECIAL reminder types to accept; the key is the name of the

15
resources/tkremind.desktop Executable file
View File

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

BIN
resources/tkremind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because it is too large Load Diff

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

View File

@@ -5,7 +5,7 @@
/* The code for generating a calendar. */
/* */
/* 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
@@ -497,7 +501,7 @@ get_month_abbrev(char const *mon)
return buf;
#else
char *s;
wchar_t tmp_buf[128];
wchar_t tmp_buf[128] = {0};
wchar_t *ws;
int i;
int len;
@@ -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;
@@ -621,7 +625,6 @@ Colorize256(int r, int g, int b, int bg, int clamp)
best = (int) i;
}
}
cur = &XTerm256Colors[best];
if (bg) {
sprintf(buf, "\x1B[48;5;%dm", best);
} else {
@@ -668,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];
}
@@ -1038,7 +1041,7 @@ static void DoCalendarOneMonth(void)
printf("\"entries\":[\n");
}
}
while (WriteCalendarRow()) continue;
while (WriteCalendarRow()) /* continue */;
if (PsCal == PSCAL_LEVEL1) {
printf("%s\n", PSEND);
@@ -1219,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);
@@ -1228,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;
@@ -1236,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 */
@@ -1298,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);
@@ -1309,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--;
@@ -1320,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();
@@ -1449,7 +1452,7 @@ static int WriteOneColLine(int col)
}
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
PutWideChar(*ws, NULL);
}
}
e->wc_pos = ws;
@@ -1464,7 +1467,7 @@ static int WriteOneColLine(int col)
if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws);
}
PutWideChar(*ws);
PutWideChar(*ws, NULL);
}
}
}
@@ -1604,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) {
@@ -1612,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);
@@ -1812,8 +1815,12 @@ static int DoCalRem(ParsePtr p, int col)
FindToken(DBufValue(&buf), &tok);
DBufFree(&buf);
if (tok.type == T_Empty || tok.type == T_Comment) {
r = OK;
if (trig.addomit) {
r = AddGlobalOmit(LastTriggerDate);
}
FreeTrig(&trig);
return OK;
return r;
}
if (tok.type != T_RemType || tok.val == SAT_TYPE) {
FreeTrig(&trig);
@@ -1831,6 +1838,10 @@ static int DoCalRem(ParsePtr p, int col)
DBufFree(&buf);
}
trig.typ = tok.val;
/* Convert some SPECIALs back to plain types */
FixSpecialType(&trig);
if (trig.typ == MSG_TYPE ||
trig.typ == CAL_TYPE ||
trig.typ == MSF_TYPE) {
@@ -2204,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);
@@ -2227,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

@@ -7,12 +7,12 @@
/* Define if your <sys/time.h> declares struct tm. */
#undef TM_IN_SYS_TIME
/* Define if you have the <sys/file.h> header file. */
#undef HAVE_SYS_FILE_H
/* 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
@@ -20,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 */
/* */
/***************************************************************/
@@ -117,7 +117,7 @@
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000
#define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */

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 */
/* */
/***************************************************************/
@@ -117,7 +117,7 @@
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000
#define VAL_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */

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 */
/* */
/***************************************************************/
@@ -123,8 +123,12 @@ int DoRem(ParsePtr p)
}
StrnCpy(trig.passthru, DBufValue(&buf), PASSTHRU_LEN);
DBufFree(&buf);
}
trig.typ = tok.val;
}
trig.typ = tok.val;
/* Convert some SPECIALs back to plain types */
FixSpecialType(&trig);
dse = LastTriggerDate;
if (!LastTrigValid || PurgeMode) {
FreeTrig(&trig);
@@ -188,16 +192,18 @@ int DoRem(ParsePtr p)
r = OK;
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
if ( (r=TriggerReminder(p, &trig, &tim, dse)) ) {
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;
}
}
}
}
@@ -344,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);
@@ -370,6 +386,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
}
StrnCpy(trig->passthru, DBufValue(&buf), PASSTHRU_LEN);
}
FixSpecialType(trig);
parsing = 0;
break;
@@ -662,6 +679,9 @@ static int ParseLocalOmit(ParsePtr s, Trigger *t)
break;
default:
if (t->localomit == NO_WD) {
return E_EXPECTING_WEEKDAY;
}
PushToken(DBufValue(&buf), s);
DBufFree(&buf);
return OK;
@@ -893,7 +913,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 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];
@@ -901,8 +921,21 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse)
DynamicBuffer buf, calRow;
DynamicBuffer pre_buf;
char const *s;
char const *msg_command = NULL;
Value v;
if (MsgCommand) {
msg_command = MsgCommand;
}
if (is_queued && QueuedMsgCommand) {
msg_command = QueuedMsgCommand;
}
/* A null command is no command */
if (msg_command && !*msg_command) {
msg_command = NULL;
}
int red = -1, green = -1, blue = -1;
int is_color = 0;
@@ -947,7 +980,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse)
}
/* 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 && !MsgCommand) {
&& !DidMsgReminder && !NextMode && !msg_command && !is_queued) {
DidMsgReminder = 1;
if (!DoSubstFromString(DBufValue(&Banner), &buf,
DSEToday, NO_TIME) &&
@@ -1014,11 +1047,18 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse)
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 */
@@ -1091,7 +1131,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse)
DBufPuts(&buf, Decolorize());
}
if ((!MsgCommand && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) {
if ((!msg_command && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) {
if (DBufPutc(&buf, '\n') != OK) {
DBufFree(&buf);
return E_NO_MEM;
@@ -1113,19 +1153,28 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse)
switch(t->typ) {
case MSG_TYPE:
case PASSTHRU_TYPE:
if (MsgCommand) {
DoMsgCommand(MsgCommand, DBufValue(&buf));
if (msg_command) {
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? */
@@ -1164,7 +1213,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 {
@@ -1172,29 +1221,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;
@@ -1219,7 +1270,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
}
/* Should we trigger the reminder? */
return (dse <= DSEToday + DeltaOffset);
return (dse <= DSEToday);
}
/***************************************************************/
@@ -1350,7 +1401,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;
@@ -1387,7 +1438,7 @@ int DoMsgCommand(char const *cmd, char const *msg)
}
r = OK;
System(DBufValue(&execBuffer));
System(DBufValue(&execBuffer), is_queued);
finished:
DBufFree(&buf);
@@ -1466,3 +1517,25 @@ static int ShouldTriggerBasedOnWarn(Trigger *t, int dse, int *err)
}
}
}
void FixSpecialType(Trigger *t)
{
if (t->typ != PASSTHRU_TYPE) {
return;
}
/* Convert SPECIAL MSG / MSF / RUN / CAL to just plain MSG / MSF / etc */
if (!StrCmpi(t->passthru, "MSG")) {
t->typ = MSG_TYPE;
} else if (!StrCmpi(t->passthru, "MSF")) {
t->typ = MSF_TYPE;
} else if (!StrCmpi(t->passthru, "RUN")) {
t->typ = RUN_TYPE;
} else if (!StrCmpi(t->passthru, "CAL")) {
t->typ = CAL_TYPE;
} else if (!StrCmpi(t->passthru, "PS")) {
t->typ = PS_TYPE;
} else if (!StrCmpi(t->passthru, "PSFILE")) {
t->typ = PSF_TYPE;
}
}

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,11 +46,11 @@
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;
int tim = tt->ttime;
int tim = NO_TIME;
int h, min, hh, ch, cmin, chh;
int i;
char const *pm, *cpm;
@@ -72,6 +72,9 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
FromDSE(dse, &y, &m, &d);
if (tt) {
tim = tt->ttime;
}
if (tim == NO_TIME) tim = curtime;
tdiff = tim - curtime;
adiff = ABS(tdiff);
@@ -196,7 +199,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
mode != CAL_MODE &&
mode != ADVANCE_MODE &&
t->typ != RUN_TYPE &&
!MsgCommand) {
!(MsgCommand && *MsgCommand)) {
if (DBufPutc(dbuf, '\n') != OK) return E_NO_MEM;
}
break;
@@ -363,464 +366,464 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
Eprint("%s", ErrMsg[r]);
}
}
switch(UPPER(c)) {
case 'A':
#ifdef L_A_OVER
L_A_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d %s, %d", get_day_name(dse%7), d,
get_month_name(m), y);
} else {
snprintf(s, sizeof(s), "%s %s, %d %s, %d", DynamicOn, get_day_name(dse%7), d,
get_month_name(m), y);
}
#endif
SHIP_OUT(s);
break;
case 'B':
#ifdef L_B_OVER
L_B_OVER
#else
snprintf(s, sizeof(s), L_INXDAYS, diff);
#endif
SHIP_OUT(s);
break;
case 'C':
#ifdef L_C_OVER
L_C_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s", get_day_name(dse%7));
} else {
snprintf(s, sizeof(s), "%s %s", DynamicOn, get_day_name(dse%7));
}
#endif
SHIP_OUT(s);
break;
case 'D':
#ifdef L_D_OVER
L_D_OVER
#else
snprintf(s, sizeof(s), "%d", d);
#endif
SHIP_OUT(s);
break;
case 'E':
#ifdef L_E_OVER
L_E_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d%c%04d", d, DateSep,
m+1, DateSep, y);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d%c%04d", DynamicOn, d, DateSep,
m+1, DateSep, y);
}
#endif
SHIP_OUT(s);
break;
case 'F':
#ifdef L_F_OVER
L_F_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d%c%04d", m+1, DateSep, d, DateSep, y);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d%c%04d", DynamicOn, m+1, DateSep, d, DateSep, y);
}
#endif
SHIP_OUT(s);
break;
case 'G':
#ifdef L_G_OVER
L_G_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d %s", get_day_name(dse%7), d, get_month_name(m));
} else {
snprintf(s, sizeof(s), "%s %s, %d %s", DynamicOn, get_day_name(dse%7), d, get_month_name(m));
}
#endif
SHIP_OUT(s);
break;
case 'H':
#ifdef L_H_OVER
L_H_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d", d, DateSep, m+1);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicOn, d, DateSep, m+1);
}
#endif
SHIP_OUT(s);
break;
case 'I':
#ifdef L_I_OVER
L_I_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d", m+1, DateSep, d);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicOn, m+1, DateSep, d);
}
#endif
SHIP_OUT(s);
break;
case 'J':
#ifdef L_J_OVER
L_J_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %s %d%s, %d", get_day_name(dse%7),
get_month_name(m), d, plu, y);
} else {
snprintf(s, sizeof(s), "%s %s, %s %d%s, %d", DynamicOn, get_day_name(dse%7),
get_month_name(m), d, plu, y);
}
#endif
SHIP_OUT(s);
break;
case 'K':
#ifdef L_K_OVER
L_K_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %s %d%s", get_day_name(dse%7),
get_month_name(m), d, plu);
} else {
snprintf(s, sizeof(s), "%s %s, %s %d%s", DynamicOn, get_day_name(dse%7),
get_month_name(m), d, plu);
}
#endif
SHIP_OUT(s);
break;
case 'L':
#ifdef L_L_OVER
L_L_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%04d%c%02d%c%02d", y, DateSep, m+1, DateSep, d);
} else {
snprintf(s, sizeof(s), "%s %04d%c%02d%c%02d", DynamicOn, y, DateSep, m+1, DateSep, d);
}
#endif
SHIP_OUT(s);
break;
case 'M':
#ifdef L_M_OVER
L_M_OVER
#else
snprintf(s, sizeof(s), "%s", get_month_name(m));
#endif
SHIP_OUT(s);
break;
case 'N':
#ifdef L_N_OVER
L_N_OVER
#else
snprintf(s, sizeof(s), "%d", m+1);
#endif
SHIP_OUT(s);
break;
case 'O':
#ifdef L_O_OVER
L_O_OVER
#else
if (RealToday == DSEToday) snprintf(s, sizeof(s), " (%s)", DynamicToday);
else *s = 0;
#endif
SHIP_OUT(s);
break;
case 'P':
#ifdef L_P_OVER
L_P_OVER
#else
snprintf(s, sizeof(s), "%s", (diff == 1 ? "" : L_PLURAL));
#endif
SHIP_OUT(s);
break;
case 'Q':
#ifdef L_Q_OVER
L_Q_OVER
#else
snprintf(s, sizeof(s), "%s", (diff == 1 ? "'s" : "s'"));
#endif
SHIP_OUT(s);
break;
case 'R':
#ifdef L_R_OVER
L_R_OVER
#else
snprintf(s, sizeof(s), "%02d", d);
#endif
SHIP_OUT(s);
break;
case 'S':
#ifdef L_S_OVER
L_S_OVER
#else
snprintf(s, sizeof(s), "%s", plu);
#endif
SHIP_OUT(s);
break;
case 'T':
#ifdef L_T_OVER
L_T_OVER
#else
snprintf(s, sizeof(s), "%02d", m+1);
#endif
SHIP_OUT(s);
break;
case 'U':
#ifdef L_U_OVER
L_U_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d%s %s, %d", get_day_name(dse%7), d,
plu, get_month_name(m), y);
} else {
snprintf(s, sizeof(s), "%s %s, %d%s %s, %d", DynamicOn, get_day_name(dse%7), d,
plu, get_month_name(m), y);
}
#endif
SHIP_OUT(s);
break;
case 'V':
#ifdef L_V_OVER
L_V_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d%s %s", get_day_name(dse%7), d, plu,
get_month_name(m));
} else {
snprintf(s, sizeof(s), "%s %s, %d%s %s", DynamicOn, get_day_name(dse%7), d, plu,
get_month_name(m));
}
#endif
SHIP_OUT(s);
break;
case 'W':
#ifdef L_W_OVER
L_W_OVER
#else
snprintf(s, sizeof(s), "%s", get_day_name(dse%7));
#endif
SHIP_OUT(s);
break;
case 'X':
#ifdef L_X_OVER
L_X_OVER
#else
snprintf(s, sizeof(s), "%d", diff);
#endif
SHIP_OUT(s);
break;
case 'Y':
#ifdef L_Y_OVER
L_Y_OVER
#else
snprintf(s, sizeof(s), "%d", y);
#endif
SHIP_OUT(s);
break;
case 'Z':
#ifdef L_Z_OVER
L_Z_OVER
#else
snprintf(s, sizeof(s), "%d", y % 100);
#endif
SHIP_OUT(s);
break;
case '1':
#ifdef L_1_OVER
L_1_OVER
#else
if (tdiff == 0)
snprintf(s, sizeof(s), "%s", DynamicNow);
else if (hdiff == 0)
snprintf(s, sizeof(s), "%d %s%s %s", mdiff, DynamicMinute, mplu, when);
else if (mdiff == 0)
snprintf(s, sizeof(s), "%d %s%s %s", hdiff, DynamicHour, hplu, when);
else
snprintf(s, sizeof(s), "%d %s%s %s %d %s%s %s", hdiff, DynamicHour, hplu,
DynamicAnd, mdiff, DynamicMinute, mplu, when);
#endif
SHIP_OUT(s);
break;
case '2':
#ifdef L_2_OVER
L_2_OVER
#else
if (altmode == '*') {
snprintf(s, sizeof(s), "%d%c%02d%s", hh, TimeSep, min, pm);
} else {
snprintf(s, sizeof(s), "%s %d%c%02d%s", DynamicAt, hh, TimeSep, min, pm);
}
#endif
SHIP_OUT(s);
break;
case '3':
#ifdef L_3_OVER
L_3_OVER
#else
if (altmode == '*') {
snprintf(s, sizeof(s), "%02d%c%02d", h, TimeSep, min);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicAt, h, TimeSep, min);
}
#endif
SHIP_OUT(s);
break;
case '4':
#ifdef L_4_OVER
L_4_OVER
#else
snprintf(s, sizeof(s), "%d", tdiff);
#endif
SHIP_OUT(s);
break;
case '5':
#ifdef L_5_OVER
L_5_OVER
#else
snprintf(s, sizeof(s), "%d", adiff);
#endif
SHIP_OUT(s);
break;
case '6':
#ifdef L_6_OVER
L_6_OVER
#else
snprintf(s, sizeof(s), "%s", when);
#endif
SHIP_OUT(s);
break;
case '7':
#ifdef L_7_OVER
L_7_OVER
#else
snprintf(s, sizeof(s), "%d", hdiff);
#endif
SHIP_OUT(s);
break;
case '8':
#ifdef L_8_OVER
L_8_OVER
#else
snprintf(s, sizeof(s), "%d", mdiff);
#endif
SHIP_OUT(s);
break;
case '9':
#ifdef L_9_OVER
L_9_OVER
#else
snprintf(s, sizeof(s), "%s", mplu);
#endif
SHIP_OUT(s);
break;
case '0':
#ifdef L_0_OVER
L_0_OVER
#else
snprintf(s, sizeof(s), "%s", hplu);
#endif
SHIP_OUT(s);
break;
case '!':
#ifdef L_BANG_OVER
L_BANG_OVER
#else
snprintf(s, sizeof(s), "%s", (tdiff >= 0 ? DynamicIs : DynamicWas));
#endif
SHIP_OUT(s);
break;
case '@':
#ifdef L_AT_OVER
L_AT_OVER
#else
snprintf(s, sizeof(s), "%d%c%02d%s", chh, TimeSep, cmin, cpm);
#endif
SHIP_OUT(s);
break;
case '#':
#ifdef L_HASH_OVER
L_HASH_OVER
#else
snprintf(s, sizeof(s), "%02d%c%02d", ch, TimeSep, cmin);
#endif
SHIP_OUT(s);
break;
case '_':
if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != CAL_MODE && mode != ADVANCE_MODE && !(MsgCommand && *MsgCommand))) {
snprintf(s, sizeof(s), "%s", NL);
} else {
snprintf(s, sizeof(s), " ");
}
SHIP_OUT(s);
break;
case QUOTE_MARKER:
/* Swallow any QUOTE_MARKERs which may somehow creep in... */
break;
case '"':
if (DontSuppressQuoteMarkers) {
if (DBufPutc(dbuf, '%') != OK) return E_NO_MEM;
if (DBufPutc(dbuf, c) != OK) return E_NO_MEM;
} else {
if (DBufPutc(dbuf, QUOTE_MARKER) != OK) return E_NO_MEM;
has_quote = 1;
}
break;
default:
if (DBufPutc(dbuf, c) != OK) return E_NO_MEM;
}
}
if (!done) switch(UPPER(c)) {
case 'A':
#ifdef L_A_OVER
L_A_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d %s, %d", get_day_name(dse%7), d,
get_month_name(m), y);
} else {
snprintf(s, sizeof(s), "%s %s, %d %s, %d", DynamicOn, get_day_name(dse%7), d,
get_month_name(m), y);
}
#endif
SHIP_OUT(s);
break;
case 'B':
#ifdef L_B_OVER
L_B_OVER
#else
snprintf(s, sizeof(s), L_INXDAYS, diff);
#endif
SHIP_OUT(s);
break;
case 'C':
#ifdef L_C_OVER
L_C_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s", get_day_name(dse%7));
} else {
snprintf(s, sizeof(s), "%s %s", DynamicOn, get_day_name(dse%7));
}
#endif
SHIP_OUT(s);
break;
case 'D':
#ifdef L_D_OVER
L_D_OVER
#else
snprintf(s, sizeof(s), "%d", d);
#endif
SHIP_OUT(s);
break;
case 'E':
#ifdef L_E_OVER
L_E_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d%c%04d", d, DateSep,
m+1, DateSep, y);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d%c%04d", DynamicOn, d, DateSep,
m+1, DateSep, y);
}
#endif
SHIP_OUT(s);
break;
case 'F':
#ifdef L_F_OVER
L_F_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d%c%04d", m+1, DateSep, d, DateSep, y);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d%c%04d", DynamicOn, m+1, DateSep, d, DateSep, y);
}
#endif
SHIP_OUT(s);
break;
case 'G':
#ifdef L_G_OVER
L_G_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d %s", get_day_name(dse%7), d, get_month_name(m));
} else {
snprintf(s, sizeof(s), "%s %s, %d %s", DynamicOn, get_day_name(dse%7), d, get_month_name(m));
}
#endif
SHIP_OUT(s);
break;
case 'H':
#ifdef L_H_OVER
L_H_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d", d, DateSep, m+1);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicOn, d, DateSep, m+1);
}
#endif
SHIP_OUT(s);
break;
case 'I':
#ifdef L_I_OVER
L_I_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%02d%c%02d", m+1, DateSep, d);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicOn, m+1, DateSep, d);
}
#endif
SHIP_OUT(s);
break;
case 'J':
#ifdef L_J_OVER
L_J_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %s %d%s, %d", get_day_name(dse%7),
get_month_name(m), d, plu, y);
} else {
snprintf(s, sizeof(s), "%s %s, %s %d%s, %d", DynamicOn, get_day_name(dse%7),
get_month_name(m), d, plu, y);
}
#endif
SHIP_OUT(s);
break;
case 'K':
#ifdef L_K_OVER
L_K_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %s %d%s", get_day_name(dse%7),
get_month_name(m), d, plu);
} else {
snprintf(s, sizeof(s), "%s %s, %s %d%s", DynamicOn, get_day_name(dse%7),
get_month_name(m), d, plu);
}
#endif
SHIP_OUT(s);
break;
case 'L':
#ifdef L_L_OVER
L_L_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%04d%c%02d%c%02d", y, DateSep, m+1, DateSep, d);
} else {
snprintf(s, sizeof(s), "%s %04d%c%02d%c%02d", DynamicOn, y, DateSep, m+1, DateSep, d);
}
#endif
SHIP_OUT(s);
break;
case 'M':
#ifdef L_M_OVER
L_M_OVER
#else
snprintf(s, sizeof(s), "%s", get_month_name(m));
#endif
SHIP_OUT(s);
break;
case 'N':
#ifdef L_N_OVER
L_N_OVER
#else
snprintf(s, sizeof(s), "%d", m+1);
#endif
SHIP_OUT(s);
break;
case 'O':
#ifdef L_O_OVER
L_O_OVER
#else
if (RealToday == DSEToday) snprintf(s, sizeof(s), " (%s)", DynamicToday);
else *s = 0;
#endif
SHIP_OUT(s);
break;
case 'P':
#ifdef L_P_OVER
L_P_OVER
#else
snprintf(s, sizeof(s), "%s", (diff == 1 ? "" : L_PLURAL));
#endif
SHIP_OUT(s);
break;
case 'Q':
#ifdef L_Q_OVER
L_Q_OVER
#else
snprintf(s, sizeof(s), "%s", (diff == 1 ? "'s" : "s'"));
#endif
SHIP_OUT(s);
break;
case 'R':
#ifdef L_R_OVER
L_R_OVER
#else
snprintf(s, sizeof(s), "%02d", d);
#endif
SHIP_OUT(s);
break;
case 'S':
#ifdef L_S_OVER
L_S_OVER
#else
snprintf(s, sizeof(s), "%s", plu);
#endif
SHIP_OUT(s);
break;
case 'T':
#ifdef L_T_OVER
L_T_OVER
#else
snprintf(s, sizeof(s), "%02d", m+1);
#endif
SHIP_OUT(s);
break;
case 'U':
#ifdef L_U_OVER
L_U_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d%s %s, %d", get_day_name(dse%7), d,
plu, get_month_name(m), y);
} else {
snprintf(s, sizeof(s), "%s %s, %d%s %s, %d", DynamicOn, get_day_name(dse%7), d,
plu, get_month_name(m), y);
}
#endif
SHIP_OUT(s);
break;
case 'V':
#ifdef L_V_OVER
L_V_OVER
#else
if (altmode == '*' || !strcmp(DynamicOn, "")) {
snprintf(s, sizeof(s), "%s, %d%s %s", get_day_name(dse%7), d, plu,
get_month_name(m));
} else {
snprintf(s, sizeof(s), "%s %s, %d%s %s", DynamicOn, get_day_name(dse%7), d, plu,
get_month_name(m));
}
#endif
SHIP_OUT(s);
break;
case 'W':
#ifdef L_W_OVER
L_W_OVER
#else
snprintf(s, sizeof(s), "%s", get_day_name(dse%7));
#endif
SHIP_OUT(s);
break;
case 'X':
#ifdef L_X_OVER
L_X_OVER
#else
snprintf(s, sizeof(s), "%d", diff);
#endif
SHIP_OUT(s);
break;
case 'Y':
#ifdef L_Y_OVER
L_Y_OVER
#else
snprintf(s, sizeof(s), "%d", y);
#endif
SHIP_OUT(s);
break;
case 'Z':
#ifdef L_Z_OVER
L_Z_OVER
#else
snprintf(s, sizeof(s), "%d", y % 100);
#endif
SHIP_OUT(s);
break;
case '1':
#ifdef L_1_OVER
L_1_OVER
#else
if (tdiff == 0)
snprintf(s, sizeof(s), "%s", DynamicNow);
else if (hdiff == 0)
snprintf(s, sizeof(s), "%d %s%s %s", mdiff, DynamicMinute, mplu, when);
else if (mdiff == 0)
snprintf(s, sizeof(s), "%d %s%s %s", hdiff, DynamicHour, hplu, when);
else
snprintf(s, sizeof(s), "%d %s%s %s %d %s%s %s", hdiff, DynamicHour, hplu,
DynamicAnd, mdiff, DynamicMinute, mplu, when);
#endif
SHIP_OUT(s);
break;
case '2':
#ifdef L_2_OVER
L_2_OVER
#else
if (altmode == '*') {
snprintf(s, sizeof(s), "%d%c%02d%s", hh, TimeSep, min, pm);
} else {
snprintf(s, sizeof(s), "%s %d%c%02d%s", DynamicAt, hh, TimeSep, min, pm);
}
#endif
SHIP_OUT(s);
break;
case '3':
#ifdef L_3_OVER
L_3_OVER
#else
if (altmode == '*') {
snprintf(s, sizeof(s), "%02d%c%02d", h, TimeSep, min);
} else {
snprintf(s, sizeof(s), "%s %02d%c%02d", DynamicAt, h, TimeSep, min);
}
#endif
SHIP_OUT(s);
break;
case '4':
#ifdef L_4_OVER
L_4_OVER
#else
snprintf(s, sizeof(s), "%d", tdiff);
#endif
SHIP_OUT(s);
break;
case '5':
#ifdef L_5_OVER
L_5_OVER
#else
snprintf(s, sizeof(s), "%d", adiff);
#endif
SHIP_OUT(s);
break;
case '6':
#ifdef L_6_OVER
L_6_OVER
#else
snprintf(s, sizeof(s), "%s", when);
#endif
SHIP_OUT(s);
break;
case '7':
#ifdef L_7_OVER
L_7_OVER
#else
snprintf(s, sizeof(s), "%d", hdiff);
#endif
SHIP_OUT(s);
break;
case '8':
#ifdef L_8_OVER
L_8_OVER
#else
snprintf(s, sizeof(s), "%d", mdiff);
#endif
SHIP_OUT(s);
break;
case '9':
#ifdef L_9_OVER
L_9_OVER
#else
snprintf(s, sizeof(s), "%s", mplu);
#endif
SHIP_OUT(s);
break;
case '0':
#ifdef L_0_OVER
L_0_OVER
#else
snprintf(s, sizeof(s), "%s", hplu);
#endif
SHIP_OUT(s);
break;
case '!':
#ifdef L_BANG_OVER
L_BANG_OVER
#else
snprintf(s, sizeof(s), "%s", (tdiff >= 0 ? DynamicIs : DynamicWas));
#endif
SHIP_OUT(s);
break;
case '@':
#ifdef L_AT_OVER
L_AT_OVER
#else
snprintf(s, sizeof(s), "%d%c%02d%s", chh, TimeSep, cmin, cpm);
#endif
SHIP_OUT(s);
break;
case '#':
#ifdef L_HASH_OVER
L_HASH_OVER
#else
snprintf(s, sizeof(s), "%02d%c%02d", ch, TimeSep, cmin);
#endif
SHIP_OUT(s);
break;
case '_':
if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != CAL_MODE && mode != ADVANCE_MODE && !MsgCommand)) {
snprintf(s, sizeof(s), "%s", NL);
} else {
snprintf(s, sizeof(s), " ");
}
SHIP_OUT(s);
break;
case QUOTE_MARKER:
/* Swallow any QUOTE_MARKERs which may somehow creep in... */
break;
case '"':
if (DontSuppressQuoteMarkers) {
if (DBufPutc(dbuf, '%') != OK) return E_NO_MEM;
if (DBufPutc(dbuf, c) != OK) return E_NO_MEM;
} else {
if (DBufPutc(dbuf, QUOTE_MARKER) != OK) return E_NO_MEM;
has_quote = 1;
}
break;
default:
if (DBufPutc(dbuf, c) != OK) return E_NO_MEM;
}
if (isupper(c)) {
os = DBufValue(dbuf);
os += strlen(os) - strlen(s);
@@ -894,7 +897,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 */
/* */
/***************************************************************/
@@ -121,6 +121,8 @@
#define E_STRING_TOO_LONG 101
#define E_TIME_TWICE 102
#define E_DURATION_NO_AT 103
#define E_EXPECTING_WEEKDAY 104
#ifdef MK_GLOBALS
#undef EXTERN
#define EXTERN
@@ -129,6 +131,10 @@
#define EXTERN extern
#endif
#define STR(X) STR2(X)
#define STR2(X) #X
#ifndef L_ERR_OVERRIDE
EXTERN char *ErrMsg[]
@@ -165,7 +171,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 +195,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",
@@ -237,7 +243,8 @@ EXTERN char *ErrMsg[]
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Expecting weekday name"
}
#endif /* MK_GLOBALS */
;

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 */
/* */
/***************************************************************/
@@ -87,10 +87,11 @@ extern BuiltinFunc Func[];
static Operator OpStack[OP_STACK_SIZE];
static int OpStackPtr = 0;
static int OpStackHiWater = 0;
/* ValStack can't be static - needed by funcs.c */
Value ValStack[VAL_STACK_SIZE];
int ValStackPtr = 0;
Value ValStack[VAL_STACK_SIZE];
int ValStackPtr = 0;
int ValStackHiWater = 0;
/***************************************************************/
/* */
@@ -376,12 +377,10 @@ int Evaluate(char const **s, Var *locals, ParsePtr p)
DBufFree(&ExprBuf);
r = Evaluate(s, locals, p); /* Leaves the last parsed token in ExprBuf */
if (r) return r;
r = OK;
if (*DBufValue(&ExprBuf) != ')') {
if (*DBufValue(&ExprBuf) != ')') {
DBufFree(&ExprBuf);
return E_MISS_RIGHT_PAREN;
}
if (r) return r;
} else if (*DBufValue(&ExprBuf) == '+') {
continue; /* Ignore unary + */
}
@@ -946,7 +945,8 @@ static int Subtract(void)
/***************************************************************/
static int Multiply(void)
{
Value v1, v2;
Value v1, v2, v3;
char *ptr;
int r;
PopValStack(v2);
@@ -966,6 +966,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;
}
@@ -1431,3 +1486,11 @@ int FnPopValStack(Value *val)
return OK;
}
}
void DebugExitFunc(void)
{
if (DebugFlag & DB_EXPR_STACKS) {
fprintf(stderr, "Operator stack high water: %d\n", OpStackHiWater);
fprintf(stderr, " Value stack high water: %d\n", ValStackHiWater);
}
}

View File

@@ -5,19 +5,20 @@
/* Contains a few definitions used by expression evaluator. */
/* */
/* 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 '['
@@ -32,10 +33,8 @@
for speed. BEWARE: These macros invoke return if an error happens ! */
#define PushOpStack(op) \
if (OpStackPtr >= OP_STACK_SIZE) \
return E_OP_STK_OVER; \
else \
OpStack[OpStackPtr++] = (op)
do { if (OpStackPtr >= OP_STACK_SIZE) return E_OP_STK_OVER; \
else { OpStack[OpStackPtr++] = (op); if (OpStackPtr > OpStackHiWater) OpStackHiWater = OpStackPtr; } } while(0)
#define PopOpStack(op) \
if (OpStackPtr <= 0) \
@@ -44,10 +43,13 @@ else \
(op) = OpStack[--OpStackPtr]
#define PushValStack(val) \
if (ValStackPtr >= VAL_STACK_SIZE) \
return E_VA_STK_OVER; \
else \
ValStack[ValStackPtr++] = (val)
do { if (ValStackPtr >= VAL_STACK_SIZE) { \
DestroyValue(val); \
return E_VA_STK_OVER; \
} else { \
ValStack[ValStackPtr++] = (val); \
if (ValStackPtr > ValStackHiWater) ValStackHiWater = ValStackPtr; \
} } while (0);
#define PopValStack(val) \
if (ValStackPtr <= 0) \

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>
@@ -42,8 +42,8 @@
/* Convenient macros for closing files */
#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
#define PCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
#define FCLOSE(fp) ((((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
#define PCLOSE(fp) ((((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
/* Define the structures needed by the file caching system */
typedef struct cache {
@@ -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,11 +6,10 @@
/* 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 */
/* */
/***************************************************************/
#include "version.h"
#include "config.h"
@@ -27,13 +26,9 @@
#include <ctype.h>
#include <math.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
@@ -70,8 +65,6 @@ static int FAbs (func_info *);
static int FAccess (func_info *);
static int FAmpm (func_info *);
static int FAnsicolor (func_info *);
static int FTrig (func_info *);
static int FIsAny (func_info *);
static int FArgs (func_info *);
static int FAsc (func_info *);
static int FBaseyr (func_info *);
@@ -101,8 +94,11 @@ static int FHebday (func_info *);
static int FHebmon (func_info *);
static int FHebyear (func_info *);
static int FHour (func_info *);
static int FHtmlEscape (func_info *);
static int FHtmlStriptags (func_info *);
static int FIif (func_info *);
static int FIndex (func_info *);
static int FIsAny (func_info *);
static int FIsdst (func_info *);
static int FIsleap (func_info *);
static int FIsomitted (func_info *);
@@ -119,11 +115,13 @@ static int FMoondate (func_info *);
static int FMoondatetime (func_info *);
static int FMoonphase (func_info *);
static int FMoontime (func_info *);
static int FMultiTrig (func_info *);
static int FNDawn (func_info *);
static int FNDusk (func_info *);
static int FNonomitted (func_info *);
static int FNow (func_info *);
static int FOrd (func_info *);
static int FOrthodoxeaster (func_info *);
static int FOstype (func_info *);
static int FPad (func_info *);
static int FPlural (func_info *);
@@ -135,6 +133,7 @@ static int FRealtoday (func_info *);
static int FRows (func_info *);
static int FSgn (func_info *);
static int FShell (func_info *);
static int FShellescape (func_info *);
static int FSlide (func_info *);
static int FSoleq (func_info *);
static int FStdout (func_info *);
@@ -157,9 +156,9 @@ static int FTrigeventstart (func_info *);
static int FTrigfrom (func_info *);
static int FTrigger (func_info *);
static int FTrigpriority (func_info *);
static int FTrigtags (func_info *);
static int FTrigrep (func_info *);
static int FTrigscanfrom (func_info *);
static int FTrigtags (func_info *);
static int FTrigtime (func_info *);
static int FTrigtimedelta (func_info *);
static int FTrigtimerep (func_info *);
@@ -167,15 +166,14 @@ static int FTriguntil (func_info *);
static int FTrigvalid (func_info *);
static int FTypeof (func_info *);
static int FTzconvert (func_info *);
static int FUpper (func_info *);
static int FUTCToLocal (func_info *);
static int FUpper (func_info *);
static int FValue (func_info *);
static int FVersion (func_info *);
static int FWeekno (func_info *);
static int FWkday (func_info *);
static int FWkdaynum (func_info *);
static int FYear (func_info *);
static int FShellescape (func_info *);
static int CleanUpAfterFunc (func_info *);
static int CheckArgs (BuiltinFunc *f, int nargs);
@@ -196,6 +194,7 @@ static int CacheHebYear, CacheHebMon, CacheHebDay;
/* We need access to the value stack */
extern Value ValStack[];
extern int ValStackPtr;
extern int ValStackHiWater;
/* Macro for accessing arguments from the value stack - args are numbered
from 0 to (Nargs - 1) */
@@ -252,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 },
@@ -264,6 +263,8 @@ BuiltinFunc Func[] = {
{ "hebmon", 1, 1, 0, FHebmon },
{ "hebyear", 1, 1, 0, FHebyear },
{ "hour", 1, 1, 1, FHour },
{ "htmlescape", 1, 1, 1, FHtmlEscape },
{ "htmlstriptags",1, 1, 1, FHtmlStriptags },
{ "iif", 1, NO_MAX, 1, FIif },
{ "index", 2, 3, 1, FIndex },
{ "isany", 1, NO_MAX, 1, FIsAny },
@@ -283,11 +284,13 @@ BuiltinFunc Func[] = {
{ "moondatetime", 1, 3, 0, FMoondatetime },
{ "moonphase", 0, 2, 0, FMoonphase },
{ "moontime", 1, 3, 0, FMoontime },
{ "multitrig", 1, NO_MAX, 0, FMultiTrig },
{ "ndawn", 0, 1, 0, FNDawn},
{ "ndusk", 0, 1, 0, FNDusk},
{ "nonomitted", 2, NO_MAX, 0, FNonomitted },
{ "now", 0, 0, 0, FNow },
{ "ord", 1, 1, 1, FOrd },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster },
{ "ostype", 0, 0, 1, FOstype },
{ "pad", 3, 4, 1, FPad },
{ "plural", 1, 3, 1, FPlural },
@@ -912,9 +915,14 @@ static int FAbs(func_info *info)
ASSERT_TYPE(0, INT_TYPE);
v = ARGV(0);
if (v == INT_MIN) return E_2HIGH;
RetVal.type = INT_TYPE;
RETVAL = (v < 0) ? (-v) : v;
v = RETVAL;
/* The following test is probably redundant given the test
for v == INT_MIN above, but I'll leave it in just in case. */
if (v < 0) return E_2HIGH;
return OK;
}
@@ -955,7 +963,7 @@ static int parse_color_helper(char const *str, int *r, int *g, int *b)
static int FAnsicolor(func_info *info)
{
int r=0, g=0, b=0, bg=0, clamp=1;
int status = 0;
int status;
int index = 0;
bg = 0;
clamp = 1;
@@ -1160,15 +1168,27 @@ static int FPad(func_info *info)
if (Nargs < 4 || !ARGV(3)) {
/* Pad on the LEFT */
for (i=0; i<wantlen-len; i++) {
DBufPutc(&dbuf, *s++);
if (DBufPutc(&dbuf, *s++) != OK) {
DBufFree(&dbuf);
return E_NO_MEM;
}
if (!*s) s = ARGSTR(1);
}
DBufPuts(&dbuf, ARGSTR(0));
if (DBufPuts(&dbuf, ARGSTR(0)) != OK) {
DBufFree(&dbuf);
return E_NO_MEM;
}
} else {
/* Pad on the RIGHT */
DBufPuts(&dbuf, ARGSTR(0));
if (DBufPuts(&dbuf, ARGSTR(0)) != OK) {
DBufFree(&dbuf);
return E_NO_MEM;
}
for (i=0; i<wantlen-len; i++) {
DBufPutc(&dbuf, *s++);
if (DBufPutc(&dbuf, *s++) != OK) {
DBufFree(&dbuf);
return E_NO_MEM;
}
if (!*s) s = ARGSTR(1);
}
}
@@ -1402,28 +1422,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;
}
@@ -2241,6 +2261,92 @@ static int FHebyear(func_info *info)
RETVAL = y;
return OK;
}
/****************************************************************/
/* */
/* htmlescape - replace <. > and & by &lt; &gt; and &amp; */
/* */
/****************************************************************/
static int FHtmlEscape(func_info *info)
{
DynamicBuffer dbuf;
char const *s;
int r;
ASSERT_TYPE(0, STR_TYPE);
DBufInit(&dbuf);
s = ARGSTR(0);
while(*s) {
switch(*s) {
case '<':
r = DBufPuts(&dbuf, "&lt;");
break;
case '>':
r = DBufPuts(&dbuf, "&gt;");
break;
case '&':
r = DBufPuts(&dbuf, "&amp;");
break;
default:
r = DBufPutc(&dbuf, *s);
break;
}
if (r != OK) {
DBufFree(&dbuf);
return r;
}
s++;
}
r = RetStrVal(DBufValue(&dbuf), info);
DBufFree(&dbuf);
return r;
}
/****************************************************************/
/* */
/* htmlstriptags - strip out HTML tags from a string */
/* */
/****************************************************************/
static int FHtmlStriptags(func_info *info)
{
DynamicBuffer dbuf;
char const *s;
int r = OK;
int in_tag = 0;
ASSERT_TYPE(0, STR_TYPE);
DBufInit(&dbuf);
s = ARGSTR(0);
while(*s) {
if (!in_tag) {
if (*s == '<') {
in_tag = 1;
} else {
r = DBufPutc(&dbuf, *s);
}
} else {
if (*s == '>') {
in_tag = 0;
}
}
if (r != OK) {
DBufFree(&dbuf);
return r;
}
s++;
}
r = RetStrVal(DBufValue(&dbuf), info);
DBufFree(&dbuf);
return r;
}
/****************************************************************/
/* */
/* FEasterdate - calc. easter Sunday from a year. */
@@ -2259,13 +2365,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 */
@@ -2292,6 +2402,49 @@ static int FEasterdate(func_info *info)
return OK;
}
/****************************************************************/
/* */
/* FOrthodoxeaster - calc. Orthodox easter Sunday */
/* */
/* From Meeus, Astronomical Algorithms */
/* */
/****************************************************************/
static int FOrthodoxeaster(func_info *info)
{
int y, m, d;
int a, b, c, dd, e, f, dse;
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;
b = y % 7;
c = y % 19;
dd = (19 * c + 15) % 30;
e = (2*a + 4*b - dd + 34) % 7;
f = dd + e + 114;
m = (f / 31) - 1;
d = (f % 31) + 1;
dse = DSE(y, m, d);
dse += JulianToGregorianOffset(y, m);
RetVal.type = DATE_TYPE;
RETVAL = dse;
y++;
} while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0)));
return OK;
}
/***************************************************************/
/* */
/* FIsdst and FMinsfromutc */
@@ -2348,7 +2501,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));
@@ -3146,6 +3299,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);
@@ -3155,8 +3310,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;
@@ -3167,14 +3327,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;
@@ -3191,6 +3351,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)) ||
@@ -3199,10 +3361,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;
@@ -3211,11 +3383,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;
@@ -3348,6 +3521,57 @@ FEvalTrig(func_info *info)
return OK;
}
static int
FMultiTrig(func_info *info)
{
Parser p;
Trigger trig;
TimeTrig tim;
int dse;
int r;
int i;
int earliest = -1;
RetVal.type = DATE_TYPE;
RETVAL = 0;
for (i=0; i<Nargs; i++) {
ASSERT_TYPE(i, STR_TYPE);
}
for (i=0; i<Nargs; i++) {
CreateParser(ARGSTR(i), &p);
p.allownested = 0;
r = ParseRem(&p, &trig, &tim, 0);
if (r) {
DestroyParser(&p);
return r;
}
if (trig.typ != NO_TYPE) {
DestroyParser(&p);
FreeTrig(&trig);
return E_PARSE_ERR;
}
if (tim.ttime != NO_TIME) {
Eprint("Cannot use AT clause in multitrig() function");
return E_PARSE_ERR;
}
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0);
DestroyParser(&p);
if (r != E_CANT_TRIG) {
if (dse < earliest || earliest < 0) {
earliest = dse;
}
}
FreeTrig(&trig);
}
if (earliest >= 0) {
RETVAL = earliest;
}
return OK;
}
static int LastTrig = 0;
static int
FTrig(func_info *info)
@@ -3385,20 +3609,18 @@ FTrig(func_info *info)
return E_PARSE_ERR;
}
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 0);
DestroyParser(&p);
if (r == E_CANT_TRIG) {
DestroyParser(&p);
FreeTrig(&trig);
continue;
}
if (ShouldTriggerReminder(&trig, &tim, dse, &r)) {
LastTrig = dse;
RETVAL = dse;
DestroyParser(&p);
FreeTrig(&trig);
return OK;
}
DestroyParser(&p);
FreeTrig(&trig);
}
return OK;
@@ -3410,6 +3632,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);
@@ -3417,6 +3641,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;
@@ -3424,7 +3649,7 @@ rows_or_cols(func_info *info, int want_rows)
} else {
RETVAL = -1;
}
if (fd != STDOUT_FILENO) {
if (opened) {
close(fd);
}
return OK;
@@ -3508,11 +3733,8 @@ mean_december_solstice(double y)
}
/* Cosine of an angle specified in degrees */
static double
cosd(double degrees)
{
return cos((degrees / 180.0) * 3.14159265358979);
}
#define PI_BY_180 0.01745329251994329576923690768
#define cosd(theta) cos( (theta) * PI_BY_180)
/* Astronomical Algorithms by Meeus, p. 179
These weird periodic components refine the mean solstice/equinox dates
@@ -3593,7 +3815,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 */
@@ -3641,8 +3863,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

@@ -39,7 +39,7 @@ EXTERN FILE *ErrFp;
#define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 ))
#define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y))
#define DestroyValue(x) (void) (((x).type == STR_TYPE && (x).v.str) ? (free((x).v.str),(x).type = ERR_TYPE) : 0)
#define DestroyValue(x) do { if ((x).type == STR_TYPE && (x).v.str) { free((x).v.str); (x).v.str = NULL; } (x).type = ERR_TYPE; } while (0)
EXTERN int DSEToday;
EXTERN int RealToday;
@@ -50,8 +50,10 @@ 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);
EXTERN INIT( int ShowAllErrors, 0);
EXTERN INIT( int DebugFlag, 0);
EXTERN INIT( int DoCalendar, 0);
@@ -69,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);
@@ -77,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;
@@ -89,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);
@@ -99,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);
@@ -154,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:
@@ -69,6 +78,7 @@
* t = Display trigger dates
* v = Dump variables at end
* l = Display entire line in error messages
* s = Display expression-parsing stack usage before exit
* -e = Send messages normally sent to stderr to stdout instead
* -z[n] = Daemon mode waking up every n (def 1) minutes.
* -bn = Time format for cal (0, 1, or 2)
@@ -219,7 +229,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 +253,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 +274,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 +364,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 +426,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 +564,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 +575,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;
@@ -568,6 +605,7 @@ void InitRemind(int argc, char const *argv[])
case 'D':
while (*arg) {
switch(*arg++) {
case 's': case 'S': DebugFlag |= DB_EXPR_STACKS; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
@@ -600,7 +638,12 @@ void InitRemind(int argc, char const *argv[])
case 'k':
case 'K':
MsgCommand = arg;
if (*arg == ':') {
arg++;
QueuedMsgCommand = arg;
} else {
MsgCommand = arg;
}
while (*arg) arg++; /* Chew up remaining chars in this arg */
break;
@@ -716,7 +759,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);
}
@@ -730,7 +773,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
@@ -948,3 +991,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;
unsigned 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 = (int) ((rr >> 8) & 255);
*g = (int) ((gg >> 8) & 255);
*b = (int) ((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

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

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 */
/* */
/***************************************************************/
@@ -245,7 +245,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT".
"Odotettu viikonpäivän nimi"
};
#endif /* MK_GLOBALS */
@@ -254,7 +255,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 */
@@ -219,7 +219,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Nom du jour de la semaine attendu",
};
#endif /* MK_GLOBALS */
@@ -228,7 +229,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 */
/* */
/***************************************************************/
@@ -235,7 +235,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Oczekiwana nazwa dnia tygodnia"
};
#endif /* MK_GLOBALS */
@@ -244,7 +245,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 */
@@ -244,7 +244,8 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem",
"String too long",
"Time specified twice",
"Cannot specify DURATION without specifying AT"
"Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana",
};
#endif /* MK_GLOBALS */
@@ -253,7 +254,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)
/***************************************************************/
/***************************************************************/
/** **/
@@ -77,6 +76,8 @@ int main(int argc, char *argv[])
DBufInit(&(LastTrigger.tags));
ClearLastTriggers();
atexit(DebugExitFunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar();
return 0;
@@ -187,14 +188,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 +203,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);
@@ -385,6 +386,23 @@ void FromDSE(int dse, int *y, int *m, int *d)
return;
}
int JulianToGregorianOffset(int y, int m)
{
int offset = 13;
int centuries;
int four_centuries;
if (y >= 2100) {
centuries = (y - 2000) / 100;
four_centuries = (y - 2000) / 400;
offset += centuries - four_centuries;
if (!(y%100) && (y % 400)) {
if (m < 2) {
offset--; /* Offset increments in March */
}
}
}
return offset;
}
/***************************************************************/
/* */
/* ParseChar */
@@ -574,8 +592,8 @@ int EvaluateExpr(ParsePtr p, Value *v)
int r;
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */
while (isempty(*p->pos)) (p->pos)++;
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
while (isempty(*p->pos)) (p->pos)++;
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
@@ -736,19 +754,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 */
@@ -760,11 +791,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;
@@ -987,6 +1018,12 @@ int DoDebug(ParsePtr p)
else DebugFlag &= ~DB_ECHO_LINE;
break;
case 's':
case 'S':
if (val) DebugFlag |= DB_EXPR_STACKS;
else DebugFlag &= ~DB_EXPR_STACKS;
break;
case 'x':
case 'X':
if (val) DebugFlag |= DB_PRTEXPR;
@@ -1227,19 +1264,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++;
}
}
@@ -1248,19 +1285,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++;
}
}
@@ -1269,7 +1306,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;
@@ -1284,7 +1321,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++;
@@ -1297,7 +1334,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 */
@@ -1310,7 +1347,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++;
}
@@ -1318,7 +1355,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);
@@ -1329,17 +1366,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;
}
@@ -1350,7 +1387,7 @@ FillParagraphWCAux(wchar_t const *s)
}
static int
FillParagraphWC(char const *s)
FillParagraphWC(char const *s, DynamicBuffer *output)
{
size_t len;
wchar_t *buf;
@@ -1360,7 +1397,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;
}
@@ -1380,7 +1417,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;
@@ -1398,7 +1435,7 @@ void FillParagraph(char const *s)
if (!*s) return;
#ifdef REM_USE_WCHAR
if (FillParagraphWC(s) == OK) {
if (FillParagraphWC(s, output) == OK) {
return;
}
#endif
@@ -1408,7 +1445,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++;
@@ -1421,7 +1458,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 */
@@ -1434,7 +1471,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++;
}
@@ -1442,7 +1479,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++;
@@ -1453,17 +1490,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;
}
@@ -1627,14 +1664,53 @@ 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;
int do_exit = 0;
if (is_queued && IsServerMode()) {
do_exit = 1;
/* Server mode... redirect stdin and stdout to /dev/null */
kid = fork();
if (kid == (pid_t) -1) {
/* Fork failed... nothing we can do */
return;
} else if (kid == 0) {
/* In the child */
(void) close(STDIN_FILENO);
(void) close(STDOUT_FILENO);
fd = open("/dev/null", O_RDONLY);
if (fd >= 0 && fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(STDIN_FILENO);
}
fd = open("/dev/null", O_WRONLY);
if (fd >= 0 && fd != STDOUT_FILENO) {
dup2(fd, STDOUT_FILENO);
close(STDOUT_FILENO);
}
} else {
/* In the parent */
while (waitpid(kid, &status, 0) != kid) /* continue */ ;
return;
}
}
/* This is the child process or original if we never forked */
r = system(cmd);
if (do_exit) {
/* In the child process, so exit! */
exit(0);
}
if (r == 0) {
r = 1;
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 */
/* */
/***************************************************************/
@@ -403,7 +403,7 @@ static double phase(double pdate,
double Day, N, M, Ec, Lambdasun, ml, MM, Ev, Ae, A3, MmP,
mEc, A4, lP, V, lPP,
MoonAge, MoonPhase,
MoonAge, Phase,
MoonDist, MoonDFrac, MoonAng,
F, SunDist, SunAng;
@@ -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 */
@@ -465,7 +465,7 @@ static double phase(double pdate,
MoonAge = lPP - Lambdasun;
/* Phase of the Moon */
MoonPhase = (1 - cos(torad(MoonAge))) / 2;
Phase = (1 - cos(torad(MoonAge))) / 2;
/* Calculate distance of moon from the centre of the Earth */
@@ -477,7 +477,7 @@ static double phase(double pdate,
MoonDFrac = MoonDist / msmax;
MoonAng = mangsiz / MoonDFrac;
if(pphase) *pphase = MoonPhase;
if(pphase) *pphase = Phase;
if(mage) *mage = synmonth * (fixangle(MoonAge) / 360.0);
if(dist) *dist = MoonDist;
if(angdia) *angdia = MoonAng;

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 */
/* */
/***************************************************************/
@@ -22,7 +22,7 @@
#include "err.h"
#include "expr.h"
static int BexistsIntArray (int array[], int num, int key);
static int BexistsIntArray (int const array[], int num, int key);
static void InsertIntoSortedArray (int *array, int num, int key);
/* Arrays for the global omits */
@@ -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 {
@@ -251,7 +251,7 @@ int IsOmitted(int dse, int localomit, char const *omitfunc, int *omit)
/* element is found, 0 otherwise. */
/* */
/***************************************************************/
static int BexistsIntArray(int array[], int num, int key)
static int BexistsIntArray(int const array[], int num, int key)
{
int top=num-1, bot=0, mid;
@@ -377,8 +377,12 @@ int DoOmit(ParsePtr p)
break;
default:
Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN],
DBufValue(&buf));
if (tok.type == T_Until) {
Eprint("OMIT: UNTIL not allowed; did you mean THROUGH?");
} else {
Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN],
DBufValue(&buf));
}
DBufFree(&buf);
return E_UNKNOWN_TOKEN;
}
@@ -441,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 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);
@@ -58,6 +61,7 @@ void InitRemind (int argc, char const *argv[]);
void Usage (void);
int DSE (int year, int month, int day);
void FromDSE (int dse, int *y, int *m, int *d);
int JulianToGregorianOffset(int y, int m);
int ParseChar (ParsePtr p, int *err, int peek);
int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
@@ -70,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);
@@ -97,15 +102,13 @@ void FindNumericToken (char const *s, Token *t);
int ComputeTrigger (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals);
int ComputeTriggerNoAdjustDuration (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals, int duration_days);
int AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int save_in_globals);
int ComputeScanStart(int today, Trigger *trig, TimeTrig *tt);
char *StrnCpy (char *dest, char const *source, int n);
int StrMatch (char const *s1, char const *s2, int n);
int StrinCmp (char const *s1, char const *s2, int n);
char *StrDup (char const *s);
int StrCmpi (char const *s1, char const *s2);
Var *FindVar (char const *str, int create);
int DeleteVar (char const *str);
int SetVar (char const *str, Value *val);
int SetVar (char const *str, Value const *val);
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p);
int DoSet (Parser *p);
int DoUnset (Parser *p);
@@ -115,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);
@@ -139,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);
@@ -165,23 +168,30 @@ 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);
void DebugExitFunc(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 have_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,105 @@ 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;
}
static void del_reminder(QueuedRem *qid)
{
QueuedRem *q = QueueHead;
QueuedRem *next;
if (!q) {
return;
}
if (q == qid) {
QueueHead = q->next;
if (q->text) free((void *) q->text);
free(q);
return;
}
while(q->next) {
next = q->next;
if (q->next == qid) {
q->next = q->next->next;
if (next->text) free((void *) next->text);
free(next);
return;
}
q = q->next;
}
}
static void del_reminder_ul(unsigned long qid) {
del_reminder((QueuedRem *) qid);
}
/***************************************************************/
/* */
@@ -71,7 +191,7 @@ static void PrintQueue(void);
/* */
/***************************************************************/
int QueueReminder(ParsePtr p, Trigger *trig,
TimeTrig *tim, char const *sched)
TimeTrig *tim, char const *sched)
{
QueuedRem *qelem;
@@ -79,7 +199,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 +211,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 +302,20 @@ 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;
char qid[64];
/* 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 +325,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 +343,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 +369,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 +388,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);
@@ -209,6 +399,8 @@ void HandleQueuedReminders(void)
sleep_tv.tv_usec = 0;
}
DaemonWait(&sleep_tv);
/* A DEL command might have deleted our queued reminder! */
q = FindNextReminder();
} else {
sleep(SleepTime);
}
@@ -222,14 +414,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 +436,55 @@ 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\",");
snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
PrintJSONKeyPairString("qid", qid);
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);
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 +493,26 @@ void HandleQueuedReminders(void)
/* Calculate the next trigger time */
q->tt.nexttime = CalculateNextTime(q);
if (q->tt.nexttime != NO_TIME) {
/* If trigger time is way in the past because computer has been
suspended or hibernated, remove from queue */
if (q->tt.ttime < MinutesPastMidnight(1) - MaxLateMinutes &&
q->tt.nexttime < MinutesPastMidnight(1) - MaxLateMinutes) {
q->tt.nexttime = NO_TIME;
}
}
/* If queued reminder has expired, actually remove it from queue
and update status */
if (q->tt.nexttime == NO_TIME) {
del_reminder(q);
if (IsServerMode()) {
print_num_queued();
}
}
}
exit(0);
exit(EXIT_SUCCESS);
}
@@ -474,7 +708,11 @@ static void
json_queue(QueuedRem const *q)
{
int done = 0;
if (DaemonJSON) {
printf("{\"response\":\"queue\",\"queue\":");
}
printf("[");
char idbuf[64];
while(q) {
if (q->tt.nexttime == NO_TIME) {
q = q->next;
@@ -485,6 +723,14 @@ json_queue(QueuedRem const *q)
}
done = 1;
printf("{");
WriteJSONTrigger(&(q->t), 1, DSEToday);
WriteJSONTimeTrigger(&(q->tt));
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
PrintJSONKeyPairString("qid", idbuf);
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairString("filename", q->fname);
PrintJSONKeyPairInt("lineno", q->lineno);
switch(q->typ) {
case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break;
case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break;
@@ -497,26 +743,6 @@ json_queue(QueuedRem const *q)
case PASSTHRU_TYPE: PrintJSONKeyPairString("type", "PASSTHRU_TYPE"); break;
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 +756,12 @@ json_queue(QueuedRem const *q)
printf("\"}");
q = q->next;
}
printf("]\n");
printf("]");
if (DaemonJSON) {
printf(",\"command\":\"QUEUE\"}\n");
} else {
printf("\n");
}
}
/***************************************************************/
@@ -545,15 +776,28 @@ static void DaemonWait(struct timeval *sleep_tv)
fd_set readSet;
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 +805,108 @@ 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 if (!strncmp(cmdLine, "DEL ", 4)) {
unsigned long qid;
if (sscanf(cmdLine, "DEL %lx", &qid) == 1) {
del_reminder_ul(qid);
}
print_num_queued();
fflush(stdout);
} else {
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 +923,56 @@ 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;
int slept = 0;
/* Consume all the inotify events */
while(1) {
n = read(fd, buf, sizeof(buf));
if (n > 0) {
/* Something new since we slept */
slept = 0;
}
if (n < 0) {
if (errno == EINTR) continue;
if (slept) {
/* Nothing new since we slept */
return;
}
slept = 1;
/* HACK: sleep for 0.2 seconds to let multiple events queue up so we
only do a single reread */
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 200000000;
nanosleep(&sleeptime, NULL);
}
}
}
static int setup_inotify_watch(void)
{
int fd;
/* Don't inotify_watch stdin */
if (!strcmp(InitialFile, "-")) {
return -1;
}
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd < 0) {
return fd;
}
if (inotify_add_watch(fd, InitialFile, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) {
close(fd);
return -1;
}
return fd;
}
#endif

View File

@@ -5,7 +5,7 @@
/* Print a PostScript calendar. */
/* */
/* 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);
@@ -390,7 +390,7 @@ void DoPsCal(void)
month */
DBufInit(&buf);
DBufGets(&buf, stdin);
sscanf(DBufValue(&buf), "%s %s %d %d %d", month, year, &days, &wkday,
sscanf(DBufValue(&buf), "%39s %39s %d %d %d", month, year, &days, &wkday,
&MondayFirst);
/* Replace underscores in month name with spaces */
@@ -422,9 +422,9 @@ void DoPsCal(void)
}
DBufGets(&buf, stdin);
sscanf(DBufValue(&buf), "%s %d", prevm, &prevdays);
sscanf(DBufValue(&buf), "%39s %d", prevm, &prevdays);
DBufGets(&buf, stdin);
sscanf(DBufValue(&buf), "%s %d", nextm, &nextdays);
sscanf(DBufValue(&buf), "%39s %d", nextm, &nextdays);
/* Replace underscores with spaces in names of next/prev month */
s = prevm;
@@ -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 */
/* */
/***************************************************************/
@@ -135,8 +135,8 @@ void IssueSortedReminders(void)
next = cur->next;
switch(cur->typ) {
case MSG_TYPE:
if (MsgCommand) {
DoMsgCommand(MsgCommand, cur->text);
if (MsgCommand && *MsgCommand) {
DoMsgCommand(MsgCommand, cur->text, 0);
} else {
if (cur->trigdate != olddate) {
IssueSortBanner(cur->trigdate);
@@ -147,11 +147,15 @@ void IssueSortedReminders(void)
break;
case MSF_TYPE:
FillParagraph(cur->text);
if (cur->trigdate != olddate) {
IssueSortBanner(cur->trigdate);
olddate = cur->trigdate;
}
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 */
/* */
/***************************************************************/
@@ -258,7 +258,6 @@ void FindNumericToken(char const *s, Token *t)
/* If we hit a comma, swallow it. This allows stuff
like Jan 6, 1998 */
if (*s == ',') {
s++;
/* Classify the number we've got */
if (t->val >= BASE && t->val <= BASE+YR_RANGE) t->type = T_Year;
else if (t->val >= 1 && t->val <= 31) t->type = T_Day;

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;
@@ -241,6 +244,7 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
case GOT_WD+GOT_MON+GOT_YR:
if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
/* cppcheck-suppress knownConditionTrueFalse */
if (trig->y > y || (trig->y == y && trig->m > m)) {
j = DSE(trig->y, trig->m, 1);
ADVANCE_TO_WD(j, trig->wd);
@@ -324,8 +328,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 +395,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;
@@ -622,12 +633,10 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
fprintf(ErrFp, "%s(%d): %s\n",
FileName, LineNo, ErrMsg[E_EXPIRED]);
}
if (result != -1) {
if (save_in_globals) {
LastTriggerDate = result;
LastTrigValid = 1;
}
}
if (save_in_globals) {
LastTriggerDate = result;
LastTrigValid = 1;
}
return -1;
}
@@ -640,12 +649,10 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
/* Keep scanning... unless there's no point in doing it.*/
if (nextstart <= start) {
if (result != -1) {
if (save_in_globals) {
LastTriggerDate = result;
LastTrigValid = 1;
}
}
if (save_in_globals) {
LastTriggerDate = result;
LastTrigValid = 1;
}
trig->expired = 1;
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",
@@ -662,40 +669,3 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
return -1;
}
/***************************************************************/
/* */
/* ComputeScanStart */
/* */
/* Figure out where to start scan from by examining SCANFROM */
/* and DURATION */
/* */
/***************************************************************/
int
ComputeScanStart(int today, Trigger *trig, TimeTrig *tt)
{
int minutes, days;
/* If we don't have a time/duration, just use scanfrom */
if (tt->ttime == NO_TIME ||
tt->duration == NO_TIME) {
if (trig->scanfrom == NO_DATE) {
return today;
}
return trig->scanfrom;
}
/* Calculate time-based SCANFROM */
minutes = tt->ttime + tt->duration - 1;
/* Figure out how many days to scan backwards from */
days = minutes / MINUTES_PER_DAY;
if (trig->scanfrom != NO_DATE) {
if (trig->scanfrom <= today - days) {
return trig->scanfrom;
} else {
return today - days;
}
}
return today - days;
}

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 */
/* */
/***************************************************************/
@@ -150,6 +150,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32
#define DB_EXPR_STACKS 64
/* Enumeration of the tokens */
enum TokTypes

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 */
/* */
/***************************************************************/
@@ -124,8 +124,13 @@ int DoFset(ParsePtr p)
DBufFree(&buf);
return E_NO_MEM;
}
func->filename = StrDup(FileName);
if (FileName) {
func->filename = StrDup(FileName);
} else {
func->filename = StrDup("[cmdline]");
}
if (!func->filename) {
free(func);
return E_NO_MEM;
}
func->lineno = LineNo;
@@ -182,7 +187,7 @@ int DoFset(ParsePtr p)
/* Allow an optional = sign: FSET f(x) = x*x */
c = ParseNonSpaceChar(p, &r, 1);
if (c == '=') {
c = ParseNonSpaceChar(p, &r, 0);
(void) ParseNonSpaceChar(p, &r, 0);
}
/* Copy the text over */
if (p->isnested) {
@@ -344,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,27 +36,16 @@ 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;
}
/***************************************************************/
/* */
/* StrMatch */
/* */
/* Checks that two strings match (case-insensitive) to at */
/* least the specified number of characters, or the length */
/* of the first string, whichever is greater. */
/* */
/***************************************************************/
int StrMatch(char const *s1, char const *s2, int n)
{
int l;
if ((l = strlen(s1)) < n) return 0;
return !StrinCmp(s1, s2, l);
}
/***************************************************************/
/* */
/* StrinCmp - compare strings, case-insensitive */
@@ -124,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 */
@@ -223,13 +212,6 @@ clear_callstack(void)
callstack = NULL;
}
int
have_callstack(void)
{
if (callstack) return 1;
return 0;
}
static void
print_callstack_aux(FILE *fp, cs *entry)
{

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)
{
@@ -473,7 +480,7 @@ int DeleteVar(char const *str)
/* Set the indicate variable to the specified value. */
/* */
/***************************************************************/
int SetVar(char const *str, Value *val)
int SetVar(char const *str, Value const *val)
{
Var *v = FindVar(str, 1);
@@ -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
# ---------------------------------------------------------------------------
@@ -27,6 +27,19 @@ if test `id -u` = 0 ; then
exit 1
fi
# Set a known timezone so moon phases show up in predictable places
TZ=UTC
export TZ
RESULT=`(echo 'BANNER %'; echo 'IF now() > 23:55'; echo 'MSG late%'; echo 'ENDIF') | ../src/remind -h -`
if test "$RESULT" = "late" ; then
echo ""
echo "*** Please do not run the test suite between 23:55 and 00:00 UTC; it will fail."
echo ""
exit 1
fi
# If we're already in a utf-8 locale, do
# nothing; otherwise, set LC_ALL
OK=0
@@ -52,6 +65,8 @@ echo "Test 1" > ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo 'set a 1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+2*3))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))' | ../src/remind -ds - 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo "Test 2" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
@@ -404,6 +419,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
@@ -418,6 +445,102 @@ chmod 0777 include_dir/ww
../src/remind include_dir/ww >> ../tests/test.out 2>&1
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' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind -zj ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
# Test for leap year bug that was fixed
../src/remind -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 1 Mar 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 28 Feb 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
../src/remind -dte - 1 Mar 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1
# Remove references to SysInclude, which is build-specific
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
@@ -428,7 +551,11 @@ else
echo "Remind: Acceptance test FAILED"
echo ""
echo "Examine the file test.out to see where it differs from the"
echo "reference file test.cmp."
echo "reference file test.cmp. Here are the first 200 lines of"
echo "diff -u test.out test.cmp"
echo ""
diff -u ../tests/test.out ../tests/test.cmp | head -n 200
echo ""
exit 1
fi

File diff suppressed because one or more lines are too long

View File

@@ -381,8 +381,18 @@ 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)
set a080 orthodoxeaster(2024)
set a080 orthodoxeaster(2025)
set a080 orthodoxeaster(2026)
set a080 orthodoxeaster(2027)
set a081 ""
OMIT 1991-03-11
set a082 slide('1991-03-01', 7, "Sat", "Sun")
@@ -474,7 +484,7 @@ msg [$December]%
msg [$DefaultColor]%
msg [$DefaultPrio]%
msg [$DefaultTDelta]%
msg [$DeltaOffset]%
msg [$DeltaOverride]%
msg [$DontFork]%
msg [$DontQueue]%
msg [$DontTrigAts]%
@@ -774,6 +784,15 @@ ENDIF
REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo
REM [trig("Mon", "Tue", "Wed")] MSG bar
# Multitrig
REM [multitrig("10", "17")] MSG multitrig-1
REM [multitrig("Feb 15", "Mar 20")] MSG multitrig-2
REM [multitrig("Oct 7 1992", "1991")] MSG multitrig-3
REM [multitrig("16 Feb AFTER OMIT Sat Sun", "29 March")] MSG multitrig-4
REM [multitrig("2", "3", "5", "7")] MSG multitrig-5
REM [multitrig("15 SCANFROM -7", "14 SCANFROM -7")] MSG multitrig-6
REM [multitrig("15 SCANFROM -7", "14 SCANFROM -7")] SCANFROM -7 MSG multitrig-7
# The new syntactic sugar
REM First Monday January MSG x
REM Second Tuesday in April MSG x
@@ -858,6 +877,54 @@ FUNSET circle square rectangle
# Should fail
SET a square(5)
# htmlescape
set a htmlescape("foo")
REM MSG [a]
set a htmlescape("<&>")
REM MSG [a]
set a htmlescape("@&^#*@&^##$*&@><><@#@#><@#>%%_#$foobarquux")
REM MSG [a]
# htmlstriptags
set a htmlstriptags("foobar")
set a htmlstriptags("This is <b>bold</b>")
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)
# Should result in an error
REM Tue OMIT 2024-01-01 MSG Wookie
# No error
REM Tue OMIT Wed 2024-01-01 MSG Blort
# Don't want Remind to queue reminders
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

@@ -12,10 +12,10 @@ CGIDIR = /cgi-bin
# The complete path to the directory containing the HTML file "calendar.html".
# This is a sample file containing links to all the scripts. This path
# should be the path as seen by the UNIX operating system
HTMLDIR = /var/www/remind
HTMLDIR = /var/www/html/remind
# Where you stick images and CSS files, as seen by UNIX
IMAGEDIR = /var/www/remind/resources
IMAGEDIR = /var/www/html/remind/resources
# Where images and CSS files are, as seen by web browsers
IMAGEBASE = /remind/resources
@@ -32,6 +32,7 @@ datarootdir=@datarootdir@
# Where do Remind and Rem2PS executables live?
REMIND = $(bindir)/remind
REM2PS = $(bindir)/rem2ps
REM2PDF = $(bindir)/rem2pdf
REM2HTML = $(bindir)/rem2html
# If your Web server requires CGI programs to have a .cgi suffix, use
# the next line. Otherwise, comment it out
@@ -46,6 +47,7 @@ SEDSCRIPT = -e 's@%CGIDIR%@$(CGIDIR)@g' \
-e 's@%REMIND%@$(REMIND)@g' \
-e 's@%IMAGEBASE%@$(IMAGEBASE)@g' \
-e 's@%REM2PS%@$(REM2PS)@g' \
-e 's@%REM2PDF%@$(REM2PDF)@g' \
-e 's@%REM2HTML%@$(REM2HTML)@g' \
-e 's@cal_dispatch@cal_dispatch$(CGISUFFIX)@g' \
@@ -57,7 +59,8 @@ all:
install:
-mkdir -p $(DESTDIR)$(SCRIPTDIR)
-mkdir -p $(DESTDIR)$(HTMLDIR)
cp calps hebdate hebps hebhtml moon sunrise sunset $(DESTDIR)$(SCRIPTDIR)
cp calps calpdf hebps hebpdf hebhtml moon sunrise sunset $(DESTDIR)$(SCRIPTDIR)
sed $(SEDSCRIPT) < hebdate > $(DESTDIR)$(SCRIPTDIR)/hebdate
sed $(SEDSCRIPT) < cal_dispatch-DIST > $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
sed $(SEDSCRIPT) < hebdate.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/hebdate.rem
sed $(SEDSCRIPT) < moon.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/moon.rem
@@ -73,14 +76,14 @@ install:
chmod 644 $(DESTDIR)$(SCRIPTDIR)/blank.rem
chmod 644 $(DESTDIR)$(HTMLDIR)/calendar.html
chmod 755 $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
chmod 755 $(DESTDIR)$(SCRIPTDIR)/calps $(DESTDIR)$(SCRIPTDIR)/hebdate \
$(DESTDIR)$(SCRIPTDIR)/hebps $(DESTDIR)$(SCRIPTDIR)/moon \
chmod 755 $(DESTDIR)$(SCRIPTDIR)/calpdf $(DESTDIR)$(SCRIPTDIR)/calps $(DESTDIR)$(SCRIPTDIR)/hebdate \
$(DESTDIR)$(SCRIPTDIR)/hebps $(DESTDIR)$(SCRIPTDIR)/hebpdf $(DESTDIR)$(SCRIPTDIR)/moon \
$(DESTDIR)$(SCRIPTDIR)/sunrise $(DESTDIR)$(SCRIPTDIR)/sunset \
$(DESTDIR)$(SCRIPTDIR)/hebhtml \
-mkdir -p $(DESTDIR)$(IMAGEDIR)
cp rem-default.css *.png $(DESTDIR)$(IMAGEDIR)
chmod 644 $(DESTDIR)$(IMAGEDIR)/rem-default.css $(DESTDIR)$(IMAGEDIR)/*.png
cp calendar.css rem-default.css *.png $(DESTDIR)$(IMAGEDIR)
chmod 644 $(DESTDIR)$(IMAGEDIR)/calendar.css $(DESTDIR)$(IMAGEDIR)/rem-default.css $(DESTDIR)$(IMAGEDIR)/*.png

View File

@@ -28,6 +28,10 @@ export REMIND
REM2PS=%REM2PS%
export REM2PS
# Set REM2PDF to the full pathname of the rem2pdf executable
REM2PDF=%REM2PDF%
export REM2PDF
#########################
#
# Don't change anything after this.
@@ -56,6 +60,10 @@ case "$1" in
exec $DIR/calps
;;
calpdf)
exec $DIR/calpdf
;;
moon)
exec $DIR/moon
;;
@@ -64,6 +72,10 @@ case "$1" in
exec $DIR/hebps
;;
hebpdf)
exec $DIR/hebpdf
;;
hebhtml)
if [ "$2" = "" -o "$3" = "" ] ; then
exec $DIR/hebhtml

Some files were not shown because too many files have changed in this diff Show More