Compare commits

...

421 Commits

Author SHA1 Message Date
Dianne Skoll
a6c166ae0c Update release notes.
All checks were successful
Remind unit tests / tests (push) Successful in 36s
2024-09-16 09:41:12 -04:00
Dianne Skoll
e9c89b770f Bump version to 05.00.06 2024-09-16 08:29:58 -04:00
Dianne Skoll
df6298bd63 Add test for $SuppressImplicitWarnings
All checks were successful
Remind unit tests / tests (push) Successful in 1m15s
2024-09-15 16:59:46 -04:00
Dianne Skoll
739d285e36 Indent system variable dump better.
All checks were successful
Remind unit tests / tests (push) Successful in 48s
2024-09-15 10:21:22 -04:00
Dianne Skoll
ee1c931932 Add $SuppressImplicitWarnings system variable.
This suppresses warnings about missing REM or MSG tokens.
2024-09-15 09:39:15 -04:00
Dianne Skoll
0806b6738f Add test for time constants of the form '12:34'
All checks were successful
Remind unit tests / tests (push) Successful in 41s
2024-09-13 13:07:49 -04:00
Dianne Skoll
020e82d575 Document that TIME constants may be surrounded by single quotes. 2024-09-13 13:06:57 -04:00
Dianne Skoll
a20f2b588e Accept either 12:34 or '12:34' as a time constant. 2024-09-13 13:02:46 -04:00
Dianne Skoll
631e721a96 More helpful error if someone uses '...' around a TIME constant. 2024-09-13 12:46:38 -04:00
Dianne Skoll
8453e17c6c Add a guard to avoid calling free(NULL)
All checks were successful
Remind unit tests / tests (push) Successful in 29s
2024-09-12 11:50:11 -04:00
Dianne Skoll
76c1e2abb3 Avoid unnecessary double-initialization. 2024-09-12 11:38:59 -04:00
Dianne Skoll
3389f1c91b Refactor init_token and token_error. 2024-09-12 10:58:35 -04:00
Dianne Skoll
b2d47ae979 Initialize token.val in a few places. 2024-09-12 10:51:00 -04:00
Dianne Skoll
e2c615f310 Fix comment. 2024-09-12 10:16:01 -04:00
Dianne Skoll
e8492a4303 Add Chinese New Year file. 2024-09-11 12:09:57 -04:00
Dianne Skoll
4695efaabd Use DBG macro
Some checks failed
Remind unit tests / tests (push) Failing after 28s
2024-09-11 11:53:07 -04:00
Dianne Skoll
c433f42587 Get rid of useless function. 2024-09-11 11:49:04 -04:00
Dianne Skoll
4708e59a43 No need to munch following whitespace. 2024-09-11 11:46:40 -04:00
Dianne Skoll
d56ac6332a Use switch rather than if...else chains
All checks were successful
Remind unit tests / tests (push) Successful in 33s
2024-09-11 11:37:30 -04:00
Dianne Skoll
b054baf590 Stricter tokenization of datetime 2024-09-11 09:01:52 -04:00
Dianne Skoll
42f5e3467d Better error messages for ill-formed time. 2024-09-11 08:52:06 -04:00
Dianne Skoll
97013ae89b Return T_Illegal with a negative tok.val if we can't parse a numeric token. 2024-09-11 08:45:31 -04:00
Dianne Skoll
2acead9118 Shorten error message.
All checks were successful
Remind unit tests / tests (push) Successful in 29s
2024-09-10 16:25:00 -04:00
Dianne Skoll
a53a80acb4 Make error message more explicit. 2024-09-10 16:23:52 -04:00
Dianne Skoll
56e62b1b4d More error checking of bad date specs. 2024-09-10 14:27:46 -04:00
Dianne Skoll
c645db5ede Diagnose bad YYYY-MM-DD constants in REM command. 2024-09-10 14:09:52 -04:00
Dianne Skoll
ef6b9c3783 Better error message when a number that isn't recognized as a day number or year is encountered. 2024-09-10 13:57:34 -04:00
Dianne Skoll
019bee26cb Start refactoring calendar code.
All checks were successful
Remind unit tests / tests (push) Successful in 30s
2024-09-10 10:47:32 -04:00
Dianne Skoll
152cd4090b Better error message.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-09-09 17:01:56 -04:00
Dianne Skoll
b7fc2b5776 Tweak remind-conf-mode.el with new Remind keywords and better handling of comment lines. 2024-09-09 16:50:27 -04:00
Dianne Skoll
723aba9b7c Document and test --print-tokens
All checks were successful
Remind unit tests / tests (push) Successful in 30s
2024-09-08 10:33:24 -04:00
Dianne Skoll
8a5b88338b Add --print-tokens long option to dump Remind tokens to stdout. 2024-09-08 10:29:49 -04:00
Dianne Skoll
7236441e02 Remove note saying REM can be omitted. 2024-09-08 10:26:07 -04:00
Dianne Skoll
e4bab0dda4 Be more explicit. 2024-09-08 09:57:52 -04:00
Dianne Skoll
5b7d4a07ec More explicit error messages.
All checks were successful
Remind unit tests / tests (push) Successful in 47s
2024-09-07 13:25:31 -04:00
Dianne Skoll
8c3d2c4003 More specific error message. 2024-09-07 13:14:31 -04:00
Dianne Skoll
f485d607ff Fix error message. 2024-09-07 10:16:57 -04:00
Dianne Skoll
a0effa5f0b Update keyword/variable/function list in remind-conf-mode.el. 2024-09-07 10:15:02 -04:00
Dianne Skoll
7d501cda6f Update to ical2rem version 0.7.
All checks were successful
Remind unit tests / tests (push) Successful in 31s
2024-09-05 12:44:17 -04:00
Dianne Skoll
5d7f55c8d5 Make error messages more consistent.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-09-03 10:53:45 -04:00
Dianne Skoll
097bf92bea Better error message. 2024-09-03 10:51:50 -04:00
Dianne Skoll
b9dea59206 Update changelog.
All checks were successful
Remind unit tests / tests (push) Successful in 39s
2024-09-02 14:46:58 -04:00
Dianne Skoll
46aa144b65 Bump version to 05.00.05 2024-09-02 10:15:56 -04:00
Dianne Skoll
914971308d Better error message.
All checks were successful
Remind unit tests / tests (push) Successful in 30s
2024-09-01 23:42:29 -04:00
Dianne Skoll
a22e81040f Check error return in a couple of spots. 2024-09-01 23:22:00 -04:00
Dianne Skoll
72f74f03cf Check for errors. 2024-09-01 23:18:09 -04:00
Dianne Skoll
d0e45e727e Don't print closing ] in error indicator.
All checks were successful
Remind unit tests / tests (push) Successful in 31s
2024-09-01 22:39:49 -04:00
Dianne Skoll
ce2b2e80da Remove unnecessary _h() function definition.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-09-01 11:31:28 -04:00
Dianne Skoll
11771b7d3d Better formatting of ^-- here message for expressions with embedded newlines 2024-09-01 11:27:34 -04:00
Dianne Skoll
01cb028532 Fix up tests to pass in September 2024. 2024-09-01 11:09:32 -04:00
Dianne Skoll
58b6f43b9c Better diagnosis of wrong #args for builtin functions. 2024-09-01 09:03:26 -04:00
Dianne Skoll
1dedb667e8 Prevent a couple of file descriptor leaks.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-08-31 09:06:05 -04:00
Dianne Skoll
8a96236788 Avoid warnings if REM_USE_WCHAR is undefined.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-08-30 20:44:08 -04:00
Dianne Skoll
2a13163659 Fix some weird edge cases in text-mode calendar formatting.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-08-29 13:25:36 -04:00
Dianne Skoll
336a9684d4 In calendar mode, SHADE the entire calendar box including day number.
All checks were successful
Remind unit tests / tests (push) Successful in 29s
2024-08-29 08:50:57 -04:00
Dianne Skoll
86945c6e18 Remove incorrect test obsoleted by changes in 05.00.04. 2024-08-29 08:39:55 -04:00
Dianne Skoll
684280db5e Remove "SCHED _sfun" and hard-code "+15" instead. 2024-08-29 08:15:10 -04:00
Dianne Skoll
d801408933 Update WHATSNEW. 2024-08-29 08:01:50 -04:00
Dianne Skoll
79b3da3820 Update man page.
All checks were successful
Remind unit tests / tests (push) Successful in 27s
2024-08-28 20:43:27 -04:00
Dianne Skoll
ed021d3f46 Let IIF's boolean arg be any type... we use truthy(). 2024-08-28 20:41:21 -04:00
Dianne Skoll
27d0fda280 Factor out truthy code from Fiif 2024-08-28 20:38:04 -04:00
Dianne Skoll
ef12da4ec6 Refactor out "truthy". 2024-08-28 20:36:30 -04:00
Dianne Skoll
7b098e95ad Make error messages more consistent. 2024-08-28 18:21:35 -04:00
Dianne Skoll
7d13f4b09e Better diagnostics when printing "Too many/few arguments" errors 2024-08-28 18:14:43 -04:00
Dianne Skoll
cecdfe6ade Bump version to 05.00.04. 2024-08-28 18:00:50 -04:00
Dianne Skoll
216bbd6378 Fix the logic that recurses into user-defined functions. 2024-08-28 17:56:47 -04:00
Dianne Skoll
30e2e9c633 When looking at SATISFY expressions to make sure they use the trigger date, look recursively into user-defined functions in the expression also. 2024-08-28 17:49:12 -04:00
Dianne Skoll
bf2aabd610 Update release notes. 2024-08-28 15:42:23 -04:00
Dianne Skoll
c019221d15 Add diagnostics for missing/bad subst_XXX functions. 2024-08-28 15:15:34 -04:00
Dianne Skoll
b7bd6faf07 Warn if SCHED/WARN/OMITFUNC functions take wrong number of args. 2024-08-28 15:02:22 -04:00
Dianne Skoll
80d58220fe In warnings, give file/lineno where function is defined. 2024-08-28 14:59:13 -04:00
Dianne Skoll
cd8624e176 Tweak diagnostic messages. 2024-08-28 14:35:49 -04:00
Dianne Skoll
8515fb7ddd Update / clarify manual. 2024-08-28 14:28:34 -04:00
Dianne Skoll
84f9f4ae0a Update tests. 2024-08-28 14:23:14 -04:00
Dianne Skoll
e201ebcfa4 Document that logical operators can take any type; add tests. 2024-08-28 14:22:07 -04:00
Dianne Skoll
4e15c3ec35 Remove an optimization that was interfering with expression debugging. 2024-08-28 14:21:44 -04:00
Dianne Skoll
1adccf9b1f Add tests for SCHED/WARN/OMITFUNC functions that don't use their argument. 2024-08-28 13:54:47 -04:00
Dianne Skoll
a1aa5c2ad9 Change wording of warning slightly. 2024-08-28 13:54:40 -04:00
Dianne Skoll
1e0d650737 Fix tests. 2024-08-28 13:49:34 -04:00
Dianne Skoll
553d092ca8 Diagnost if WARN/SCHED/OMITFUNC functions don't reference their argument. 2024-08-28 13:49:17 -04:00
Dianne Skoll
dc62841517 Fix accidental broken commit. :( 2024-08-28 13:45:42 -04:00
Dianne Skoll
326e7bfc53 Document calendar back-end handling of %_ 2024-08-28 13:23:54 -04:00
Dianne Skoll
b9dc7c16ad Use /s modifier on regex. 2024-08-28 13:09:18 -04:00
Dianne Skoll
abd54b016b Add tests for newlines in calendars. 2024-08-28 12:56:53 -04:00
Dianne Skoll
db02b54067 Collapse multiple whitespace; convert newlines to "<br />" in HTML output. 2024-08-28 12:52:39 -04:00
Dianne Skoll
40a78dfbbb Collapse multiple newlines after all other processing. 2024-08-28 12:52:29 -04:00
Dianne Skoll
c860b46baa Collapse multiple runs of whitespace 2024-08-28 12:52:07 -04:00
Dianne Skoll
1458ba8856 Clean up whitespace runs in rem2pdf.
All checks were successful
Remind unit tests / tests (push) Successful in 45s
2024-08-28 10:44:41 -04:00
Dianne Skoll
7b9b6ebc96 Preserve %_ newlines in "-C" mode. 2024-08-28 10:14:06 -04:00
Dianne Skoll
7ee4073c7a Document warning on function redefinitiion.
All checks were successful
Remind unit tests / tests (push) Successful in 26s
2024-08-27 21:43:46 -04:00
Dianne Skoll
8c072cd9b6 Add a warning if a function is redefined. 2024-08-27 21:41:45 -04:00
Dianne Skoll
b794a45c3f Add more SATISFY diagnostics.
All checks were successful
Remind unit tests / tests (push) Successful in 34s
2024-08-27 15:02:11 -04:00
Dianne Skoll
98e491ed1d Remove some left-over debugging code. 2024-08-27 15:01:59 -04:00
Dianne Skoll
c397cc06da Rewrite code to avoid need to pass address of "mentioned" variable.
All checks were successful
Remind unit tests / tests (push) Successful in 30s
2024-08-27 11:33:41 -04:00
Dianne Skoll
8616236b3c Document that SATISFY clauses that don't mention trigdate are diagnosed. 2024-08-27 11:26:13 -04:00
Dianne Skoll
702704af1a Make sure SATISFY expressions are either constant or mention trigdate. 2024-08-27 11:22:42 -04:00
Dianne Skoll
a0d1b19050 Fix typo in comment.
All checks were successful
Remind unit tests / tests (push) Successful in 39s
2024-08-23 15:08:35 -04:00
Dianne Skoll
a5c8ae491c Add test for diagnosing function definitions with too many arguments. 2024-08-23 15:05:24 -04:00
Dianne Skoll
b3cf741d15 Suppress warning in GCC 14.2.1. Patch courtesy of Emanuele Torre
All checks were successful
Remind unit tests / tests (push) Successful in 38s
2024-08-23 07:44:36 -04:00
Dianne Skoll
0b28dde9c7 Add optional argument to ampm() to specify that hour should be zero-padded to two digits.
All checks were successful
Remind unit tests / tests (push) Successful in 46s
2024-08-22 17:38:54 -04:00
Dianne Skoll
5a3980b5b8 Make note about intended use of defs.rem 2024-08-22 12:51:58 -04:00
Dianne Skoll
a8e33118d6 Suppress warnings. 2024-08-22 10:32:27 -04:00
Dianne Skoll
2223277f64 Fix many warnings. 2024-08-22 10:26:48 -04:00
Dianne Skoll
c72413e3c1 Fix up tests for previous commit.
All checks were successful
Remind unit tests / tests (push) Successful in 29s
2024-08-21 15:10:28 -04:00
Dianne Skoll
fa1033db6f Don't warn about assuming REM type if we've already warned about assuming "REM" 2024-08-21 15:09:41 -04:00
Dianne Skoll
61e3edd2ac Add tests for new diagnostics. 2024-08-21 12:57:04 -04:00
Dianne Skoll
093c97ff91 Add tests for missing REM or missing MSG being diagnosed. 2024-08-21 12:50:51 -04:00
Dianne Skoll
6e64b175aa Diagnose lines that are implicitly treated as REM lines.
Diagnose REM commands that are implicitly treated as having MSG type.

Properly start all lines that should start with REM, with REM.
2024-08-21 12:45:30 -04:00
Dianne Skoll
09dba4bc94 Clarify caching of INCLUDECMD output.
All checks were successful
Remind unit tests / tests (push) Successful in 27s
2024-08-07 13:26:00 -04:00
Dianne Skoll
2e443ac5b7 Properly handle landscape mode in PostScript output.
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-08-04 08:04:56 -04:00
Dianne Skoll
59a8c88178 Use %%PageOrientation, not %%Orientation in DSC comments. 2024-08-04 07:55:55 -04:00
Dianne Skoll
40eab03d84 Add two ways to produce PostScript code: Using rem2ps or rem2pdf
All checks were successful
Remind unit tests / tests (push) Successful in 28s
2024-08-01 12:27:33 -04:00
Dianne Skoll
e993bf59cf Add support for Encapsulated PostScript to rem2pdf. 2024-08-01 09:57:16 -04:00
Dianne Skoll
c6de5a2c8f Add support for producing PostScript to rem2pdf. 2024-08-01 09:40:29 -04:00
Dianne Skoll
51cc939d0c Suppress a couple of cppcheck warnings.
All checks were successful
Remind unit tests / tests (push) Successful in 30s
2024-07-30 15:17:13 -04:00
Dianne Skoll
c857192e6d Change the man page to remove statements that are no longer true (since the new expression-evaluation code in 05.00.00.)
All checks were successful
Remind unit tests / tests (push) Successful in 27s
2024-07-27 08:51:00 -04:00
Dianne Skoll
4591c2b181 Bump version number; fix a bunch of man page warnings; make "make test" fail if there are any man page warnings iff the "man" command accepts --warning
All checks were successful
Remind unit tests / tests (push) Successful in 6m7s
2024-07-26 09:59:46 -04:00
Dianne Skoll
7843a1b2ba Fix typo: .RP should be .PP 2024-07-26 08:18:33 -04:00
Dianne Skoll
649481cf01 Bump version to 05.00.02 2024-07-26 07:56:50 -04:00
Dianne Skoll
c253bdfcbe Update release date. 2024-07-26 07:54:21 -04:00
Dianne Skoll
1910808fd7 Update WHATSNEW 2024-07-10 14:04:06 -04:00
Dianne Skoll
1d8cb9749e Add a test to ensure ONCE works even when taking input from STDIN if $OnceFile is set. 2024-07-10 13:55:41 -04:00
Dianne Skoll
e3f9380fcd Allow ONCE to be used if input is stdin, as long as $OnceFile is set.
Clarify documentation.
2024-07-10 09:32:05 -04:00
Dianne Skoll
326c3f59b0 Make tests pass on any day, not just 2024-07-09. :) 2024-07-10 09:23:21 -04:00
Dianne Skoll
03f1c5a047 Fix typo 2024-07-09 09:36:52 -04:00
Dianne Skoll
02122491c3 Add test to ensure we warn if we try to set $OnceFile to something else after a ONCE has been seen. 2024-07-09 09:33:58 -04:00
Dianne Skoll
735f6f5686 Ignore attempts to set $OnceFile to the value it already has. 2024-07-09 09:33:02 -04:00
Dianne Skoll
ac033d75c0 Add the $OnceFile special variable.
This lets us specify a timestamp file that Remind
uses to track the last run for the purpose of handling
the ONCE keyword, rather than using the last access date
of the reminder file.

Keeping a timestamp file is a more reliable way to track
when Remind was last run.
2024-07-09 09:23:15 -04:00
Dianne Skoll
e2185e773a Add warning if someone attempts to create a multi-page SVG calendar. 2024-07-05 16:57:18 -04:00
Dianne Skoll
d9ae417e01 Fix typo 2024-07-05 14:22:55 -04:00
Dianne Skoll
e1d0948538 Update doc. 2024-07-05 14:22:16 -04:00
Dianne Skoll
357ddf285a Add --svg option to rem2pdf to produce SVG instead of PDF output. 2024-07-05 14:18:29 -04:00
Dianne Skoll
41859fc484 Remove some unused definitions; use symbolic value for sort initializer. 2024-07-04 16:58:41 -04:00
Dianne Skoll
07275e71b0 Remove obsolete definitions. 2024-07-04 13:13:27 -04:00
Dianne Skoll
f68521cb95 Fix spacing. 2024-07-04 13:09:21 -04:00
Dianne Skoll
526610bdd2 Stop parsing user func early if possible. 2024-07-02 09:16:47 -04:00
Dianne Skoll
973e3448ae Fix typo in comment. 2024-07-02 07:58:24 -04:00
Dianne Skoll
9a3f28f6fc Optimize storage of short string constants in expr_node objects. 2024-06-12 10:19:27 -04:00
Dianne Skoll
a3e32d2dc4 Update changelog. 2024-06-08 10:57:38 -04:00
Dianne Skoll
a8b78eff00 Add a few more tests. 2024-06-08 09:20:48 -04:00
Dianne Skoll
460db83298 Bump version to 05.00.01 2024-06-08 09:18:42 -04:00
Dianne Skoll
4560712778 Fix a couple of potential memory leaks. 2024-06-07 21:27:24 -04:00
Dianne Skoll
ce8803dde9 The zero-argument form of weekno() is not constant. 2024-06-07 21:22:32 -04:00
Dianne Skoll
60ca5d45e3 Convert "isany" to short-circuit evaluation. 2024-06-07 15:55:12 -04:00
Dianne Skoll
4454613d00 Fix typo 2024-06-06 12:51:02 -04:00
Dianne Skoll
0704808500 Don't rely on newer C feature that allows anonymous parameters 2024-06-06 12:48:44 -04:00
Dianne Skoll
166b1ac499 Only reset alarm if expression time limit was set 2024-06-06 11:47:39 -04:00
Dianne Skoll
e33bf4e80a Explicitly set sa_flags in sigaction. 2024-06-06 10:42:04 -04:00
Dianne Skoll
b3af44d212 Update docs/WHATSNEW for 05.00.00 release. 2024-06-06 10:28:38 -04:00
Dianne Skoll
1e753d5209 Preserve variables needed across iterations. 2024-06-05 15:08:12 -04:00
Dianne Skoll
4bf31005ea Better error location in a couple more cases. 2024-06-05 09:15:10 -04:00
Dianne Skoll
7c86bc910a Better diagnostics of unmatched IFs 2024-06-05 09:08:04 -04:00
Dianne Skoll
4f146a99a9 Simplify Eprint logic. 2024-06-04 21:10:17 -04:00
Dianne Skoll
a6a638e0e6 Add test for UNMATCHED PUSH 2024-06-04 16:27:23 -04:00
Dianne Skoll
325814f5e1 Print the location of unmatched PUSH 2024-06-04 16:25:14 -04:00
Dianne Skoll
5c4ea7d09e Allow "configure" to work, haha. 2024-06-04 12:02:54 -04:00
Dianne Skoll
2bf73987ac Don't need to flush unless we're about to print. 2024-06-04 11:56:05 -04:00
Dianne Skoll
3e9eeea8dc Update docs; kill time-limiter when we queue. 2024-06-04 11:51:27 -04:00
Dianne Skoll
d164d72c1c Update test file 2024-06-04 09:02:50 -04:00
Dianne Skoll
632cee62d9 Don't permit unary-minus overflow. 2024-06-04 09:02:26 -04:00
Dianne Skoll
7a40260f0d Add some expression evaluation tests. 2024-06-04 08:37:19 -04:00
Dianne Skoll
09f043b3de Don't print bytes allocated --- makes test output differ on different architectures. 2024-06-04 07:32:53 -04:00
Dianne Skoll
c0341c8ba3 Fix typo. 2024-06-03 21:54:12 -04:00
Dianne Skoll
850c717803 Handle unary '+' properly. 2024-06-03 20:20:54 -04:00
Dianne Skoll
f13f9e18bd Somewhat better error reporting. 2024-06-03 20:14:11 -04:00
Dianne Skoll
8bdca0d684 More code refactoring. 2024-06-03 19:49:36 -04:00
Dianne Skoll
129bf5612e Refactor some code. 2024-06-03 19:43:28 -04:00
Dianne Skoll
829962fae1 We don't need to check for excessive parse depth if we're not at the high water mark. 2024-06-03 19:35:42 -04:00
Dianne Skoll
c5f9ed8541 Fix logic error 2024-06-03 18:04:10 -04:00
Dianne Skoll
4a7cef4644 Better error diagnostics. 2024-06-03 18:02:19 -04:00
Dianne Skoll
0e010b56ec Fix some error messages. 2024-06-03 17:59:11 -04:00
Dianne Skoll
ee179ee2f5 Suppress duplicate call frames. 2024-06-03 17:47:38 -04:00
Dianne Skoll
e28712cef3 Add "s" to list of debug flags. 2024-06-03 16:16:44 -04:00
Dianne Skoll
8f0a2a7e79 Limit parse high-water to 2000 2024-06-03 15:15:58 -04:00
Dianne Skoll
ef23bba77f Track parse level high-water mark. 2024-06-03 15:13:32 -04:00
Dianne Skoll
602086ae2d Make "-ds" show built-in functions with ucfirst and user-defined ones all lower-case. 2024-06-03 14:56:32 -04:00
Dianne Skoll
f5a170acbd Update man page. 2024-06-03 11:58:37 -04:00
Dianne Skoll
8125b96f0b Add --max-excution-time cmdline option. 2024-06-03 10:40:58 -04:00
Dianne Skoll
0bb7d89bb9 Update man page. 2024-06-03 10:14:36 -04:00
Dianne Skoll
eb109bbbc0 Finish commenting expr.c 2024-06-03 10:01:38 -04:00
Dianne Skoll
1a0809fd31 Start updating man page. 2024-06-02 23:23:06 -04:00
Dianne Skoll
09625b9d68 More docs 2024-06-02 23:07:49 -04:00
Dianne Skoll
4e164c4268 More comments. 2024-06-02 21:08:04 -04:00
Dianne Skoll
691185f22c Update test file so tests pass. 2024-06-02 20:50:34 -04:00
Dianne Skoll
a8bfb41a9e Add $ExpressionTimeLimit system variable. 2024-06-02 14:01:17 -04:00
Dianne Skoll
fafb30db05 Add --max-execution-time option 2024-06-02 12:56:46 -04:00
Dianne Skoll
243e816523 Add translated error messages 2024-06-02 12:43:32 -04:00
Dianne Skoll
b49c0f52bd Implement EXPR ON and EXPR OFF 2024-06-02 12:39:00 -04:00
Dianne Skoll
07fca94a7f Properly handle purging of SCANFROM reminders. 2024-06-02 12:07:12 -04:00
Dianne Skoll
73917ee537 Only set nonconst_expr in purge mode in a couple of cases. 2024-06-02 12:02:29 -04:00
Dianne Skoll
76f9edecf6 Don't purge reminders with a SCANFROM clause. 2024-06-02 10:36:21 -04:00
Dianne Skoll
d77d9854d2 Remove src/expr.h - everything in it is now in types.h or protos.h 2024-06-02 10:26:41 -04:00
Dianne Skoll
c2b53f95a4 Remove dead code 2024-06-02 10:23:24 -04:00
Dianne Skoll
caef8b80d6 Make it easier to match error messages to error constants. 2024-06-02 10:00:04 -04:00
Dianne Skoll
5e016768af More comments. 2024-06-02 09:46:53 -04:00
Dianne Skoll
ee08ce98d7 More comments. 2024-06-01 23:33:33 -04:00
Dianne Skoll
581bd95838 Allow placing a literal [ in a reminder by using [[ 2024-06-01 18:46:17 -04:00
Dianne Skoll
bb92dab1ab More docs. 2024-06-01 16:44:37 -04:00
Dianne Skoll
83b5c52c76 Make parse_expr_token much any following whitespace to replicate prior behavior.
There may be other parts of the code that rely on this.
2024-06-01 16:35:20 -04:00
Dianne Skoll
93eca25141 Fix spurious "Missing ']'" error reported by Jochen Sprickerhof 2024-06-01 16:30:48 -04:00
Dianne Skoll
34421cb10e Don't funset the Jewish holiday functions. 2024-06-01 14:08:48 -04:00
Dianne Skoll
29b87898aa Add some ASCII art 2024-06-01 14:00:39 -04:00
Dianne Skoll
e9e4db94bd More comments 2024-06-01 13:44:21 -04:00
Dianne Skoll
c95ad0261a Start adding comments; do not distinguish between N_BINARY_OPERATOR and N_UNARY_OPERATOR 2024-06-01 13:37:58 -04:00
Dianne Skoll
7fef456483 Don't allocate - nodes if we don't need to 2024-06-01 13:23:52 -04:00
Dianne Skoll
386131e74d Show bytes used for expression notes. 2024-06-01 09:45:37 -04:00
Dianne Skoll
824d3c88f1 Store at most 5 function args on stack. Any more and we malloc. 2024-06-01 09:41:26 -04:00
Dianne Skoll
18a206abd2 Store args on stack if possible. 2024-06-01 09:30:42 -04:00
Dianne Skoll
8dbae776c9 Fix SEGV. 2024-06-01 09:27:27 -04:00
Dianne Skoll
b78702cc53 Assume all function names are lower-case. 2024-06-01 08:46:27 -04:00
Dianne Skoll
d2b43605ad Completely revamp expression engine. 2024-06-01 08:19:12 -04:00
Dianne Skoll
7728e09337 Don't allow duplicate arg names in function definitions. 2024-06-01 08:19:09 -04:00
Dianne Skoll
2666353ce6 Put an include guard around auto.rem 2024-06-01 08:19:08 -04:00
Dianne Skoll
0b8a306483 Disable CI for "wip" branches. 2024-06-01 08:19:08 -04:00
Dianne Skoll
b51a0b2d08 Document that -ivar is the same as -ivar=0 2024-05-25 10:26:00 -04:00
Dianne Skoll
959355b19c Stricter parsing of '-i' option. 2024-05-25 09:36:33 -04:00
Dianne Skoll
9c3f0f1994 Allow "-ifoo" on the command-line, which is the same as "-ifoo=0" 2024-05-25 09:22:41 -04:00
Dianne Skoll
dac337a65b Convert all leading spaces to tabs. Per Tim Chase. 2024-05-01 09:48:59 -04:00
Dianne Skoll
98739dfdbc Replace spaces with tab (per Emanuele Torre)
Because different invisible things are different.
2024-05-01 09:11:19 -04:00
Dianne Skoll
17b7a1ea84 Fix typo 2024-04-29 17:13:52 -04:00
Dianne Skoll
4d45925758 Fix typo 2024-04-29 16:18:49 -04:00
Dianne Skoll
8cadb23f48 Update release notes. 2024-04-29 16:16:42 -04:00
Dianne Skoll
63211b65c2 Bump version to 04.03.07 2024-04-22 14:55:47 -04:00
Dianne Skoll
1be84525b1 Don't rely on behavior of "%" with negative args. 2024-04-22 09:57:32 -04:00
Dianne Skoll
67ae95a464 Make sure shellescape() does not mangle UTF-8 characters. 2024-04-22 09:50:17 -04:00
Dianne Skoll
c03a95ad94 Use built-in versions of strdup, strcasecmp and strncasecmp instead of writing our own. 2024-04-21 14:44:24 -04:00
Dianne Skoll
51aa7aecb9 Make $Tt a synonym for trigtime() 2024-04-20 11:50:39 -04:00
Dianne Skoll
592cfe5a20 Use "uint32_t" if we have <stdint.h> for MD5 code. 2024-04-20 10:50:44 -04:00
Dianne Skoll
b4cf15e73e Remove some unused autoconf cruft. 2024-04-20 10:45:22 -04:00
Dianne Skoll
862e143372 Ugh, forgot to regen ./configure. 2024-04-20 10:40:06 -04:00
Dianne Skoll
1f10ca49ad Pass proper args to AC_INIT; include Remind home page in usage output. 2024-04-20 10:39:12 -04:00
Dianne Skoll
4a0c4ffdca Add a test to ensure we don't save trigger time while parsing. 2024-04-18 23:48:30 -04:00
Dianne Skoll
27c8737f3a Only save trigger date when computing it, not while parsing. 2024-04-18 23:41:47 -04:00
Dianne Skoll
ecf45fc453 Add tests for commit 0a1178cfd7: Don't clear out last trigger time unnecessarily. 2024-04-18 18:32:38 -04:00
Dianne Skoll
0a1178cfd7 Don't clear out last trigger time unnecessarily. 2024-04-18 17:56:49 -04:00
Dianne Skoll
20a35dc627 Put the tabbed notebook blurb after the blurb about obtaining default settings. 2024-04-10 09:44:40 -04:00
Dianne Skoll
79887c06f0 Tweak wording. 2024-04-10 09:43:13 -04:00
Dianne Skoll
f7ff424904 Remove debugging line. 2024-04-04 13:43:44 -04:00
Dianne Skoll
6678721fe3 Make build.tk add a little note if it obtained default settings from an existing Remind installation. 2024-04-04 13:42:31 -04:00
Dianne Skoll
496302097b Add a missing release note... sigh. 2024-04-02 09:13:59 -04:00
Dianne Skoll
fe3e2b9a20 Install include files with proper permissions. 2024-04-02 09:05:25 -04:00
Dianne Skoll
400a6b066f Add Portuguese holidays, courtesy of Joop Kiefte. 2024-04-02 09:01:45 -04:00
Dianne Skoll
76d181e7fc Update docs 2024-04-02 08:56:27 -04:00
Dianne Skoll
77373eed2d Fix tests on FreeBSD. We need to copy the results of getenv or when we change it, it will be overwritten. 2024-04-02 08:54:40 -04:00
Dianne Skoll
6b52be388f Update docs. 2024-04-02 08:23:21 -04:00
Dianne Skoll
0518a12a91 Don't fail make install if we can't jigger desktop icons. 2024-04-01 14:42:17 -04:00
Dianne Skoll
362a02c4b8 Don't include inotify-related code if we don't have inotify.
Fixes bug that broke compilation on FreeBSD.
2024-04-01 14:24:00 -04:00
Dianne Skoll
3e3a0cde47 Add "all" option to compare-language-mods.pl 2024-04-01 12:03:34 -04:00
Dianne Skoll
c16f93effd Update WHATSNEW. 2024-04-01 09:14:25 -04:00
Dianne Skoll
61b27c02b5 Be a little more aggressive. 2024-04-01 09:07:13 -04:00
Dianne Skoll
ba28eaad53 Fix typo. 2024-04-01 08:42:48 -04:00
Dianne Skoll
92c2d0cc9f Remove obsolete #ifdef tests 2024-04-01 08:41:29 -04:00
Dianne Skoll
21d5e8a095 Tweak daemon mode documentation. 2024-04-01 08:39:24 -04:00
Dianne Skoll
60417d68a7 Bump version to 04.03.05 2024-04-01 08:31:59 -04:00
Dianne Skoll
7d25387403 Remove the MICROSOFT-AND-APPLE file. I think I've made my point. 2024-03-31 12:07:11 -04:00
Dianne Skoll
b454cf5b20 Refactor set_cloexec to make it more convenient to call. 2024-03-31 09:49:32 -04:00
Dianne Skoll
9ea6385b72 Tweak whitespace. 2024-03-31 09:42:13 -04:00
Dianne Skoll
55b7908444 Add International Transgender Day of Visibility to man page
Done to annoy the anti-LGBT crowd.
2024-03-31 09:40:58 -04:00
Dianne Skoll
e9ff66478b Document that we use inotify, where possible, to detect changes to reminder scripts / directories. 2024-03-30 13:25:57 -04:00
Dianne Skoll
d3240d711d Rename DaemonWait to ServerWait. 2024-03-29 19:57:47 -04:00
Dianne Skoll
a8d63a4199 Use inotfy even in normal daemon mode (-zn where n>0) to detect file changes. 2024-03-29 08:28:41 -04:00
Dianne Skoll
a4807a21c3 Save the value of $DefaultColor that was in effect at the time a reminder was queued. Restore this value when triggering the reminder. 2024-03-26 11:44:42 -04:00
Dianne Skoll
a394ef53a0 Print the "passthru" field for PASSTHRU_TYPE reminders when listing the queue. 2024-03-26 11:29:37 -04:00
Dianne Skoll
eb8243743a Prevent mishandling of SPECIAL COLOR reminders when pulling them from the queue.
Bug reported by Damien Tardy-Panis.
2024-03-26 11:21:17 -04:00
Dianne Skoll
8444bb15c5 Fix typo. 2024-03-26 09:54:31 -04:00
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
127 changed files with 11719 additions and 4947 deletions

2
.gitignore vendored
View File

@@ -16,6 +16,7 @@ man/remind.1
man/tkremind.1 man/tkremind.1
pm_to_blib pm_to_blib
rem2html/Makefile rem2html/Makefile
rem2html/rem2html
rem2html/rem2html.1 rem2html/rem2html.1
rem2pdf/Makefile.PL rem2pdf/Makefile.PL
rem2pdf/Makefile.old rem2pdf/Makefile.old
@@ -32,3 +33,4 @@ src/version.h
tests/test.out tests/test.out
www/Makefile www/Makefile
gmon.out gmon.out
tests/once.timestamp

View File

@@ -1,3 +1,8 @@
workflow:
rules:
- if: $CI_COMMIT_BRANCH =~ /wip/
when: never
tests: tests:
image: 'debian:stable-slim' image: 'debian:stable-slim'
before_script: before_script:

View File

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

View File

@@ -1,30 +0,0 @@
MICROSOFT WINDOWS
=================
I used to prohibit porting Remind to Microsoft Windows. However, this
may cause problems with the GPL, so I have removed that restriction.
Although I cannot prevent you from porting Remind to Windows, I appeal
to you not to do it. I am trying to encourage the growth of free
software, not proprietary software.
If you port Remind to Windows, I will not provide support or answers to
questions -- you're on your own. On the other hand, I will feel no guilt
in taking enhancements and merging them into the UNIX stream.
APPLE
=====
I can't prevent you from using Remind on Apple's products, but I hope
you don't. Apple's corporate culture is the very antithesis of Free
Software. Rather than using Mac OS X, I encourage you to switch to
Linux or FreeBSD, two Free Software operating systems that are every
bit as capable as Mac OS X and which are unencumbered by Apple's
arbitrary restrictions.
And if you're looking to port Remind to other Apple products like the
iPhone or iPad, please don't. Those products enforce Apple's rigorous
controls much more stringently than Mac OS X on an Apple PC.
--
Dianne Skoll

View File

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

54
build.tk Normal file → Executable file
View File

@@ -15,6 +15,8 @@
# the next line restarts using wish \ # the next line restarts using wish \
exec wish "$0" "$@" exec wish "$0" "$@"
global RemindExecutable
#*********************************************************************** #***********************************************************************
# %PROCEDURE: SetConfigDefaults # %PROCEDURE: SetConfigDefaults
# %ARGUMENTS: # %ARGUMENTS:
@@ -27,10 +29,10 @@ exec wish "$0" "$@"
proc SetConfigDefaults {} { proc SetConfigDefaults {} {
global Config global Config
set Config(LAT_DEG) 45 set Config(LAT_DEG) 45
set Config(LAT_MIN) 24 set Config(LAT_MIN) 25
set Config(LAT_SEC) 14 set Config(LAT_SEC) 14
set Config(LON_DEG) 75 set Config(LON_DEG) 75
set Config(LON_MIN) 39 set Config(LON_MIN) 41
set Config(LON_SEC) 23 set Config(LON_SEC) 23
set Config(LOCATION) "Ottawa" set Config(LOCATION) "Ottawa"
set Config(DEFAULT_PAGE) "Letter" set Config(DEFAULT_PAGE) "Letter"
@@ -40,7 +42,7 @@ proc SetConfigDefaults {} {
set Config(WESTERN_HEMISPHERE) 1 set Config(WESTERN_HEMISPHERE) 1
set Config(LANGUAGE) "English" set Config(LANGUAGE) "English"
set Config(INST_DIR) "/usr/local/bin" set Config(INST_DIR) "/usr/local/bin"
set Config(MAN_DIR) "/usr/local/man" set Config(MAN_DIR) "/usr/local/share/man"
} }
#*********************************************************************** #***********************************************************************
@@ -53,7 +55,7 @@ proc SetConfigDefaults {} {
# Pops up an error dialog; then calls exit. # Pops up an error dialog; then calls exit.
#*********************************************************************** #***********************************************************************
proc Bail { msg } { proc Bail { msg } {
tk_dialog .err "Remind Configuration Error" $msg error 0 "Bummer" tk_messageBox -message "Remind Build Error" -detail $msg -icon error -type ok
exit 1 exit 1
} }
@@ -124,7 +126,7 @@ proc CreateMainDialog {} {
# Creates the "installation directories" dialog. # Creates the "installation directories" dialog.
#*********************************************************************** #***********************************************************************
proc CreateInstallDirDialog { w } { proc CreateInstallDirDialog { w } {
global Config global Config RemindExecutable
label $w.binlabel -text "Location for programs: " label $w.binlabel -text "Location for programs: "
entry $w.bin -width 30 entry $w.bin -width 30
$w.bin insert end $Config(INST_DIR) $w.bin insert end $Config(INST_DIR)
@@ -133,16 +135,19 @@ proc CreateInstallDirDialog { w } {
entry $w.man -width 30 entry $w.man -width 30
$w.man insert end $Config(MAN_DIR) $w.man insert end $Config(MAN_DIR)
text $w.blurb -width 1 -height 5 -wrap word -relief flat -takefocus 0 text $w.blurb -width 1 -height 20 -wrap word -relief flat -takefocus 0
$w.blurb insert end "\n(Tabbed-notebook Tcl code taken from \"Effective Tcl/Tk Programming\" by Mark Harrison and Michael McLennan, Addison-Wesley Professional Computing Series.)" if { "$RemindExecutable" != "" } {
$w.blurb configure -state disabled $w.blurb insert end "Note: Default settings were obtained by querying the existing installed version of Remind found at: $RemindExecutable\n"
# Disable all text-window behaviour }
bindtags $w.blurb {NoSuchTag} $w.blurb insert end "\n(Tabbed-notebook Tcl code taken from \"Effective Tcl/Tk Programming\" by Mark Harrison and Michael McLennan, Addison-Wesley Professional Computing Series.)\n"
grid $w.binlabel -row 0 -column 0 -sticky e grid $w.binlabel -row 0 -column 0 -sticky e
grid $w.bin -row 0 -column 1 -sticky nsew grid $w.bin -row 0 -column 1 -sticky nsew
grid $w.manlabel -row 1 -column 0 -sticky e grid $w.manlabel -row 1 -column 0 -sticky e
grid $w.man -row 1 -column 1 -sticky nsew grid $w.man -row 1 -column 1 -sticky nsew
grid $w.blurb - -sticky nsew grid $w.blurb - -sticky nsew
# Disable all text-window behaviour
bindtags $w.blurb {NoSuchTag}
$w.blurb configure -state disabled
} }
#*********************************************************************** #***********************************************************************
@@ -190,8 +195,8 @@ proc CreateLocationDialog { w } {
grid $w.north $w.west grid $w.north $w.west
grid $w.south $w.east grid $w.south $w.east
grid $w.loclab -sticky e grid $w.loclab -sticky e
grid $w.location -sticky nsew -row 6 -column 1 grid $w.location -sticky nsew -row 8 -column 1
} }
#*********************************************************************** #***********************************************************************
@@ -293,9 +298,9 @@ proc BuildRemind {} {
.msgs insert end "\n>>> Creating src/custom.h...\n\n" green .msgs insert end "\n>>> Creating src/custom.h...\n\n" green
CreateCustomH CreateCustomH
.msgs insert end ">>> Calling `./configure'...\n\n" green .msgs insert end "\n>>> Calling `./configure'...\n\n" green
CallConfigure CallConfigure
.msgs insert end ">>> Calling `make'...\n\n" green .msgs insert end "\n>>> Calling `make'...\n\n" green
CallMake CallMake
.msgs insert end "\n----------------------------------------------\n\n" .msgs insert end "\n----------------------------------------------\n\n"
.msgs insert end "Remind" red .msgs insert end "Remind" red
@@ -447,12 +452,12 @@ proc CreateCustomH {} {
"#define DEFAULT_LATITUDE *" { "#define DEFAULT_LATITUDE *" {
set lat [expr $LAT_DEG + ($LAT_MIN/60.0) + ($LAT_SEC/3600.0)]; set lat [expr $LAT_DEG + ($LAT_MIN/60.0) + ($LAT_SEC/3600.0)];
puts $out "#define DEFAULT_LATITUDE $lat" puts $out "#define DEFAULT_LATITUDE $lat"
.msgs insert end "#define DEFAULT_LATITUDE $lat" .msgs insert end "#define DEFAULT_LATITUDE $lat\n"
} }
"#define DEFAULT_LONGITUDE *" { "#define DEFAULT_LONGITUDE *" {
set lon [expr -1.0 * ($LON_DEG + ($LON_MIN/60.0) + ($LON_SEC/3600.0))] set lon [expr -1.0 * ($LON_DEG + ($LON_MIN/60.0) + ($LON_SEC/3600.0))]
puts $out "#define DEFAULT_LONGITUDE $lon" puts $out "#define DEFAULT_LONGITUDE $lon"
.msgs insert end "#define DEFAULT_LONGITUDE $lon" .msgs insert end "#define DEFAULT_LONGITUDE $lon\n"
} }
"#define LOCATION *" { "#define LOCATION *" {
puts $out "#define LOCATION \"$Config(LOCATION)\"" puts $out "#define LOCATION \"$Config(LOCATION)\""
@@ -506,7 +511,13 @@ proc CallMake {} {
"Icelandic" { set lang ICELANDIC } "Icelandic" { set lang ICELANDIC }
default { set lang ENGLISH } default { set lang ENGLISH }
} }
RunCommand "make \"LANGDEF=-DLANG=$lang\"" set nproc 0
catch { set nproc [exec nproc] }
if { $nproc != 0 } {
RunCommand "make -j $nproc \"LANGDEF=-DLANG=$lang\""
} else {
RunCommand "make \"LANGDEF=-DLANG=$lang\""
}
} }
@@ -721,7 +732,7 @@ proc notebook_fix_size {win} {
#*********************************************************************** #***********************************************************************
proc FindRemind {} { proc FindRemind {} {
global env global env
set path [concat [split $env(PATH) ":"] "/bin" "/usr/bin" "/usr/local/bin"] set path [concat [split $env(PATH) ":"] "/usr/local/bin" "/bin" "/usr/bin" ]
foreach thing $path { foreach thing $path {
if [file executable [file join $thing "remind"]] { if [file executable [file join $thing "remind"]] {
return [file join $thing "remind"] return [file join $thing "remind"]
@@ -739,16 +750,17 @@ proc FindRemind {} {
# sensible defaults. # sensible defaults.
#*********************************************************************** #***********************************************************************
proc SetConfigFromRemind {} { proc SetConfigFromRemind {} {
global Config global Config RemindExecutable
SetConfigDefaults SetConfigDefaults
set rem [FindRemind] set rem [FindRemind]
set RemindExecutable $rem
if {"$rem" == ""} { if {"$rem" == ""} {
return return
} }
set dir [file dirname $rem] set dir [file dirname $rem]
set Config(INST_DIR) $dir set Config(INST_DIR) $dir
if {"$dir" == "/usr/local/bin"} { if {"$dir" == "/usr/local/bin"} {
set Config(MAN_DIR) "/usr/local/man" set Config(MAN_DIR) "/usr/local/share/man"
} elseif {$dir == "/usr/bin"} { } elseif {$dir == "/usr/bin"} {
set Config(MAN_DIR) "/usr/share/man" set Config(MAN_DIR) "/usr/share/man"
} }
@@ -758,6 +770,8 @@ proc SetConfigFromRemind {} {
set Config(MAN_DIR) "/usr/share/man" set Config(MAN_DIR) "/usr/share/man"
} elseif {[file readable "/usr/man/man1/remind.1"]} { } elseif {[file readable "/usr/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/man" set Config(MAN_DIR) "/usr/man"
} elseif {[file readable "/usr/local/share/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/local/share/man"
} elseif {[file readable "/usr/local/man/man1/remind.1"]} { } elseif {[file readable "/usr/local/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/local/man" set Config(MAN_DIR) "/usr/local/man"
} }

130
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71. # Generated by GNU Autoconf 2.71 for remind 05.00.06.
# #
# #
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -606,12 +606,12 @@ MFLAGS=
MAKEFLAGS= MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='' PACKAGE_NAME='remind'
PACKAGE_TARNAME='' PACKAGE_TARNAME='remind'
PACKAGE_VERSION='' PACKAGE_VERSION='05.00.06'
PACKAGE_STRING='' PACKAGE_STRING='remind 05.00.06'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
PACKAGE_URL='' PACKAGE_URL='https://dianne.skoll.ca/projects/remind/'
ac_unique_file="src/queue.c" ac_unique_file="src/queue.c"
# Factoring default headers for most tests. # Factoring default headers for most tests.
@@ -757,7 +757,7 @@ localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run' runstatedir='${localstatedir}/run'
includedir='${prefix}/include' includedir='${prefix}/include'
oldincludedir='/usr/include' oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
infodir='${datarootdir}/info' infodir='${datarootdir}/info'
htmldir='${docdir}' htmldir='${docdir}'
dvidir='${docdir}' dvidir='${docdir}'
@@ -1264,7 +1264,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures this package to adapt to many kinds of systems. \`configure' configures remind 05.00.06 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1313,7 +1313,7 @@ Fine tuning of the installation directories:
--infodir=DIR info documentation [DATAROOTDIR/info] --infodir=DIR info documentation [DATAROOTDIR/info]
--localedir=DIR locale-dependent data [DATAROOTDIR/locale] --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
--mandir=DIR man documentation [DATAROOTDIR/man] --mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] --docdir=DIR documentation root [DATAROOTDIR/doc/remind]
--htmldir=DIR html documentation [DOCDIR] --htmldir=DIR html documentation [DOCDIR]
--dvidir=DIR dvi documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR]
--pdfdir=DIR pdf documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR]
@@ -1325,7 +1325,9 @@ _ACEOF
fi fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of remind 05.00.06:";;
esac
cat <<\_ACEOF cat <<\_ACEOF
Optional Features: Optional Features:
@@ -1348,6 +1350,7 @@ Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations. it to find libraries and programs with nonstandard names/locations.
Report bugs to the package provider. Report bugs to the package provider.
remind home page: <https://dianne.skoll.ca/projects/remind/>.
_ACEOF _ACEOF
ac_status=$? ac_status=$?
fi fi
@@ -1411,7 +1414,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
configure remind configure 05.00.06
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1861,7 +1864,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by $as_me, which was It was created by remind $as_me 05.00.06, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@@ -2450,7 +2453,6 @@ as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H" as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H"
as_fn_append ac_header_c_list " utime.h utime_h HAVE_UTIME_H"
# Auxiliary files required by this configure script. # Auxiliary files required by this configure script.
ac_aux_files="install-sh" ac_aux_files="install-sh"
@@ -4004,6 +4006,12 @@ printf "%s\n" "#define SIZEOF_TIME_T $ac_cv_sizeof_time_t" >>confdefs.h
ac_fn_c_check_header_compile "$LINENO" "strings.h" "ac_cv_header_strings_h" "$ac_includes_default"
if test "x$ac_cv_header_strings_h" = xyes
then :
printf "%s\n" "#define HAVE_STRINGS_H 1" >>confdefs.h
fi
ac_fn_c_check_header_compile "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default" ac_fn_c_check_header_compile "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_types_h" = xyes if test "x$ac_cv_header_sys_types_h" = xyes
then : then :
@@ -4034,6 +4042,12 @@ then :
printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h
fi fi
ac_fn_c_check_header_compile "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_inotify_h" = xyes
then :
printf "%s\n" "#define HAVE_SYS_INOTIFY_H 1" >>confdefs.h
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
@@ -4074,60 +4088,6 @@ printf "%s\n" "#define TM_IN_SYS_TIME 1" >>confdefs.h
fi fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether utime accepts a null argument" >&5
printf %s "checking whether utime accepts a null argument... " >&6; }
if test ${ac_cv_func_utime_null+y}
then :
printf %s "(cached) " >&6
else $as_nop
rm -f conftest.data; >conftest.data
# Sequent interprets utime(file, 0) to mean use start of epoch. Wrong.
if test "$cross_compiling" = yes
then :
ac_cv_func_utime_null='guessing yes'
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
#ifdef HAVE_UTIME_H
# include <utime.h>
#endif
int
main (void)
{
struct stat s, t;
return ! (stat ("conftest.data", &s) == 0
&& utime ("conftest.data", 0) == 0
&& stat ("conftest.data", &t) == 0
&& t.st_mtime >= s.st_mtime
&& t.st_mtime - s.st_mtime < 120);
;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"
then :
ac_cv_func_utime_null=yes
else $as_nop
ac_cv_func_utime_null=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_utime_null" >&5
printf "%s\n" "$ac_cv_func_utime_null" >&6; }
if test "x$ac_cv_func_utime_null" != xno; then
ac_cv_func_utime_null=yes
printf "%s\n" "#define HAVE_UTIME_NULL 1" >>confdefs.h
fi
rm -f conftest.data
if test "$GCC" = yes; then if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes" CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes"
# Check for link-time optimization support # Check for link-time optimization support
@@ -4175,6 +4135,24 @@ if test "$?" != 0 ; then
echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!" echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!"
exit 1 exit 1
fi fi
ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup"
if test "x$ac_cv_func_strdup" = xyes
then :
printf "%s\n" "#define HAVE_STRDUP 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "strcasecmp" "ac_cv_func_strcasecmp"
if test "x$ac_cv_func_strcasecmp" = xyes
then :
printf "%s\n" "#define HAVE_STRCASECMP 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "strncasecmp" "ac_cv_func_strncasecmp"
if test "x$ac_cv_func_strncasecmp" = xyes
then :
printf "%s\n" "#define HAVE_STRNCASECMP 1" >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv"
if test "x$ac_cv_func_setenv" = xyes if test "x$ac_cv_func_setenv" = xyes
then : then :
@@ -4211,14 +4189,20 @@ then :
printf "%s\n" "#define HAVE_INITGROUPS 1" >>confdefs.h printf "%s\n" "#define HAVE_INITGROUPS 1" >>confdefs.h
fi fi
ac_fn_c_check_func "$LINENO" "inotify_init1" "ac_cv_func_inotify_init1"
if test "x$ac_cv_func_inotify_init1" = xyes
then :
printf "%s\n" "#define HAVE_INOTIFY_INIT1 1" >>confdefs.h
fi
VERSION=04.02.08 VERSION=$PACKAGE_VERSION
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1" ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1"
cat >confcache <<\_ACEOF cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure # This file is a shell script that caches the results of configure
@@ -4719,7 +4703,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by $as_me, which was This file was extended by remind $as_me 05.00.06, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@@ -4775,7 +4759,8 @@ $config_files
Configuration headers: Configuration headers:
$config_headers $config_headers
Report bugs to the package provider." Report bugs to the package provider.
remind home page: <https://dianne.skoll.ca/projects/remind/>."
_ACEOF _ACEOF
ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
@@ -4783,7 +4768,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
config.status remind config.status 05.00.06
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"
@@ -4910,6 +4895,7 @@ do
"www/Makefile") CONFIG_FILES="$CONFIG_FILES www/Makefile" ;; "www/Makefile") CONFIG_FILES="$CONFIG_FILES www/Makefile" ;;
"src/version.h") CONFIG_FILES="$CONFIG_FILES src/version.h" ;; "src/version.h") CONFIG_FILES="$CONFIG_FILES src/version.h" ;;
"rem2html/Makefile") CONFIG_FILES="$CONFIG_FILES rem2html/Makefile" ;; "rem2html/Makefile") CONFIG_FILES="$CONFIG_FILES rem2html/Makefile" ;;
"rem2html/rem2html") CONFIG_FILES="$CONFIG_FILES rem2html/rem2html" ;;
"rem2pdf/Makefile.PL") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.PL" ;; "rem2pdf/Makefile.PL") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.PL" ;;
"rem2pdf/Makefile.top") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.top" ;; "rem2pdf/Makefile.top") CONFIG_FILES="$CONFIG_FILES rem2pdf/Makefile.top" ;;
"rem2pdf/bin/rem2pdf") CONFIG_FILES="$CONFIG_FILES rem2pdf/bin/rem2pdf" ;; "rem2pdf/bin/rem2pdf") CONFIG_FILES="$CONFIG_FILES rem2pdf/bin/rem2pdf" ;;

View File

@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
AC_INIT AC_INIT(remind, 05.00.06, , , https://dianne.skoll.ca/projects/remind/)
AC_CONFIG_SRCDIR([src/queue.c]) AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF' cat <<'EOF'
@@ -30,7 +30,7 @@ AC_PATH_PROG([PERL], [perl])
dnl Checks for libraries. dnl Checks for libraries.
AC_CHECK_LIB(m, sqrt) AC_CHECK_LIB(m, sqrt)
AC_CHECK_HEADERS_ONCE([sys/time.h]) AC_CHECK_HEADERS_ONCE([sys/time.h stdint.h])
dnl Integer sizes dnl Integer sizes
AC_CHECK_SIZEOF(unsigned int) AC_CHECK_SIZEOF(unsigned int)
@@ -38,13 +38,11 @@ AC_CHECK_SIZEOF(unsigned long)
AC_CHECK_SIZEOF(time_t) AC_CHECK_SIZEOF(time_t)
dnl Checks for header files. dnl Checks for header files.
AC_CHECK_HEADERS(sys/types.h glob.h wctype.h locale.h langinfo.h) AC_CHECK_HEADERS(strings.h sys/types.h glob.h wctype.h locale.h langinfo.h sys/inotify.h)
dnl Checks for typedefs, structures, and compiler characteristics. dnl Checks for typedefs, structures, and compiler characteristics.
AC_STRUCT_TM AC_STRUCT_TM
dnl Checks for library functions.
AC_FUNC_UTIME_NULL
if test "$GCC" = yes; then if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes" CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes"
# Check for link-time optimization support # Check for link-time optimization support
@@ -86,13 +84,13 @@ if test "$?" != 0 ; then
echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!" echo "*** COULD NOT DETERMINE RELEASE DATE: docs/WHATSNEW is incorrect!"
exit 1 exit 1
fi fi
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale initgroups) AC_CHECK_FUNCS(strdup strcasecmp strncasecmp setenv unsetenv glob mbstowcs setlocale initgroups inotify_init1)
VERSION=04.02.08 VERSION=$PACKAGE_VERSION
AC_SUBST(VERSION) AC_SUBST(VERSION)
AC_SUBST(PERL) AC_SUBST(PERL)
AC_SUBST(PERLARTIFACTS) AC_SUBST(PERLARTIFACTS)
AC_SUBST(RELEASE_DATE) AC_SUBST(RELEASE_DATE)
AC_CONFIG_FILES([src/Makefile www/Makefile src/version.h rem2html/Makefile rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1]) AC_CONFIG_FILES([src/Makefile www/Makefile src/version.h rem2html/Makefile rem2html/rem2html rem2pdf/Makefile.PL rem2pdf/Makefile.top rem2pdf/bin/rem2pdf man/rem.1 man/rem2ps.1 man/remind.1 man/tkremind.1])
AC_OUTPUT AC_OUTPUT
chmod a+x rem2pdf/bin/rem2pdf chmod a+x rem2pdf/bin/rem2pdf

View File

@@ -0,0 +1,3 @@
The upstream GitHub project for ical2rem is:
https://github.com/jbalcorn/ical2rem

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Justin B. Alcorn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,103 @@
# ical2rem
The original iCal to Remind script, first released in 2005.
Reads iCal files and outputs remind-compatible files. Tested ONLY with
calendar files created by Mozilla Calendar/Sunbird. Use at your own risk.
## License
In 2005, this was released with the Gnu Public License V2. However, I am changing it to the MIT License, since that provides greater freedom to do with this code what you want.
Copyright (c) 2005, 2007, 2019 Justin B. Alcorn
## How I use Remind and Google Calendar together
- My family has a Google Email domain, and our email addresses all end in the same domain. We all use Google Calendars and I want to mail reminders to each of the family members containing both Google Calendar and .reminder information.
- Under my ~/.rem/ directory each family member has a directory. Each directory contains a standard remind file called 'reminders' that at the very least has the line "INCLUDE /home/jalcorn/.rem/<username>/ical2rem" and flag files indicating whether they want Daily or Weekly reminders. My reminders files references my standard .reminders file, and I also have a flag so if I run a Test run I'll get it. There's actually a lot more files (I have a big family).
````
./rem
./son1:
drwxrwxr-x 2 jalcorn jalcorn 4096 Dec 12 14:02 .
drwxr-xr-x 12 jalcorn jalcorn 4096 Dec 12 14:13 ..
-rw-rw-r-- 1 jalcorn jalcorn 51 Mar 3 06:10 ical2rem
lrwxrwxrwx 1 jalcorn jalcorn 33 Oct 27 2016 son1.ics -> /home/jalcorn/calendars/son1.ics
-rw-rw-r-- 1 jalcorn jalcorn 976 Dec 12 14:02 reminders
-rw-rw-r-- 1 jalcorn jalcorn 0 Oct 27 2016 Weekly
./justin:
drwxrwxr-x 2 jalcorn jalcorn 4096 Feb 27 08:29 .
drwxr-xr-x 12 jalcorn jalcorn 4096 Dec 12 14:13 ..
lrwxrwxrwx 1 jalcorn jalcorn 32 Oct 27 2016 son1.ics -> /home/jalcorn/calendars/son1.ics
-rw-rw-r-- 1 jalcorn jalcorn 0 Nov 7 2016 Daily
lrwxrwxrwx 1 jalcorn jalcorn 34 Oct 27 2016 family.ics -> /home/jalcorn/calendars/family.ics
-rw-rw-r-- 1 jalcorn jalcorn 37320 Mar 3 06:10 ical2rem
lrwxrwxrwx 1 jalcorn jalcorn 34 Oct 27 2016 justin.ics -> /home/jalcorn/calendars/justin.ics
lrwxrwxrwx 1 jalcorn jalcorn 24 Nov 7 2016 reminders -> /home/jalcorn/.reminders
lrwxrwxrwx 1 jalcorn jalcorn 34 Oct 27 2016 vmd.ics -> /home/jalcorn/calendars/vmd.ics
-rw-rw-r-- 1 jalcorn jalcorn 0 Oct 27 2016 Test
-rw-rw-r-- 1 jalcorn jalcorn 0 Nov 7 2016 Weekly
````
- bin/getgooglecals.sh runs out of crontab and downloads whatever calendars I want. Note that we can also download organization calendars, I've included a public one here (Cleveland Heights Vocal Music Department calendar).
- dailyreminders.sh is linked to weeklyreminders.sh and testreminders.sh so I can run it in different modes. The concatenate the various calendar outputs as a single remind file then send the reminders via email.
### Example: .rem/son1/reminders file:
````
INCLUDE /home/jalcorn/.rem/defs.rem
INCLUDE /home/jalcorn/.rem/float
INCLUDE /home/jalcorn/.rem/son1/ical2rem
fset _weeks() coerce("STRING", (trigdate()-today())/7) + plural((trigdate()-today())/7, " week")
FSET _sfun(x) choose(x, -60, 30, 5, 0)
FSET oldfloat(y,m,d) trigger(MAX(realtoday(), date(y,m,d)))
FSET due(y,m,d) "(" + (date(y,m,d)-trigdate()) + ")"
SET fullmoon moondate(2)
REM [trigger(realtoday())] SPECIAL SHADE 145 70 100 %
REM [float(2019,4,15,105)] MSG File tax return [due(2017,4,15)]%
REM PRIORITY 9999 MSG %"%"%
INCLUDE /home/jalcorn/.rem/bdays
SET $LongDeg 81
SET $LongMin 11
SET $LongSec 11
SET $LatDeg 41
SET $LatMin 11
SET $LatSec 11
REM [trigger(moondate(2))] +1 MSG %"Full Moon%" %b%
fset _srtd() coerce("STRING", _no_lz(_am_pm(sunrise(today()))))
fset _sstd() coerce("STRING", _no_lz(_am_pm(sunset(today()))))
MSG Sun is up today from [_srtd()] to [_sstd()].%"%"%
````
## Revision History
### Version 0.7 2024-09-04
- ISSUE 8: New version of remind complains if _sfun isn't defined. Output a header
to define a function that does nothing if the function doesn't exist.
### Version 0.6 2019-03-01
- Publish on GitHub and change license to MIT License
- Add supporting files and explanation of how I use it
### version 0.5.2 2007-03-23
- BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME
- remove project-lead-time, since Category was a non-standard attribute
- NOTE: There is a bug in iCal::Parser v1.14 that causes multiple calendars to
fail if a calendar with recurring events is followed by a calendar with no
recurring events. This has been reported to the iCal::Parser author.
### version 0.5.1 2007-03-21
- BUG: Handle multiple calendars on STDIN
- add --heading option for priority on section headers
### version 0.5 2007-03-21
- Add more help options
- --project-lead-time option
- Supress printing of heading if there are no todos to print
### version 0.4
- Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg
- Change to GetOptions
- Change to pipe
- Add --label, --help options
- Add Help Text
- Change to subroutines
- Efficiency and Cleanup
### version 0.3
- Convert to GPL (Thanks to Mark Stosberg)
- Add usage
### version 0.2
- add command line switches
- add debug code
- add SCHED _sfun keyword
- fix typos
### version 0.1 - ALPHA CODE.

View File

@@ -0,0 +1,82 @@
#!/usr/bin/perl -w
#
# cal_futureonly.pl -
# Reads iCal files and outputs events between 1 month ago and 1 year from now.
# Copyright (c) 2005, 2007, 2019 Justin B. Alcorn
=head1 SYNOPSIS
cal_futureonly.pl --file=filname.ics > output.ics
--help Usage
--man Complete man page
--infile (REQUIRED) name of input calendar file
--file (REQUIRED) name of output calendar file
Expects an ICAL stream on STDIN. Converts it to the format
used by the C<remind> script and prints it to STDOUT.
=head2 --infile
Input file
=head2 --file
Output File
=cut
use strict;
use Data::ICal;
use Data::ICal::Entry;
use DateTime::Span;
use Data::ICal::DateTime;
use DateTime;
use Getopt::Long 2.24 qw':config auto_help';
use Pod::Usage;
use Data::Dumper;
use vars '$VERSION';
$VERSION = "0.1";
my $help;
my $man;
my $infile;
my $file;
my $debug = 0;
GetOptions (
"help|?" => \$help,
"man" => \$man,
"debug" => \$debug,
"infile=s" => \$infile,
"file=s" => \$file
);
pod2usage(1) if $help;
pod2usage(1) if (! $file);
pod2usage(-verbose => 2) if $man;
my $limit = DateTime->now();
$limit->subtract( months => 1);
my $endlimit = DateTime->now()->add(years =>1);
print STDERR "including events from: ",$limit->ymd," to: ".$endlimit->ymd,"\n" if $debug;
my $span = DateTime::Span->from_datetimes( start => $limit, end => $endlimit );
print STDERR "Parsing $infile\n" if $debug;
my $cal = Data::ICal->new(filename => $infile);
if (! $cal) {
die "Died Trying to read $infile :".$cal->error_message;
}
#my $archive = Data::ICal->new(filename => 'archive.ics');
print "Output = $file\n" if $debug;
my $new = Data::ICal->new();
if (! $new) {
die $new->error_message;
}
my @events = $cal->events($span);
$new->add_entries(@events);
open(NEW, ">$file");
print NEW $new->as_string;
close NEW;
exit 0;
#:vim set ft=perl ts=4 sts=4 expandtab :

View File

@@ -0,0 +1,45 @@
#!/bin/bash
r=`basename $0`
if [ $r == 'weeklyreminders.sh' ];
then
t=14;
w=Weekly;
elif [ $r == 'dailyreminders.sh' ];
then
t=3;
w=Daily;
else
t=5
w=Test;
fi
cd .rem
for d in * ;
do
if [ "$( ls -A $d/$w 2>/dev/null )" ];
then
echo "Sending a $w reminder to $d"
ft=/tmp/$d-t-$$.txt
f=/tmp/$d-$$.txt
echo "Reminders for next $t days:" >> $f
cat /dev/null > $d/ical2rem
for c in $d/*.ics
do
calname=`basename $c .ics | tr a-z A-Z`
cat $c 2>/dev/null | sed -e "s/^SUMMARY:/SUMMARY: {${calname}} /" \
| sed -e 's/DT\([A-Z]*\);TZID=UTC:\([0-9T]*\)/DT\1:\2Z/' >> $ft
done
cat $ft | ~/bin/ical2rem.pl --label "Online Calendar" --heading "PRIORITY 9999" --lead-time $t >> $d/ical2rem
if [ -e $d/reminders ];then r="${d}/reminders"; else r="${d}/ical2rem";fi
/usr/bin/remind -q -iplain=1 $r >> $f
echo "
All calendars can be accessed by logging into https://calendar.google.com/ as $d@jalcorn.net
" >> $f
cat $f | mail -s "$w Reminders for $d" $d@jalcorn.net;
cat $f
rm $f
rm $ft
fi;
done

View File

@@ -0,0 +1,21 @@
#!/bin/sh
#
# Get google calendars, fix issues caused by changes in Google calendars, and remove all past events.
#
# Obviously, I've removed the private hashes from private calendars.
#
cd ~/calendars
wget -q -O full/justin.ics --no-check-certificate https://www.google.com/calendar/ical/jbalcorn\%40gmail.com/private-aaaaaaaaaaaaaaaaaaaaaaaaaa/basic.ics
wget -q -O full/family.ics --no-check-certificate https://www.google.com/calendar/ical/jalcorn.net_aaaaaaaaaaaaaaaaaaaaaaaaaa\%40group.calendar.google.com/private-6c42a79dec0b3b3bb7b9b0ebf9776bc1/basic.ics
wget -q -O full/son1.ics --no-check-certificate https://www.google.com/calendar/ical/son1\%40jalcorn.net/private-aaaaaaaaaaaaaaaaaaaaaaaaaa/basic.ics
wget -q -O full/vmd.ics --no-check-certificate https://calendar.google.com/calendar/ical/chuh.org_0pmkefjkiqc4snoel7occlslh8%40group.calendar.google.com/public/basic.ics
for i in full/*.ics;do
cat $i 2>/dev/null | sed -e 's/DT\([A-Z]*\);TZID=UTC:\([0-9T]*\)/DT\1:\2Z/' > /tmp/temp.ics
cp /tmp/temp.ics $i
done
~/bin/cal_futureonly.pl --infile=full/justin.ics --file=justin.ics
~/bin/cal_futureonly.pl --infile=full/family.ics --file=family.ics
~/bin/cal_futureonly.pl --infile=full/son1.ics --file=son1.ics
~/bin/cal_futureonly.pl --infile=full/vmd.ics --file=vmd.ics

View File

@@ -19,7 +19,8 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# #
# # version 0.6 2019-03-01
# - Updates to put on GitHub
# version 0.5.2 2007-03-23 # version 0.5.2 2007-03-23
# - BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME # - BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME
# - remove project-lead-time, since Category was a non-standard attribute # - remove project-lead-time, since Category was a non-standard attribute
@@ -32,7 +33,7 @@
# version 0.5 2007-03-21 # version 0.5 2007-03-21
# - Add more help options # - Add more help options
# - --project-lead-time option # - --project-lead-time option
# - Suppress printing of heading if there are no todos to print # - Supress printing of heading if there are no todos to print
# version 0.4 # version 0.4
# - Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg # - Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg
# - Change to GetOptions # - Change to GetOptions
@@ -56,12 +57,19 @@
cat /path/to/file*.ics | ical2rem.pl > ~/.ical2rem cat /path/to/file*.ics | ical2rem.pl > ~/.ical2rem
All options have reasonable defaults: All options have reasonable defaults:
--label Calendar name (Default: Calendar) --label Calendar name (Default: Calendar)
--start Start of time period to parse (parsed by str2time)
--end End of time period to parse
--lead-time Advance days to start reminders (Default: 3) --lead-time Advance days to start reminders (Default: 3)
--todos, --no-todos Process Todos? (Default: Yes) --todos, --no-todos Process Todos? (Default: Yes)
--heading Define a priority for static entries --iso8601 Use YYYY-MM-DD date format
--help Usage --locations, --no-locations Include location? (Default: Yes)
--man Complete man page --end-times, --no-end-times Include event end times in reminder text
(Default: No)
--heading Define a priority for static entries
--help Usage
--debug Enable debug output
--man Complete man page
Expects an ICAL stream on STDIN. Converts it to the format Expects an ICAL stream on STDIN. Converts it to the format
used by the C<remind> script and prints it to STDOUT. used by the C<remind> script and prints it to STDOUT.
@@ -74,6 +82,14 @@ The syntax generated includes a label for the calendar parsed.
By default this is "Calendar". You can customize this with By default this is "Calendar". You can customize this with
the "--label" option. the "--label" option.
=head2 --iso8601
Use YYYY-MM-DD date format in output instead of Mmm DD YYYY
=head2 --locations, --no-locations
Whether or not to include locations in events
=head2 --lead-time =head2 --lead-time
ical2rem.pl --lead-time 3 ical2rem.pl --lead-time 3
@@ -84,7 +100,7 @@ How may days in advance to start getting reminders about the events. Defaults to
ical2rem.pl --no-todos ical2rem.pl --no-todos
If you don't care about the ToDos the calendar, this will suppress If you don't care about the ToDos the calendar, this will surpress
printing of the ToDo heading, as well as skipping ToDo processing. printing of the ToDo heading, as well as skipping ToDo processing.
=head2 --heading =head2 --heading
@@ -98,6 +114,7 @@ the calendar entries. See the file defs.rem from the remind distribution for mo
use strict; use strict;
use iCal::Parser; use iCal::Parser;
use Date::Parse;
use DateTime; use DateTime;
use Getopt::Long 2.24 qw':config auto_help'; use Getopt::Long 2.24 qw':config auto_help';
use Pod::Usage; use Pod::Usage;
@@ -108,19 +125,31 @@ $VERSION = "0.5.2";
# Declare how many days in advance to remind # Declare how many days in advance to remind
my $DEFAULT_LEAD_TIME = 3; my $DEFAULT_LEAD_TIME = 3;
my $PROCESS_TODOS = 1; my $PROCESS_TODOS = 1;
my $HEADING = ""; my $HEADING = "";
my $help; my $help;
my $debug;
my $man; my $man;
my $iso8601;
my $do_location = 1;
my $do_end_times;
my $start;
my $end;
my $label = 'Calendar'; my $label = 'Calendar';
GetOptions ( GetOptions (
"label=s" => \$label, "label=s" => \$label,
"start=s" => \$start,
"end=s" => \$end,
"lead-time=i" => \$DEFAULT_LEAD_TIME, "lead-time=i" => \$DEFAULT_LEAD_TIME,
"todos!" => \$PROCESS_TODOS, "todos!" => \$PROCESS_TODOS,
"iso8601!" => \$iso8601,
"locations!" => \$do_location,
"end-times!" => \$do_end_times,
"heading=s" => \$HEADING, "heading=s" => \$HEADING,
"help|?" => \$help, "help|?" => \$help,
"debug" => \$debug,
"man" => \$man "man" => \$man
); ) or pod2usage(1);
pod2usage(1) if $help; pod2usage(1) if $help;
pod2usage(-verbose => 2) if $man; pod2usage(-verbose => 2) if $man;
@@ -136,8 +165,22 @@ while (<>) {
$in = ""; $in = "";
} }
} }
my $parser = iCal::Parser->new(); print STDERR "Read all calendars\n" if $debug;
my(%parser_opts) = ("debug" => $debug);
if ($start) {
my $t = str2time($start);
die "Invalid time $start\n" if (! $t);
$parser_opts{'start'} = DateTime->from_epoch(epoch => $t);
}
if ($end) {
my $t = str2time($end);
die "Invalid time $end\n" if (! $t);
$parser_opts{'end'} = DateTime->from_epoch(epoch => $t);
}
print STDERR "About to parse calendars\n" if $debug;
my $parser = iCal::Parser->new(%parser_opts);
my $hash = $parser->parse_strings(@calendars); my $hash = $parser->parse_strings(@calendars);
print STDERR "Calendars parsed\n" if $debug;
############################################################## ##############################################################
# #
@@ -209,6 +252,13 @@ sub _process_todos {
# #
###################################################################### ######################################################################
# Issue 8 https://github.com/jbalcorn/ical2rem/issues/8
# Make sure there is a _sfun function declared in the reminder file. We'll just make it do nothing here.
print 'IF args("_sfun") < 1
FSET _sfun(x) choose(x,0)
ENDIF
';
print _process_todos($hash->{'todos'}) if $PROCESS_TODOS; print _process_todos($hash->{'todos'}) if $PROCESS_TODOS;
my ($leadtime, $yearkey, $monkey, $daykey,$uid,%eventsbyuid); my ($leadtime, $yearkey, $monkey, $daykey,$uid,%eventsbyuid);
@@ -260,20 +310,67 @@ foreach $yearkey (sort keys %{$events} ) {
$leadtime = "+".$DEFAULT_LEAD_TIME; $leadtime = "+".$DEFAULT_LEAD_TIME;
} }
my $start = $event->{'DTSTART'}; my $start = $event->{'DTSTART'};
print "REM ".$start->month_abbr." ".$start->day." ".$start->year." $leadtime "; my $end = $event->{'DTEND'};
if ($start->hour > 0) { my $duration = "";
print " AT "; if ($end and ($start->hour or $start->minute or $end->hour or $end->minute)) {
print $start->strftime("%H:%M"); # We need both an HH:MM version of the delta, to put in the
print " SCHED _sfun MSG %a %2 "; # DURATION specifier, and a human-readable version of the
} else { # delta, to put in the message if the user requested it.
print " MSG %a "; my $seconds = $end->epoch - $start->epoch;
my $minutes = int($seconds / 60);
my $hours = int($minutes / 60);
$minutes -= $hours * 60;
$duration = sprintf("DURATION %d:%02d ", $hours, $minutes);
} }
print "%\"$event->{'SUMMARY'}"; print "REM ";
print " at $event->{'LOCATION'}" if $event->{'LOCATION'}; if ($iso8601) {
print "\%\"%\n"; print $start->strftime("%F ");
} else {
print $start->month_abbr." ".$start->day." ".$start->year." ";
}
print "$leadtime ";
if ($duration or $start->hour > 0 or $start->minute > 0) {
print "AT ";
print $start->strftime("%H:%M");
print " SCHED _sfun ${duration}MSG %a %2 ";
} else {
print "MSG %a ";
}
print "%\"", &quote($event->{'SUMMARY'});
print(" at ", &quote($event->{'LOCATION'}))
if ($do_location and $event->{'LOCATION'});
print "\%\"";
if ($do_end_times and ($start->hour or $start->minute or
$end->hour or $end->minute)) {
my $start_date = $start->strftime("%F");
my $start_time = $start->strftime("%k:%M");
my $end_date = $end->strftime("%F");
my $end_time = $end->strftime("%k:%M");
# We don't want leading whitespace; some strftime's support
# disabling the pdding in the format string, but not all,
# so for maximum portability we do it ourselves.
$start_time =~ s/^\s+//;
$end_time =~ s/^\s+//;
my(@pieces);
if ($start_date ne $end_date) {
push(@pieces, $end_date);
}
if ($start_time ne $end_time) {
push(@pieces, $end_time);
}
print " (-", join(" ", @pieces), ")";
}
print "%\n";
} }
} }
} }
} }
sub quote {
local($_) = @_;
s/\[/["["]/g;
return $_;
}
exit 0; exit 0;
#:vim set ft=perl ts=4 sts=4 expandtab : #:vim set ft=perl ts=4 sts=4 expandtab :

View File

@@ -19,8 +19,7 @@
;; You should have received a copy of the GNU General Public License ;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software ;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
;; 02111-1307, USA.
;;; Commentary: ;;; Commentary:
@@ -110,18 +109,15 @@
(defconst remind-keywords (defconst remind-keywords
(sort (sort
(list "ADDOMIT" "AFTER" "AT" "BANNER" "BEFORE" (list "ADDOMIT" "AFTER" "AT" "BAN" "BANNER" "BEFORE" "CAL" "CLEAR"
"CAL" "CLEAR-OMIT-CONTEXT" "DEBUG" "DO" "DUMPVARS" "CLEAR-OMIT-CONTEXT" "DEBUG" "DO" "DUMP" "DUMPVARS" "DURATION" "ELSE"
"DURATION" "ELSE" "ENDIF" "ERRMSG" "EXIT" "FIRST" "ENDIF" "ERRMSG" "EXIT" "EXPR" "FIRST" "FLUSH" "FOURTH" "FROM" "FSET"
"FLUSH" "FOURTH" "FROM" "FSET" "IF" "IFTRIG" "IN" "FUNSET" "IF" "IFTRIG" "IN" "INC" "INCLUDE" "INCLUDECMD" "LAST"
"INCLUDE" "INCLUDECMD" "LAST" "LASTDAY" "LASTDAY" "LASTWORKDAY" "MAYBE" "MAYBE-UNCOMPUTABLE" "MSF" "MSG"
"LASTWORKDAY" "MAYBE-UNCOMPUTABLE" "MSF" "NOQUEUE" "OMIT" "OMITFUNC" "ONCE" "POP" "POP-OMIT-CONTEXT" "PRESERVE"
"MSG" "OMIT" "OMITFUNC" "ONCE" "PRIORITY" "PS" "PSFILE" "PUSH" "PUSH-OMIT-CONTEXT" "REM" "RUN"
"POP-OMIT-CONTEXT" "PRESERVE" "PRIORITY" "PS" "PSFILE" "SATISFY" "SCAN" "SCANFROM" "SCHED" "SECOND" "SET" "SKIP" "SPECIAL"
"PUSH-OMIT-CONTEXT" "REM" "RUN" "SATISFY" "SCANFROM" "TAG" "THIRD" "THROUGH" "UNSET" "UNTIL" "WARN")
"SCHED" "SECOND" "SET" "SKIP" "SPECIAL"
"TAG" "THIRD" "THROUGH" "UNSET" "UNTIL"
"WARN")
#'(lambda (a b) (> (length a) (length b))))) #'(lambda (a b) (> (length a) (length b)))))
@@ -132,50 +128,57 @@
(defconst remind-builtin-variables (defconst remind-builtin-variables
(sort (sort
(list "$Ago" "$Am" "$And" "$April" "$At" "$August" "$CalcUTC" "$CalMode" "$Daemon" "$DateSep" (list "$AddBlankLines" "$Ago" "$Am" "$And" "$April" "$At" "$August"
"$DateTimeSep" "$December" "$DefaultColor" "$DefaultPrio" "$CalcUTC" "$CalMode" "$Daemon" "$DateSep" "$DateTimeSep" "$December"
"$DefaultTDelta" "$DeltaOffset" "$DontFork" "$DontQueue" "$DefaultColor" "$DefaultPrio" "$DefaultTDelta" "$DeltaOverride"
"$DontTrigAts" "$EndSent" "$EndSentIg" "$February" "$FirstIndent" "$DontFork" "$DontQueue" "$DontTrigAts" "$EndSent" "$EndSentIg"
"$FoldYear" "$FormWidth" "$Friday" "$Fromnow" "$Hour" "$Hplu" "$HushMode" "$IgnoreOnce" "$ExpressionTimeLimit" "$February" "$FirstIndent" "$FoldYear"
"$InfDelta" "$IntMax" "$IntMin" "$Is" "$January" "$July" "$June" "$LatDeg" "$FormWidth" "$Friday" "$Fromnow" "$Hour" "$Hplu" "$HushMode"
"$Latitude" "$LatMin" "$LatSec" "$Location" "$LongDeg" "$Longitude" "$IgnoreOnce" "$InfDelta" "$IntMax" "$IntMin" "$Is" "$January" "$July"
"$LongMin" "$LongSec" "$March" "$MaxSatIter" "$MaxStringLen" "$May" "$June" "$LatDeg" "$Latitude" "$LatMin" "$LatSec" "$Location"
"$MinsFromUTC" "$Minute" "$Monday" "$Mplu" "$NextMode" "$November" "$Now" "$NumQueued" "$LongDeg" "$Longitude" "$LongMin" "$LongSec" "$March" "$MaxFullOmits"
"$NumTrig" "$October" "$On" "$Pm" "$PrefixLineNo" "$PSCal" "$RunOff" "$Saturday" "$MaxLateMinutes" "$MaxPartialOmits" "$MaxSatIter" "$MaxStringLen"
"$September" "$SimpleCal" "$SortByDate" "$SortByPrio" "$SortByTime" "$May" "$MinsFromUTC" "$Minute" "$Monday" "$Mplu" "$NextMode"
"$SubsIndent" "$Sunday" "$SysInclude" "$T" "$Td" "$Thursday" "$TimeSep" "$Tm" "$November" "$Now" "$NumFullOmits" "$NumPartialOmits" "$NumQueued"
"$Today" "$Tomorrow" "$Tuesday" "$Tw" "$Ty" "$U" "$Ud" "$Um" "$UntimedFirst" "$Uw" "$Uy" "$NumTrig" "$October" "$On" "$OnceFile" "$ParseUntriggered" "$Pm"
"$Was" "$Wednesday") "$PrefixLineNo" "$PSCal" "$RunOff" "$Saturday" "$September"
"$SimpleCal" "$SortByDate" "$SortByPrio" "$SortByTime" "$SubsIndent"
"$Sunday" "$SuppressImplicitWarnings" "$SuppressLRM" "$SysInclude" "$T" "$Td"
"$TerminalBackground" "$Thursday" "$TimeSep" "$Tm" "$Today"
"$Tomorrow" "$Tt" "$Tuesday" "$Tw" "$Ty" "$U" "$Ud" "$Um"
"$UntimedFirst" "$Use256Colors" "$UseBGVTColors" "$UseTrueColors"
"$UseVTColors" "$Uw" "$Uy" "$Was" "$Wednesday")
#'(lambda (a b) (> (length a) (length b))))) #'(lambda (a b) (> (length a) (length b)))))
(defconst remind-time-words (defconst remind-time-words
(sort (sort
(list "Jan" "January" "Feb" "Mar" "Apr" "Jun" "Jul" "Aug" "Sept" "Sep" "Oct" "Nov" "Dec" (list "Apr" "April" "Aug" "August" "Dec" "December" "Feb" "February"
"February" "March" "April" "May" "June" "July" "August" "September" "October" "Jan" "January" "Jul" "July" "Jun" "June" "Mar" "March" "May"
"November" "December" "Mon" "Monday" "Tue" "Tues" "Tuesday" "Wed" "Wednesday" "Nov" "November" "Oct" "October" "Sep" "September" "Fri"
"Thu" "Thursday" "Thurs" "Fri" "Friday" "Saturday" "Sat" "Sun" "Sunday") "Friday" "Mon" "Monday" "Sat" "Saturday" "Sun" "Sunday" "Thu"
"Thursday" "Tue" "Tuesday" "Wed" "Wednesday")
#'(lambda (a b) (> (length a) (length b))))) #'(lambda (a b) (> (length a) (length b)))))
(defconst remind-builtin-functions (defconst remind-builtin-functions
(sort (sort
(list "abs" "access" "adawn" "adusk" "ampm" "args" "asc" "baseyr" "char" (list "abs" "access" "adawn" "adusk" "ampm" "ansicolor" "args" "asc"
"choose" "coerce" "current" "date" "datepart" "datetime" "dawn" "day" "baseyr" "char" "choose" "coerce" "columns" "current" "date"
"daysinmon" "defined" "dosubst" "dusk" "easterdate" "evaltrig" "datepart" "datetime" "dawn" "day" "daysinmon" "defined" "dosubst"
"filedate" "filedatetime" "filedir" "filename" "getenv" "hebdate" "dusk" "easterdate" "evaltrig" "filedate" "filedatetime" "filedir"
"hebday" "hebmon" "hebyear" "hour" "iif" "index" "isany" "isdst" "filename" "getenv" "hebdate" "hebday" "hebmon" "hebyear" "hour"
"isleap" "isomitted" "language" "lower" "max" "min" "minsfromutc" "htmlescape" "htmlstriptags" "iif" "index" "isany" "isdst" "isleap"
"isomitted" "language" "localtoutc" "lower" "max" "min" "minsfromutc"
"minute" "mon" "monnum" "moondate" "moondatetime" "moonphase" "minute" "mon" "monnum" "moondate" "moondatetime" "moonphase"
"moontime" "ndawn" "ndusk" "nonomitted" "now" "ord" "ostype" "pad" "plural" "moontime" "multitrig" "ndawn" "ndusk" "nonomitted" "now" "ord"
"psmoon" "psshade" "realcurrent" "realnow" "realtoday" "sgn" "shell" "orthodoxeaster" "ostype" "pad" "plural" "psmoon" "psshade"
"shellescape" "slide" "strlen" "substr" "sunrise" "sunset" "time" "realcurrent" "realnow" "realtoday" "rows" "sgn" "shell" "shellescape"
"timepart" "today" "trig" "trigback" "trigdate" "trigdatetime" "slide" "soleq" "stdout" "strlen" "substr" "sunrise" "sunset" "time"
"trigdelta" "trigduration" "trigeventduration" "trigeventstart" "timepart" "timezone" "today" "trig" "trigback" "trigdate"
"trigfrom" "trigger" "trigpriority" "trigrep" "trigscanfrom" "trigdatetime" "trigdelta" "trigduration" "trigeventduration"
"trigtime" "trigtimedelta" "trigtimerep" "triguntil" "trigvalid" "trigeventstart" "trigfrom" "trigger" "trigpriority" "trigrep"
"typeof" "tzconvert" "upper" "value" "version" "weekno" "wkday" "trigscanfrom" "trigtags" "trigtime" "trigtimedelta" "trigtimerep"
"wkdaynum" "year") "triguntil" "trigvalid" "typeof" "tzconvert" "upper" "utctolocal"
"value" "version" "weekno" "wkday" "wkdaynum" "year")
#'(lambda (a b) (> (length a) (length b))))) #'(lambda (a b) (> (length a) (length b)))))
;;; faces ;;; faces
@@ -291,7 +294,7 @@
(defconst remind-conf-font-lock-keywords-1 (defconst remind-conf-font-lock-keywords-1
(list (list
'("^[\;\#]\\s-+.*$" . remind-comment-face) '("^\s*[\;\#].*$" . remind-comment-face)
'(remind-keywords-matcher . remind-conf-keyword-face) '(remind-keywords-matcher . remind-conf-keyword-face)
'("%[\"_]" . font-lock-warning-face) '("%[\"_]" . font-lock-warning-face)
'("\\(%[a-mops-w]\\)" . remind-conf-substitutes-face) '("\\(%[a-mops-w]\\)" . remind-conf-substitutes-face)

View File

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

View File

@@ -1,5 +1,433 @@
CHANGES TO REMIND CHANGES TO REMIND
* VERSION 5.0 Patch 6 - 2024-09-16
- NEW FEATURE: remind: Include a file containing the dates of Chinese
New Year through 2050.
- NEW FEATURE: remind: Add $SuppressImplicitWarnings system variable.
Setting this to 1 suppresses the warnings "Unrecognized command;
interpreting as REM" and "Missing REM type; assuming MSG"
- NEW FEATURE: remind: Add --print-tokens command-line argument. This
simply prints all of Remind's built-in tokens, functions, and system
variables. It's designed to help people who are writing editor
add-ons for syntax highlighting.
- CHANGE: remind: allow '12:34' to be parsed as a TIME constant. This
matches DATEs and DATETIMEs which are enclosed in single-quotes.
(The quotes are optional for TIME constants, however.)
- IMPROVEMENT: Improve many error emssages and warnings.
- UPDATE: contrib/ical2rem: Update to upstream version 0.7
- IMPROVEMENT: contrib/remind-conf-mode: Improve the Emacs
remind-conf-mode package. Add new keywords and make comment
highlighting correct.
- FIX: remind man page: Remove note saying REM can be omitted.
* VERSION 5.0 Patch 5 - 2024-09-02
- CHANGE: remind: When using the "-c" option and with the SHADE special
enabled, shade the entire calendar box including the line containing the
day number.
- IMPROVEMENT: remind: Better error messages when diagnosing certain
errors in expressions.
- IMPROVEMENT: include/holidays/jewish.rem: Remove unnecessary _h()
function definition.
- BUG FIX: remind: In a couple of spots when we parsed a character, we did
not check for an error return. This has been fixed.
- BUG FIX: remind: Fix edge-case bugs in "remind -c" output formatting.
- BUG FIX: make test: Fix a test that was broken for all of September 2024.
- BUG FIX: remind: Fix a couple of potential file-descriptor leaks.
- BUG FIX: contrib/ical2rem.pl: Replace "SCHED _sfun" with "+15" to
hard-code 15-minutes advance warning rather than using an undefined
scheduling function. If this is not what you want, you should edit
ical2rem.pl to suit your taste; it's not officially part of Remind and
is meant more as a starting point for you to customize than a finished
product.
- DOCUMENTATION FIX: Remove obsolete info from Remind man page.
* VERSION 5.0 Patch 4 - 2024-08-29
- IMPROVEMENT: remind: When checking if a SATISFY expression refers to the
trigger date, look recursively at any user-defined functions it calls.
This reduces spurious warnings.
- CHANGE: remind: Allow any type to be used as the test argument for IIF.
- IMPROVEMENT: remind man page: Clarify how various types are treated
in boolean context.
* VERSION 5.0 Patch 3 - 2024-08-28
- IMPROVEMENT: remind: Preserve newlines ("%_" sequences) in calendar
mode in most cases. See the remind man page for details.
- IMPROVEMENT: rem2pdf: rem2pdf can now produce PostScript and
Encapsulated PostScript output in addition to PDF and SVG.
- IMPROVEMENT: remind: Emit warnings if a subst_XXX function takes the
wrong number of arguments, or for custom sequences, if the function
is not defined.
- IMPROVEMENT: remind: Emit warnings if WARN/SCHED/OMITFUNC functions
do not reference their argument.
- IMPROVEMENT: remind: Allow strings to be used with logical
operators. The empty string "" is considered false and all other
strings are considered true.
- IMPROVEMENT: remind: Emit warnings for lines that are implicitly
treated as REM commands; add warnings for REM commands that are
implicitly treated as MSG-type reminders.
- IMPROVEMENT: remind: Add an optional fourth argument to the built-in
ampm() function that specifies not to suppress a leading zero in the
hour component.
- IMPROVEMENT: remind: If a SATISFY expression is not constant and
doesn't reference the trigger date somehow, issue a warning.
- IMPROVEMENT: remind: Add a warning if a user-defined function is
redefined. If you do have a use-case that requires you to redefine
a function, simply FUNSET it first before FSETting it for the second
time.
- DOCUMENTATION FIX: Clarify man pages and remove some information that
has become incorrect as Remind has evolved.
- BUG FIX: Fix typos in man pages
* VERSION 5.0 Patch 2 - 2024-07-26
- IMPROVEMENT: Remind: Revamp how ONCE works. You can now set a
special variable $OnceFile to be the path to a timestamp file. The
ONCE directive uses this timestamp file to track when it was last
run rather than the access date of the main reminder script. This
is more reliable because it doesn't rely on the atime of a file
(which might not be maintained accurately) and is not affected if
you edit your reminder script.
- CHANGE: Taking input from stdin no longer implies the "-o" option; ONCE can
work if you set $OnceFile
- CHANGE: Any of the -c, -n, -p and -s options implicitly enable the
-o option. As before, specifying a repeat factor *N or a date that
is not today on the command-line also implies -o.
- MINOR NEW FEATURE: Rem2PDF: Add the "--svg" command-line option to produce
SVG output rather than PDF.
- MINOR IMPROVEMENT: Remind: Improve the storage efficiency of short
string constants in compiled expression trees.
- MINOR IMPROVEMENT: Remind: Remove some obsolete macro definitions
* VERSION 5.0 Patch 1 - 2024-06-08
- MINOR IMPROVEMENT: Add short-circuit evaluation to the isany() function,
which now only evaluates those arguments absolutely necessary to determine
the result.
- BUG FIX: Mark weekno() as a non-constant function (the zero-argument form
depends on external conditions.)
- BUG FIX: Fix a couple of memory leaks.
- BUG FIX: Don't rely on support for unnamed function parameters; this caused
compilation failures with older versions of gcc.
* VERSION 5.0 Patch 0 - 2024-06-06
- MAJOR CHANGE: The expression evaluation engine has been completely replaced
with a new one that splits parsing and evaluating into two separate steps.
It also features short-circuit evaluation of &&, ||, iif() and choose().
This should speed up expression-heavy reminder files.
NOTE INCOMPATIBILITY: In expressions with side-effects, the short-circuit
evaluation might change the result you get. For example, consider
running the following file through: remind file.rem 2024-06-04
SET a trig("Mon +7") || trig("Thu +7")
MSG trig = [trig()]
Older Remind versions will output:
trig = 2024-06-06
whereas this version outputs:
trig = 2024-06-10
because the second part of the "||" expression is not evaluated. The vast
majority of Remind expressions do not have side-effects and should yield
the same results as before.
The newer expression engine also permits recursive functions, but
these are not recommended. Still, if you want to, you can do:
fset factorial(n) iif(n <= 1, 1, n*factorial(n-1))
and it will work for values of n that don't cause integer overflow.
- IMPROVEMENT: If there's an unmatched PUSH-OMIT-CONTEXT, print the
filename and line number containing it.
- IMPROVEMENT: If there's an IF with a missing ENDIF, print the filename
and line number of the IF statement.
- NEW FEATURE: Add EXPR OFF command to completely disable expression
evaluation. Useful if you INCLUDE files that you don't expect to
contain expressions and may come from slightly untrustworthy sources.
- NEW FEATURE: Add $ExpressionTimeLimit system variable to enforce a
maximum limit on how long evaluating an expression is allowed to take.
- NEW FEATURE: Add --max-execution-time=n command-line option to terminate
Remind if it runs for more than n seconds.
- CHANGE: Make the command-line option "-ifoo" equivalent to "-ifoo=0"
- CHANGE: Permit a literal [ in a reminder by using the sequence [[
The old ["["] still works.
- BUG FIX: In "purge" mode, Remind would sometimes purge reminders with
a relative "SCANFROM" which haven't actually expired. This has been fixed.
- BUG FIX: Disallow something like: FSET func(x, x) expr
which shouldn't have been allowed in the first place.
- BUG FIX: Replace leading spaces with tabs in Makefiles (per Emanuele Torre
and Tim Chase)
* VERSION 4.3 Patch 7 - 2024-04-29
- IMPROVEMENT: build.tk: Add a note if build.tk obtains default settings
from an existing Remind installation.
- IMPROVEMENT: configure: Pass all args to AC_INIT including the Remind
home page. Remove some unused autoconf cruft.
- IMPROVEMENT: Use standard C library versions of strdup, strcasecmp and
strncasecmp where available, rather than using our own versions.
- MINOR FEATURE: remind: Make $Tt a synonym for trigtime().
- BUG FIX: remind: Make sure shellescape() doesn't mangle UTF-8 characters
with high-bits set.
- BUG FIX: remind: Don't rely on undefined behavior of "%" operator in
the ord() built-in function.
- BUG FIX: remind: Do not clear out trigtime() unnecessarily. Before,
you could not write things like the following; now you can:
REM Tue AT 11:30 DURATION 0:30 MSG Thing 1
REM Tue AT [trigtime()+trigduration()] DURATION 1:00 MSG Thing 2
REM Tue AT [trigtime()+trigduration()] DURATION 0:45 MSG Thing 3
for successive reminders that should be moved as a block if the time of
the first one changes.
- BUG FIX: Don't update trigdate() or trigtime() while parsing a REM
statement... only when actually computing the trigger.
* VERSION 4.3 Patch 6 - 2024-04-02
- NEW FILE: Add [$SysInclude]/holidays/pt.rem - Portuguese holidays, courtesy
of Joop Kiefte.
- BUG FIX: remind: Fix compile error on systems that don't support inotify(7).
- BUG FIX: remind: Fix test failures on FreeBSD. On FreeBSD, you have to copy
the result of getenv() or else a subsequent setenv() can change the stored
value.
* VERSION 4.3 Patch 5 - 2024-04-01
- IMPROVEMENT: remind: Use inotify to detect reminder file changes
even in regular daemon mode (-zn where n>0).
- INTERNAL IMPROVEMENTS: Rearrange and refactor some code.
- FIXES: Various fixes and improvements to man pages.
- BUG FIX: remind: Handle queued "SPECIAL COLOR" reminders correctly.
- BUG FIX: remind: Preserve the value of $DefaultColor that was in effect
when a reminder was queued; restore it before issuing the queued reminder.
* 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 * VERSION 4.2 Patch 8 - 2023-12-14
- NEW FEATURE: Add the $MaxLateMinutes system variable. This suppresses - NEW FEATURE: Add the $MaxLateMinutes system variable. This suppresses
@@ -868,7 +1296,7 @@ CHANGES TO REMIND
- MINOR IMPROVEMENT: Add the "ampm()" built-in function. - MINOR IMPROVEMENT: Add the "ampm()" built-in function.
* Version 3.3 Patch 0 - 2020-01-31 * VERSION 3.3 Patch 0 - 2020-01-31
- FIX: rem2ps: Add a %%PageBoundingBox: document structuring convention - FIX: rem2ps: Add a %%PageBoundingBox: document structuring convention
comment. comment.
@@ -903,7 +1331,7 @@ CHANGES TO REMIND
- CHANGE: SPECIALs are now case-insensitive. Before, only SPECIAL COLOR - CHANGE: SPECIALs are now case-insensitive. Before, only SPECIAL COLOR
would work. Now you can use Special Color, special color, etc. would work. Now you can use Special Color, special color, etc.
* Version 3.2 Patch 0 - 2020-01-03 * VERSION 3.2 Patch 0 - 2020-01-03
- IMPROVEMENT: Add support for events spanning multiple days (with AT - IMPROVEMENT: Add support for events spanning multiple days (with AT
and DURATION). Add trigeventstart() and trigeventduration() and DURATION). Add trigeventstart() and trigeventduration()
@@ -945,7 +1373,7 @@ CHANGES TO REMIND
- BUG FIX: TkRemind: Fix startup failure of TkRemind if options are at - BUG FIX: TkRemind: Fix startup failure of TkRemind if options are at
default. :( default. :(
* Version 3.1 Patch 17 - 2019-11-15 * VERSION 3.1 Patch 17 - 2019-11-15
- IMPROVEMENT: Add "Extra Remind Options" setting to TkRemind. - IMPROVEMENT: Add "Extra Remind Options" setting to TkRemind.
@@ -962,7 +1390,7 @@ CHANGES TO REMIND
- BUG FIX: Fix various documentation errors and update man page. - BUG FIX: Fix various documentation errors and update man page.
* Version 3.1 Patch 16 - 2018-11-09 * VERSION 3.1 Patch 16 - 2018-11-09
- IMPROVEMENT: Add patch from Stephen Morgan to calculate astronomical and - IMPROVEMENT: Add patch from Stephen Morgan to calculate astronomical and
nautical twilight in addition to civil twilight. nautical twilight in addition to civil twilight.
@@ -992,7 +1420,7 @@ CHANGES TO REMIND
- BUG FIX: In "remind -z0" mode, remind wakes up exactly on the minute instead - BUG FIX: In "remind -z0" mode, remind wakes up exactly on the minute instead
of sleeping for 60 seconds each time, which could cause it to fall behind. of sleeping for 60 seconds each time, which could cause it to fall behind.
* Version 3.1 Patch 15 - 2015-07-27 * VERSION 3.1 Patch 15 - 2015-07-27
- BUG FIX: Fix a buffer overflow found by Alexander Keller - BUG FIX: Fix a buffer overflow found by Alexander Keller
@@ -1002,7 +1430,7 @@ CHANGES TO REMIND
- BUG FIX: Make parser reject repeated delta or *repeat values. - BUG FIX: Make parser reject repeated delta or *repeat values.
* Version 3.1 Patch 14 - 2015-04-24 * VERSION 3.1 Patch 14 - 2015-04-24
- NEW FEATURE: Putting the line __EOF__ in a .rem file causes Remind - NEW FEATURE: Putting the line __EOF__ in a .rem file causes Remind
to treat it as end-of-file. to treat it as end-of-file.
@@ -1019,7 +1447,7 @@ CHANGES TO REMIND
- BUG FIX: Typo in Spanish translation was fixed. - BUG FIX: Typo in Spanish translation was fixed.
* Version 3.1 Patch 13 - 2013-03-22 * VERSION 3.1 Patch 13 - 2013-03-22
- BUG FIX: Sunrise/Sunset calculations greatly improved thanks to John - BUG FIX: Sunrise/Sunset calculations greatly improved thanks to John
McGowan. Accuracy should now be within a couple of minutes in most McGowan. Accuracy should now be within a couple of minutes in most
@@ -1036,7 +1464,7 @@ CHANGES TO REMIND
- BUG FIX: Spurious test harness failure was fixed. - BUG FIX: Spurious test harness failure was fixed.
* Version 3.1 Patch 12 - 2012-01-23 * VERSION 3.1 Patch 12 - 2012-01-23
- NEW FEATURE: Many substitution sequences "%x" have an alternate mode - NEW FEATURE: Many substitution sequences "%x" have an alternate mode
denoted by "%*x". This alternate mode leaves out prepositions. For denoted by "%*x". This alternate mode leaves out prepositions. For
@@ -1048,7 +1476,7 @@ CHANGES TO REMIND
so the results may be off by a minute or two compared to previous versions so the results may be off by a minute or two compared to previous versions
of Remind. of Remind.
* Version 3.1 Patch 11 - 2011-12-16 * VERSION 3.1 Patch 11 - 2011-12-16
- BUG FIX: For some inexplicable reason, dawn was considered to happen when - BUG FIX: For some inexplicable reason, dawn was considered to happen when
the sun was 14 degrees below the horizon instead of the standard 6 the sun was 14 degrees below the horizon instead of the standard 6
@@ -1062,7 +1490,7 @@ CHANGES TO REMIND
- BUG FIX: Apply minor Debian cleanups reported by Kurt B. Kaiser. - BUG FIX: Apply minor Debian cleanups reported by Kurt B. Kaiser.
* Version 3.1 Patch 10 - 2010-11-01 * VERSION 3.1 Patch 10 - 2010-11-01
- NOTE: This is the 20th anniversary of Remind's first public release. - NOTE: This is the 20th anniversary of Remind's first public release.
@@ -1086,7 +1514,7 @@ CHANGES TO REMIND
- BUG FIX: Don't declare variables in the middle of statements (old C - BUG FIX: Don't declare variables in the middle of statements (old C
compilers choke.) compilers choke.)
* Version 3.1 Patch 9 - 2010-06-20 * VERSION 3.1 Patch 9 - 2010-06-20
- MAJOR ENHANCEMENT: New "purge mode" to delete expired reminders. See - MAJOR ENHANCEMENT: New "purge mode" to delete expired reminders. See
the PURGE MODE section of the remind man page. the PURGE MODE section of the remind man page.
@@ -1102,7 +1530,7 @@ CHANGES TO REMIND
- BUG FIX: Yom HaShoah is moved to Thursday if it would normally fall on - BUG FIX: Yom HaShoah is moved to Thursday if it would normally fall on
a Friday. Thanks to Jonathan Kamens for pointing this out. a Friday. Thanks to Jonathan Kamens for pointing this out.
* Version 3.1 Patch 8 - 2010-03-09 * VERSION 3.1 Patch 8 - 2010-03-09
- ENHANCEMENT: Include some useful scripts in contrib/ - ENHANCEMENT: Include some useful scripts in contrib/
@@ -1122,7 +1550,7 @@ CHANGES TO REMIND
- BUG FIX: Fix bug in SCHED calculations if Remind is started in the middle - BUG FIX: Fix bug in SCHED calculations if Remind is started in the middle
of a SCHED interval. of a SCHED interval.
* Version 3.1 Patch 7 - 2009-05-31 * VERSION 3.1 Patch 7 - 2009-05-31
- ENHANCEMENT: Wherever you could write "day Mon year", the parser now - ENHANCEMENT: Wherever you could write "day Mon year", the parser now
accepts "YYYY-MM-DD". This applies on the command-line and to the accepts "YYYY-MM-DD". This applies on the command-line and to the
@@ -1131,7 +1559,7 @@ CHANGES TO REMIND
- ENHANCEMENT: New slide() built-in function eases some complicated reminders. - ENHANCEMENT: New slide() built-in function eases some complicated reminders.
* Version 3.1 Patch 6 - 2008-11-16 * VERSION 3.1 Patch 6 - 2008-11-16
- MAJOR ENHANCEMENT: A new OMITFUNC clause gives you additional - MAJOR ENHANCEMENT: A new OMITFUNC clause gives you additional
control and flexibility over "omitted days" calculations. This is control and flexibility over "omitted days" calculations. This is
@@ -1159,7 +1587,7 @@ CHANGES TO REMIND
weekday would fail if it needed to cross a year boundary. This has weekday would fail if it needed to cross a year boundary. This has
been fixed. been fixed.
* Version 3.1 Patch 5 - 2008-04-15 * VERSION 3.1 Patch 5 - 2008-04-15
- MAJOR ENHANCEMENT: If you supply a directory name on the command line - MAJOR ENHANCEMENT: If you supply a directory name on the command line
or for an INCLUDE command, then Remind reads all *.rem file in that or for an INCLUDE command, then Remind reads all *.rem file in that
@@ -1189,7 +1617,7 @@ CHANGES TO REMIND
- BUG FIX: Parse error in calendar mode was fixed. - BUG FIX: Parse error in calendar mode was fixed.
* Version 3.1 Patch 4 - 2008-02-03 * VERSION 3.1 Patch 4 - 2008-02-03
- ENHANCEMENT: tkremind respects the "-b1" option and operates in 24-hour - ENHANCEMENT: tkremind respects the "-b1" option and operates in 24-hour
clock mode if the option is supplied. clock mode if the option is supplied.
@@ -1211,7 +1639,7 @@ CHANGES TO REMIND
- BUG FIX: The "-ivar=value" command-line option failed if Remind re-execed - BUG FIX: The "-ivar=value" command-line option failed if Remind re-execed
itself because we overwrote argv[]. This has been fixed. itself because we overwrote argv[]. This has been fixed.
* Version 3.1 Patch 3 - 2007-10-15 * VERSION 3.1 Patch 3 - 2007-10-15
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1230,7 +1658,7 @@ CHANGES TO REMIND
is not evaluated. This helps avoid spurious error messages in some is not evaluated. This helps avoid spurious error messages in some
reminders. reminders.
* Version 3.1 Patch 2 - 2007-09-12 * VERSION 3.1 Patch 2 - 2007-09-12
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1284,7 +1712,7 @@ CHANGES TO REMIND
- Using the psshade() or psmoon() functions emits a warning on stderr. You - Using the psshade() or psmoon() functions emits a warning on stderr. You
should use SPECIAL SHADE or SPECIAL MOON instead. should use SPECIAL SHADE or SPECIAL MOON instead.
* Version 3.1 Patch 1 - 2007-08-23 * VERSION 3.1 Patch 1 - 2007-08-23
+ MAJOR ENHANCEMENTS + MAJOR ENHANCEMENTS
@@ -1314,7 +1742,7 @@ CHANGES TO REMIND
- Various man-page fixes. - Various man-page fixes.
* Version 3.1 Patch 0 - 2007-07-14 * VERSION 3.1 Patch 0 - 2007-07-14
+ MAJOR ENHANCEMENTS + MAJOR ENHANCEMENTS
@@ -1377,7 +1805,7 @@ CHANGES TO REMIND
- rem2ps would produce invalid PostScript in some rare cases - rem2ps would produce invalid PostScript in some rare cases
(eg, for February 2007). This has been fixed. (eg, for February 2007). This has been fixed.
* Version 3.0 Patch 24 - 2005-11-19 * VERSION 3.0 Patch 24 - 2005-11-19
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1400,7 +1828,7 @@ CHANGES TO REMIND
- Fixed a bug in the tokenizer that could make Remind segfault. Fix courtesy - Fixed a bug in the tokenizer that could make Remind segfault. Fix courtesy
of Stan Tobias. of Stan Tobias.
* Version 3.0 Patch 23 - 2005-04-14 * VERSION 3.0 Patch 23 - 2005-04-14
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1424,7 +1852,7 @@ CHANGES TO REMIND
- Fixed parser error for unterminated date constant: '2005/01/01 - Fixed parser error for unterminated date constant: '2005/01/01
* Version 3.0 Patch 22 - 2000-06-16 * VERSION 3.0 Patch 22 - 2000-06-16
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1446,7 +1874,7 @@ CHANGES TO REMIND
- Fixed serious bug in which background queued reminders were ignored and - Fixed serious bug in which background queued reminders were ignored and
Remind simply exited. Doh! Sorry about that. Remind simply exited. Doh! Sorry about that.
* Version 3.0 Patch 21 - 2000-03-15 * VERSION 3.0 Patch 21 - 2000-03-15
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1477,7 +1905,7 @@ CHANGES TO REMIND
- Fixed compilation problem on FreeBSD, IRIX, Tru64 and other UNIXes. - Fixed compilation problem on FreeBSD, IRIX, Tru64 and other UNIXes.
* Version 3.0 Patch 20 - 1999-04-12 * VERSION 3.0 Patch 20 - 1999-04-12
+ LICENSE CHANGE + LICENSE CHANGE
@@ -1499,7 +1927,7 @@ CHANGES TO REMIND
- Fixed a typo in danish.h, courtesy of Niels Kristian Bech Jensen. - Fixed a typo in danish.h, courtesy of Niels Kristian Bech Jensen.
* Version 3.0 Patch 19 - 1998-05-09 * VERSION 3.0 Patch 19 - 1998-05-09
+ MAJOR ENHANCEMENTS + MAJOR ENHANCEMENTS
@@ -1541,7 +1969,7 @@ CHANGES TO REMIND
- Lots more silly little bugs squashed -- too many to go into in - Lots more silly little bugs squashed -- too many to go into in
detail. detail.
* Version 3.0 Patch 18 - 1998-02-15 * VERSION 3.0 Patch 18 - 1998-02-15
+ MAJOR ENHANCEMENTS + MAJOR ENHANCEMENTS
@@ -1605,7 +2033,7 @@ CHANGES TO REMIND
- Getting rid of fixed-sized buffers meant lots of changes to code. - Getting rid of fixed-sized buffers meant lots of changes to code.
No doubt, I missed a few regression tests. No doubt, I missed a few regression tests.
* Version 3.0 Patch 17 - 1997-09-07 * VERSION 3.0 Patch 17 - 1997-09-07
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1646,7 +2074,7 @@ CHANGES TO REMIND
library. All three of these fixes are courtesy of Christopher library. All three of these fixes are courtesy of Christopher
J. Madsen <madsen@iglobal.net>. Thanks, Christopher. J. Madsen <madsen@iglobal.net>. Thanks, Christopher.
* Version 3.0 Patch 16 - 1997-02-11 * VERSION 3.0 Patch 16 - 1997-02-11
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1674,7 +2102,7 @@ CHANGES TO REMIND
- Fixed a problem under Solaris 2.5 whereby rem2ps was skipping some - Fixed a problem under Solaris 2.5 whereby rem2ps was skipping some
latin1 characters which it interpreted as white space. latin1 characters which it interpreted as white space.
* Version 3.0 Patch 15 - 1996-10-27 * VERSION 3.0 Patch 15 - 1996-10-27
+ IMPORTANT NOTES + IMPORTANT NOTES
@@ -1704,7 +2132,7 @@ CHANGES TO REMIND
- Fixed bug in TkRemind which caused a crash if the "-m" option was used - Fixed bug in TkRemind which caused a crash if the "-m" option was used
for a month beginning on Sunday. Doh!!! for a month beginning on Sunday. Doh!!!
* Version 3.0 Patch 14 - 1996-05-25 * VERSION 3.0 Patch 14 - 1996-05-25
+ CHANGE IN COPYING POLICY + CHANGE IN COPYING POLICY
@@ -1763,7 +2191,7 @@ CHANGES TO REMIND
_not_ support MS Windows, and in fact do not allow Remind to run _not_ support MS Windows, and in fact do not allow Remind to run
under Windows (see COPYRIGHT). under Windows (see COPYRIGHT).
* Version 3.0 Patch 13 - 1994-05-06 * VERSION 3.0 Patch 13 - 1994-05-06
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1798,7 +2226,7 @@ CHANGES TO REMIND
- Updated the copyright notices everywhere. - Updated the copyright notices everywhere.
* Version 3.0 Patch 12 - 1994-02-01 * VERSION 3.0 Patch 12 - 1994-02-01
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1817,7 +2245,7 @@ CHANGES TO REMIND
- Fixed a problem with the '-k' option which resulted in a newline being - Fixed a problem with the '-k' option which resulted in a newline being
placed after the message text. This was giving sh(1) heartburn... placed after the message text. This was giving sh(1) heartburn...
* Version 3.0 Patch 11 - 1993-11-26 * VERSION 3.0 Patch 11 - 1993-11-26
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -1849,7 +2277,7 @@ CHANGES TO REMIND
- Fixed typos in french.h - Fixed typos in french.h
* Version 3.0 Patch 10 * VERSION 3.0 Patch 10
+ MAJOR ENHANCEMENT + MAJOR ENHANCEMENT
@@ -1897,7 +2325,7 @@ CHANGES TO REMIND
- Fixed the Finnish language support which was missing a few newlines. - Fixed the Finnish language support which was missing a few newlines.
* Version 3.0 Patch 9 - 1993-10-04 * VERSION 3.0 Patch 9 - 1993-10-04
+ NOTES + NOTES
@@ -1954,7 +2382,7 @@ CHANGES TO REMIND
so that newlines in the body start new paragraphs, rather than being so that newlines in the body start new paragraphs, rather than being
swallowed as white-space. swallowed as white-space.
* Version 3.0 Patch 8 - 1993-09-08 * VERSION 3.0 Patch 8 - 1993-09-08
+ MAJOR ENHANCEMENTS + MAJOR ENHANCEMENTS
@@ -2006,7 +2434,7 @@ CHANGES TO REMIND
range [-128, 255] (but not 0) so that char(asc(s)) works even range [-128, 255] (but not 0) so that char(asc(s)) works even
on machines with signed char types. on machines with signed char types.
* Version 3.0 Patch 7 - 1993-07-22 * VERSION 3.0 Patch 7 - 1993-07-22
+ MAJOR ENHANCEMENTS + MAJOR ENHANCEMENTS
@@ -2049,7 +2477,7 @@ CHANGES TO REMIND
- Changed kall so that "kall sh" doesn't commit suicide - patch courtesy - Changed kall so that "kall sh" doesn't commit suicide - patch courtesy
of Michael Salmon. of Michael Salmon.
* Version 3.0 Patch 6 - 1993-05-05 * VERSION 3.0 Patch 6 - 1993-05-05
+ MINOR ENHANCEMENTS + MINOR ENHANCEMENTS
@@ -2072,7 +2500,7 @@ CHANGES TO REMIND
- Fixed a bug in the -u option which sometimes caused a core dump - Fixed a bug in the -u option which sometimes caused a core dump
(embarrassed grin!) The fix is due to Tina Hoeltig. Thanks, Tina! (embarrassed grin!) The fix is due to Tina Hoeltig. Thanks, Tina!
* Version 3.0 Patch 5 - 1993-04-27 * VERSION 3.0 Patch 5 - 1993-04-27
+ MAJOR ENHANCEMENTS: + MAJOR ENHANCEMENTS:
@@ -2107,7 +2535,7 @@ CHANGES TO REMIND
- Fixed a bug in the man page - thanks to Ed Oskiewicz. - Fixed a bug in the man page - thanks to Ed Oskiewicz.
* Version 3.0 Patch 4 - 1993-03-08 * VERSION 3.0 Patch 4 - 1993-03-08
- Added the -g option - this sorts reminders by date/time before - Added the -g option - this sorts reminders by date/time before
issuing them. (You can see I'm running out of letters to issuing them. (You can see I'm running out of letters to
@@ -2166,13 +2594,13 @@ CHANGES TO REMIND
- Put my new mailing address in the README files. - Put my new mailing address in the README files.
* Version 3.0 Patch 3 - 1993-02-21 * VERSION 3.0 Patch 3 - 1993-02-21
- Corrected bugs in Remind and Rem2PS. No new features added. You - Corrected bugs in Remind and Rem2PS. No new features added. You
should NOT use patch level 2 - either stick to 3.0.1 or upgrade to should NOT use patch level 2 - either stick to 3.0.1 or upgrade to
3.0.3. 3.0.3.
* Version 3.0 Patch 2 - 1993-02-04 * VERSION 3.0 Patch 2 - 1993-02-04
- Added the -u option to Remind so that root can run it as any user. - Added the -u option to Remind so that root can run it as any user.
This simplifies the remind-all scripts, and makes them more efficient. This simplifies the remind-all scripts, and makes them more efficient.
@@ -2212,7 +2640,7 @@ CHANGES TO REMIND
- Changed Remind so that supplying the -a option causes timed reminders - Changed Remind so that supplying the -a option causes timed reminders
not to be placed into the calendar in calendar mode. not to be placed into the calendar in calendar mode.
* Version 3.0 Patch 1 - 1992-12-18 * VERSION 3.0 Patch 1 - 1992-12-18
- Wrote the Rem2ps program to produce PostScript calendars - Wrote the Rem2ps program to produce PostScript calendars
@@ -2252,7 +2680,7 @@ CHANGES TO REMIND
- Re-formatted the WHATSNEW.30 file. - Re-formatted the WHATSNEW.30 file.
* Version 3.0 - 1992-11-09 * VERSION 3.0 - 1992-11-09
- Total rewrite from previous versions - Total rewrite from previous versions
@@ -2273,23 +2701,23 @@ CHANGES TO REMIND
- Fixed a lurking bug in trigger date calculation which, amazingly, had not - Fixed a lurking bug in trigger date calculation which, amazingly, had not
been caught in the couple of years that Remind has been out! been caught in the couple of years that Remind has been out!
* Version 2.3 Patch 5 - 1992-04-11 * VERSION 2.3 Patch 5 - 1992-04-11
- Added the "c+n" option for printing a calendar by - Added the "c+n" option for printing a calendar by
weeks instead of months, courtesy Dennis Cottel (dennis@peanuts.nosc.mil). weeks instead of months, courtesy Dennis Cottel (dennis@peanuts.nosc.mil).
* Version 2.3 Patch 4 - 1991-11-06 * VERSION 2.3 Patch 4 - 1991-11-06
- Made the init.c file nicer. Made the Makefile - Made the init.c file nicer. Made the Makefile
prettier. Added "make test", "make tar" and "make shar" Makefile targets. prettier. Added "make test", "make tar" and "make shar" Makefile targets.
* Version 2.3 Patch 3 - 1991-09-11 * VERSION 2.3 Patch 3 - 1991-09-11
- Added a command-line option for Remind to process - Added a command-line option for Remind to process
queued reminders in the foreground. This makes automatic termination queued reminders in the foreground. This makes automatic termination
of Remind processes from within X-Windows and Sunview easier. of Remind processes from within X-Windows and Sunview easier.
* Version 2.3 Patch 2 - 1991-07-19 * VERSION 2.3 Patch 2 - 1991-07-19
- Fixed up a problem with timed reminders which resulted - Fixed up a problem with timed reminders which resulted
in cursor not starting from left side of screen on some systems. in cursor not starting from left side of screen on some systems.
@@ -2306,7 +2734,7 @@ CHANGES TO REMIND
- Made the Makefile more portable - Made the Makefile more portable
* Version 2.3 Patch 1 - 1991-03-08 * VERSION 2.3 Patch 1 - 1991-03-08
- Added the "-t" command-line option to get Remind - Added the "-t" command-line option to get Remind
to trigger all non-expired reminders. to trigger all non-expired reminders.
@@ -2320,7 +2748,7 @@ CHANGES TO REMIND
- Added manual pages for "kall" and "rem". - Added manual pages for "kall" and "rem".
* Version 2.3 - 1991-02-20 * VERSION 2.3 - 1991-02-20
- Added the UNTIL keyword for forcing reminders to expire. - Added the UNTIL keyword for forcing reminders to expire.
@@ -2342,32 +2770,32 @@ CHANGES TO REMIND
- Modified the calendar and SimpleCalendar formats so that the % escape - Modified the calendar and SimpleCalendar formats so that the % escape
substitutions ARE performed. substitutions ARE performed.
* Version 2.2 - Patch 5 - 1990-12-03 * VERSION 2.2 - Patch 5 - 1990-12-03
- Added the BEFORE, AFTER and SKIP tokens to make the - Added the BEFORE, AFTER and SKIP tokens to make the
handling of holidays more sensible. Also corrected a few more bugs. handling of holidays more sensible. Also corrected a few more bugs.
* Version 2.2 - Patch 3 - 1990-11-28 * VERSION 2.2 - Patch 3 - 1990-11-28
- Added the MSG or RUN tokens in an OMIT command; also - Added the MSG or RUN tokens in an OMIT command; also
allowed RUN-type reminders to be explicitly included in the calendar by allowed RUN-type reminders to be explicitly included in the calendar by
using the %" escape sequence. using the %" escape sequence.
* Version 2.2 - 1990-11-16 * VERSION 2.2 - 1990-11-16
- Added the AT keyword, the timed reminders daemon, and the - Added the AT keyword, the timed reminders daemon, and the
calendar facility. calendar facility.
* Version 2.1 - 1990-11-06 * VERSION 2.1 - 1990-11-06
- Added the "repeat" token for repeating reminders with a period - Added the "repeat" token for repeating reminders with a period
other than 7 days. Also fixed some bugs from version 2.0 other than 7 days. Also fixed some bugs from version 2.0
* Version 2.0 - 1990-11-01 * VERSION 2.0 - 1990-11-01
- first public release. Included advanced date specifications, - first public release. Included advanced date specifications,
character substitution, and the RUN keyword. character substitution, and the RUN keyword.
* Version 1.0 * VERSION 1.0
- never publicly released. - never publicly released.

View File

@@ -6,6 +6,11 @@
# Cut and paste as desired! Also, near the end, there are a bunch of # # Cut and paste as desired! Also, near the end, there are a bunch of #
# holiday definitions for the U.S. # # holiday definitions for the U.S. #
# # # #
# *** NOTE *** #
# #
# This file is simply a grab-bag of examples. It is NOT meant to be #
# included as-is in a live reminder file. #
# #
# Some examples provided by George M. Sipe <gsipe@pyratl.ga.pyramid.com> # # Some examples provided by George M. Sipe <gsipe@pyratl.ga.pyramid.com> #
# # # #
# U.S. holidays provided by Dave Rickel <drickel@sjc.mentorg.com> # # U.S. holidays provided by Dave Rickel <drickel@sjc.mentorg.com> #
@@ -16,7 +21,7 @@
# "#PSSTUFF" for nifty PostScript examples # # "#PSSTUFF" for nifty PostScript examples #
# # # #
# This file is part of REMIND. # # This file is part of REMIND. #
# Copyright (C) 1992-2023 Dianne Skoll # # Copyright (C) 1992-2024 Dianne Skoll #
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# # # #
############################################################################# #############################################################################
@@ -27,7 +32,7 @@ RUN OFF
# Ensure required version of remind is used... # # Ensure required version of remind is used... #
################################################ ################################################
IF version() < "03.04.02" IF version() < "03.04.02"
ERRMSG This file requires at least version 03.01.10 of Remind.% ERRMSG This file requires at least version 03.04.02 of Remind.%
ERRMSG This version is version [version()]. ERRMSG This version is version [version()].
EXIT EXIT
ENDIF ENDIF
@@ -305,51 +310,51 @@ FSET _PastMon(x, y) IIF(WKDAYNUM(_h2(x,y))!=1, _h2(x,y), _h2(x,y)+1)
SET InIsrael VALUE("InIsrael", 0) SET InIsrael VALUE("InIsrael", 0)
SET Reform VALUE("Reform", 0) SET Reform VALUE("Reform", 0)
[_h(1, "Tishrey")] ++4 MSG %"Rosh Hashana 1%" is %b. REM [_h(1, "Tishrey")] ++4 MSG %"Rosh Hashana 1%" is %b.
# No RH-2 or Tzom Gedalia in Reform # No RH-2 or Tzom Gedalia in Reform
IF !Reform IF !Reform
[_h(2, "Tishrey")] ++4 MSG %"Rosh Hashana 2%" is %b. REM [_h(2, "Tishrey")] ++4 MSG %"Rosh Hashana 2%" is %b.
[_PastSat(3, "Tishrey")] ++4 MSG %"Tzom Gedalia%" is %b. REM [_PastSat(3, "Tishrey")] ++4 MSG %"Tzom Gedalia%" is %b.
ENDIF ENDIF
[_h(10, "Tishrey")] ++4 MSG %"Yom Kippur%" is %b. REM [_h(10, "Tishrey")] ++4 MSG %"Yom Kippur%" is %b.
[_h(15, "Tishrey")] ++4 MSG %"Sukkot 1%" is %b. REM [_h(15, "Tishrey")] ++4 MSG %"Sukkot 1%" is %b.
IF !InIsrael IF !InIsrael
[_h(16, "Tishrey")] MSG %"Sukkot 2%" REM [_h(16, "Tishrey")] MSG %"Sukkot 2%"
ENDIF ENDIF
[_h(21, "Tishrey")] ++4 MSG %"Hoshana Rabba%" is %b. REM [_h(21, "Tishrey")] ++4 MSG %"Hoshana Rabba%" is %b.
[_h(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b. REM [_h(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b.
IF InIsrael IF InIsrael
[_h(22, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b. REM [_h(22, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
ELSE ELSE
[_h(23, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b. REM [_h(23, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
ENDIF ENDIF
# Because Kislev can change length, we must be more careful about Chanukah # Because Kislev can change length, we must be more careful about Chanukah
FSET _chan(x) HEBDATE(24, "Kislev", $U-9)+x FSET _chan(x) HEBDATE(24, "Kislev", $U-9)+x
[_chan(1)] ++4 MSG %"Chanukah 1%" is %b. REM [_chan(1)] ++4 MSG %"Chanukah 1%" is %b.
[_chan(2)] MSG %"Chanukah 2%" REM [_chan(2)] MSG %"Chanukah 2%"
[_chan(3)] MSG %"Chanukah 3%" REM [_chan(3)] MSG %"Chanukah 3%"
[_chan(4)] MSG %"Chanukah 4%" REM [_chan(4)] MSG %"Chanukah 4%"
[_chan(5)] MSG %"Chanukah 5%" REM [_chan(5)] MSG %"Chanukah 5%"
[_chan(6)] MSG %"Chanukah 6%" REM [_chan(6)] MSG %"Chanukah 6%"
[_chan(7)] MSG %"Chanukah 7%" REM [_chan(7)] MSG %"Chanukah 7%"
[_chan(8)] MSG %"Chanukah 8%" REM [_chan(8)] MSG %"Chanukah 8%"
# Not sure about Reform's position on the next one. # Not sure about Reform's position on the next one.
IF !Reform IF !Reform
# 10 Tevet will never be a Saturday, so whether or not to # 10 Tevet will never be a Saturday, so whether or not to
# move it is moot. (Thanks to Art Werschulz.) # move it is moot. (Thanks to Art Werschulz.)
[_h(10, "Tevet")] MSG %"Tzom Tevet%" is %b. REM [_h(10, "Tevet")] MSG %"Tzom Tevet%" is %b.
ENDIF ENDIF
[_h(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b. REM [_h(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b.
[_h(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b. REM [_h(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b.
[_h(15, "Adar A")] ++4 MSG %"Shushan Purim Katan%" is %b. REM [_h(15, "Adar A")] ++4 MSG %"Shushan Purim Katan%" is %b.
# If Purim is on Sunday, then Fast of Esther is 11 Adar. # If Purim is on Sunday, then Fast of Esther is 11 Adar.
IF WKDAYNUM(_h2(13, "Adar")) != 6 IF WKDAYNUM(_h2(13, "Adar")) != 6
@@ -357,18 +362,18 @@ IF WKDAYNUM(_h2(13, "Adar")) != 6
ELSE ELSE
REM [_h2(11, "Adar")] ++4 MSG %"Fast of Esther%" is %b. REM [_h2(11, "Adar")] ++4 MSG %"Fast of Esther%" is %b.
ENDIF ENDIF
[_h(14, "Adar")] ++4 MSG %"Purim%" is %b. REM [_h(14, "Adar")] ++4 MSG %"Purim%" is %b.
[_h(15, "Adar")] ++4 MSG %"Shushan Purim%" is %b. REM [_h(15, "Adar")] ++4 MSG %"Shushan Purim%" is %b.
[_h(15, "Nisan")] ++4 MSG %"Pesach%" is %b. REM [_h(15, "Nisan")] ++4 MSG %"Pesach%" is %b.
IF !InIsrael IF !InIsrael
[_h(16, "Nisan")] MSG %"Pesach 2%" REM [_h(16, "Nisan")] MSG %"Pesach 2%"
ENDIF ENDIF
[_h(21, "Nisan")] MSG %"Pesach 7%" REM [_h(21, "Nisan")] MSG %"Pesach 7%"
IF !InIsrael && !Reform IF !InIsrael && !Reform
[_h(22, "Nisan")] MSG %"Pesach 8%" REM [_h(22, "Nisan")] MSG %"Pesach 8%"
ENDIF ENDIF
REM [_PastSun(27, "Nisan")] SATISFY 1 REM [_PastSun(27, "Nisan")] SATISFY 1
@@ -384,36 +389,36 @@ ENDIF
# Thursday. If 4 Iyar is a Sunday, then Yom Hazikaron # Thursday. If 4 Iyar is a Sunday, then Yom Hazikaron
# moves to 5 Iyar and Yom Ha'atzmaut to 6 Iyar. # moves to 5 Iyar and Yom Ha'atzmaut to 6 Iyar.
IF WKDAYNUM(_h2(4, "Iyar")) == 4 || WKDAYNUM(_h2(4, "Iyar")) == 5 IF WKDAYNUM(_h2(4, "Iyar")) == 4 || WKDAYNUM(_h2(4, "Iyar")) == 5
[_h(2, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b. REM [_h(2, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b.
[_h(3, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. REM [_h(3, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b.
ELSE ELSE
IF WKDAYNUM(_h2(4, "Iyar")) == 0 IF WKDAYNUM(_h2(4, "Iyar")) == 0
[_h(5, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b. REM [_h(5, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b.
[_h(6, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. REM [_h(6, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b.
ELSE ELSE
[_h(4, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b. REM [_h(4, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b.
[_h(5, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. REM [_h(5, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b.
ENDIF ENDIF
ENDIF ENDIF
# Not sure about Reform's position on Lag B'Omer # Not sure about Reform's position on Lag B'Omer
IF !Reform IF !Reform
[_h(18, "Iyar")] ++4 MSG %"Lag B'Omer%" is %b. REM [_h(18, "Iyar")] ++4 MSG %"Lag B'Omer%" is %b.
ENDIF ENDIF
[_h(28, "Iyar")] ++4 MSG %"Yom Yerushalayim%" is %b. REM [_h(28, "Iyar")] ++4 MSG %"Yom Yerushalayim%" is %b.
[_h(6, "Sivan")] ++4 MSG %"Shavuot%" is %b. REM [_h(6, "Sivan")] ++4 MSG %"Shavuot%" is %b.
IF !InIsrael && !Reform IF !InIsrael && !Reform
[_h(7, "Sivan")] MSG %"Shavuot 2%" REM [_h(7, "Sivan")] MSG %"Shavuot 2%"
ENDIF ENDIF
# Fairly sure Reform Jews don't observe the next two # Fairly sure Reform Jews don't observe the next two
IF !Reform IF !Reform
# Tzom Tamuz and Tish'a B'Av are moved to Sunday if they normally # Tzom Tamuz and Tish'a B'Av are moved to Sunday if they normally
# fall on a Saturday # fall on a Saturday
[_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b. REM [_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b.
[_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b. REM [_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b.
ENDIF ENDIF
# Counting the omer - do the whole spiel, i.e: # Counting the omer - do the whole spiel, i.e:

View File

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

View File

@@ -0,0 +1,29 @@
REM 1 Feb 2022 MSG Chinese New Year (Tiger)
REM 22 Jan 2023 MSG Chinese New Year (Rabbit)
REM 10 Feb 2024 MSG Chinese New Year (Dragon)
REM 29 Jan 2025 MSG Chinese New Year (Snake)
REM 17 Feb 2026 MSG Chinese New Year (Horse)
REM 6 Feb 2027 MSG Chinese New Year (Goat)
REM 26 Jan 2028 MSG Chinese New Year (Monkey)
REM 13 Feb 2029 MSG Chinese New Year (Rooster)
REM 3 Feb 2030 MSG Chinese New Year (Dog)
REM 23 Jan 2031 MSG Chinese New Year (Pig)
REM 11 Feb 2032 MSG Chinese New Year (Rat)
REM 31 Jan 2033 MSG Chinese New Year (Ox)
REM 19 Feb 2034 MSG Chinese New Year (Tiger)
REM 8 Feb 2035 MSG Chinese New Year (Rabbit)
REM 28 Jan 2036 MSG Chinese New Year (Dragon)
REM 15 Feb 2037 MSG Chinese New Year (Snake)
REM 4 Feb 2038 MSG Chinese New Year (Horse)
REM 24 Jan 2039 MSG Chinese New Year (Goat)
REM 12 Feb 2040 MSG Chinese New Year (Monkey)
REM 1 Feb 2041 MSG Chinese New Year (Rooster)
REM 22 Jan 2042 MSG Chinese New Year (Dog)
REM 10 Feb 2043 MSG Chinese New Year (Pig)
REM 30 Jan 2044 MSG Chinese New Year (Rat)
REM 17 Feb 2045 MSG Chinese New Year (Ox)
REM 6 Feb 2046 MSG Chinese New Year (Tiger)
REM 26 Jan 2047 MSG Chinese New Year (Rabbit)
REM 14 Feb 2048 MSG Chinese New Year (Dragon)
REM 2 Feb 2049 MSG Chinese New Year (Snake)
REM 23 Jan 2050 MSG Chinese New Year (Horse)

View File

@@ -10,7 +10,7 @@ REM 28 Oct MSG ΟΧΙ
REM 25 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ REM 25 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ
REM 26 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ2 REM 26 Dec MSG ΧΡΙΣΤΟΥΓΕΝΝΑ2
REM [orthodoxeaster($Uy)+1] ΔΕΥΤΕΡΑ ΤΟΥ ΠΑΣΧΑ REM [orthodoxeaster($Uy)+1] MSG ΔΕΥΤΕΡΑ ΤΟΥ ΠΑΣΧΑ
# May first is a national holiday except if Sunday, day of great week (week before easter) or Monday after easter, then # May first is a national holiday except if Sunday, day of great week (week before easter) or Monday after easter, then

View File

@@ -10,7 +10,6 @@ SET InIsrael value("InIsrael", 0)
SET Reform value("Reform", 0) SET Reform value("Reform", 0)
# Convenient function definition to save typing # Convenient function definition to save typing
FSET _h(x, y) HEBDATE(x,y)
FSET _h2(x, y) HEBDATE(x, y, $U-7) FSET _h2(x, y) HEBDATE(x, y, $U-7)
FSET _PastSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)+1) FSET _PastSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)+1)
FSET _BackTwoFri(x, y) IIF(WKDAYNUM(_h2(x,y))!=5, _h2(x,y), _h2(x,y)-2) FSET _BackTwoFri(x, y) IIF(WKDAYNUM(_h2(x,y))!=5, _h2(x,y), _h2(x,y)-2)
@@ -19,50 +18,50 @@ FSET _BackTwoSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)-2)
SET InIsrael VALUE("InIsrael", 0) SET InIsrael VALUE("InIsrael", 0)
SET Reform VALUE("Reform", 0) SET Reform VALUE("Reform", 0)
[_h(1, "Tishrey")] ++4 MSG %"Rosh Hashana 1%" is %b. REM [hebdate(1, "Tishrey")] ++4 MSG %"Rosh Hashana 1%" is %b.
# No RH-2 or Tzom Gedalia in Reform # No RH-2 or Tzom Gedalia in Reform
IF !Reform IF !Reform
[_h(2, "Tishrey")] ++4 MSG %"Rosh Hashana 2%" is %b. REM [hebdate(2, "Tishrey")] ++4 MSG %"Rosh Hashana 2%" is %b.
[_PastSat(3, "Tishrey")] ++4 MSG %"Tzom Gedalia%" is %b. REM [_PastSat(3, "Tishrey")] ++4 MSG %"Tzom Gedalia%" is %b.
ENDIF ENDIF
[_h(10, "Tishrey")] ++4 MSG %"Yom Kippur%" is %b. REM [hebdate(10, "Tishrey")] ++4 MSG %"Yom Kippur%" is %b.
[_h(15, "Tishrey")] ++4 MSG %"Sukkot 1%" is %b. REM [hebdate(15, "Tishrey")] ++4 MSG %"Sukkot 1%" is %b.
IF !InIsrael IF !InIsrael
[_h(16, "Tishrey")] MSG %"Sukkot 2%" REM [hebdate(16, "Tishrey")] MSG %"Sukkot 2%"
ENDIF ENDIF
[_h(21, "Tishrey")] ++4 MSG %"Hoshana Rabba%" is %b. REM [hebdate(21, "Tishrey")] ++4 MSG %"Hoshana Rabba%" is %b.
[_h(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b. REM [hebdate(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b.
IF InIsrael IF InIsrael
[_h(22, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b. REM [hebdate(22, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
ELSE ELSE
[_h(23, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b. REM [hebdate(23, "Tishrey")] ++4 MSG %"Simchat Torah%" is %b.
ENDIF ENDIF
# Because Kislev can change length, we must be more careful about Chanukah # Because Kislev can change length, we must be more careful about Chanukah
FSET _chan(x) HEBDATE(24, "Kislev", $U-9)+x FSET _chan(x) HEBDATE(24, "Kislev", $U-9)+x
[_chan(1)] ++4 MSG %"Chanukah 1%" is %b. REM [_chan(1)] ++4 MSG %"Chanukah 1%" is %b.
[_chan(2)] MSG %"Chanukah 2%" REM [_chan(2)] MSG %"Chanukah 2%"
[_chan(3)] MSG %"Chanukah 3%" REM [_chan(3)] MSG %"Chanukah 3%"
[_chan(4)] MSG %"Chanukah 4%" REM [_chan(4)] MSG %"Chanukah 4%"
[_chan(5)] MSG %"Chanukah 5%" REM [_chan(5)] MSG %"Chanukah 5%"
[_chan(6)] MSG %"Chanukah 6%" REM [_chan(6)] MSG %"Chanukah 6%"
[_chan(7)] MSG %"Chanukah 7%" REM [_chan(7)] MSG %"Chanukah 7%"
[_chan(8)] MSG %"Chanukah 8%" REM [_chan(8)] MSG %"Chanukah 8%"
# Not sure about Reform's position on the next one. # Not sure about Reform's position on the next one.
IF !Reform IF !Reform
# 10 Tevet will never be a Saturday, so whether or not to # 10 Tevet will never be a Saturday, so whether or not to
# move it is moot. (Thanks to Art Werschulz.) # move it is moot. (Thanks to Art Werschulz.)
[_h(10, "Tevet")] MSG %"Tzom Tevet%" is %b. REM [hebdate(10, "Tevet")] MSG %"Tzom Tevet%" is %b.
ENDIF ENDIF
[_h(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b. REM [hebdate(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b.
[_h(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b. REM [hebdate(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b.
# If Purim is on Sunday, then Fast of Esther is 11 Adar. # If Purim is on Sunday, then Fast of Esther is 11 Adar.
IF WKDAYNUM(_h2(13, "Adar")) != 6 IF WKDAYNUM(_h2(13, "Adar")) != 6
@@ -70,43 +69,39 @@ IF WKDAYNUM(_h2(13, "Adar")) != 6
ELSE ELSE
REM [_h2(11, "Adar")] ++4 MSG %"Fast of Esther%" is %b. REM [_h2(11, "Adar")] ++4 MSG %"Fast of Esther%" is %b.
ENDIF ENDIF
[_h(14, "Adar")] ++4 MSG %"Purim%" is %b. REM [hebdate(14, "Adar")] ++4 MSG %"Purim%" is %b.
[_h(15, "Nisan")] ++4 MSG %"Pesach%" is %b. REM [hebdate(15, "Nisan")] ++4 MSG %"Pesach%" is %b.
IF !InIsrael IF !InIsrael
[_h(16, "Nisan")] MSG %"Pesach 2%" REM [hebdate(16, "Nisan")] MSG %"Pesach 2%"
ENDIF ENDIF
[_h(21, "Nisan")] MSG %"Pesach 7%" REM [hebdate(21, "Nisan")] MSG %"Pesach 7%"
IF !InIsrael && !Reform IF !InIsrael && !Reform
[_h(22, "Nisan")] MSG %"Pesach 8%" REM [hebdate(22, "Nisan")] MSG %"Pesach 8%"
ENDIF ENDIF
[_h(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b. REM [hebdate(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b.
[_BackTwoFri(4, "Iyar")] ++4 MSG %"Yom HaZikaron%" is %b. REM [_BackTwoFri(4, "Iyar")] ++4 MSG %"Yom HaZikaron%" is %b.
[_BackTwoSat(5, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b. REM [_BackTwoSat(5, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b.
# Not sure about Reform's position on Lag B'Omer # Not sure about Reform's position on Lag B'Omer
IF !Reform IF !Reform
[_h(18, "Iyar")] ++4 MSG %"Lag B'Omer%" is %b. REM [hebdate(18, "Iyar")] ++4 MSG %"Lag B'Omer%" is %b.
ENDIF ENDIF
[_h(28, "Iyar")] ++4 MSG %"Yom Yerushalayim%" is %b. REM [hebdate(28, "Iyar")] ++4 MSG %"Yom Yerushalayim%" is %b.
[_h(6, "Sivan")] ++4 MSG %"Shavuot%" is %b. REM [hebdate(6, "Sivan")] ++4 MSG %"Shavuot%" is %b.
IF !InIsrael && !Reform IF !InIsrael && !Reform
[_h(7, "Sivan")] MSG %"Shavuot 2%" REM [hebdate(7, "Sivan")] MSG %"Shavuot 2%"
ENDIF ENDIF
# Fairly sure Reform Jews don't observe the next two # Fairly sure Reform Jews don't observe the next two
IF !Reform IF !Reform
# Tzom Tamuz and Tish'a B'Av are moved to Sunday if they normally # Tzom Tamuz and Tish'a B'Av are moved to Sunday if they normally
# fall on a Saturday # fall on a Saturday
[_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b. REM [_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b.
[_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b. REM [_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b.
ENDIF ENDIF
# Clean up
FUNSET _h _h2 _PastSat _BackTwoFri _BackTwoSat _chan

17
include/holidays/pt.rem Normal file
View File

@@ -0,0 +1,17 @@
# Portuguese holidays
# Courtesy of Joop Kiefte
OMIT 1 Jan MSG Ano Novo
OMIT [easterdate()-47] MSG Carnaval
OMIT [easterdate()-2] MSG Sexta-feira Santa
OMIT [easterdate()] MSG Domingo de Páscoa
OMIT 25 Apr MSG Dia da Liberdade
OMIT 1 May MSG Dia do Trabalhador
OMIT [easterdate()+60] MSG Corpo de Deus
OMIT 10 Jun MSG Dia de Portugal, de Camões e das Comunidades Portuguesas
OMIT 15 Aug MSG Assunção de Nossa Senhora
OMIT 5 Oct MSG Implantação da República
OMIT 1 Nov MSG Dia de Todos os Santos
OMIT 1 Dec MSG Restauração da Independência
OMIT 8 Dec MSG Imaculada Conceição
OMIT 25 Dec MSG Natal

View File

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

View File

@@ -1,24 +1,28 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
SET autolang getenv("REMIND_LANG") if !defined("__autolang__")
SET __autolang__ 1
PRESERVE __autolang__
SET autolang getenv("REMIND_LANG")
IF autolang == "" IF autolang == ""
SET autolang getenv("LC_ALL") SET autolang getenv("LC_ALL")
ENDIF ENDIF
IF autolang == "" IF autolang == ""
SET autolang getenv("LANGUAGE") SET autolang getenv("LANGUAGE")
ENDIF ENDIF
IF autolang == "" IF autolang == ""
SET autolang getenv("LANG") SET autolang getenv("LANG")
ENDIF ENDIF
IF autolang != "" IF autolang != ""
IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 5)) + ".rem", "r") == 0 IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 5)) + ".rem", "r") == 0
INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 5))].rem INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 5))].rem
ELSE ELSE
IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 2)) + ".rem", "r") == 0 IF access($SysInclude + "/lang/" + lower(substr(autolang, 1, 2)) + ".rem", "r") == 0
INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 2))].rem INCLUDE [$SysInclude]/lang/[lower(substr(autolang, 1, 2))].rem
ENDIF
ENDIF ENDIF
ENDIF ENDIF
UNSET autolang
ENDIF ENDIF
UNSET autolang

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

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

View File

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

View File

@@ -1,6 +1,6 @@
# Support for the German language. # Support for the German language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by Wolfgang Thronicke # This file is derived from a translation by Wolfgang Thronicke
# Day names # Day names
@@ -87,3 +87,5 @@ SET daylightST_starts_str "Beginn Sommerzeit"
# Daylight saving time ends # Daylight saving time ends
SET daylightST_ends_str "Ende Sommerzeit" SET daylightST_ends_str "Ende Sommerzeit"
PRESERVE earthseasons_Perihelion_str earthseasons_EquinoxMar_str earthseasons_SolsticeJun_str earthseasons_Aphelion_str earthseasons_EquinoxSep_str earthseasons_SolsticeDec_str daylightST_starts_str daylightST_ends_str

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# Support for the Hellenic (Greek) language. # Support for the Hellenic (Greek) language.
# This file is part of REMIND. # This file is part of REMIND.
# REMIND is Copyright (C) 1992-2023 by Dianne Skoll # REMIND is Copyright (C) 1992-2024 by Dianne Skoll
# This file is derived from a translation by jarlaxl lamat (jarlaxl@freemail.gr) # This file is derived from a translation by jarlaxl lamat (jarlaxl@freemail.gr)
SET $Sunday "Κυριακή" SET $Sunday "Κυριακή"
@@ -81,3 +81,5 @@ SET daylightST_starts_str "Έναρξη θέρους"
# Daylight saving time ends # Daylight saving time ends
SET daylightST_ends_str "Τέλος θέρους" SET daylightST_ends_str "Τέλος θέρους"
PRESERVE earthseasons_Perihelion_str earthseasons_EquinoxMar_str earthseasons_SolsticeJun_str earthseasons_Aphelion_str earthseasons_EquinoxSep_str earthseasons_SolsticeDec_str daylightST_starts_str daylightST_ends_str

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -15,7 +15,7 @@ install:
@if test "$(PERL)" = "" ; then \ @if test "$(PERL)" = "" ; then \
echo "Not installing rem2html; Perl is required"; exit 0; fi; \ echo "Not installing rem2html; Perl is required"; exit 0; fi; \
for m in $(PERLMODS_NEEDED) ; \ for m in $(PERLMODS_NEEDED) ; \
do \ do \
$(PERL) -M$$m -e 1 > /dev/null 2>&1; \ $(PERL) -M$$m -e 1 > /dev/null 2>&1; \
if test $$? != 0 ; then echo "Not installing rem2html; missing $$m"; exit 0; fi; \ if test $$? != 0 ; then echo "Not installing rem2html; missing $$m"; exit 0; fi; \
done; \ done; \

View File

@@ -10,7 +10,7 @@ use Encode;
my %Options; my %Options;
my $rem2html_version = '2.1'; my $rem2html_version = '@VERSION@';
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks, my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
@Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen); @Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);
@@ -374,19 +374,37 @@ sub parse_input
($1 % 256), ($1 % 256), ($1 % 256)); ($1 % 256), ($1 % 256), ($1 % 256));
} }
} elsif ($special eq 'COLOR' || $special eq 'COLOUR') { } elsif ($special eq 'COLOR' || $special eq 'COLOUR') {
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/) { if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/s) {
my($r, $g, $b, $text) = ($1, $2, $3, $4); my($r, $g, $b, $text) = ($1, $2, $3, $4);
my $color = sprintf("style=\"color: #%02X%02X%02X;\"", my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
$r % 256, $g % 256, $b % 256); $r % 256, $g % 256, $b % 256);
push(@{$days->[$d]}, "<p$class $color>" . escape_html($text) . '</p>'); push(@{$days->[$d]}, "<p$class $color>" . fix_whitespace(escape_html($text)) . '</p>');
} }
} elsif ($special eq '*') { } elsif ($special eq '*') {
push(@{$days->[$d]}, "<p$class>" . escape_html($body) . '</p>'); push(@{$days->[$d]}, "<p$class>" . fix_whitespace(escape_html($body)) . '</p>');
} }
} }
return $found_data; return $found_data;
} }
sub fix_whitespace
{
my ($text) = @_;
# Collapse multiple spaces/tabs to a single space
$text =~ s/[ \t]+/ /gs;
# Remove whitespace before/after newlines
$text =~ s/\s+\n/\n/gs;
$text =~ s/\n\s+/\n/gs;
# Collapse multiple newlines to a single newline
$text =~ s/\n+/\n/gs;
# Convert newlines to <br />
$text =~ s|\n|<br />|g;
return $text;
}
sub small_calendar sub small_calendar
{ {
my($month, $monlen, $url, $first_col) = @_; my($month, $monlen, $url, $first_col) = @_;

View File

@@ -60,7 +60,9 @@ my $settings = {
margin_bottom => 36, margin_bottom => 36,
margin_left => 36, margin_left => 36,
margin_right => 36, margin_right => 36,
svg => 0,
ps => 0,
eps => 0,
verbose => 0, verbose => 0,
}; };
@@ -80,6 +82,9 @@ Options:
--landscape, -l Print in landscape orientation --landscape, -l Print in landscape orientation
--small-calendars=N Choose location for small calendars --small-calendars=N Choose location for small calendars
--svg Output SVG instead of PDF
--ps Output PostScript instead of PDF
--eps Output encapsulated PostScript instead of PDF
-cN Synonym for --small-calendars=N -cN Synonym for --small-calendars=N
--left-numbers, -x Print day numbers on the left --left-numbers, -x Print day numbers on the left
--fill-page, -e Fill the entire page --fill-page, -e Fill the entire page
@@ -112,6 +117,9 @@ Getopt::Long::Configure('bundling_values');
my $ret = GetOptions('landscape|l' => \$settings->{landscape}, my $ret = GetOptions('landscape|l' => \$settings->{landscape},
'small-calendars|c=i' => \$settings->{small_calendars}, 'small-calendars|c=i' => \$settings->{small_calendars},
'left-numbers|x' => \$settings->{numbers_on_left}, 'left-numbers|x' => \$settings->{numbers_on_left},
'svg' => \$settings->{svg},
'ps' => \$settings->{ps},
'eps' => \$settings->{eps},
'fill-page|e' => \$settings->{fill_entire_page}, 'fill-page|e' => \$settings->{fill_entire_page},
'media|m=s' => \$settings->{media}, 'media|m=s' => \$settings->{media},
'width|w=i' => \$settings->{width}, 'width|w=i' => \$settings->{width},
@@ -176,6 +184,17 @@ if ($settings->{landscape}) {
$settings->{height} = $tmp; $settings->{height} = $tmp;
} }
if ($settings->{svg} && $settings->{ps} ||
$settings->{svg} && $settings->{eps} ||
$settings->{eps} && $settings->{ps}) {
print STDERR "Only one of --eps, --ps or --svg may be used.\n";
exit(1);
}
if ($settings->{eps}) {
$settings->{ps} = 1;
}
# Don't read from a terminal # Don't read from a terminal
if (-t STDIN) { ## no critic if (-t STDIN) { ## no critic
print STDERR "I can't read data from a terminal. Please run like this:\n"; print STDERR "I can't read data from a terminal. Please run like this:\n";
@@ -187,8 +206,25 @@ my $done_one = 0;
my $errored_out = 0; my $errored_out = 0;
my $surface = Cairo::PdfSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef, my $surface;
$settings->{width}, $settings->{height}); if ($settings->{svg}) {
$surface = Cairo::SvgSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height});
} elsif ($settings->{ps}) {
if ($settings->{landscape}) {
$surface = Cairo::PsSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{height}, $settings->{width});
} else {
$surface = Cairo::PsSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height});
}
if ($settings->{eps}) {
$surface->set_eps(1);
}
} else {
$surface = Cairo::PdfSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height});
}
# set_metadata not available in older versions of Cairo # set_metadata not available in older versions of Cairo
eval { $surface->set_metadata('title', 'Calendar'); }; eval { $surface->set_metadata('title', 'Calendar'); };
@@ -196,10 +232,26 @@ eval { $surface->set_metadata('author', 'Remind (https://dianne.skoll.ca/project
eval { $surface->set_metadata('creator', 'rem2pdf (https://dianne.skoll.ca/projects/remind/)'); }; eval { $surface->set_metadata('creator', 'rem2pdf (https://dianne.skoll.ca/projects/remind/)'); };
eval { $surface->set_metadata('subject', 'Calendar'); }; eval { $surface->set_metadata('subject', 'Calendar'); };
if ($settings->{ps}) {
$surface->dsc_comment('%%Title: Calendar');
$surface->dsc_comment('%%Producer: rem2pdf (https://dianne.skoll.ca/projects/remind/)');
$surface->dsc_comment('%%PageOrientation: ' . (($settings->{landscape}) ? 'Landscape' : 'Portrait'));
$surface->dsc_begin_setup();
}
my $cr = Cairo::Context->create($surface); my $cr = Cairo::Context->create($surface);
$cr->set_line_width($settings->{line_thickness}); $cr->set_line_width($settings->{line_thickness});
if ($settings->{ps} && $settings->{landscape}) {
$cr->translate(0, $settings->{width});
$cr->rotate(-1.5707963267949); # Rotate -90 degrees
}
my $warned = 0;
while(1) { while(1) {
if ($settings->{ps}) {
$surface->dsc_begin_page_setup();
$surface->dsc_comment('%%PageOrientation: ' . (($settings->{landscape}) ? 'Landscape' : 'Portrait'));
}
my ($obj, $err) = Remind::PDF->create_from_stream(*STDIN, my ($obj, $err) = Remind::PDF->create_from_stream(*STDIN,
{color => 1, {color => 1,
shade => 1, shade => 1,
@@ -215,8 +267,15 @@ while(1) {
} }
last; last;
} }
$done_one = 1; if (($settings->{eps} || $settings->{svg}) && $done_one) {
if (!$warned) {
print STDERR "WARNING: --eps and --svg can only output one page; ignoring subsequent\nmonths in a multi-month calendar.\n";
$warned = 1;
}
next;
}
$obj->render($cr, $settings); $obj->render($cr, $settings);
$done_one = 1;
} }
$surface->finish(); $surface->finish();
@@ -273,17 +332,22 @@ __END__
=head1 NAME =head1 NAME
rem2pdf - draw a PDF calendar from Remind output rem2pdf - draw a PDF, SVG or PostScript calendar from Remind output
=head1 SYNOPSIS =head1 SYNOPSIS
remind -pp [options] file | rem2pdf [options] > output.pdf remind -pp [options] file | rem2pdf [options] > output.pdf
remind -pp [options] file | rem2pdf --svg [options] > output.svg
remind -pp [options] file | rem2pdf --ps [options] > output.ps
remind -pp [options] file | rem2pdf --eps [options] > output.eps
=head1 DESCRIPTION =head1 DESCRIPTION
B<rem2pdf> reads the standard input, which should be the results of B<rem2pdf> reads the standard input, which should be the results of
running B<remind> with the B<-p>, B<-pp> or B<-ppp> options. It emits running B<remind> with the B<-p>, B<-pp> or B<-ppp> options. It emits
PDF code that draws a calendar to standard output. PDF, SVG or PostScript code that draws a calendar to standard output. (The
addition of support for SVG and PostScript means that rem2pdf is increasingly
misnamed...)
B<rem2pdf> uses the Pango text formatting library (L<https://pango.gnome.org/>) B<rem2pdf> uses the Pango text formatting library (L<https://pango.gnome.org/>)
and the Cairo graphics library (L<https://www.cairographics.org/>) to produce and the Cairo graphics library (L<https://www.cairographics.org/>) to produce
@@ -298,6 +362,22 @@ output at all.
=over =over
=item --ps
Output PostScript instead of PDF.
=item --eps
Output Encapsulated PostScript instead of PDF. In this case, you
should feed C<rem2pdf> only one month's worth of calendar data,
because it cannot create a multi-page encapsulated PostScript file.
=item --svg
Output SVG instead of PDF. In this case, you should feed C<rem2pdf>
only one month's worth of calendar data, because it cannot create
a multi-page SVG file.
=item --landscape, -l =item --landscape, -l
Print the calendar in landscape orientation. Essentially, this swaps Print the calendar in landscape orientation. Essentially, this swaps
@@ -486,7 +566,7 @@ output for the invalid reminder.
=head1 ABSOLUTELY-POSITIONED TEXT =head1 ABSOLUTELY-POSITIONED TEXT
If your B<PANGO> special reminder starts with C<@I<x>,I<y>> where I<x> If your B<PANGO> special reminder starts with C<@I<x>,I<y>> where I<x>
and I<y> are floating-point numbers, then the Pango marked-up test is and I<y> are floating-point numbers, then the Pango marked-up text is
positioned absolutely with respect to the day's box (and is not positioned absolutely with respect to the day's box (and is not
counted when calculating the box's height.) counted when calculating the box's height.)

View File

@@ -1008,7 +1008,17 @@ as were read from the C<remind -ppp> stream
sub render sub render
{ {
my ($self, $cr, $settings) = @_; my ($self, $cr, $settings) = @_;
my $done = 0;
my $warned = 0;
foreach my $e (@{$self->{entries}}) { foreach my $e (@{$self->{entries}}) {
if ($settings->{svg} && $done) {
if (!$warned) {
print STDERR "WARNING: --svg can only output one page; ignoring subsequent\nmonths in a multi-month calendar.\n";
$warned = 1;
}
next;
}
$done = 1;
$e->render($cr, $settings); $e->render($cr, $settings);
} }
} }

View File

@@ -85,6 +85,18 @@ sub render
} else { } else {
$body = $self->{body}; $body = $self->{body};
} }
# Clean up the body:
# Collapse multiple spaces/tabs to a single space
$body =~ s/[ \t]+/ /gs;
# Remove whitespace before/after newlines
$body =~ s/\s+\n/\n/gs;
$body =~ s/\n\s+/\n/gs;
# Collapse multiple newlines to a single newline
$body =~ s/\n+/\n/gs;
$layout->set_text(Encode::decode('UTF-8', $body)); $layout->set_text(Encode::decode('UTF-8', $body));
my $desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . $settings->{entry_size} . 'px'); my $desc = Pango::FontDescription->from_string($settings->{entry_font} . ' ' . $settings->{entry_size} . 'px');
$layout->set_font_description($desc); $layout->set_font_description($desc);

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

@@ -30,7 +30,7 @@ REMINDSRCS= calendar.c dynbuf.c dorem.c dosubst.c expr.c files.c funcs.c \
globals.c hbcal.c init.c main.c md5.c moon.c omit.c queue.c \ globals.c hbcal.c init.c main.c md5.c moon.c omit.c queue.c \
sort.c token.c trigger.c userfns.c utils.c var.c sort.c token.c trigger.c userfns.c utils.c var.c
REMINDHDRS=config.h custom.h dynbuf.h err.h expr.h globals.h lang.h \ REMINDHDRS=config.h custom.h dynbuf.h err.h globals.h lang.h \
md5.h protos.h rem2ps.h types.h version.h md5.h protos.h rem2ps.h types.h version.h
REMINDOBJS= $(REMINDSRCS:.c=.o) REMINDOBJS= $(REMINDSRCS:.c=.o)
@@ -63,6 +63,16 @@ install: all
done done
-mkdir -p $(DESTDIR)$(datarootdir)/remind || true -mkdir -p $(DESTDIR)$(datarootdir)/remind || true
cp -R ../include/* $(DESTDIR)$(datarootdir)/remind cp -R ../include/* $(DESTDIR)$(datarootdir)/remind
chmod -R a+rX $(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 || true; \
xdg-desktop-menu install --novendor $(DESTDIR)$(prefix)/share/applications/tkremind.desktop < /dev/null > /dev/null 2>&1 || true; \
fi
install-stripped: install install-stripped: install
strip $(DESTDIR)$(bindir)/remind || true strip $(DESTDIR)$(bindir)/remind || true
@@ -71,9 +81,6 @@ install-stripped: install
clean: clean:
rm -f *.o *~ core *.bak $(PROGS) rm -f *.o *~ core *.bak $(PROGS)
cppcheck:
cppcheck --force --enable=all --suppress=variableScope --suppress=ConfigurationNotChecked *.c
clobber: clobber:
rm -f *.o *~ remind rem2ps test.out core *.bak rm -f *.o *~ remind rem2ps test.out core *.bak
@@ -83,6 +90,9 @@ depend:
# The next targets are not very useful to you. I use them to build # The next targets are not very useful to you. I use them to build
# distributions, etc. # distributions, etc.
cppcheck:
cppcheck -j`nproc` --force --enable=all --suppress=ConfigurationNotChecked --suppress=unmatchedSuppression --suppress=variableScope --inline-suppr .
# Build a tar file based on all files checked into git. # Build a tar file based on all files checked into git.
distro: distro:
cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)/ HEAD > src/remind-$(VERSION).tar cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)/ HEAD > src/remind-$(VERSION).tar

View File

@@ -5,7 +5,7 @@
/* The code for generating a calendar. */ /* The code for generating a calendar. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -35,7 +35,6 @@
#include "lang.h" #include "lang.h"
#include "types.h" #include "types.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "md5.h" #include "md5.h"
@@ -282,6 +281,7 @@ static void ColorizeEntry(CalEntry const *e, int clamp);
static void SortCol (CalEntry **col); static void SortCol (CalEntry **col);
static void DoCalendarOneWeek (int nleft); static void DoCalendarOneWeek (int nleft);
static void DoCalendarOneMonth (void); static void DoCalendarOneMonth (void);
static void DoSimpleCalendarOneMonth (void);
static int WriteCalendarRow (void); static int WriteCalendarRow (void);
static void WriteWeekHeaderLine (void); static void WriteWeekHeaderLine (void);
static void WritePostHeaderLine (void); static void WritePostHeaderLine (void);
@@ -339,6 +339,7 @@ UnBackgroundize(int d)
printf("%s", Decolorize()); printf("%s", Decolorize());
} }
#ifdef REM_USE_WCHAR
static void static void
send_lrm(void) send_lrm(void)
{ {
@@ -353,6 +354,7 @@ send_lrm(void)
printf("\xE2\x80\x8E"); printf("\xE2\x80\x8E");
} }
} }
#endif
static char const * static char const *
despace(char const *s) despace(char const *s)
@@ -475,7 +477,7 @@ void PrintJSONKeyPairTime(char const *name, int t)
} }
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
void PutWideChar(wchar_t const wc) void PutWideChar(wchar_t const wc, DynamicBuffer *output)
{ {
char buf[MB_CUR_MAX+1]; char buf[MB_CUR_MAX+1];
int len; int len;
@@ -483,7 +485,11 @@ void PutWideChar(wchar_t const wc)
len = wctomb(buf, wc); len = wctomb(buf, wc);
if (len > 0) { if (len > 0) {
buf[len] = 0; buf[len] = 0;
fputs(buf, stdout); if (output) {
DBufPuts(output, buf);
} else {
fputs(buf, stdout);
}
} }
} }
#endif #endif
@@ -523,9 +529,9 @@ get_month_abbrev(char const *mon)
#endif #endif
} }
#ifdef REM_USE_WCHAR
static int make_wchar_versions(CalEntry *e) static int make_wchar_versions(CalEntry *e)
{ {
#ifdef REM_USE_WCHAR
size_t len; size_t len;
wchar_t *buf; wchar_t *buf;
len = mbstowcs(NULL, e->text, 0); len = mbstowcs(NULL, e->text, 0);
@@ -539,10 +545,8 @@ static int make_wchar_versions(CalEntry *e)
e->wc_text = buf; e->wc_text = buf;
e->wc_pos = buf; e->wc_pos = buf;
return 1; return 1;
#else
return 1;
#endif
} }
#endif
static void gon(void) static void gon(void)
{ {
@@ -556,11 +560,11 @@ static void goff(void)
static void static void
ClampColor(int *r, int *g, int *b) ClampColor(int *r, int *g, int *b)
{ {
if (TerminalBackground == TERMINAL_BACKGROUND_UNKNOWN) { if (GetTerminalBackground() == TERMINAL_BACKGROUND_UNKNOWN) {
/* No special clamping if terminal background is unknown */ /* No special clamping if terminal background is unknown */
return; return;
} }
if (TerminalBackground == TERMINAL_BACKGROUND_DARK) { if (GetTerminalBackground() == TERMINAL_BACKGROUND_DARK) {
if (*r <= 64 && *g <= 64 && *b <= 64) { if (*r <= 64 && *g <= 64 && *b <= 64) {
int max = *r; int max = *r;
double factor; double factor;
@@ -579,7 +583,7 @@ ClampColor(int *r, int *g, int *b)
} }
return; return;
} }
if (TerminalBackground == TERMINAL_BACKGROUND_LIGHT) { if (GetTerminalBackground() == TERMINAL_BACKGROUND_LIGHT) {
if (*r > 191 && *g > 191 && *b > 191) { if (*r > 191 && *g > 191 && *b > 191) {
int min = *r; int min = *r;
if (*g < min) min = *g; if (*g < min) min = *g;
@@ -667,11 +671,11 @@ Colorize(int r, int g, int b, int bg, int clamp)
if (b > 64) b = 1; if (b > 64) b = 1;
else b = 0; else b = 0;
if (clamp && TerminalBackground == TERMINAL_BACKGROUND_DARK && !bg) { if (clamp && GetTerminalBackground() == TERMINAL_BACKGROUND_DARK && !bg) {
/* Convert black-on-black to grey */ /* Convert black-on-black to grey */
if (!r && !g && !b) return VT100Colors[1][0][0][0]; if (!r && !g && !b) return VT100Colors[1][0][0][0];
} }
if (clamp && TerminalBackground == TERMINAL_BACKGROUND_LIGHT && !bg) { if (clamp && GetTerminalBackground() == TERMINAL_BACKGROUND_LIGHT && !bg) {
/* Convert white-on-white to grey */ /* Convert white-on-white to grey */
if (r && g && b) return VT100Colors[1][0][0][0]; if (r && g && b) return VT100Colors[1][0][0][0];
} }
@@ -755,13 +759,11 @@ SetMoonEntry(int dse, char const *moon)
if (sscanf(moon, "%d %*d %*d %27[^\x01]", &phase, msg) < 4) { if (sscanf(moon, "%d %*d %*d %27[^\x01]", &phase, msg) < 4) {
if (sscanf(moon, "%d", &phase) != 1) { if (sscanf(moon, "%d", &phase) != 1) {
/* Malformed MOON special; ignore */ /* Malformed MOON special; ignore */
fprintf(stderr, "Oops 1\n");
return; return;
} }
} }
if (phase < 0 || phase > 3) { if (phase < 0 || phase > 3) {
/* Bad phase */ /* Bad phase */
fprintf(stderr, "Oops 2\n");
return; return;
} }
FromDSE(dse, &y, &m, &d); FromDSE(dse, &y, &m, &d);
@@ -899,13 +901,17 @@ static void DoCalendarOneWeek(int nleft)
if (UseVTColors) { if (UseVTColors) {
printf("\x1B[1m"); /* Bold */ printf("\x1B[1m"); /* Bold */
} }
Backgroundize(d);
PrintLeft(buf, ColSpaces-1, '*'); PrintLeft(buf, ColSpaces-1, '*');
putchar(' ');
UnBackgroundize(d);
if (UseVTColors) { if (UseVTColors) {
printf("\x1B[0m"); /* Normal */ printf("\x1B[0m"); /* Normal */
} }
putchar(' ');
} else { } else {
Backgroundize(d);
PrintLeft(buf, ColSpaces, ' '); PrintLeft(buf, ColSpaces, ' ');
UnBackgroundize(d);
} }
gon(); gon();
DRAW(tb); DRAW(tb);
@@ -962,21 +968,20 @@ static void DoCalendarOneWeek(int nleft)
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DoCalendarOneMonth */ /* DoSimpleCalendarOneMonth */
/* */ /* */
/* Produce a calendar for the current month. */ /* Produce a "simple" calendar for the current month. */
/* */
/* A simple calendar is produced if the -s or -p option */
/* was used. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static void DoCalendarOneMonth(void) static void DoSimpleCalendarOneMonth(void)
{ {
int y, m, d, mm, yy, i, j; int y, m, d, mm, yy, i, j;
InitMoonsAndShades(); InitMoonsAndShades();
if (!DoSimpleCalendar) WriteCalHeader();
DidADay = 0; DidADay = 0;
if (PsCal) { if (PsCal) {
FromDSE(DSEToday, &y, &m, &d); FromDSE(DSEToday, &y, &m, &d);
if (PsCal == PSCAL_LEVEL1) { if (PsCal == PSCAL_LEVEL1) {
@@ -1037,7 +1042,7 @@ static void DoCalendarOneMonth(void)
printf("\"entries\":[\n"); printf("\"entries\":[\n");
} }
} }
while (WriteCalendarRow()) continue; while (WriteCalendarRow()) /* continue */;
if (PsCal == PSCAL_LEVEL1) { if (PsCal == PSCAL_LEVEL1) {
printf("%s\n", PSEND); printf("%s\n", PSEND);
@@ -1049,7 +1054,29 @@ static void DoCalendarOneMonth(void)
} }
printf("]\n}"); printf("]\n}");
} }
if (!DoSimpleCalendar) WriteCalTrailer(); }
/***************************************************************/
/* */
/* DoCalendarOneMonth */
/* */
/* Produce a calendar for the current month. */
/* */
/***************************************************************/
static void DoCalendarOneMonth(void)
{
InitMoonsAndShades();
if (DoSimpleCalendar) {
DoSimpleCalendarOneMonth();
return;
}
WriteCalHeader();
while (WriteCalendarRow()) /* continue */;
WriteCalTrailer();
} }
/***************************************************************/ /***************************************************************/
@@ -1121,13 +1148,17 @@ static int WriteCalendarRow(void)
if (UseVTColors) { if (UseVTColors) {
printf("\x1B[1m"); /* Bold */ printf("\x1B[1m"); /* Bold */
} }
Backgroundize(d+i-wd);
PrintLeft(buf, ColSpaces-1, '*'); PrintLeft(buf, ColSpaces-1, '*');
putchar(' ');
if (UseVTColors) { if (UseVTColors) {
printf("\x1B[0m"); /* Normal */ printf("\x1B[0m"); /* Normal */
} }
putchar(' '); UnBackgroundize(d+i-wd);
} else { } else {
Backgroundize(d+i-wd);
PrintLeft(buf, ColSpaces, ' '); PrintLeft(buf, ColSpaces, ' ');
UnBackgroundize(d+i-wd);
} }
} }
gon(); gon();
@@ -1195,15 +1226,17 @@ static void PrintLeft(char const *s, int width, char pad)
{ {
#ifndef REM_USE_WCHAR #ifndef REM_USE_WCHAR
int len = strlen(s); int len = strlen(s);
printf("%s", s); int i;
while (len++ < width) putchar(pad); for (i=0; i<len && i<width; i++) {
fputc(*(s+i), stdout);
}
while (i++ < width) putchar(pad);
#else #else
size_t len = mbstowcs(NULL, s, 0); size_t len = mbstowcs(NULL, s, 0);
int i; int i;
wchar_t static_buf[128]; wchar_t static_buf[128];
wchar_t *buf; wchar_t *buf;
wchar_t *ws; wchar_t *ws;
int display_len;
if (!len) { if (!len) {
for (i=0; i<width; i++) { for (i=0; i<width; i++) {
@@ -1222,26 +1255,32 @@ static void PrintLeft(char const *s, int width, char pad)
} }
} }
(void) mbstowcs(buf, s, len+1); (void) mbstowcs(buf, s, len+1);
display_len = wcswidth(buf, len+1);
ws = buf; ws = buf;
for (i=0; i<width;) { i=0;
while (i<width) {
if (*ws) { if (*ws) {
PutWideChar(*ws++); if (i + wcwidth(*ws) > width) {
i+= wcwidth(*ws); break;
}
i += wcwidth(*ws);
PutWideChar(*ws++, NULL);
} else { } else {
break; break;
} }
} }
/* Mop up any potential combining characters */ /* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) { while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
} }
/* Possibly send lrm control sequence */ /* Possibly send lrm control sequence */
send_lrm(); send_lrm();
for (i=display_len; i<width; i++) fputc(pad, stdout); while (i<width) {
fputc(pad, stdout);
i++;
}
if (buf != static_buf) free(buf); if (buf != static_buf) free(buf);
#endif #endif
@@ -1262,7 +1301,7 @@ static void PrintCentered(char const *s, int width, char *pad)
int i; int i;
for (i=0; i<d; i++) fputs(pad, stdout); for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) { for (i=0; i<width-d; i++) {
if (*s) { if (*s) {
if (isspace(*s)) { if (isspace(*s)) {
putchar(' '); putchar(' ');
@@ -1306,25 +1345,29 @@ static void PrintCentered(char const *s, int width, char *pad)
if (d < 0) d = 0; if (d < 0) d = 0;
ws = buf; ws = buf;
for (i=0; i<d; i++) fputs(pad, stdout); for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) { i=0;
while (i+d < width) {
if (*ws) { if (*ws) {
PutWideChar(*ws++); if (i+d + wcwidth(*ws) > width) {
if (wcwidth(*ws) == 0) { break;
/* Don't count this character... it's zero-width */
i--;
} }
i += wcwidth(*ws);
PutWideChar(*ws++, NULL);
} else { } else {
break; break;
} }
} }
/* Mop up any potential combining characters */ /* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) { while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
} }
/* Possibly send lrm control sequence */ /* Possibly send lrm control sequence */
send_lrm(); send_lrm();
for (i=d+display_len; i<width; i++) fputs(pad, stdout); while (i+d<width) {
fputs(pad, stdout);
i++;
}
if (buf != static_buf) free(buf); if (buf != static_buf) free(buf);
#endif #endif
} }
@@ -1411,7 +1454,7 @@ static int WriteOneColLine(int col)
/* Find the last space char within the column. */ /* Find the last space char within the column. */
width = 0; width = 0;
while (width <= ColSpaces) { while (width <= ColSpaces) {
if (!*ws) { if (!*ws || *ws == '\n') {
wspace = ws; wspace = ws;
break; break;
} }
@@ -1448,7 +1491,7 @@ static int WriteOneColLine(int col)
} }
numwritten += wcwidth(*ws); numwritten += wcwidth(*ws);
} }
PutWideChar(*ws); PutWideChar(*ws, NULL);
} }
} }
e->wc_pos = ws; e->wc_pos = ws;
@@ -1463,7 +1506,7 @@ static int WriteOneColLine(int col)
if (wcwidth(*ws) > 0) { if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws); numwritten += wcwidth(*ws);
} }
PutWideChar(*ws); PutWideChar(*ws, NULL);
} }
} }
} }
@@ -1517,7 +1560,7 @@ static int WriteOneColLine(int col)
/* Find the last space char within the column. */ /* Find the last space char within the column. */
while (s - e->pos <= ColSpaces) { while (s - e->pos <= ColSpaces) {
if (!*s) {space = s; break;} if (!*s || *s == '\n') {space = s; break;}
if (isspace(*s)) space = s; if (isspace(*s)) space = s;
s++; s++;
} }
@@ -1666,6 +1709,7 @@ static void GenerateCalEntries(int col)
case T_Pop: r=PopOmitContext(&p); break; case T_Pop: r=PopOmitContext(&p); break;
case T_Push: r=PushOmitContext(&p); break; case T_Push: r=PushOmitContext(&p); break;
case T_Preserve: r=DoPreserve(&p); break; case T_Preserve: r=DoPreserve(&p); break;
case T_Expr: r = DoExpr(&p); break;
case T_RemType: if (tok.val == RUN_TYPE) { case T_RemType: if (tok.val == RUN_TYPE) {
r=DoRun(&p); r=DoRun(&p);
break; break;
@@ -1679,7 +1723,12 @@ static void GenerateCalEntries(int col)
/* Note: Since the parser hasn't been used yet, we don't */ /* Note: Since the parser hasn't been used yet, we don't */
/* need to destroy it here. */ /* need to destroy it here. */
default: CreateParser(CurLine, &p); default:
if (!SuppressImplicitRemWarnings) {
Wprint("Unrecognized command; interpreting as REM");
WarnedAboutImplicit = 1;
}
CreateParser(CurLine, &p);
r=DoCalRem(&p, col); r=DoCalRem(&p, col);
break; break;
} }
@@ -1766,7 +1815,7 @@ static int DoCalRem(ParsePtr p, int col)
DBufInit(&raw_buf); DBufInit(&raw_buf);
/* Parse the trigger date and time */ /* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim, 1)) ) { if ( (r=ParseRem(p, &trig, &tim)) ) {
FreeTrig(&trig); FreeTrig(&trig);
return r; return r;
} }
@@ -2138,7 +2187,9 @@ static int DoCalRem(ParsePtr p, int col)
FreeTrig(&trig); FreeTrig(&trig);
return E_NO_MEM; return E_NO_MEM;
} }
#ifdef REM_USE_WCHAR
make_wchar_versions(e); make_wchar_versions(e);
#endif
DBufInit(&(e->tags)); DBufInit(&(e->tags));
DBufPuts(&(e->tags), DBufValue(&(trig.tags))); DBufPuts(&(e->tags), DBufValue(&(trig.tags)));
if (SynthesizeTags) { if (SynthesizeTags) {
@@ -2211,10 +2262,114 @@ static void WriteSimpleEntryProtocol1(CalEntry *e)
printf("%s\n", e->text); printf("%s\n", e->text);
} }
void WriteJSONTimeTrigger(TimeTrig const *tt)
{
PrintJSONKeyPairTime("time", tt->ttime);
PrintJSONKeyPairTime("nexttime", tt->nexttime);
PrintJSONKeyPairInt("tdelta", tt->delta);
PrintJSONKeyPairInt("trep", tt->rep);
if (tt->duration != NO_TIME) {
PrintJSONKeyPairInt("duration", tt->duration);
}
}
void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
{
/* wd is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (t->wd != NO_WD) {
printf("\"wd\":[");
int done = 0;
int i;
for (i=0; i<7; i++) {
if (t->wd & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
if (t->d != NO_DAY) {
PrintJSONKeyPairInt("d", t->d);
}
if (t->m != NO_MON) {
PrintJSONKeyPairInt("m", t->m+1);
}
if (t->y != NO_YR) {
PrintJSONKeyPairInt("y", t->y);
}
if (t->back) {
PrintJSONKeyPairInt("back", t->back);
}
if (t->delta) {
PrintJSONKeyPairInt("delta", t->delta);
}
if (t->rep) {
PrintJSONKeyPairInt("rep", t->rep);
}
/* Local omit is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (t->localomit != NO_WD) {
printf("\"localomit\":[");
int done = 0;
int i;
for (i=0; i<7; i++) {
if (t->localomit & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
switch(t->skip) {
case SKIP_SKIP:
PrintJSONKeyPairString("skip", "SKIP");
break;
case BEFORE_SKIP:
PrintJSONKeyPairString("skip", "BEFORE");
break;
case AFTER_SKIP:
PrintJSONKeyPairString("skip", "AFTER");
break;
}
PrintJSONKeyPairDate("until", t->until);
if (t->once != NO_ONCE) {
PrintJSONKeyPairInt("once", t->once);
}
if (t->scanfrom != today) {
PrintJSONKeyPairDate("scanfrom", t->scanfrom);
}
PrintJSONKeyPairDate("from", t->from);
PrintJSONKeyPairInt("priority", t->priority);
PrintJSONKeyPairDateTime("eventstart", t->eventstart);
if (t->eventduration != NO_TIME) {
PrintJSONKeyPairInt("eventduration", t->eventduration);
}
if (t->maybe_uncomputable) {
PrintJSONKeyPairInt("maybe_uncomputable", 1);
}
if (t->noqueue) {
PrintJSONKeyPairInt("noqueue", 1);
}
PrintJSONKeyPairString("sched", t->sched);
PrintJSONKeyPairString("warn", t->warn);
PrintJSONKeyPairString("omitfunc", t->omitfunc);
if (t->addomit) {
PrintJSONKeyPairInt("addomit", 1);
}
if (include_tags) {
PrintJSONKeyPairString("tags", DBufValue(&(t->tags)));
}
}
static void WriteSimpleEntryProtocol2(CalEntry *e, int today) static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
{ {
int done = 0;
char const *s; char const *s;
if (DoPrefixLineNo) { if (DoPrefixLineNo) {
PrintJSONKeyPairString("filename", e->filename); PrintJSONKeyPairString("filename", e->filename);
@@ -2234,88 +2389,13 @@ static void WriteSimpleEntryProtocol2(CalEntry *e, int today)
PrintJSONKeyPairInt("trep", e->tt.rep); PrintJSONKeyPairInt("trep", e->tt.rep);
} }
} }
if (e->trig.eventduration != NO_TIME) { WriteJSONTrigger(&e->trig, 0, today);
PrintJSONKeyPairInt("eventduration", e->trig.eventduration);
}
/* wd is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (e->trig.wd != NO_WD) {
printf("\"wd\":[");
done = 0;
int i;
for (i=0; i<7; i++) {
if (e->trig.wd & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
if (e->trig.d != NO_DAY) {
PrintJSONKeyPairInt("d", e->trig.d);
}
if (e->trig.m != NO_MON) {
PrintJSONKeyPairInt("m", e->trig.m+1);
}
if (e->trig.y != NO_YR) {
PrintJSONKeyPairInt("y", e->trig.y);
}
PrintJSONKeyPairDateTime("eventstart", e->trig.eventstart);
if (e->trig.back) {
PrintJSONKeyPairInt("back", e->trig.back);
}
if (e->trig.delta) {
PrintJSONKeyPairInt("delta", e->trig.delta);
}
if (e->trig.rep) {
PrintJSONKeyPairInt("rep", e->trig.rep);
}
if (e->nonconst_expr) { if (e->nonconst_expr) {
PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr); PrintJSONKeyPairInt("nonconst_expr", e->nonconst_expr);
} }
if (e->if_depth) { if (e->if_depth) {
PrintJSONKeyPairInt("if_depth", e->if_depth); PrintJSONKeyPairInt("if_depth", e->if_depth);
} }
switch(e->trig.skip) {
case SKIP_SKIP:
PrintJSONKeyPairString("skip", "SKIP");
break;
case BEFORE_SKIP:
PrintJSONKeyPairString("skip", "BEFORE");
break;
case AFTER_SKIP:
PrintJSONKeyPairString("skip", "AFTER");
break;
}
/* Local omit is an array of days from 0=monday to 6=sunday.
We convert to array of strings */
if (e->trig.localomit != NO_WD) {
printf("\"localomit\":[");
done = 0;
int i;
for (i=0; i<7; i++) {
if (e->trig.localomit & (1 << i)) {
if (done) {
printf(",");
}
done = 1;
printf("\"%s\"", EnglishDayName[i]);
}
}
printf("],");
}
PrintJSONKeyPairDate("until", e->trig.until);
if (e->trig.once != NO_ONCE) {
PrintJSONKeyPairInt("once", e->trig.once);
}
if (e->trig.scanfrom != today) {
PrintJSONKeyPairDate("scanfrom", e->trig.scanfrom);
}
PrintJSONKeyPairDate("from", e->trig.from);
PrintJSONKeyPairInt("priority", e->trig.priority);
if (e->is_color) { if (e->is_color) {
PrintJSONKeyPairInt("r", e->r); PrintJSONKeyPairInt("r", e->r);

View File

@@ -24,27 +24,46 @@ if (!$ARGV[0]) {
} }
my $lang = $ARGV[0]; my $lang = $ARGV[0];
if (!exists($language_map->{$lang})) { my $rc = 0;
print STDERR "$lang is not a valid language.\n"; if ($lang eq 'all') {
exit(1); foreach my $l (sort(keys(%$language_map))) {
} if (check($l)) {
$rc = 1;
my $flag = $language_map->{$lang}; }
print STDERR "Testing for: $lang - $flag.\n"; }
my_sys("make clean > /dev/null 2>&1") && die("make clean failed");
my_sys("make -j6 all LANGDEF=-DLANG=$flag > /dev/null 2>&1") && die("make all failed");
my_sys("./remind -q -r ../tests/tstlang.rem 2022-03-23 11:44 > test-$lang-compiled.out 2>&1");
my_sys("make clean > /dev/null 2>&1") && die("make clean failed");
my_sys("make -j6 all > /dev/null 2>&1") && die("make all failed");
my_sys("./remind -q -r -ii=\\\"../include/lang/$lang.rem\\\" ../tests/tstlang.rem 2022-03-23 11:44 > test-$lang-runtime.out 2>&1");
my $rc = my_sys("cmp test-$lang-compiled.out test-$lang-runtime.out > /dev/null 2>&1");
if ($rc == 0) {
print STDERR "Congrats! Compiled and runtime language output matches for $lang.\n";
} else { } else {
print STDERR "Whoops. Compiled and runtime language output differs for $lang.\n" $rc = check($lang);
} }
exit($rc);
sub check
{
my ($lang) = @_;
if (!exists($language_map->{$lang})) {
print STDERR "$lang is not a valid language.\n";
return 1;
}
my $flag = $language_map->{$lang};
print STDERR "Testing for: $lang - $flag.\n";
my_sys("make clean > /dev/null 2>&1") && die("make clean failed");
my_sys("make -j18 all LANGDEF=-DLANG=$flag > /dev/null 2>&1") && die("make all failed");
my_sys("./remind -q -r ../tests/tstlang.rem 2022-03-23 11:44 > test-$lang-compiled.out 2>&1");
my_sys("make clean > /dev/null 2>&1") && die("make clean failed");
my_sys("make -j18 all > /dev/null 2>&1") && die("make all failed");
my_sys("./remind -q -r -ii=\\\"../include/lang/$lang.rem\\\" ../tests/tstlang.rem 2022-03-23 11:44 > test-$lang-runtime.out 2>&1");
my $rc = my_sys("cmp test-$lang-compiled.out test-$lang-runtime.out > /dev/null 2>&1");
if ($rc == 0) {
print STDERR "Congrats! Compiled and runtime language output matches for $lang.\n";
} else {
print STDERR "Whoops. Compiled and runtime language output differs for $lang.\n"
}
return $rc;
}
exit(0); exit(0);
sub my_sys sub my_sys

View File

@@ -1,22 +1,30 @@
/* Define if utime(file, NULL) sets file's timestamp to the present. */
#undef HAVE_UTIME_NULL
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define if your <sys/time.h> declares struct tm. */ /* Define if your <sys/time.h> declares struct tm. */
#undef TM_IN_SYS_TIME #undef TM_IN_SYS_TIME
/* Define if you have the <sys/types.h> header file. */ /* Define if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H #undef HAVE_SYS_TYPES_H
/* Define if you have the <sys/inotify.h> header file. */
#undef HAVE_SYS_INOTIFY_H
/* Define if you have the <glob.h> header file */ /* Define if you have the <glob.h> header file */
#undef HAVE_GLOB_H #undef HAVE_GLOB_H
/* Define if you have <stdint.h> */
#undef HAVE_STDINT_H
#undef HAVE_STRINGS_H
#undef HAVE_STRDUP
#undef HAVE_STRCASECMP
#undef HAVE_STRNCASECMP
#undef HAVE_WCTYPE_H #undef HAVE_WCTYPE_H
#undef HAVE_LOCALE_H #undef HAVE_LOCALE_H
#undef HAVE_INOTIFY_INIT1
#undef HAVE_LANGINFO_H #undef HAVE_LANGINFO_H
#undef HAVE_GLOB #undef HAVE_GLOB
@@ -37,4 +45,8 @@
/* The number of bytes in a unsigned long. */ /* The number of bytes in a unsigned long. */
#undef SIZEOF_UNSIGNED_LONG #undef SIZEOF_UNSIGNED_LONG
#define PACKAGE_NAME "@PACKAGE_NAME@"
#define PACKAGE_URL "@PACKAGE_URL@"
#include "custom.h" #include "custom.h"

View File

@@ -6,7 +6,7 @@
/* which you can customize. */ /* which you can customize. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -20,7 +20,7 @@
/* western hemisphere. */ /* western hemisphere. */
/* */ /* */
/* The default values are initially set to the city hall in Ottawa, */ /* The default values are initially set to the city hall in Ottawa, */
/* Ontario, Canada. */ /* Ontario, Canada. */
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
#define DEFAULT_LATITUDE 45.420556 #define DEFAULT_LATITUDE 45.420556
#define DEFAULT_LONGITUDE -75.689722 #define DEFAULT_LONGITUDE -75.689722
@@ -68,12 +68,6 @@
/**********************************************************************/ /**********************************************************************/
/**********************************************************************/ /**********************************************************************/
/*---------------------------------------------------------------------*/
/* WANT_SHELL_ESCAPING: Define this if you want special shell */
/* characters to be escaped with a backslash for the -k option. */
/*---------------------------------------------------------------------*/
#define WANT_SHELL_ESCAPING 1
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* BASE: The base year for date calculation. NOTE! January 1 of the */ /* BASE: The base year for date calculation. NOTE! January 1 of the */
/* base year MUST be a Monday, else Remind will not work! */ /* base year MUST be a Monday, else Remind will not work! */
@@ -109,16 +103,6 @@
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
#define MAX_STR_LEN 65535 #define MAX_STR_LEN 65535
/*---------------------------------------------------------------------*/
/* OP_STACK_SIZE: The size of the operator stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define OP_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */ /* INCLUDE_NEST: How many nested INCLUDES do we handle? */
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
@@ -166,6 +150,10 @@
#define PASSTHRU_LEN 32 #define PASSTHRU_LEN 32
#define MAX_RECURSION_LEVEL 1000
#define MAX_FUNC_ARGS 64
#define PSBEGIN "# rem2ps begin" #define PSBEGIN "# rem2ps begin"
#define PSEND "# rem2ps end" #define PSEND "# rem2ps end"

View File

@@ -6,7 +6,7 @@
/* which you can customize. */ /* which you can customize. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -20,7 +20,7 @@
/* western hemisphere. */ /* western hemisphere. */
/* */ /* */
/* The default values are initially set to the city hall in Ottawa, */ /* The default values are initially set to the city hall in Ottawa, */
/* Ontario, Canada. */ /* Ontario, Canada. */
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
#define DEFAULT_LATITUDE 45.420556 #define DEFAULT_LATITUDE 45.420556
#define DEFAULT_LONGITUDE -75.689722 #define DEFAULT_LONGITUDE -75.689722
@@ -68,12 +68,6 @@
/**********************************************************************/ /**********************************************************************/
/**********************************************************************/ /**********************************************************************/
/*---------------------------------------------------------------------*/
/* WANT_SHELL_ESCAPING: Define this if you want special shell */
/* characters to be escaped with a backslash for the -k option. */
/*---------------------------------------------------------------------*/
#define WANT_SHELL_ESCAPING 1
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* BASE: The base year for date calculation. NOTE! January 1 of the */ /* BASE: The base year for date calculation. NOTE! January 1 of the */
/* base year MUST be a Monday, else Remind will not work! */ /* base year MUST be a Monday, else Remind will not work! */
@@ -109,16 +103,6 @@
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
#define MAX_STR_LEN 65535 #define MAX_STR_LEN 65535
/*---------------------------------------------------------------------*/
/* OP_STACK_SIZE: The size of the operator stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define OP_STACK_SIZE 100
/*---------------------------------------------------------------------*/
/* VAL_STACK_SIZE: The size of the operand stack for expr. parsing */
/*---------------------------------------------------------------------*/
#define VAL_STACK_SIZE 1000
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */ /* INCLUDE_NEST: How many nested INCLUDES do we handle? */
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
@@ -166,6 +150,10 @@
#define PASSTHRU_LEN 32 #define PASSTHRU_LEN 32
#define MAX_RECURSION_LEVEL 1000
#define MAX_FUNC_ARGS 64
#define PSBEGIN "# rem2ps begin" #define PSBEGIN "# rem2ps begin"
#define PSEND "# rem2ps end" #define PSEND "# rem2ps end"

View File

@@ -7,7 +7,7 @@
/* commands. */ /* commands. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -23,9 +23,8 @@
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
static int ParseTimeTrig (ParsePtr s, TimeTrig *tim, int save_in_globals); static int ParseTimeTrig (ParsePtr s, TimeTrig *tim);
static int ParseLocalOmit (ParsePtr s, Trigger *t); static int ParseLocalOmit (ParsePtr s, Trigger *t);
static int ParseScanFrom (ParsePtr s, Trigger *t, int type); static int ParseScanFrom (ParsePtr s, Trigger *t, int type);
static int ParsePriority (ParsePtr s, Trigger *t); static int ParsePriority (ParsePtr s, Trigger *t);
@@ -33,6 +32,152 @@ static int ParseUntil (ParsePtr s, Trigger *t, int type);
static int ShouldTriggerBasedOnWarn (Trigger *t, int dse, int *err); static int ShouldTriggerBasedOnWarn (Trigger *t, int dse, int *err);
static int ComputeTrigDuration(TimeTrig *t); static int ComputeTrigDuration(TimeTrig *t);
static int
ensure_expr_references_first_local_arg(expr_node *node)
{
expr_node *other;
if (!node) {
return 0;
}
if (node->type == N_LOCAL_VAR && node->u.arg == 0) {
return 1;
}
if (ensure_expr_references_first_local_arg(node->child)) {
return 1;
}
other = node->sibling;
while (other) {
if (ensure_expr_references_first_local_arg(other)) {
return 1;
}
other = other->sibling;
}
return 0;
}
static void
check_trigger_function(char const *fname, char const *type)
{
UserFunc *f;
if (!*fname) {
return;
}
f = FindUserFunc(fname);
if (!f) {
if (strcmp(type, "WARN")) {
/* Undefined WARN functions are diagnosed elsewhere... */
Wprint("Undefined %s function: `%s'", type, fname);
}
return;
}
if (f->nargs != 1) {
Wprint("%s function `%s' defined at %s:%d should take 1 argument but actually takes %d", type, fname, f->filename, f->lineno, f->nargs);
return;
}
if (ensure_expr_references_first_local_arg(f->node)) {
return;
}
Wprint("%s function `%s' defined at %s:%d does not use its argument", type, fname, f->filename, f->lineno);
}
static void
ensure_satnode_mentions_trigdate_aux(expr_node *node, int *mentioned)
{
char const *name;
expr_node *other;
UserFunc *f;
if (!node) {
return;
}
if (*mentioned) {
return;
}
if (node->type == N_BUILTIN_FUNC) {
name = node->u.builtin_func->name;
if (!strcmp(name, "trigdate") ||
!strcmp(name, "trigdatetime")) {
*mentioned = 1;
return;
}
} else if (node->type == N_SHORT_SYSVAR || node->type == N_SYSVAR) {
if (node->type == N_SHORT_SYSVAR) {
name = node->u.name;
} else {
name = node->u.value.v.str;
}
if (!StrCmpi(name, "T") ||
!StrCmpi(name, "Td") ||
!StrCmpi(name, "Tm") ||
!StrCmpi(name, "Tw") ||
!StrCmpi(name, "Ty")) {
*mentioned = 1;
return;
}
} else if (node->type == N_SHORT_USER_FUNC || node->type == N_USER_FUNC) {
if (node->type == N_SHORT_USER_FUNC) {
name = node->u.name;
} else {
name = node->u.value.v.str;
}
f = FindUserFunc(name);
if (f && !f->recurse_flag) {
f->recurse_flag = 1;
ensure_satnode_mentions_trigdate_aux(f->node, mentioned);
f->recurse_flag = 0;
if (*mentioned) {
return;
}
}
}
ensure_satnode_mentions_trigdate_aux(node->child, mentioned);
if (*mentioned) {
return;
}
other = node->sibling;
while (other) {
ensure_satnode_mentions_trigdate_aux(other, mentioned);
if (*mentioned) {
return;
}
other = other->sibling;
}
}
static void ensure_satnode_mentions_trigdate(expr_node *node)
{
int mentioned = 0;
char const *str;
if (node->type == N_CONSTANT || node->type == N_SHORT_STR) {
if (node->type == N_CONSTANT) {
if (node->u.value.type == INT_TYPE) {
if (node->u.value.v.val == 0) {
Wprint("SATISFY: constant 0 will never be true");
}
return;
}
if (node->u.value.type != STR_TYPE) {
return;
}
str = node->u.value.v.str;
} else {
str = node->u.name;
}
if (!*str) {
Wprint("SATISFY: constant \"\" will never be true");
}
return;
}
ensure_satnode_mentions_trigdate_aux(node, &mentioned);
if (!mentioned) {
Wprint("SATISFY: expression has no reference to trigdate() or $T...");
}
}
static int static int
ComputeTrigDuration(TimeTrig *t) ComputeTrigDuration(TimeTrig *t)
{ {
@@ -63,7 +208,7 @@ int DoRem(ParsePtr p)
DBufInit(&buf); DBufInit(&buf);
/* Parse the trigger date and time */ /* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim, 1)) ) { if ( (r=ParseRem(p, &trig, &tim)) != OK ) {
FreeTrig(&trig); FreeTrig(&trig);
return r; return r;
} }
@@ -163,6 +308,7 @@ int DoRem(ParsePtr p)
if (p->expr_happened) { if (p->expr_happened) {
if (p->nonconst_expr) { if (p->nonconst_expr) {
PurgeEchoLine("%s\n", "#!P: Next line may have expired, but contains non-constant expression"); PurgeEchoLine("%s\n", "#!P: Next line may have expired, but contains non-constant expression");
PurgeEchoLine("%s\n", "#!P: or a relative SCANFROM clause");
PurgeEchoLine("%s\n", CurLine); PurgeEchoLine("%s\n", CurLine);
} else { } else {
PurgeEchoLine("%s\n", "#!P: Next line has expired, but contains expression... please verify"); PurgeEchoLine("%s\n", "#!P: Next line has expired, but contains expression... please verify");
@@ -182,7 +328,7 @@ int DoRem(ParsePtr p)
if (dse == DSEToday && if (dse == DSEToday &&
!(!IgnoreOnce && !(!IgnoreOnce &&
trig.once != NO_ONCE && trig.once != NO_ONCE &&
FileAccessDate == DSEToday)) GetOnceDate() == DSEToday))
QueueReminder(p, &trig, &tim, trig.sched); QueueReminder(p, &trig, &tim, trig.sched);
/* If we're in daemon mode, do nothing over here */ /* If we're in daemon mode, do nothing over here */
if (Daemon) { if (Daemon) {
@@ -192,7 +338,7 @@ int DoRem(ParsePtr p)
r = OK; r = OK;
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) { if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
if ( (r=TriggerReminder(p, &trig, &tim, dse, 0)) ) { if ( (r=TriggerReminder(p, &trig, &tim, dse, 0, NULL)) ) {
FreeTrig(&trig); FreeTrig(&trig);
return r; return r;
} }
@@ -220,7 +366,7 @@ int DoRem(ParsePtr p)
/* trigger structure. */ /* trigger structure. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals) int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
{ {
register int r; register int r;
DynamicBuffer buf; DynamicBuffer buf;
@@ -262,10 +408,6 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
trig->need_wkday = 0; trig->need_wkday = 0;
trig->adj_for_last = 0; trig->adj_for_last = 0;
if (save_in_globals) {
LastTriggerTime = NO_TIME;
}
int parsing = 1; int parsing = 1;
while(parsing) { while(parsing) {
/* Read space-delimited string */ /* Read space-delimited string */
@@ -295,10 +437,17 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
break; break;
case T_Date: case T_Date:
DBufFree(&buf); DBufFree(&buf);
if (trig->d != NO_DAY) return E_DAY_TWICE; if (trig->d != NO_DAY) {
if (trig->m != NO_MON) return E_MON_TWICE; return E_DAY_TWICE;
if (trig->y != NO_YR) return E_YR_TWICE; }
if (trig->m != NO_MON) {
return E_MON_TWICE;
}
if (trig->y != NO_YR) {
return E_YR_TWICE;
}
FromDSE(tok.val, &y, &m, &d); FromDSE(tok.val, &y, &m, &d);
trig->y = y; trig->y = y;
trig->m = m; trig->m = m;
@@ -315,10 +464,6 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
trig->m = m; trig->m = m;
trig->d = d; trig->d = d;
tim->ttime = (tok.val % MINUTES_PER_DAY); tim->ttime = (tok.val % MINUTES_PER_DAY);
if (save_in_globals) {
LastTriggerTime = tim->ttime;
SaveLastTimeTrig(tim);
}
break; break;
case T_WkDay: case T_WkDay:
@@ -355,14 +500,14 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
DBufFree(&buf); DBufFree(&buf);
if (tim->ttime != NO_TIME) return E_TIME_TWICE; if (tim->ttime != NO_TIME) return E_TIME_TWICE;
tim->ttime = tok.val; tim->ttime = tok.val;
r = ParseTimeTrig(s, tim, save_in_globals); r = ParseTimeTrig(s, tim);
if (r) return r; if (r) return r;
trig->duration_days = ComputeTrigDuration(tim); trig->duration_days = ComputeTrigDuration(tim);
break; break;
case T_At: case T_At:
DBufFree(&buf); DBufFree(&buf);
r=ParseTimeTrig(s, tim, save_in_globals); r=ParseTimeTrig(s, tim);
if (r) return r; if (r) return r;
trig->duration_days = ComputeTrigDuration(tim); trig->duration_days = ComputeTrigDuration(tim);
break; break;
@@ -404,6 +549,12 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
if (r) return r; if (r) return r;
break; break;
case T_Number:
DBufFree(&buf);
Eprint("`%d' is not recognized as a year (%d-%d) or a day number (1-31)",
tok.val, BASE, BASE+YR_RANGE);
return E_PARSE_ERR;
case T_Year: case T_Year:
DBufFree(&buf); DBufFree(&buf);
if (trig->y != NO_YR) return E_YR_TWICE; if (trig->y != NO_YR) return E_YR_TWICE;
@@ -481,10 +632,10 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
r=ParseToken(s, &buf); r=ParseToken(s, &buf);
if (r) return r; if (r) return r;
StrnCpy(trig->omitfunc, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(trig->omitfunc, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->omitfunc);
/* An OMITFUNC counts as a nonconst_expr! */ /* An OMITFUNC counts as a nonconst_expr! */
s->expr_happened = 1; s->expr_happened = 1;
s->nonconst_expr = 1; s->nonconst_expr = 1;
DBufFree(&buf); DBufFree(&buf);
break; break;
@@ -492,6 +643,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
r=ParseToken(s, &buf); r=ParseToken(s, &buf);
if(r) return r; if(r) return r;
StrnCpy(trig->warn, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(trig->warn, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->warn);
DBufFree(&buf); DBufFree(&buf);
break; break;
@@ -522,9 +674,6 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
} else { } else {
tim->duration = NO_TIME; tim->duration = NO_TIME;
} }
if (save_in_globals) {
SaveLastTimeTrig(tim);
}
trig->duration_days = ComputeTrigDuration(tim); trig->duration_days = ComputeTrigDuration(tim);
break; break;
default: default:
@@ -536,6 +685,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
r=ParseToken(s, &buf); r=ParseToken(s, &buf);
if(r) return r; if(r) return r;
StrnCpy(trig->sched, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(trig->sched, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->sched);
DBufFree(&buf); DBufFree(&buf);
break; break;
@@ -545,10 +695,19 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
break; break;
default: default:
if (tok.type == T_Illegal && tok.val < 0) {
Eprint("%s: `%s'", ErrMsg[-tok.val], DBufValue(&buf));
DBufFree(&buf);
return -tok.val;
}
PushToken(DBufValue(&buf), s); PushToken(DBufValue(&buf), s);
DBufFree(&buf); DBufFree(&buf);
trig->typ = MSG_TYPE; trig->typ = MSG_TYPE;
if (s->isnested) return E_CANT_NEST_RTYPE; if (s->isnested) return E_CANT_NEST_RTYPE;
if (!WarnedAboutImplicit && !SuppressImplicitRemWarnings) {
Wprint("Missing REM type; assuming MSG");
WarnedAboutImplicit = 1;
}
parsing = 0; parsing = 0;
break; break;
} }
@@ -600,6 +759,11 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
trig->scanfrom = DSEToday; trig->scanfrom = DSEToday;
} }
/* Check that any SCHED / WARN / OMITFUNC functions refer to
their arguments */
check_trigger_function(trig->sched, "SCHED");
check_trigger_function(trig->warn, "WARN");
check_trigger_function(trig->omitfunc, "OMITFUNC");
return OK; return OK;
} }
@@ -608,7 +772,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
/* ParseTimeTrig - parse the AT part of a timed reminder */ /* ParseTimeTrig - parse the AT part of a timed reminder */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int ParseTimeTrig(ParsePtr s, TimeTrig *tim, int save_in_globals) static int ParseTimeTrig(ParsePtr s, TimeTrig *tim)
{ {
Token tok; Token tok;
int r; int r;
@@ -641,13 +805,13 @@ static int ParseTimeTrig(ParsePtr s, TimeTrig *tim, int save_in_globals)
break; break;
default: default:
if (tok.type == T_Illegal && tok.val < 0) {
Eprint("%s: `%s'", ErrMsg[-tok.val], DBufValue(&buf));
DBufFree(&buf);
return -tok.val;
}
if (tim->ttime == NO_TIME) return E_EXPECT_TIME; if (tim->ttime == NO_TIME) return E_EXPECT_TIME;
/* Save trigger time in global variable */
if (save_in_globals) {
LastTriggerTime = tim->ttime;
SaveLastTimeTrig(tim);
}
PushToken(DBufValue(&buf), s); PushToken(DBufValue(&buf), s);
DBufFree(&buf); DBufFree(&buf);
return OK; return OK;
@@ -679,6 +843,9 @@ static int ParseLocalOmit(ParsePtr s, Trigger *t)
break; break;
default: default:
if (t->localomit == NO_WD) {
return E_EXPECTING_WEEKDAY;
}
PushToken(DBufValue(&buf), s); PushToken(DBufValue(&buf), s);
DBufFree(&buf); DBufFree(&buf);
return OK; return OK;
@@ -760,6 +927,11 @@ static int ParseUntil(ParsePtr s, Trigger *t, int type)
break; break;
default: default:
if (tok.type == T_Illegal && tok.val < 0) {
Eprint("%s: `%s'", ErrMsg[-tok.val], DBufValue(&buf));
DBufFree(&buf);
return -tok.val;
}
if (y == NO_YR || m == NO_MON || d == NO_DAY) { if (y == NO_YR || m == NO_MON || d == NO_DAY) {
Eprint("%s: %s", which, ErrMsg[E_INCOMPLETE]); Eprint("%s: %s", which, ErrMsg[E_INCOMPLETE]);
DBufFree(&buf); DBufFree(&buf);
@@ -873,9 +1045,17 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
tok.val = -tok.val; tok.val = -tok.val;
} }
FromDSE(DSEToday - tok.val, &y, &m, &d); FromDSE(DSEToday - tok.val, &y, &m, &d);
/* Don't purge reminders with a relative scanfrom */
s->expr_happened = 1;
s->nonconst_expr = 1;
break; break;
default: default:
if (tok.type == T_Illegal && tok.val < 0) {
Eprint("%s: `%s'", ErrMsg[-tok.val], DBufValue(&buf));
DBufFree(&buf);
return -tok.val;
}
if (y == NO_YR || m == NO_MON || d == NO_DAY) { if (y == NO_YR || m == NO_MON || d == NO_DAY) {
Eprint("%s: %s", word, ErrMsg[E_INCOMPLETE]); Eprint("%s: %s", word, ErrMsg[E_INCOMPLETE]);
DBufFree(&buf); DBufFree(&buf);
@@ -910,7 +1090,7 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
/* Trigger the reminder if it's a RUN or MSG type. */ /* Trigger the reminder if it's a RUN or MSG type. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued) int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output)
{ {
int r, y, m, d; int r, y, m, d;
char PrioExpr[VAR_NAME_LEN+25]; char PrioExpr[VAR_NAME_LEN+25];
@@ -977,7 +1157,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
} }
/* If it's a MSG-type reminder, and no -k option was used, issue the banner. */ /* If it's a MSG-type reminder, and no -k option was used, issue the banner. */
if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE) if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE)
&& !DidMsgReminder && !NextMode && !msg_command) { && !DidMsgReminder && !NextMode && !msg_command && !is_queued) {
DidMsgReminder = 1; DidMsgReminder = 1;
if (!DoSubstFromString(DBufValue(&Banner), &buf, if (!DoSubstFromString(DBufValue(&Banner), &buf,
DSEToday, NO_TIME) && DSEToday, NO_TIME) &&
@@ -1044,11 +1224,18 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
return E_NO_MEM; return E_NO_MEM;
} }
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf)); r = OK;
if (output) {
if (DBufPuts(output, DBufValue(&calRow)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&pre_buf)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&buf)) != OK) r = E_NO_MEM;
} else {
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf));
}
DBufFree(&buf); DBufFree(&buf);
DBufFree(&pre_buf); DBufFree(&pre_buf);
DBufFree(&calRow); DBufFree(&calRow);
return OK; return r;
} }
/* Correct colors */ /* Correct colors */
@@ -1144,23 +1331,27 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
case MSG_TYPE: case MSG_TYPE:
case PASSTHRU_TYPE: case PASSTHRU_TYPE:
if (msg_command) { if (msg_command) {
DoMsgCommand(msg_command, DBufValue(&buf)); DoMsgCommand(msg_command, DBufValue(&buf), is_queued);
} else { } else {
/* Add a space before "NOTE endreminder" */ if (output) {
if (Daemon < 0 && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) { DBufPuts(output, DBufValue(&buf));
printf(" %s", DBufValue(&buf));
} else { } else {
printf("%s", DBufValue(&buf)); /* Add a space before "NOTE endreminder" */
if (IsServerMode() && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) {
printf(" %s", DBufValue(&buf));
} else {
printf("%s", DBufValue(&buf));
}
} }
} }
break; break;
case MSF_TYPE: case MSF_TYPE:
FillParagraph(DBufValue(&buf)); FillParagraph(DBufValue(&buf), output);
break; break;
case RUN_TYPE: case RUN_TYPE:
System(DBufValue(&buf)); System(DBufValue(&buf), is_queued);
break; break;
default: /* Unknown/illegal type? */ default: /* Unknown/illegal type? */
@@ -1188,7 +1379,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
*err = 0; *err = 0;
/* Handle the ONCE modifier in the reminder. */ /* Handle the ONCE modifier in the reminder. */
if (!IgnoreOnce && t->once !=NO_ONCE && FileAccessDate == DSEToday) if (!IgnoreOnce && t->once !=NO_ONCE && GetOnceDate() == DSEToday)
return 0; return 0;
if (dse < DSEToday) return 0; if (dse < DSEToday) return 0;
@@ -1270,20 +1461,32 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
{ {
int iter, dse, r, start; int iter, dse, r, start;
Value v; Value v;
char const *s; expr_node *sat_node;
char const *t; int nonconst = 0;
sat_node = ParseExpr(p, &r);
if (r != OK) {
return r;
}
if (!sat_node) {
return E_SWERR;
}
/* Diagnose if SAT_NODE does not reference trigdate */
ensure_satnode_mentions_trigdate(sat_node);
t = p->pos;
iter = 0; iter = 0;
start = trig->scanfrom; start = trig->scanfrom;
while (iter++ < MaxSatIter) { while (iter++ < MaxSatIter) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0); dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0);
if (r) { if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r; if (r == E_CANT_TRIG) return OK; else return r;
} }
if (dse != start && trig->duration_days) { if (dse != start && trig->duration_days) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days); dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days);
if (r) { if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r; if (r == E_CANT_TRIG) return OK; else return r;
} }
} else if (dse == start) { } else if (dse == start) {
@@ -1296,13 +1499,18 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
SaveAllTriggerInfo(trig, tt, dse, tt->ttime, 1); SaveAllTriggerInfo(trig, tt, dse, tt->ttime, 1);
} }
if (dse == -1) { if (dse == -1) {
free_expr_tree(sat_node);
return E_EXPIRED; return E_EXPIRED;
} }
s = p->pos; r = evaluate_expression(sat_node, NULL, &v, &nonconst);
r = EvaluateExpr(p, &v); if (r) {
t = p->pos; free_expr_tree(sat_node);
if (r) return r; return r;
if (v.type != INT_TYPE && v.type != STR_TYPE) return E_BAD_TYPE; }
if (v.type != INT_TYPE && v.type != STR_TYPE) {
free_expr_tree(sat_node);
return E_BAD_TYPE;
}
if ((v.type == INT_TYPE && v.v.val) || if ((v.type == INT_TYPE && v.v.val) ||
(v.type == STR_TYPE && *v.v.str)) { (v.type == STR_TYPE && *v.v.str)) {
AdjustTriggerForDuration(trig->scanfrom, dse, trig, tt, 1); AdjustTriggerForDuration(trig->scanfrom, dse, trig, tt, 1);
@@ -1327,17 +1535,17 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
} }
fprintf(ErrFp, "\n"); fprintf(ErrFp, "\n");
} }
free_expr_tree(sat_node);
return OK; return OK;
} }
p->pos = s;
if (dse+trig->duration_days < start) { if (dse+trig->duration_days < start) {
start++; start++;
} else { } else {
start = dse+trig->duration_days+1; start = dse+trig->duration_days+1;
} }
} }
p->pos = t;
LastTrigValid = 0; LastTrigValid = 0;
free_expr_tree(sat_node);
return E_CANT_TRIG; return E_CANT_TRIG;
} }
@@ -1387,7 +1595,7 @@ static int ParsePriority(ParsePtr s, Trigger *t)
/* Execute the '-k' command, escaping shell chars in message. */ /* Execute the '-k' command, escaping shell chars in message. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int DoMsgCommand(char const *cmd, char const *msg) int DoMsgCommand(char const *cmd, char const *msg, int is_queued)
{ {
int r; int r;
int i, l; int i, l;
@@ -1424,7 +1632,7 @@ int DoMsgCommand(char const *cmd, char const *msg)
} }
r = OK; r = OK;
System(DBufValue(&execBuffer)); System(DBufValue(&execBuffer), is_queued);
finished: finished:
DBufFree(&buf); DBufFree(&buf);

View File

@@ -6,21 +6,19 @@
/* reminders are triggered. */ /* reminders are triggered. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
#include "config.h" #include "config.h"
#include "expr.h" #include "types.h"
#define L_IN_DOSUBST
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include "types.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "protos.h" #include "protos.h"
@@ -33,6 +31,19 @@
#define SHIP_OUT(s) if(DBufPuts(dbuf, s) != OK) return E_NO_MEM #define SHIP_OUT(s) if(DBufPuts(dbuf, s) != OK) return E_NO_MEM
static int
check_subst_args(UserFunc *f, int n)
{
if (!f) {
return 0;
}
if (f->nargs == n) {
return 1;
}
Wprint("Function `%s' defined at %s:%d should take %d argument%s, but actually takes %d",
f->name, f->filename, f->lineno, n, (n == 1 ? "" : "s"), f->nargs);
return 0;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DoSubst */ /* DoSubst */
@@ -50,7 +61,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int err, done; int err, done;
int c; int c;
int d, m, y; int d, m, y;
int tim = tt->ttime; int tim = NO_TIME;
int h, min, hh, ch, cmin, chh; int h, min, hh, ch, cmin, chh;
int i; int i;
char const *pm, *cpm; char const *pm, *cpm;
@@ -69,9 +80,13 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int altmode; int altmode;
int r; int r;
Value v; Value v;
UserFunc *func;
FromDSE(dse, &y, &m, &d); FromDSE(dse, &y, &m, &d);
if (tt) {
tim = tt->ttime;
}
if (tim == NO_TIME) tim = curtime; if (tim == NO_TIME) tim = curtime;
tdiff = tim - curtime; tdiff = tim - curtime;
adiff = ABS(tdiff); adiff = ABS(tdiff);
@@ -99,7 +114,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
L_AMPM_OVERRIDE (pm, h) L_AMPM_OVERRIDE (pm, h)
#else #else
r = -1; r = -1;
if (UserFuncExists("subst_ampm") == 1) { func = FindUserFunc("subst_ampm");
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ampm(%d)", h); snprintf(s, sizeof(s), "subst_ampm(%d)", h);
expr = (char const *) s; expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL); r = EvalExpr(&expr, &v, NULL);
@@ -128,7 +144,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
L_AMPM_OVERRIDE (cpm, ch) L_AMPM_OVERRIDE (cpm, ch)
#else #else
r = -1; r = -1;
if (UserFuncExists("subst_ampm") == 1) { func = FindUserFunc("subst_ampm");
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ampm(%d)", ch); snprintf(s, sizeof(s), "subst_ampm(%d)", ch);
expr = (char const *) s; expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL); r = EvalExpr(&expr, &v, NULL);
@@ -139,6 +156,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else { } else {
r = -1; r = -1;
} }
DestroyValue(v);
} else { } else {
Eprint("%s", ErrMsg[r]); Eprint("%s", ErrMsg[r]);
} }
@@ -152,7 +170,8 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
#ifdef L_ORDINAL_OVERRIDE #ifdef L_ORDINAL_OVERRIDE
L_ORDINAL_OVERRIDE; L_ORDINAL_OVERRIDE;
#else #else
if (UserFuncExists("subst_ordinal") == 1) { func = FindUserFunc("subst_ordinal");
if (func && check_subst_args(func, 1)) {
snprintf(s, sizeof(s), "subst_ordinal(%d)", d); snprintf(s, sizeof(s), "subst_ordinal(%d)", d);
expr = (char const *) s; expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL); r = EvalExpr(&expr, &v, NULL);
@@ -163,6 +182,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else { } else {
r = -1; r = -1;
} }
DestroyValue(v);
} else { } else {
Eprint("%s", ErrMsg[r]); Eprint("%s", ErrMsg[r]);
} }
@@ -239,7 +259,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
break; break;
} }
if (i < 64) { if (i < 64) {
*ss++ = c; *ss++ = tolower(c);
*ss = 0; *ss = 0;
i++; i++;
} }
@@ -247,7 +267,13 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
if (!c) { if (!c) {
Wprint("Warning: Unterminated %%{...} substitution sequence"); Wprint("Warning: Unterminated %%{...} substitution sequence");
} }
if (UserFuncExists(s) != 3) { func = FindUserFunc(s);
if (!func) {
Wprint("No substition function `%s' defined", s);
continue;
}
if (!check_subst_args(func, 3)) {
continue; continue;
} }
snprintf(ss, sizeof(s) - (ss-s), "(%d,'%04d-%02d-%02d',%02d:%02d)", snprintf(ss, sizeof(s) - (ss-s), "(%d,'%04d-%02d-%02d',%02d:%02d)",
@@ -266,10 +292,11 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
continue; continue;
} }
done = 0; done = 0;
snprintf(uf, sizeof(uf), "subst_%c", c); snprintf(uf, sizeof(uf), "subst_%c", tolower(c));
if (UserFuncExists(uf) == 3) { func = FindUserFunc(uf);
if (func && check_subst_args(func, 3)) {
snprintf(s, sizeof(s), "subst_%c(%d,'%04d-%02d-%02d',%02d:%02d)", snprintf(s, sizeof(s), "subst_%c(%d,'%04d-%02d-%02d',%02d:%02d)",
c, altmode ? 1 : 0, y, m+1, d, h, min); tolower(c), altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s; expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL); r = EvalExpr(&expr, &v, NULL);
if (r == OK) { if (r == OK) {
@@ -341,10 +368,11 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
if (!done) { if (!done) {
snprintf(uf, sizeof(uf), "subst_%cx", c); snprintf(uf, sizeof(uf), "subst_%cx", tolower(c));
if (UserFuncExists(uf) == 3) { func = FindUserFunc(uf);
if (func && check_subst_args(func, 3)) {
snprintf(s, sizeof(s), "subst_%cx(%d,'%04d-%02d-%02d',%02d:%02d)", snprintf(s, sizeof(s), "subst_%cx(%d,'%04d-%02d-%02d',%02d:%02d)",
c, altmode ? 1 : 0, y, m+1, d, h, min); tolower(c), altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s; expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL); r = EvalExpr(&expr, &v, NULL);
if (r == OK) { if (r == OK) {
@@ -794,7 +822,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
break; break;
case '_': case '_':
if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != CAL_MODE && mode != ADVANCE_MODE && !(MsgCommand && *MsgCommand))) { if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || DoCalendar || (mode != CAL_MODE && mode != ADVANCE_MODE && !(MsgCommand && *MsgCommand))) {
snprintf(s, sizeof(s), "%s", NL); snprintf(s, sizeof(s), "%s", NL);
} else { } else {
snprintf(s, sizeof(s), " "); snprintf(s, sizeof(s), " ");

View File

@@ -6,7 +6,7 @@
/* buffers. */ /* buffers. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -124,7 +124,9 @@ int DBufPuts(DynamicBuffer *dbuf, char const *str)
**********************************************************************/ **********************************************************************/
void DBufFree(DynamicBuffer *dbuf) void DBufFree(DynamicBuffer *dbuf)
{ {
if (dbuf->buffer != dbuf->staticBuf) free(dbuf->buffer); if (dbuf->buffer != NULL && dbuf->buffer != dbuf->staticBuf) {
free(dbuf->buffer);
}
DBufInit(dbuf); DBufInit(dbuf);
} }
@@ -150,7 +152,6 @@ int DBufGets(DynamicBuffer *dbuf, FILE *fp)
/* Try reading the first few bytes right into the buffer -- /* Try reading the first few bytes right into the buffer --
we can usually save some unnecessary copying */ we can usually save some unnecessary copying */
*(dbuf->buffer) = 0;
if (fgets(dbuf->buffer, dbuf->allocatedLen, fp) == NULL) { if (fgets(dbuf->buffer, dbuf->allocatedLen, fp) == NULL) {
return OK; return OK;
} }

View File

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

223
src/err.h
View File

@@ -5,7 +5,7 @@
/* Error definitions. */ /* Error definitions. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -121,6 +121,11 @@
#define E_STRING_TOO_LONG 101 #define E_STRING_TOO_LONG 101
#define E_TIME_TWICE 102 #define E_TIME_TWICE 102
#define E_DURATION_NO_AT 103 #define E_DURATION_NO_AT 103
#define E_EXPECTING_WEEKDAY 104
#define E_REPEATED_ARG 105
#define E_EXPR_DISABLED 106
#define E_TIME_EXCEEDED 107
#ifdef MK_GLOBALS #ifdef MK_GLOBALS
#undef EXTERN #undef EXTERN
#define EXTERN #define EXTERN
@@ -129,115 +134,123 @@
#define EXTERN extern #define EXTERN extern
#endif #endif
#define STR(X) STR2(X)
#define STR2(X) #X
#ifndef L_ERR_OVERRIDE #ifndef L_ERR_OVERRIDE
EXTERN char *ErrMsg[] EXTERN char *ErrMsg[]
#ifdef MK_GLOBALS #ifdef MK_GLOBALS
= { = {
"Ok", /* OK */ "Ok",
"Missing ']'", /* E_MISS_END */ "Missing ']'",
"Missing quote", /* E_MISS_QUOTE */ "Missing quote",
"Expression too complex - too many operators", /* E_OP_STK_OVER */ "Expression too complex",
"Expression too complex - too many operands", /* E_VA_STK_OVER */ "Expression too complex - too many operands",
"Missing ')'", /* E_MISS_RIGHT_PAREN */ "Missing ')'",
"Undefined function", /* E_UNDEF_FUNC */ "Undefined function",
"Illegal character", /* E_ILLEGAL_CHAR */ "Illegal character",
"Expecting binary operator", /* E_EXPECTING_BINOP */ "Expecting binary operator",
"Out of memory", /* E_NO_MEM */ "Out of memory",
"Ill-formed number", /* E_BAD_NUMBER */ "Ill-formed number",
"Op stack underflow - internal error", /* E_OP_STK_UNDER */ "Op stack underflow - internal error",
"Va stack underflow - internal error", /* E_VA_STK_UNDER */ "Va stack underflow - internal error",
"Can't coerce", /* E_CANT_COERCE */ "Can't coerce",
"Type mismatch", /* E_BAD_TYPE */ "Type mismatch",
"Date overflow", /* E_DATE_OVER */ "Date overflow",
"Stack error - internal error", /* E_STACK_ERR */ "Stack error - internal error",
"Division by zero", /* E_DIV_ZERO */ "Division by zero",
"Undefined variable", /* E_NOSUCH_VAR */ "Undefined variable",
"Unexpected end of line", /* E_EOLN */ "Unexpected end of line",
"Unexpected end of file", /* E_EOF */ "Unexpected end of file",
"I/O error", /* E_IO_ERR */ "I/O error",
"Line too long", /* E_LINE_2_LONG */ "Line too long",
"Internal error", /* E_SWERR */ "Internal error",
"Bad date specification", /* E_BAD_DATE */ "Bad date specification",
"Not enough arguments", /* E_2FEW_ARGS */ "Not enough arguments",
"Too many arguments", /* E_2MANY_ARGS */ "Too many arguments",
"Ill-formed time", /* E_BAD_TIME */ "Ill-formed time",
"Number too high", /* E_2HIGH */ "Number too high",
"Number too low", /* E_2LOW */ "Number too low",
"Can't open file", /* E_CANT_OPEN */ "Can't open file",
"INCLUDE nested too deeply", /* E_NESTED_INCLUDE */ "INCLUDE nested too deeply (max. " STR(INCLUDE_NEST) ")",
"Parse error", /* E_PARSE_ERR */ "Parse error",
"Can't compute trigger", /* E_CANT_TRIG */ "Can't compute trigger",
"Too many nested IFs", /* E_NESTED_IF */ "Too many nested IFs",
"ELSE with no matching IF", /* E_ELSE_NO_IF */ "ELSE with no matching IF",
"ENDIF with no matching IF", /* E_ENDIF_NO_IF */ "ENDIF with no matching IF",
"Can't OMIT every weekday", /* E_2MANY_LOCALOMIT */ "Can't OMIT every weekday",
"Extraneous token(s) on line", /* E_EXTRANEOUS_TOKEN */ "Extraneous token(s) on line",
"POP-OMIT-CONTEXT without matching PUSH-OMIT-CONTEXT", /* E_POP_NO_PUSH */ "POP-OMIT-CONTEXT without matching PUSH-OMIT-CONTEXT",
"RUN disabled", /* E_RUN_DISABLED */ "RUN disabled",
"Domain error", /* E_DOMAIN_ERR */ "Domain error",
"Invalid identifier", /* E_BAD_ID */ "Invalid identifier",
"Recursive function call detected", /* E_RECURSIVE */ "Too many recursive function calls",
"", /* E_PARSE_AS_REM */ "",
"Cannot modify system variable", /* E_CANT_MODIFY */ "Cannot modify system variable",
"C library function can't represent date/time", /* E_MKTIME_PROBLEM */ "C library function can't represent date/time",
"Attempt to redefine built-in function", /* E_REDEF_FUNC */ "Attempt to redefine built-in function",
"Can't nest function definition in expression", /* E_CANTNEST_FDEF */ "Can't nest function definition in expression",
"Must fully specify date to use repeat factor", /* E_REP_FULSPEC */ "Must fully specify date to use repeat factor",
"Year specified twice", /* E_YR_TWICE */ "Year specified twice",
"Month specified twice", /* E_MON_TWICE */ "Month specified twice",
"Day specified twice", /* E_DAY_TWICE */ "Day specified twice",
"Unknown token", /* E_UNKNOWN_TOKEN */ "Unknown token",
"Must specify month in OMIT command", /* E_SPEC_MON */ "Must specify month in OMIT command",
"Too many partial OMITs", /* E_2MANY_PART */ "Too many partial OMITs (max. " STR(MAX_PARTIAL_OMITS) ")",
"Too many full OMITs", /* E_2MANY_FULL */ "Too many full OMITs (max. " STR(MAX_FULL_OMITS) ")",
"Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT", /* E_PUSH_NOPOP */ "Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT",
"Error reading", /* E_ERR_READING */ "Error reading",
"Expecting end-of-line", /* E_EXPECTING_EOL */ "Expecting end-of-line",
"Invalid Hebrew date", /* E_BAD_HEBDATE */ "Invalid Hebrew date",
"IIF needs odd number of arguments", /* E_IIF_ODD */ "iif(): odd number of arguments required",
"Warning: Missing ENDIF", /* E_MISS_ENDIF */ "Warning: Missing ENDIF",
"Expecting comma", /* E_EXPECT_COMMA */ "Expecting comma",
"Weekday specified twice", /* E_WD_TWICE */ "Weekday specified twice",
"Only use one of BEFORE, AFTER or SKIP", /* E_SKIP_ERR */ "Only use one of BEFORE, AFTER or SKIP",
"Can't nest MSG, MSF, RUN, etc. in expression", /* E_CANT_NEST_RTYPE */ "Can't nest MSG, MSF, RUN, etc. in expression",
"Repeat value specified twice", /* E_REP_TWICE */ "Repeat value specified twice",
"Delta value specified twice", /* E_DELTA_TWICE */ "Delta value specified twice",
"Back value specified twice", /* E_BACK_TWICE */ "Back value specified twice",
"ONCE keyword used twice. (Hah.)", /* E_ONCE_TWICE */ "ONCE keyword used twice. (Hah.)",
"Expecting time after AT", /* E_EXPECT_TIME */ "Expecting time after AT",
"THROUGH/UNTIL keyword used twice", /* E_UNTIL_TWICE */ "THROUGH/UNTIL keyword used twice",
"Incomplete date specification", /* E_INCOMPLETE */ "Incomplete date specification",
"FROM/SCANFROM keyword used twice", /* E_SCAN_TWICE */ "FROM/SCANFROM keyword used twice",
"Variable", /* E_VAR */ "Variable",
"Value", /* E_VAL */ "Value",
"*UNDEFINED*", /* E_UNDEF */ "*UNDEFINED*",
"Entering UserFN", /* E_ENTER_FUN */ "Entering UserFN",
"Leaving UserFN", /* E_LEAVE_FUN */ "Leaving UserFN",
"Expired", /* E_EXPIRED */ "Expired",
"fork() failed - can't do queued reminders", /* E_CANTFORK */ "fork() failed - can't do queued reminders",
"Can't access file", /* E_CANTACCESS */ "Can't access file",
"Illegal system date: Year is less than %d\n", /* M_BAD_SYS_DATE */ "Illegal system date: Year is less than %d\n",
"Unknown debug flag '%c'\n", /* M_BAD_DB_FLAG */ "Unknown debug flag '%c'\n",
"Unknown option '%c'\n", /* M_BAD_OPTION */ "Unknown option '%c'\n",
"Unknown user '%s'\n", /* M_BAD_USER */ "Unknown user '%s'\n",
"Could not change gid to %d\n", /* M_NO_CHG_GID */ "Could not change gid to %d\n",
"Could not change uid to %d\n", /* M_NO_CHG_UID */ "Could not change uid to %d\n",
"Out of memory for environment\n", /* M_NOMEM_ENV */ "Out of memory for environment\n",
"Missing '=' sign", /* E_MISS_EQ */ "Missing '=' sign",
"Missing variable name", /* E_MISS_VAR */ "Missing variable name",
"Missing expression", /* E_MISS_EXPR */ "Missing expression",
"Can't reset access date of %s\n", /* M_CANTSET_ACCESS */ "Can't reset access date of %s\n",
"Remind: '-i' option: %s\n", /* M_I_OPTION */ "Remind: '-i' option: %s\n",
"No reminders.", /* E_NOREMINDERS */ "No reminders.",
"%d reminder(s) queued for later today.\n", /* M_QUEUED */ "%d reminder(s) queued for later today.\n",
"Expecting number", /* E_EXPECTING_NUMBER */ "Expecting number",
"Bad function in WARN clause", /* M_BAD_WARN_FUNC */ "Undefined WARN function",
"Can't convert between time zones", /* E_CANT_CONVERT_TZ */ "Can't convert between time zones",
"No files matching *.rem", /* E_NO_MATCHING_REMS */ "No files matching *.rem",
"String too long", /* E_STRING_TOO_LONG */ "String too long",
"Time specified twice", /* E_TIME_TWICE */ "Time specified twice",
"Cannot specify DURATION without specifying AT" /* E_DURATION_NO_AT */ "Cannot specify DURATION without specifying AT",
/* E_EXPECTING_WEEKDAY */ "Expecting weekday name",
/* E_REPEATED_ARG */ "Duplicate argument name",
/* E_EXPR_DISABLED */ "Expression evaluation is disabled",
/* E_TIME_EXCEEDED */ "Time limit for expression evaluation exceeded",
} }
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
; ;

3680
src/expr.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,65 +0,0 @@
/***************************************************************/
/* */
/* EXPR.H */
/* */
/* Contains a few definitions used by expression evaluator. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2023 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 stuff for parsing expressions */
#define BEG_OF_EXPR '['
#define END_OF_EXPR ']'
#define COMMA ','
#define UN_OP 0 /* Unary operator */
#define BIN_OP 1 /* Binary Operator */
#define FUNC 2 /* Function */
/* Make the pushing and popping of values and operators in-line code
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)
#define PopOpStack(op) \
if (OpStackPtr <= 0) \
return E_OP_STK_UNDER; \
else \
(op) = OpStack[--OpStackPtr]
#define PushValStack(val) \
if (ValStackPtr >= VAL_STACK_SIZE) \
return E_VA_STK_OVER; \
else \
ValStack[ValStackPtr++] = (val)
#define PopValStack(val) \
if (ValStackPtr <= 0) \
return E_VA_STK_UNDER; \
else \
(val) = ValStack[--ValStackPtr]
/* These functions are in utils.c and are used to detect overflow
in various arithmetic operators. They have to be in separate
functions with extern linkage to defeat compiler optimizations
that would otherwise break the overflow checks. */
extern int _private_mul_overflow(int a, int b);
extern int _private_add_overflow(int a, int b);
extern int _private_sub_overflow(int a, int b);

View File

@@ -7,7 +7,7 @@
/* files. */ /* files. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -15,7 +15,7 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <fcntl.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
@@ -42,8 +42,8 @@
/* Convenient macros for closing files */ /* Convenient macros for closing files */
#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL)) #define FCLOSE(fp) ((((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
#define PCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL)) #define PCLOSE(fp) ((((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
/* Define the structures needed by the file caching system */ /* Define the structures needed by the file caching system */
typedef struct cache { typedef struct cache {
@@ -79,6 +79,7 @@ typedef struct {
int LineNo; int LineNo;
unsigned int IfFlags; unsigned int IfFlags;
int NumIfs; int NumIfs;
int IfLinenos[IF_NEST];
long offset; long offset;
CachedLine *CLine; CachedLine *CLine;
int ownedByMe; int ownedByMe;
@@ -100,6 +101,29 @@ static int CheckSafety (void);
static int CheckSafetyAux (struct stat *statbuf); static int CheckSafetyAux (struct stat *statbuf);
static int PopFile (void); static int PopFile (void);
static int IncludeCmd(char const *); static int IncludeCmd(char const *);
static void
got_a_fresh_line(void)
{
FreshLine = 1;
WarnedAboutImplicit = 0;
}
void set_cloexec(FILE *fp)
{
int flags;
int fd;
if (fp) {
fd = fileno(fp);
flags = fcntl(fd, F_GETFD);
if (flags >= 0) {
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
}
}
}
static void OpenPurgeFile(char const *fname, char const *mode) static void OpenPurgeFile(char const *fname, char const *mode)
{ {
DynamicBuffer fname_buf; DynamicBuffer fname_buf;
@@ -123,6 +147,7 @@ static void OpenPurgeFile(char const *fname, char const *mode)
if (!PurgeFP) { if (!PurgeFP) {
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno)); fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
} }
set_cloexec(PurgeFP);
DBufFree(&fname_buf); DBufFree(&fname_buf);
} }
@@ -164,7 +189,7 @@ int ReadLine(void)
CurLine = CLine->text; CurLine = CLine->text;
LineNo = CLine->LineNo; LineNo = CLine->LineNo;
CLine = CLine->next; CLine = CLine->next;
FreshLine = 1; got_a_fresh_line();
clear_callstack(); clear_callstack();
if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp); if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp);
return OK; return OK;
@@ -265,7 +290,7 @@ static int ReadLineFromFile(int use_pclose)
CurLine = DBufValue(&LineBuffer); CurLine = DBufValue(&LineBuffer);
} }
FreshLine = 1; got_a_fresh_line();
clear_callstack(); clear_callstack();
if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp); if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp);
return OK; return OK;
@@ -327,6 +352,7 @@ int OpenFile(char const *fname)
} }
} else { } else {
fp = fopen(fname, "r"); fp = fopen(fname, "r");
set_cloexec(fp);
if (DebugFlag & DB_TRACE_FILES) { if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname); fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
} }
@@ -346,6 +372,7 @@ int OpenFile(char const *fname)
if (strcmp(fname, "-")) { if (strcmp(fname, "-")) {
fp = fopen(fname, "r"); fp = fopen(fname, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN; if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fp);
if (PurgeMode) OpenPurgeFile(fname, "w"); if (PurgeMode) OpenPurgeFile(fname, "w");
} else { } else {
fp = stdin; fp = stdin;
@@ -507,8 +534,14 @@ static int NextChainedFile(IncludeStruct *i)
static int PopFile(void) static int PopFile(void)
{ {
IncludeStruct *i; IncludeStruct *i;
int j;
if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]); if (!Hush && NumIfs) {
Eprint("%s", ErrMsg[E_MISS_ENDIF]);
for (j=NumIfs-1; j >=0; j--) {
fprintf(ErrFp, "%s(%d): IF without ENDIF\n", FileName, IfLinenos[j]);
}
}
if (!IStackPtr) return E_EOF; if (!IStackPtr) return E_EOF;
i = &IStack[IStackPtr-1]; i = &IStack[IStackPtr-1];
@@ -528,6 +561,7 @@ static int PopFile(void)
LineNo = i->LineNo; LineNo = i->LineNo;
IfFlags = i->IfFlags; IfFlags = i->IfFlags;
memcpy(IfLinenos, i->IfLinenos, IF_NEST);
NumIfs = i->NumIfs; NumIfs = i->NumIfs;
CLine = i->CLine; CLine = i->CLine;
fp = NULL; fp = NULL;
@@ -542,6 +576,7 @@ static int PopFile(void)
if (strcmp(i->filename, "-")) { if (strcmp(i->filename, "-")) {
fp = fopen(i->filename, "r"); fp = fopen(i->filename, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN; if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fp);
if (PurgeMode) OpenPurgeFile(i->filename, "a"); if (PurgeMode) OpenPurgeFile(i->filename, "a");
} else { } else {
fp = stdin; fp = stdin;
@@ -821,7 +856,7 @@ static int IncludeCmd(char const *cmd)
char const *fname; char const *fname;
int old_flag; int old_flag;
FreshLine = 1; got_a_fresh_line();
clear_callstack(); clear_callstack();
if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE; if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE;
i = &IStack[IStackPtr]; i = &IStack[IStackPtr];
@@ -851,6 +886,7 @@ static int IncludeCmd(char const *cmd)
i->LineNo = LineNo; i->LineNo = LineNo;
i->NumIfs = NumIfs; i->NumIfs = NumIfs;
i->IfFlags = IfFlags; i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
i->CLine = CLine; i->CLine = CLine;
i->offset = -1L; i->offset = -1L;
i->chain = NULL; i->chain = NULL;
@@ -939,7 +975,7 @@ int IncludeFile(char const *fname)
int oldRunDisabled; int oldRunDisabled;
struct stat statbuf; struct stat statbuf;
FreshLine = 1; got_a_fresh_line();
clear_callstack(); clear_callstack();
if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE; if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE;
i = &IStack[IStackPtr]; i = &IStack[IStackPtr];
@@ -953,6 +989,7 @@ int IncludeFile(char const *fname)
i->LineNo = LineNo; i->LineNo = LineNo;
i->NumIfs = NumIfs; i->NumIfs = NumIfs;
i->IfFlags = IfFlags; i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
i->CLine = CLine; i->CLine = CLine;
i->offset = -1L; i->offset = -1L;
i->chain = NULL; i->chain = NULL;

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -23,6 +23,7 @@
#define INIT(var, val) var #define INIT(var, val) var
#endif #endif
#include <signal.h>
#ifdef HAVE_SYS_TYPES_H #ifdef HAVE_SYS_TYPES_H
#include <sys/types.h> #include <sys/types.h>
#endif #endif
@@ -39,7 +40,7 @@ EXTERN FILE *ErrFp;
#define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 )) #define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 ))
#define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y)) #define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y))
#define DestroyValue(x) (void) (((x).type == STR_TYPE && (x).v.str) ? (free((x).v.str),(x).v.str = NULL,(x).type = ERR_TYPE) : 0) #define DestroyValue(x) do { if ((x).type == STR_TYPE && (x).v.str) { free((x).v.str); (x).v.str = NULL; } (x).type = ERR_TYPE; } while (0)
EXTERN int DSEToday; EXTERN int DSEToday;
EXTERN int RealToday; EXTERN int RealToday;
@@ -48,6 +49,7 @@ EXTERN int CurMon;
EXTERN int CurYear; EXTERN int CurYear;
EXTERN int LineNo; EXTERN int LineNo;
EXTERN int FreshLine; EXTERN int FreshLine;
EXTERN int WarnedAboutImplicit;
EXTERN uid_t TrustedUsers[MAX_TRUSTED_USERS]; EXTERN uid_t TrustedUsers[MAX_TRUSTED_USERS];
EXTERN INIT( int MaxLateMinutes, 0); EXTERN INIT( int MaxLateMinutes, 0);
@@ -73,13 +75,19 @@ EXTERN INIT( int InfiniteDelta, 0);
EXTERN INIT( int DefaultTDelta, 0); EXTERN INIT( int DefaultTDelta, 0);
EXTERN INIT( int DeltaOverride, 0); EXTERN INIT( int DeltaOverride, 0);
EXTERN INIT( int RunDisabled, 0); EXTERN INIT( int RunDisabled, 0);
EXTERN INIT( int ExpressionEvaluationDisabled, 0);
EXTERN INIT( int ExpressionEvaluationTimeLimit, 0);
EXTERN INIT( volatile sig_atomic_t ExpressionTimeLimitExceeded, 0);
EXTERN INIT( int IgnoreOnce, 0); EXTERN INIT( int IgnoreOnce, 0);
EXTERN INIT( int SortByTime, 0); EXTERN INIT( char const *OnceFile, NULL);
EXTERN INIT( int SortByDate, 0); EXTERN INIT( int OnceDate, -1);
EXTERN INIT( int SortByPrio, 0); EXTERN INIT( int ProcessedOnce, 0);
EXTERN INIT( int SortByTime, SORT_NONE);
EXTERN INIT( int SortByDate, SORT_NONE);
EXTERN INIT( int SortByPrio, SORT_NONE);
EXTERN INIT( int UntimedBeforeTimed, 0); EXTERN INIT( int UntimedBeforeTimed, 0);
EXTERN INIT( int DefaultPrio, NO_PRIORITY); EXTERN INIT( int DefaultPrio, NO_PRIORITY);
EXTERN INIT( long SysTime, -1L); EXTERN INIT( int SysTime, -1);
EXTERN INIT( int ParseUntriggered, 1); EXTERN INIT( int ParseUntriggered, 1);
EXTERN char const *InitialFile; EXTERN char const *InitialFile;
@@ -92,6 +100,7 @@ EXTERN INIT( int DontQueue, 0);
EXTERN INIT( int NumQueued, 0); EXTERN INIT( int NumQueued, 0);
EXTERN INIT( int DontIssueAts, 0); EXTERN INIT( int DontIssueAts, 0);
EXTERN INIT( int Daemon, 0); EXTERN INIT( int Daemon, 0);
EXTERN INIT( int DaemonJSON, 0);
EXTERN INIT( char DateSep, DATESEP); EXTERN INIT( char DateSep, DATESEP);
EXTERN INIT( char TimeSep, TIMESEP); EXTERN INIT( char TimeSep, TIMESEP);
EXTERN INIT( char DateTimeSep, DATETIMESEP); EXTERN INIT( char DateTimeSep, DATETIMESEP);
@@ -102,18 +111,19 @@ EXTERN INIT( int SynthesizeTags, 0);
EXTERN INIT( int ScFormat, SC_AMPM); EXTERN INIT( int ScFormat, SC_AMPM);
EXTERN INIT( int MaxSatIter, 1000); EXTERN INIT( int MaxSatIter, 1000);
EXTERN INIT( int MaxStringLen, MAX_STR_LEN); EXTERN INIT( int MaxStringLen, MAX_STR_LEN);
EXTERN INIT( char *FileName, NULL); EXTERN INIT( char *FileName, NULL);
EXTERN INIT( int UseStdin, 0); EXTERN INIT( int UseStdin, 0);
EXTERN INIT( int PurgeMode, 0); EXTERN INIT( int PurgeMode, 0);
EXTERN INIT( int PurgeIncludeDepth, 0); EXTERN INIT( int PurgeIncludeDepth, 0);
EXTERN INIT( FILE *PurgeFP, NULL); EXTERN INIT( FILE *PurgeFP, NULL);
EXTERN INIT( int NumIfs, 0); EXTERN INIT( int NumIfs, 0);
EXTERN INIT( unsigned int IfFlags, 0); EXTERN INIT( unsigned int IfFlags, 0);
EXTERN INIT( int IfLinenos[IF_NEST], {0});
EXTERN INIT( int LastTrigValid, 0); EXTERN INIT( int LastTrigValid, 0);
EXTERN Trigger LastTrigger; EXTERN Trigger LastTrigger;
EXTERN TimeTrig LastTimeTrig; EXTERN TimeTrig LastTimeTrig;
EXTERN INIT( int LastTriggerDate, 0); EXTERN INIT( int LastTriggerDate, 0);
EXTERN INIT( int LastTriggerTime, 0); EXTERN INIT( int LastTriggerTime, NO_TIME);
EXTERN INIT( int ShouldCache, 0); EXTERN INIT( int ShouldCache, 0);
EXTERN char const *CurLine; EXTERN char const *CurLine;
EXTERN INIT( int NumTriggered, 0); EXTERN INIT( int NumTriggered, 0);
@@ -157,6 +167,15 @@ EXTERN INIT( char *EndSentIg, "\"')]}>");
EXTERN DynamicBuffer Banner; EXTERN DynamicBuffer Banner;
EXTERN DynamicBuffer LineBuffer; EXTERN DynamicBuffer LineBuffer;
EXTERN DynamicBuffer ExprBuf; EXTERN DynamicBuffer ExprBuf;
/* User-func recursion level */
EXTERN INIT( unsigned int FuncRecursionLevel, 0);
/* Suppress warnings about implicit REM and MSG */
EXTERN INIT( int SuppressImplicitRemWarnings, 0);
extern int NumFullOmits, NumPartialOmits;
/* List of months */ /* List of months */
EXTERN char *EnglishMonthName[] EXTERN char *EnglishMonthName[]
#ifdef MK_GLOBALS #ifdef MK_GLOBALS

View File

@@ -5,7 +5,7 @@
/* Support for the Hebrew calendar */ /* Support for the Hebrew calendar */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/* Derived from code written by Amos Shapir in 1978; revised */ /* Derived from code written by Amos Shapir in 1978; revised */
@@ -20,6 +20,12 @@
#include "protos.h" #include "protos.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#define HOUR 1080L #define HOUR 1080L
#define DAY (24L*HOUR) #define DAY (24L*HOUR)
#define WEEK (7L*DAY) #define WEEK (7L*DAY)

View File

@@ -7,7 +7,7 @@
/* in normal mode. */ /* in normal mode. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -19,7 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <time.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -38,7 +38,6 @@
#include "types.h" #include "types.h"
#include "globals.h" #include "globals.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#include "err.h" #include "err.h"
static int should_guess_terminal_background = 1; static int should_guess_terminal_background = 1;
@@ -78,6 +77,7 @@ static void ProcessLongOption(char const *arg);
* t = Display trigger dates * t = Display trigger dates
* v = Dump variables at end * v = Dump variables at end
* l = Display entire line in error messages * l = Display entire line in error messages
* s = Display expression-parsing stack usage before exit
* -e = Send messages normally sent to stderr to stdout instead * -e = Send messages normally sent to stderr to stdout instead
* -z[n] = Daemon mode waking up every n (def 1) minutes. * -z[n] = Daemon mode waking up every n (def 1) minutes.
* -bn = Time format for cal (0, 1, or 2) * -bn = Time format for cal (0, 1, or 2)
@@ -176,7 +176,6 @@ void InitRemind(int argc, char const *argv[])
int x; int x;
int dse; int dse;
int ttyfd; int ttyfd;
int r, g, b;
dse = NO_DATE; dse = NO_DATE;
@@ -242,7 +241,6 @@ void InitRemind(int argc, char const *argv[])
arg++; arg++;
if (!*arg) { if (!*arg) {
UseStdin = 1; UseStdin = 1;
IgnoreOnce = 1;
i--; i--;
break; break;
} }
@@ -325,6 +323,7 @@ void InitRemind(int argc, char const *argv[])
NextMode = 1; NextMode = 1;
DontQueue = 1; DontQueue = 1;
Daemon = 0; Daemon = 0;
IgnoreOnce = 1;
break; break;
case 'r': case 'r':
@@ -426,7 +425,11 @@ void InitRemind(int argc, char const *argv[])
case 'z': case 'z':
case 'Z': case 'Z':
DontFork = 1; DontFork = 1;
if (*arg == '0') { if (*arg == 'j' || *arg == 'J') {
while (*arg) arg++;
Daemon = -1;
DaemonJSON = 1;
} else if (*arg == '0') {
PARSENUM(Daemon, arg); PARSENUM(Daemon, arg);
if (Daemon == 0) Daemon = -1; if (Daemon == 0) Daemon = -1;
else if (Daemon < 1) Daemon = 1; else if (Daemon < 1) Daemon = 1;
@@ -454,6 +457,7 @@ void InitRemind(int argc, char const *argv[])
break; break;
case 'c': case 'c':
case 'C': case 'C':
IgnoreOnce = 1;
DoCalendar = 1; DoCalendar = 1;
weeks = 0; weeks = 0;
/* Parse the flags */ /* Parse the flags */
@@ -498,6 +502,7 @@ void InitRemind(int argc, char const *argv[])
case 's': case 's':
case 'S': case 'S':
DoSimpleCalendar = 1; DoSimpleCalendar = 1;
IgnoreOnce = 1;
weeks = 0; weeks = 0;
while(*arg) { while(*arg) {
if (*arg == 'a' || *arg == 'A') { if (*arg == 'a' || *arg == 'A') {
@@ -524,6 +529,7 @@ void InitRemind(int argc, char const *argv[])
case 'p': case 'p':
case 'P': case 'P':
DoSimpleCalendar = 1; DoSimpleCalendar = 1;
IgnoreOnce = 1;
PsCal = PSCAL_LEVEL1; PsCal = PSCAL_LEVEL1;
while (*arg == 'a' || *arg == 'A' || while (*arg == 'a' || *arg == 'A' ||
*arg == 'q' || *arg == 'Q' || *arg == 'q' || *arg == 'Q' ||
@@ -601,6 +607,7 @@ void InitRemind(int argc, char const *argv[])
case 'D': case 'D':
while (*arg) { while (*arg) {
switch(*arg++) { switch(*arg++) {
case 's': case 'S': DebugFlag |= DB_PARSE_EXPR; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break; case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break; case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break; case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
@@ -649,17 +656,6 @@ void InitRemind(int argc, char const *argv[])
} }
} }
if (should_guess_terminal_background) {
guess_terminal_background(&r, &g, &b);
if (r >= 0 && g >= 0 && b >= 0) {
if (r+g+b <= 85*3 && r <= 128 && g <= 128 && b <= 128) {
TerminalBackground = TERMINAL_BACKGROUND_DARK;
} else {
TerminalBackground = TERMINAL_BACKGROUND_LIGHT;
}
}
}
/* Get the filename. */ /* Get the filename. */
if (!InvokedAsRem) { if (!InvokedAsRem) {
if (i >= argc) { if (i >= argc) {
@@ -721,12 +717,17 @@ void InitRemind(int argc, char const *argv[])
break; break;
default: default:
if (tok.type == T_Illegal && tok.val < 0) {
fprintf(stderr, "%s: `%s'\n", ErrMsg[-tok.val], arg);
Usage();
}
Usage(); Usage();
} }
} }
if (rep > 0) { if (rep > 0) {
Iterations = rep; Iterations = rep;
IgnoreOnce = 1;
DontQueue = 1; DontQueue = 1;
Daemon = 0; Daemon = 0;
} }
@@ -779,7 +780,7 @@ void InitRemind(int argc, char const *argv[])
#ifndef L_USAGE_OVERRIDE #ifndef L_USAGE_OVERRIDE
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif #endif
@@ -812,6 +813,7 @@ void Usage(void)
fprintf(ErrFp, " -m Start calendar with Monday rather than Sunday\n"); fprintf(ErrFp, " -m Start calendar with Monday rather than Sunday\n");
fprintf(ErrFp, " -y Synthesize tags for tagless reminders\n"); fprintf(ErrFp, " -y Synthesize tags for tagless reminders\n");
fprintf(ErrFp, " -j[n] Run in 'purge' mode. [n = INCLUDE depth]\n"); fprintf(ErrFp, " -j[n] Run in 'purge' mode. [n = INCLUDE depth]\n");
fprintf(ErrFp, "\nRemind home page: %s\n", PACKAGE_URL);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#endif /* L_USAGE_OVERRIDE */ #endif /* L_USAGE_OVERRIDE */
@@ -931,7 +933,12 @@ static void InitializeVar(char const *str)
r = 0; r = 0;
while (*str && *str != '=') { while (*str && *str != '=') {
if (r < VAR_NAME_LEN) { if (r < VAR_NAME_LEN) {
varname[r++] = *str; if (isalpha(*str) || *str == '_' || (r > 0 && *str == '(') || (r == 0 && *str == '$') || (r > 0 && isdigit(*str))) {
varname[r++] = *str;
} else {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_ILLEGAL_CHAR]);
return;
}
} }
if (*str == '(') { if (*str == '(') {
/* Do a function definition if we see a paren */ /* Do a function definition if we see a paren */
@@ -941,10 +948,28 @@ static void InitializeVar(char const *str)
str++; str++;
} }
varname[r] = 0; varname[r] = 0;
if (!*str) { if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]); fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return; return;
} }
if (!*str) {
/* Setting a system var does require =expr on the commandline */
if (*varname == '$') {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]);
return;
}
val.type = INT_TYPE;
val.v.val = 0;
r = SetVar(varname, &val);
if (!r) {
r = PreserveVar(varname);
}
if (r) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]);
}
return;
}
if (!*varname) { if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]); fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return; return;
@@ -997,13 +1022,71 @@ AddTrustedUser(char const *username)
NumTrustedUsers++; NumTrustedUsers++;
} }
static pid_t LimiterPid = (pid_t) -1;
void unlimit_execution_time(void)
{
if (LimiterPid != (pid_t) -1) {
kill(LimiterPid, SIGTERM);
LimiterPid = (pid_t) -1;
}
}
static void limit_execution_time(int t)
{
pid_t parent = getpid();
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid > 0) {
LimiterPid = pid;
/* In the parent */
return;
}
/* In the child */
time_t start = time(NULL);
while(1) {
sleep(1);
if (kill(parent, 0) < 0) {
/* Parent has probably exited */
exit(0);
}
if (time(NULL) - start > t) {
kill(parent, SIGXCPU);
exit(0);
}
}
}
static void static void
ProcessLongOption(char const *arg) ProcessLongOption(char const *arg)
{ {
int t;
if (!strcmp(arg, "version")) { if (!strcmp(arg, "version")) {
printf("%s\n", VERSION); printf("%s\n", VERSION);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
if (!strcmp(arg, "print-tokens")) {
print_remind_tokens();
print_builtinfunc_tokens();
print_sysvar_tokens();
exit(0);
}
if (sscanf(arg, "max-execution-time=%d", &t) == 1) {
if (t < 0) {
fprintf(ErrFp, "%s: --max-execution-time must be non-negative\n", ArgV[0]);
return;
}
if (t > 0) {
limit_execution_time(t);
}
return;
}
fprintf(ErrFp, "%s: Unknown long option --%s\n", ArgV[0], arg); fprintf(ErrFp, "%s: Unknown long option --%s\n", ArgV[0], arg);
} }
@@ -1012,7 +1095,7 @@ guess_terminal_background(int *r, int *g, int *b)
{ {
int ttyfd; int ttyfd;
struct pollfd p; struct pollfd p;
int rr, gg, bb; unsigned int rr, gg, bb;
char buf[128]; char buf[128];
int n; int n;
@@ -1042,7 +1125,14 @@ guess_terminal_background(int *r, int *g, int *b)
return; return;
} }
tty_raw(ttyfd); tty_raw(ttyfd);
write(ttyfd, "\033]11;?\033\\", 8); n = write(ttyfd, "\033]11;?\033\\", 8);
if (n != 8) {
/* write failed... WTF? Not much we can do */
tty_reset(ttyfd);
close(ttyfd);
return;
}
/* Wait up to 0.1s for terminal to respond */ /* Wait up to 0.1s for terminal to respond */
p.fd = ttyfd; p.fd = ttyfd;
@@ -1064,6 +1154,7 @@ guess_terminal_background(int *r, int *g, int *b)
return; return;
} }
tty_reset(ttyfd); tty_reset(ttyfd);
close(ttyfd);
buf[n+1] = 0; buf[n+1] = 0;
if (n < 25) { if (n < 25) {
/* Too short */ /* Too short */
@@ -1073,9 +1164,9 @@ guess_terminal_background(int *r, int *g, int *b)
/* Couldn't scan color codes */ /* Couldn't scan color codes */
return; return;
} }
*r = (rr >> 8) & 255; *r = (int) ((rr >> 8) & 255);
*g = (gg >> 8) & 255; *g = (int) ((gg >> 8) & 255);
*b = (bb >> 8) & 255; *b = (int) ((bb >> 8) & 255);
} }
static struct termios orig_termios; static struct termios orig_termios;
@@ -1110,3 +1201,21 @@ tty_reset(int fd)
{ {
tcsetattr(fd, TCSAFLUSH, &orig_termios); tcsetattr(fd, TCSAFLUSH, &orig_termios);
} }
int
GetTerminalBackground(void)
{
int r, g, b;
if (should_guess_terminal_background) {
guess_terminal_background(&r, &g, &b);
if (r >= 0 && g >= 0 && b >= 0) {
if (r+g+b <= 85*3 && r <= 128 && g <= 128 && b <= 128) {
TerminalBackground = TERMINAL_BACKGROUND_DARK;
} else {
TerminalBackground = TERMINAL_BACKGROUND_LIGHT;
}
}
should_guess_terminal_background = 0;
}
return TerminalBackground;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* This file is Copyright (C) 1993-1998 by Mikko Silvonen. */ /* This file is Copyright (C) 1993-1998 by Mikko Silvonen. */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -145,7 +145,7 @@ EXTERN char *ErrMsg[] =
"Ok", "Ok",
"Puuttuva ']'", "Puuttuva ']'",
"Puuttuva lainausmerkki", "Puuttuva lainausmerkki",
"Liian monimutkainen lauseke - liikaa operaattoreita", "Liian monimutkainen lauseke",
"Liian monimutkainen lauseke - liikaa operandeja", "Liian monimutkainen lauseke - liikaa operandeja",
"Puuttuva ')'", "Puuttuva ')'",
"Määrittelemätön funktio", "Määrittelemätön funktio",
@@ -245,7 +245,11 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Odotettu viikonpäivän nimi",
"Päällekkäinen argumentin nimi",
"Lausekkeiden arviointi on poistettu käytöstä",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -254,7 +258,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETAVERSIO <<<<\n"); fprintf(ErrFp, ">>>> BETAVERSIO <<<<\n");
#endif #endif
@@ -271,12 +275,10 @@ void Usage(void)
fprintf(ErrFp, " -o Älä noudata ONCE-lauseita\n"); fprintf(ErrFp, " -o Älä noudata ONCE-lauseita\n");
fprintf(ErrFp, " -t Laukaise kaikki viestit deltan arvosta välittämättä\n"); fprintf(ErrFp, " -t Laukaise kaikki viestit deltan arvosta välittämättä\n");
fprintf(ErrFp, " -h Suppeat tulostukset\n"); fprintf(ErrFp, " -h Suppeat tulostukset\n");
#ifdef HAVE_QUEUED
fprintf(ErrFp, " -a Älä laukaise viestejä heti - lisää ne jonoon\n"); fprintf(ErrFp, " -a Älä laukaise viestejä heti - lisää ne jonoon\n");
fprintf(ErrFp, " -q Älä lisää viestejä jonoon\n"); fprintf(ErrFp, " -q Älä lisää viestejä jonoon\n");
fprintf(ErrFp, " -f Laukaise viestit, pysy etualalla\n"); fprintf(ErrFp, " -f Laukaise viestit, pysy etualalla\n");
fprintf(ErrFp, " -z[n] Käynnisty demonina, herätys n:n (5:n) minuutin välein\n"); fprintf(ErrFp, " -z[n] Käynnisty demonina, herätys n:n (5:n) minuutin välein\n");
#endif
fprintf(ErrFp, " -d... Virheenetsintä: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); fprintf(ErrFp, " -d... Virheenetsintä: e=echo x=expr-eval t=trig v=dumpvars l=showline\n");
fprintf(ErrFp, " -e Ohjaa virhetulostus stdout-vuohon\n"); fprintf(ErrFp, " -e Ohjaa virhetulostus stdout-vuohon\n");
fprintf(ErrFp, " -b[n] Ajan ilmaisu: 0=ap/ip, 1=24 tuntia, 2=ei aikoja\n"); fprintf(ErrFp, " -b[n] Ajan ilmaisu: 0=ap/ip, 1=24 tuntia, 2=ei aikoja\n");

View File

@@ -8,7 +8,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* */ /* */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* This file is Copyright (C) 1993 by Laurent Duperval and */ /* This file is Copyright (C) 1993 by Laurent Duperval and */
/* Dianne Skoll. */ /* Dianne Skoll. */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
@@ -119,7 +119,7 @@ EXTERN char *ErrMsg[] =
"Ok", "Ok",
"']' manquant", "']' manquant",
"Apostrophe manquant", "Apostrophe manquant",
"Expression trop complexe - trop d'opérateurs", "Expression trop complexe",
"Expression trop complexe - trop d'opérandes", "Expression trop complexe - trop d'opérandes",
"')' manquante", "')' manquante",
"Fonction non-définie", "Fonction non-définie",
@@ -219,7 +219,11 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Nom du jour de la semaine attendu",
"Nom de l'argument en double",
"L'évaluation de l'expression est désactivée",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -228,7 +232,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif #endif
@@ -245,12 +249,10 @@ void Usage(void)
fprintf(ErrFp, " -o Ignorer instructions ONCE\n"); fprintf(ErrFp, " -o Ignorer instructions ONCE\n");
fprintf(ErrFp, " -t Déclencher tous les rappels peu importe le delta\n"); fprintf(ErrFp, " -t Déclencher tous les rappels peu importe le delta\n");
fprintf(ErrFp, " -h Mode silencieux\n"); fprintf(ErrFp, " -h Mode silencieux\n");
#ifdef HAVE_QUEUED
fprintf(ErrFp, " -a Ne pas déclencher les rappels minutés immédiatement - les mettre en file\n"); fprintf(ErrFp, " -a Ne pas déclencher les rappels minutés immédiatement - les mettre en file\n");
fprintf(ErrFp, " -q Ne pas mettre les rappels minutés en file\n"); fprintf(ErrFp, " -q Ne pas mettre les rappels minutés en file\n");
fprintf(ErrFp, " -f Déclencher les rappels minutés immédiatement en restant en avant-plan\n"); fprintf(ErrFp, " -f Déclencher les rappels minutés immédiatement en restant en avant-plan\n");
fprintf(ErrFp, " -z[n] Entrer en mode 'daemon', réveil chaque n (5) minutes\n"); fprintf(ErrFp, " -z[n] Entrer en mode 'daemon', réveil chaque n (5) minutes\n");
#endif
fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trig v=dumpvars l=showline\n");
fprintf(ErrFp, " -e Envoyer les messages de stderr à stdout\n"); fprintf(ErrFp, " -e Envoyer les messages de stderr à stdout\n");
fprintf(ErrFp, " -b[n] Formats de l'heure pour le calendrier: 0=am/pm, 1=24hr, 2=aucun\n"); fprintf(ErrFp, " -b[n] Formats de l'heure pour le calendrier: 0=am/pm, 1=24hr, 2=aucun\n");

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,7 @@
/* Polish. */ /* Polish. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -135,7 +135,7 @@ EXTERN char *ErrMsg[] =
"OK", "OK",
"Brakujący ']'", "Brakujący ']'",
"Brakujący nawias", "Brakujący nawias",
"Zbyt skomplikowane wyrażenie - za dużo operatorów", "Zbyt skomplikowane wyrażenie",
"Zbyt skomplikowane wyrażenie - za dużo argumentów", "Zbyt skomplikowane wyrażenie - za dużo argumentów",
"Brakujący ')'", "Brakujący ')'",
"Nie zdefiniowana funkcja", "Nie zdefiniowana funkcja",
@@ -235,7 +235,10 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Oczekiwana nazwa dnia tygodnia",
"Zduplikowana nazwa argumentu",
"Ocena wyrażeń jest wyłączona",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -244,7 +247,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n"); fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif #endif
@@ -261,12 +264,10 @@ void Usage(void)
fprintf(ErrFp, " -o Ignoruj instrukcje ONCE\n"); fprintf(ErrFp, " -o Ignoruj instrukcje ONCE\n");
fprintf(ErrFp, " -t Odpal wszystkie przyszłe przypomnienia niezależnie od delty\n"); fprintf(ErrFp, " -t Odpal wszystkie przyszłe przypomnienia niezależnie od delty\n");
fprintf(ErrFp, " -h Praca bezszmerowa\n"); fprintf(ErrFp, " -h Praca bezszmerowa\n");
#ifdef HAVE_QUEUED
fprintf(ErrFp, " -a Nie odpalaj przyponień czasowych - kolejkuj je\n"); fprintf(ErrFp, " -a Nie odpalaj przyponień czasowych - kolejkuj je\n");
fprintf(ErrFp, " -q Nie kolejkuj przyponień czasowych\n"); fprintf(ErrFp, " -q Nie kolejkuj przyponień czasowych\n");
fprintf(ErrFp, " -f Nie przechodź do pracy w tle\n"); fprintf(ErrFp, " -f Nie przechodź do pracy w tle\n");
fprintf(ErrFp, " -z[n] Pracuj jako demon, budząc się co n (5) minut\n"); fprintf(ErrFp, " -z[n] Pracuj jako demon, budząc się co n (5) minut\n");
#endif
fprintf(ErrFp, " -d... Odpluskwianie: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); fprintf(ErrFp, " -d... Odpluskwianie: e=echo x=expr-eval t=trig v=dumpvars l=showline\n");
fprintf(ErrFp, " -e Komunikaty o błędach skieruj na stdout\n"); fprintf(ErrFp, " -e Komunikaty o błędach skieruj na stdout\n");
fprintf(ErrFp, " -b[n] Format czasu: 0=am/pm, 1=24godz., 2=żaden\n"); fprintf(ErrFp, " -b[n] Format czasu: 0=am/pm, 1=24godz., 2=żaden\n");

View File

@@ -8,7 +8,7 @@
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* */ /* */
/* REMIND is Copyright (C) 1992-2023 by Dianne Skoll */ /* REMIND is Copyright (C) 1992-2024 by Dianne Skoll */
/* This file is Copyright (C) 1996 by Marco Paganini and */ /* This file is Copyright (C) 1996 by Marco Paganini and */
/* Dianne Skoll. */ /* Dianne Skoll. */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
@@ -144,7 +144,7 @@ EXTERN char *ErrMsg[] =
"Ok", "Ok",
"Falta um ']'", "Falta um ']'",
"Falta uma aspa", "Falta uma aspa",
"Expressao muito complexa - muitos operadores", "Expressao muito complexa",
"Expressao muito complexa - muitos operandos", "Expressao muito complexa - muitos operandos",
"Falta um ')'", "Falta um ')'",
"Funcao nao definida", "Funcao nao definida",
@@ -244,7 +244,10 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana",
"Nome de argumento duplicado",
"A avaliação da expressão está desabilitada",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -253,7 +256,7 @@ EXTERN char *ErrMsg[] =
#define L_USAGE_OVERRIDE 1 #define L_USAGE_OVERRIDE 1
void Usage(void) void Usage(void)
{ {
fprintf(ErrFp, "\nREMIND %s (versao %s) (C) 1992-2023 Dianne Skoll\n", VERSION, L_LANGNAME); fprintf(ErrFp, "\nREMIND %s (versao %s) (C) 1992-2024 Dianne Skoll\n", VERSION, L_LANGNAME);
#ifdef BETA #ifdef BETA
fprintf(ErrFp, ">>>> VERSAO BETA <<<<\n"); fprintf(ErrFp, ">>>> VERSAO BETA <<<<\n");
#endif #endif
@@ -270,12 +273,10 @@ void Usage(void)
fprintf(ErrFp, " -o Ignora diretivas ONCE\n"); fprintf(ErrFp, " -o Ignora diretivas ONCE\n");
fprintf(ErrFp, " -t Aciona todos os compromissos futuros, sem considerar o delta\n"); fprintf(ErrFp, " -t Aciona todos os compromissos futuros, sem considerar o delta\n");
fprintf(ErrFp, " -h Modo `Hush' - quieto\n"); fprintf(ErrFp, " -h Modo `Hush' - quieto\n");
#ifdef HAVE_QUEUED
fprintf(ErrFp, " -a Nao aciona compromissos com hora imediatamente - apenas coloca na fila\n"); fprintf(ErrFp, " -a Nao aciona compromissos com hora imediatamente - apenas coloca na fila\n");
fprintf(ErrFp, " -q Nao coloca compromissos com hora na fila\n"); fprintf(ErrFp, " -q Nao coloca compromissos com hora na fila\n");
fprintf(ErrFp, " -f Aciona compromissos com hora em modo foreground\n"); fprintf(ErrFp, " -f Aciona compromissos com hora em modo foreground\n");
fprintf(ErrFp, " -z[n] Modo `daemon', acordando a cada n (5) minutos.\n"); fprintf(ErrFp, " -z[n] Modo `daemon', acordando a cada n (5) minutos.\n");
#endif
fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trigger v=dumpvars l=showline\n"); fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trigger v=dumpvars l=showline\n");
fprintf(ErrFp, " -e Desvia mensagens normalmente enviadas a stderr para stdout\n"); fprintf(ErrFp, " -e Desvia mensagens normalmente enviadas a stderr para stdout\n");
fprintf(ErrFp, " -b[n] Formato da hora para o cal: 0=am/pm, 1=24hr, 2=nenhum\n"); fprintf(ErrFp, " -b[n] Formato da hora para o cal: 0=am/pm, 1=24hr, 2=nenhum\n");

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
/* routines, etc. */ /* routines, etc. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -14,12 +14,19 @@
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#include "config.h" #include "config.h"
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <stdarg.h> #include <stdarg.h>
#ifdef HAVE_LOCALE_H #ifdef HAVE_LOCALE_H
#include <locale.h> #include <locale.h>
@@ -39,12 +46,48 @@
#include "types.h" #include "types.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
static void DoReminders(void); static void DoReminders(void);
/* Macro for simplifying common block so as not to litter code */
#define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0)
void
exitfunc(void)
{
/* Kill any execution-time-limiter process */
unlimit_execution_time();
if (DebugFlag & DB_PARSE_EXPR) {
fflush(stdout);
fflush(stderr);
UnsetAllUserFuncs();
print_expr_nodes_stats();
}
}
static void sigalrm(int sig)
{
UNUSED(sig);
if (ExpressionEvaluationTimeLimit) {
ExpressionTimeLimitExceeded = 1;
}
}
static void sigxcpu(int sig)
{
UNUSED(sig);
int r = write(STDERR_FILENO, "\n\nmax-execution-time exceeded.\n\n", 32);
/* Pretend to use r to avoid compiler warning */
/* cppcheck-suppress duplicateExpression */
/* cppcheck-suppress knownArgument */
_exit(1 + (r-r));
}
/***************************************************************/ /***************************************************************/
/***************************************************************/ /***************************************************************/
/** **/ /** **/
@@ -56,6 +99,8 @@ int main(int argc, char *argv[])
{ {
int pid; int pid;
struct sigaction act;
#ifdef HAVE_SETLOCALE #ifdef HAVE_SETLOCALE
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
#endif #endif
@@ -68,9 +113,30 @@ int main(int argc, char *argv[])
ArgV = (char const **) argv; ArgV = (char const **) argv;
InitRemind(argc, (char const **) argv); InitRemind(argc, (char const **) argv);
act.sa_handler = sigalrm;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
if (sigaction(SIGALRM, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno));
exit(1);
}
act.sa_handler = sigxcpu;
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
if (sigaction(SIGXCPU, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno));
exit(1);
}
DBufInit(&(LastTrigger.tags)); DBufInit(&(LastTrigger.tags));
ClearLastTriggers(); ClearLastTriggers();
atexit(exitfunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) { if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar(); ProduceCalendar();
return 0; return 0;
@@ -94,7 +160,7 @@ int main(int argc, char *argv[])
} }
if (!Hush) { if (!Hush) {
if (DestroyOmitContexts()) if (DestroyOmitContexts(1))
Eprint("%s", ErrMsg[E_PUSH_NOPOP]); Eprint("%s", ErrMsg[E_PUSH_NOPOP]);
if (!Daemon && !NextMode && !NumTriggered && !NumQueued) { if (!Daemon && !NextMode && !NumTriggered && !NumQueued) {
printf("%s\n", ErrMsg[E_NOREMINDERS]); printf("%s\n", ErrMsg[E_NOREMINDERS]);
@@ -147,7 +213,7 @@ void
PerIterationInit(void) PerIterationInit(void)
{ {
ClearGlobalOmits(); ClearGlobalOmits();
DestroyOmitContexts(); DestroyOmitContexts(1);
DestroyVars(0); DestroyVars(0);
DefaultColorR = -1; DefaultColorR = -1;
DefaultColorG = -1; DefaultColorG = -1;
@@ -176,7 +242,8 @@ static void DoReminders(void)
if (!UseStdin) { if (!UseStdin) {
FileAccessDate = GetAccessDate(InitialFile); FileAccessDate = GetAccessDate(InitialFile);
} else { } else {
FileAccessDate = DSEToday; FileAccessDate = DSEToday - 1;
if (FileAccessDate < 0) FileAccessDate = 0;
} }
if (FileAccessDate < 0) { if (FileAccessDate < 0) {
@@ -274,9 +341,11 @@ static void DoReminders(void)
case T_Pop: r=PopOmitContext(&p); break; case T_Pop: r=PopOmitContext(&p); break;
case T_Preserve: r=DoPreserve(&p); break; case T_Preserve: r=DoPreserve(&p); break;
case T_Push: r=PushOmitContext(&p); break; case T_Push: r=PushOmitContext(&p); break;
case T_Expr: r = DoExpr(&p); break;
case T_RemType: if (tok.val == RUN_TYPE) { case T_RemType: if (tok.val == RUN_TYPE) {
r=DoRun(&p); r=DoRun(&p);
} else { } else {
DestroyParser(&p);
CreateParser(CurLine, &p); CreateParser(CurLine, &p);
r=DoRem(&p); r=DoRem(&p);
purge_handled = 1; purge_handled = 1;
@@ -284,11 +353,18 @@ static void DoReminders(void)
break; break;
/* If we don't recognize the command, do a REM by default */ /* If we don't recognize the command, do a REM by default, but warn */
/* Note: Since the parser hasn't been used yet, we don't */
/* need to destroy it here. */
default: CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break; default:
if (!SuppressImplicitRemWarnings) {
Wprint("Unrecognized command; interpreting as REM");
WarnedAboutImplicit = 1;
}
DestroyParser(&p);
CreateParser(CurLine, &p);
purge_handled = 1;
r=DoRem(&p);
break;
} }
if (r && (!Hush || r != E_RUN_DISABLED)) { if (r && (!Hush || r != E_RUN_DISABLED)) {
@@ -446,6 +522,16 @@ int ParseChar(ParsePtr p, int *err, int peek)
return *(p->pos++); return *(p->pos++);
} }
} }
/* Convert [[ to just a literal [ */
if (*p->pos == BEG_OF_EXPR && *(p->pos+1) == BEG_OF_EXPR) {
if (peek) {
return *(p->pos+1);
} else {
p->pos++;
return *(p->pos++);
}
}
p->expr_happened = 1; p->expr_happened = 1;
p->pos++; p->pos++;
r = EvalExpr(&(p->pos), &val, p); r = EvalExpr(&(p->pos), &val, p);
@@ -454,8 +540,15 @@ int ParseChar(ParsePtr p, int *err, int peek)
DestroyParser(p); DestroyParser(p);
return 0; return 0;
} }
while(*p->pos && (isempty(*p->pos))) {
p->pos++;
}
if (*p->pos != END_OF_EXPR) { if (*p->pos != END_OF_EXPR) {
*err = E_MISS_END; if (*p->pos) {
*err = E_PARSE_ERR;
} else {
*err = E_MISS_END;
}
DestroyParser(p); DestroyParser(p);
DestroyValue(val); DestroyValue(val);
return 0; return 0;
@@ -570,6 +663,57 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
} }
} }
/***************************************************************/
/* */
/* ParseExpr */
/* */
/* We are expecting an expression here. Parse it and return */
/* the value node tree. */
/* */
/***************************************************************/
expr_node * ParseExpr(ParsePtr p, int *r)
{
int bracketed = 0;
expr_node *node;
if (p->isnested) {
*r = E_PARSE_ERR; /* Can't nest expressions */
return NULL;
}
if (!p->pos) {
*r = E_PARSE_ERR; /* Missing expression */
return NULL;
}
while (isempty(*p->pos)) (p->pos)++;
if (!*(p->pos)) {
*r = E_EOLN;
return NULL;
}
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
}
node = parse_expression(&(p->pos), r, NULL);
if (*r) {
return free_expr_tree(node);
}
if (bracketed) {
if (*p->pos != END_OF_EXPR) {
if (*p->pos) {
*r = E_PARSE_ERR;
} else {
*r = E_MISS_END;
}
return free_expr_tree(node);
}
(p->pos)++;
}
return node;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* EvaluateExpr */ /* EvaluateExpr */
@@ -581,21 +725,22 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
int EvaluateExpr(ParsePtr p, Value *v) int EvaluateExpr(ParsePtr p, Value *v)
{ {
int bracketed = 0;
int r; int r;
int nonconst = 0;
expr_node *node = ParseExpr(p, &r);
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */ if (r != OK) {
while (isempty(*p->pos)) (p->pos)++; return r;
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
} }
r = EvalExpr(&(p->pos), v, p); if (!node) {
return E_SWERR;
}
r = evaluate_expression(node, NULL, v, &nonconst);
free_expr_tree(node);
if (r) return r; if (r) return r;
if (bracketed) { if (nonconst) {
if (*p->pos != END_OF_EXPR) return E_MISS_END; p->nonconst_expr = 1;
(p->pos)++;
} }
return OK; return OK;
} }
@@ -631,36 +776,36 @@ void Wprint(char const *fmt, ...)
void Eprint(char const *fmt, ...) void Eprint(char const *fmt, ...)
{ {
va_list argptr; va_list argptr;
char const *fname;
/* Check if more than one error msg. from this line */ /* Check if more than one error msg. from this line */
if (!FreshLine && !ShowAllErrors) return; if (!FreshLine && !ShowAllErrors) return;
if (FreshLine && FileName) { if (!FileName) {
FreshLine = 0; return;
if (strcmp(FileName, "-")) {
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
}
} else {
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
}
}
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
} else if (FileName) {
fprintf(ErrFp, " ");
if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, ": ");
}
} }
if (strcmp(FileName, "-")) {
fname = FileName;
} else {
fname = "-stdin-";
}
if (FreshLine) {
(void) fprintf(ErrFp, "%s(%d): ", fname, LineNo);
} else {
fprintf(ErrFp, " ");
}
va_start(argptr, fmt); va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr); (void) vfprintf(ErrFp, fmt, argptr);
(void) fputc('\n', ErrFp); (void) fputc('\n', ErrFp);
va_end(argptr); va_end(argptr);
return; if (print_callstack(ErrFp)) {
(void) fprintf(ErrFp, "\n");
}
if (FreshLine) {
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
}
FreshLine = 0;
} }
/***************************************************************/ /***************************************************************/
@@ -747,17 +892,17 @@ int PushToken(char const *tok, ParsePtr p)
/* Return the system time in seconds past midnight */ /* Return the system time in seconds past midnight */
/* */ /* */
/***************************************************************/ /***************************************************************/
long SystemTime(int realtime) int SystemTime(int realtime)
{ {
time_t now; time_t now;
struct tm *t; struct tm *t;
if (!realtime && (SysTime != -1L)) return SysTime; if (!realtime && (SysTime != -1)) return SysTime;
now = time(NULL); now = time(NULL);
t = localtime(&now); t = localtime(&now);
return (long) t->tm_hour * 3600L + (long) t->tm_min * 60L + return t->tm_hour * 3600L + t->tm_min * 60L +
(long) t->tm_sec; t->tm_sec;
} }
/***************************************************************/ /***************************************************************/
@@ -769,7 +914,7 @@ long SystemTime(int realtime)
/***************************************************************/ /***************************************************************/
int MinutesPastMidnight(int realtime) int MinutesPastMidnight(int realtime)
{ {
return (int) (SystemTime(realtime) / 60); return (SystemTime(realtime) / 60);
} }
@@ -829,6 +974,7 @@ int DoIf(ParsePtr p)
} }
} }
IfLinenos[NumIfs] = LineNo;
NumIfs++; NumIfs++;
IfFlags &= ~(IF_MASK << (2*NumIfs - 2)); IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
IfFlags |= syndrome << (2 * NumIfs - 2); IfFlags |= syndrome << (2 * NumIfs - 2);
@@ -893,7 +1039,7 @@ int DoIfTrig(ParsePtr p)
if ((size_t) NumIfs >= IF_NEST) return E_NESTED_IF; if ((size_t) NumIfs >= IF_NEST) return E_NESTED_IF;
if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE; if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE;
else { else {
if ( (r=ParseRem(p, &trig, &tim, 1)) ) return r; if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if (trig.typ != NO_TYPE) return E_PARSE_ERR; if (trig.typ != NO_TYPE) return E_PARSE_ERR;
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1); dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1);
if (r) { if (r) {
@@ -1011,6 +1157,12 @@ int DoDebug(ParsePtr p)
else DebugFlag &= ~DB_ECHO_LINE; else DebugFlag &= ~DB_ECHO_LINE;
break; break;
case 's':
case 'S':
if (val) DebugFlag |= DB_PARSE_EXPR;
else DebugFlag &= ~DB_PARSE_EXPR;
break;
case 'x': case 'x':
case 'X': case 'X':
if (val) DebugFlag |= DB_PRTEXPR; if (val) DebugFlag |= DB_PRTEXPR;
@@ -1076,7 +1228,7 @@ int DoBanner(ParsePtr p)
} }
} }
DBufFree(&Banner); DBufFree(&Banner);
err = DBufPuts(&Banner, DBufValue(&buf)); err = DBufPuts(&Banner, DBufValue(&buf));
DBufFree(&buf); DBufFree(&buf);
return err; return err;
@@ -1088,7 +1240,6 @@ int DoBanner(ParsePtr p)
/* */ /* */
/* Enable or disable the RUN command under program control */ /* Enable or disable the RUN command under program control */
/* */ /* */
/* */
/***************************************************************/ /***************************************************************/
int DoRun(ParsePtr p) int DoRun(ParsePtr p)
{ {
@@ -1115,6 +1266,38 @@ int DoRun(ParsePtr p)
return VerifyEoln(p); return VerifyEoln(p);
} }
/***************************************************************/
/* */
/* DoExpr */
/* */
/* Enable or disable expression evaluation */
/* */
/***************************************************************/
int DoExpr(ParsePtr p)
{
int r;
DynamicBuffer buf;
DBufInit(&buf);
if ( (r=ParseToken(p, &buf)) ) return r;
/* Only allow EXPR ON in top-level script */
if (! StrCmpi(DBufValue(&buf), "ON")) {
if (TopLevel()) ExpressionEvaluationDisabled = 0;
}
/* But allow EXPR OFF anywhere */
else if (! StrCmpi(DBufValue(&buf), "OFF"))
ExpressionEvaluationDisabled = 1;
else {
DBufFree(&buf);
return E_PARSE_ERR;
}
DBufFree(&buf);
return VerifyEoln(p);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DoFlush */ /* DoFlush */
@@ -1251,19 +1434,19 @@ int CalcMinsFromUTC(int dse, int tim, int *mins, int *isdst)
return 0; return 0;
} }
static char const *OutputEscapeSequences(char const *s, int print) static char const *OutputEscapeSequences(char const *s, int print, DynamicBuffer *output)
{ {
while (*s == 0x1B && *(s+1) == '[') { while (*s == 0x1B && *(s+1) == '[') {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
while (*s && (*s < 0x40 || *s > 0x7E)) { while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
} }
if (*s) { if (*s) {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
} }
} }
@@ -1272,19 +1455,19 @@ static char const *OutputEscapeSequences(char const *s, int print)
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
#define ISWBLANK(c) (iswspace(c) && (c) != '\n') #define ISWBLANK(c) (iswspace(c) && (c) != '\n')
static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print) static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print, DynamicBuffer *output)
{ {
while (*s == 0x1B && *(s+1) == '[') { while (*s == 0x1B && *(s+1) == '[') {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
while (*s && (*s < 0x40 || *s > 0x7E)) { while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
} }
if (*s) { if (*s) {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
} }
} }
@@ -1293,7 +1476,7 @@ static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print)
static void static void
FillParagraphWCAux(wchar_t const *s) FillParagraphWCAux(wchar_t const *s, DynamicBuffer *output)
{ {
int line = 0; int line = 0;
int i, j; int i, j;
@@ -1308,7 +1491,7 @@ FillParagraphWCAux(wchar_t const *s)
/* If it's a carriage return, output it and start new paragraph */ /* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') { if (*s == '\n') {
putchar('\n'); OUTPUT('\n');
s++; s++;
line = 0; line = 0;
while(ISWBLANK(*s)) s++; while(ISWBLANK(*s)) s++;
@@ -1321,7 +1504,7 @@ FillParagraphWCAux(wchar_t const *s)
number of spaces */ number of spaces */
j = line ? SubsIndent : FirstIndent; j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) { for (i=0; i<j; i++) {
putchar(' '); OUTPUT(' ');
} }
/* Calculate the amount of room left on this line */ /* Calculate the amount of room left on this line */
@@ -1334,7 +1517,7 @@ FillParagraphWCAux(wchar_t const *s)
if (*s == '\n') break; if (*s == '\n') break;
while(1) { while(1) {
t = s; t = s;
s = OutputEscapeSequencesWS(s, 1); s = OutputEscapeSequencesWS(s, 1, output);
if (s == t) break; if (s == t) break;
while(ISWBLANK(*s)) s++; while(ISWBLANK(*s)) s++;
} }
@@ -1342,7 +1525,7 @@ FillParagraphWCAux(wchar_t const *s)
len = 0; len = 0;
while(*s && !iswspace(*s)) { while(*s && !iswspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') { if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequencesWS(s, 0); s = OutputEscapeSequencesWS(s, 0, output);
continue; continue;
} }
len += wcwidth(*s); len += wcwidth(*s);
@@ -1353,17 +1536,17 @@ FillParagraphWCAux(wchar_t const *s)
} }
if (!pendspace || len+pendspace <= roomleft) { if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) { for (i=0; i<pendspace; i++) {
putchar(' '); OUTPUT(' ');
} }
while(t < s) { while(t < s) {
PutWideChar(*t); PutWideChar(*t, output);
if (strchr(EndSent, *t)) doublespace = 2; if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1; else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++; t++;
} }
} else { } else {
s = t; s = t;
putchar('\n'); OUTPUT('\n');
line++; line++;
break; break;
} }
@@ -1374,7 +1557,7 @@ FillParagraphWCAux(wchar_t const *s)
} }
static int static int
FillParagraphWC(char const *s) FillParagraphWC(char const *s, DynamicBuffer *output)
{ {
size_t len; size_t len;
wchar_t *buf; wchar_t *buf;
@@ -1384,7 +1567,7 @@ FillParagraphWC(char const *s)
buf = calloc(len+1, sizeof(wchar_t)); buf = calloc(len+1, sizeof(wchar_t));
if (!buf) return E_NO_MEM; if (!buf) return E_NO_MEM;
(void) mbstowcs(buf, s, len+1); (void) mbstowcs(buf, s, len+1);
FillParagraphWCAux(buf); FillParagraphWCAux(buf, output);
free(buf); free(buf);
return OK; return OK;
} }
@@ -1404,7 +1587,7 @@ FillParagraphWC(char const *s)
/* A macro safe ONLY if used with arg with no side effects! */ /* A macro safe ONLY if used with arg with no side effects! */
#define ISBLANK(c) (isspace(c) && (c) != '\n') #define ISBLANK(c) (isspace(c) && (c) != '\n')
void FillParagraph(char const *s) void FillParagraph(char const *s, DynamicBuffer *output)
{ {
int line = 0; int line = 0;
@@ -1422,7 +1605,7 @@ void FillParagraph(char const *s)
if (!*s) return; if (!*s) return;
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
if (FillParagraphWC(s) == OK) { if (FillParagraphWC(s, output) == OK) {
return; return;
} }
#endif #endif
@@ -1432,7 +1615,7 @@ void FillParagraph(char const *s)
/* If it's a carriage return, output it and start new paragraph */ /* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') { if (*s == '\n') {
putchar('\n'); OUTPUT('\n');
s++; s++;
line = 0; line = 0;
while(ISBLANK(*s)) s++; while(ISBLANK(*s)) s++;
@@ -1445,7 +1628,7 @@ void FillParagraph(char const *s)
number of spaces */ number of spaces */
j = line ? SubsIndent : FirstIndent; j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) { for (i=0; i<j; i++) {
putchar(' '); OUTPUT(' ');
} }
/* Calculate the amount of room left on this line */ /* Calculate the amount of room left on this line */
@@ -1458,7 +1641,7 @@ void FillParagraph(char const *s)
if (*s == '\n') break; if (*s == '\n') break;
while(1) { while(1) {
t = s; t = s;
s = OutputEscapeSequences(s, 1); s = OutputEscapeSequences(s, 1, output);
if (s == t) break; if (s == t) break;
while(ISBLANK(*s)) s++; while(ISBLANK(*s)) s++;
} }
@@ -1466,7 +1649,7 @@ void FillParagraph(char const *s)
len = 0; len = 0;
while(*s && !isspace(*s)) { while(*s && !isspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') { if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequences(s, 0); s = OutputEscapeSequences(s, 0, output);
continue; continue;
} }
s++; s++;
@@ -1477,17 +1660,17 @@ void FillParagraph(char const *s)
} }
if (!pendspace || len+pendspace <= roomleft) { if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) { for (i=0; i<pendspace; i++) {
putchar(' '); OUTPUT(' ');
} }
while(t < s) { while(t < s) {
putchar(*t); OUTPUT(*t);
if (strchr(EndSent, *t)) doublespace = 2; if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1; else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++; t++;
} }
} else { } else {
s = t; s = t;
putchar('\n'); OUTPUT('\n');
line++; line++;
break; break;
} }
@@ -1651,12 +1834,51 @@ SaveLastTimeTrig(TimeTrig const *t)
memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig)); memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig));
} }
/* Wrapper to ignore warnings about ignoring return value of system() */ /* Wrapper to ignore warnings about ignoring return value of system()
Also redirects stdin and stdout to /dev/null for queued reminders */
void void
System(char const *cmd) System(char const *cmd, int is_queued)
{ {
int r; int r;
pid_t kid;
int fd;
int status;
int do_exit = 0;
if (is_queued && IsServerMode()) {
do_exit = 1;
/* Server mode... redirect stdin and stdout to /dev/null */
kid = fork();
if (kid == (pid_t) -1) {
/* Fork failed... nothing we can do */
return;
} else if (kid == 0) {
/* In the child */
(void) close(STDIN_FILENO);
(void) close(STDOUT_FILENO);
fd = open("/dev/null", O_RDONLY);
if (fd >= 0 && fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(STDIN_FILENO);
}
fd = open("/dev/null", O_WRONLY);
if (fd >= 0 && fd != STDOUT_FILENO) {
dup2(fd, STDOUT_FILENO);
close(STDOUT_FILENO);
}
} else {
/* In the parent */
while (waitpid(kid, &status, 0) != kid) /* continue */ ;
return;
}
}
/* This is the child process or original if we never forked */
r = system(cmd); r = system(cmd);
if (do_exit) {
/* In the child process, so exit! */
exit(0);
}
if (r == 0) { if (r == 0) {
return; return;
} }
@@ -1681,3 +1903,40 @@ get_month_name(int mon)
if (DynamicMonthName[mon]) return DynamicMonthName[mon]; if (DynamicMonthName[mon]) return DynamicMonthName[mon];
return MonthName[mon]; return MonthName[mon];
} }
static int GetOnceDateFromFile(void)
{
FILE *fp;
int once_date = 0;
fp = fopen(OnceFile, "r");
if (fp) {
if (fscanf(fp, "%d", &once_date) != 1) {
once_date = 0;
}
fclose(fp);
}
/* Save today to file */
fp = fopen(OnceFile, "w");
if (!fp) {
Wprint("Warning: Unable to save ONCE timestamp to %s: %s",
OnceFile, strerror(errno));
return once_date;
}
fprintf(fp, "%d\n# This is a timestamp file used by Remind to track ONCE reminders.\n# Do not edit or delete it.\n", DSEToday);
fclose(fp);
return once_date;
}
int GetOnceDate(void)
{
ProcessedOnce = 1;
if (IgnoreOnce || !OnceFile || !*OnceFile) {
return FileAccessDate;
}
if (OnceDate < 0) {
OnceDate = GetOnceDateFromFile();
}
return OnceDate;
}

View File

@@ -6,6 +6,10 @@
#include "config.h" #include "config.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
typedef uint32_t uint32;
#else
#if SIZEOF_UNSIGNED_INT == 4 #if SIZEOF_UNSIGNED_INT == 4
typedef unsigned int uint32; typedef unsigned int uint32;
#elif SIZEOF_UNSIGNED_LONG == 4 #elif SIZEOF_UNSIGNED_LONG == 4
@@ -13,6 +17,7 @@ typedef unsigned long uint32;
#else #else
# error Could not find a 32-bit integer type # error Could not find a 32-bit integer type
#endif #endif
#endif
struct MD5Context { struct MD5Context {
uint32 buf[4]; uint32 buf[4];
@@ -26,9 +31,4 @@ void MD5Update(struct MD5Context *context, unsigned char const *buf,
void MD5Final(unsigned char digest[16], struct MD5Context *context); void MD5Final(unsigned char digest[16], struct MD5Context *context);
void MD5Transform(uint32 buf[4], uint32 const in[16]); void MD5Transform(uint32 buf[4], uint32 const in[16]);
/*
* This is needed to make RSAREF happy on some MS-DOS compilers.
*/
typedef struct MD5Context MD5_CTX;
#endif /* !MD5_H */ #endif /* !MD5_H */

View File

@@ -5,7 +5,7 @@
/* Calculations for figuring out moon phases. */ /* Calculations for figuring out moon phases. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -66,7 +66,6 @@
#include <time.h> #include <time.h>
#include "types.h" #include "types.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
@@ -101,21 +100,12 @@ static double phase (double, double *, double *, double *, double *, double *, d
#define mmlong 64.975464 /* Moon's mean lonigitude at the epoch */ #define mmlong 64.975464 /* Moon's mean lonigitude at the epoch */
#define mmlongp 349.383063 /* Mean longitude of the perigee at the #define mmlongp 349.383063 /* Mean longitude of the perigee at the
epoch */ epoch */
#define mlnode 151.950429 /* Mean longitude of the node at the
epoch */
#define minc 5.145396 /* Inclination of the Moon's orbit */
#define mecc 0.054900 /* Eccentricity of the Moon's orbit */ #define mecc 0.054900 /* Eccentricity of the Moon's orbit */
#define mangsiz 0.5181 /* Moon's angular size at distance a #define mangsiz 0.5181 /* Moon's angular size at distance a
from Earth */ from Earth */
#define msmax 384401.0 /* Semi-major axis of Moon's orbit in km */ #define msmax 384401.0 /* Semi-major axis of Moon's orbit in km */
#define mparallax 0.9507 /* Parallax at distance a from Earth */
#define synmonth 29.53058868 /* Synodic month (new Moon to new Moon) */ #define synmonth 29.53058868 /* Synodic month (new Moon to new Moon) */
#define lunatbase 2423436.0 /* Base date for E. W. Brown's numbered
series of lunations (1923 January 16) */
/* Properties of the Earth */
#define earthrad 6378.16 /* Radius of Earth in kilometres */
#ifdef PI #ifdef PI
#undef PI #undef PI
#endif #endif
@@ -124,11 +114,6 @@ static double phase (double, double *, double *, double *, double *, double *, d
/* Handy mathematical functions */ /* Handy mathematical functions */
#ifdef sgn
#undef sgn
#endif
#define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0)) /* Extract sign */
#ifdef abs #ifdef abs
#undef abs #undef abs
#endif #endif
@@ -144,7 +129,7 @@ static double phase (double, double *, double *, double *, double *, double *, d
/* */ /* */
/* jdate */ /* jdate */
/* */ /* */
/* Convert a date and time to DSE day and fraction. */ /* Convert a date and time to Julian day and fraction. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static long jdate(int y, int mon, int day) static long jdate(int y, int mon, int day)
@@ -412,7 +397,7 @@ static double phase(double pdate,
Day = pdate - epoch; /* Date within epoch */ Day = pdate - epoch; /* Date within epoch */
N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */ N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */
M = fixangle(N + elonge - elongp); /* Convert from perigee M = fixangle(N + elonge - elongp); /* Convert from perigee
co-ordinates to epoch 1980.0 */ coordinates to epoch 1980.0 */
Ec = kepler(M, eccent); /* Solve equation of Kepler */ Ec = kepler(M, eccent); /* Solve equation of Kepler */
Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2); Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2);
Ec = 2 * todeg(atan(Ec)); /* 1 anomaly */ Ec = 2 * todeg(atan(Ec)); /* 1 anomaly */

View File

@@ -6,7 +6,7 @@
/* the data structures for OMITted dates. */ /* the data structures for OMITted dates. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -14,15 +14,14 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "types.h" #include "types.h"
#include "protos.h" #include "protos.h"
#include "globals.h" #include "globals.h"
#include "err.h" #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); static void InsertIntoSortedArray (int *array, int num, int key);
/* Arrays for the global omits */ /* Arrays for the global omits */
@@ -32,11 +31,13 @@ static int PartialOmitArray[MAX_PARTIAL_OMITS];
/* WeekdayOmits is declared in global.h */ /* WeekdayOmits is declared in global.h */
/* How many of each omit types do we have? */ /* How many of each omit types do we have? */
static int NumFullOmits, NumPartialOmits; int NumFullOmits, NumPartialOmits;
/* The structure for saving and restoring OMIT contexts */ /* The structure for saving and restoring OMIT contexts */
typedef struct omitcontext { typedef struct omitcontext {
struct omitcontext *next; struct omitcontext *next;
char *filename;
int lineno;
int numfull, numpart; int numfull, numpart;
int *fullsave; int *fullsave;
int *partsave; int *partsave;
@@ -79,19 +80,25 @@ int DoClear(ParsePtr p)
/* */ /* */
/* Free all the memory used by saved OMIT contexts. */ /* Free all the memory used by saved OMIT contexts. */
/* As a side effect, return the number of OMIT contexts */ /* As a side effect, return the number of OMIT contexts */
/* destroyed. */ /* destroyed. If print_unmatched is true, print an error for */
/* each undestroyed OMIT contect */
/* */ /* */
/***************************************************************/ /***************************************************************/
int DestroyOmitContexts(void) int DestroyOmitContexts(int print_unmatched)
{ {
OmitContext *c = SavedOmitContexts; OmitContext *c = SavedOmitContexts;
OmitContext *d; OmitContext *d;
int num = 0; int num = 0;
while (c) { while (c) {
if (print_unmatched) {
Wprint("Unmatched PUSH-OMIT-CONTEXT at %s(%d)",
c->filename, c->lineno);
}
num++; num++;
if (c->fullsave) free(c->fullsave); if (c->fullsave) free(c->fullsave);
if (c->partsave) free(c->partsave); if (c->partsave) free(c->partsave);
if (c->filename) free(c->filename);
d = c->next; d = c->next;
free(c); free(c);
c = d; c = d;
@@ -116,17 +123,31 @@ int PushOmitContext(ParsePtr p)
context = NEW(OmitContext); context = NEW(OmitContext);
if (!context) return E_NO_MEM; if (!context) return E_NO_MEM;
if (FileName) {
context->filename = StrDup(FileName);
} else {
context->filename = StrDup("");
}
if (!context->filename) {
free(context);
return E_NO_MEM;
}
context->lineno = LineNo;
context->numfull = NumFullOmits; context->numfull = NumFullOmits;
context->numpart = NumPartialOmits; context->numpart = NumPartialOmits;
context->weekdaysave = WeekdayOmits; context->weekdaysave = WeekdayOmits;
context->fullsave = malloc(NumFullOmits * sizeof(int)); context->fullsave = malloc(NumFullOmits * sizeof(int));
if (NumFullOmits && !context->fullsave) { if (NumFullOmits && !context->fullsave) {
free(context->filename);
free(context); free(context);
return E_NO_MEM; return E_NO_MEM;
} }
context->partsave = malloc(NumPartialOmits * sizeof(int)); context->partsave = malloc(NumPartialOmits * sizeof(int));
if (NumPartialOmits && !context->partsave) { if (NumPartialOmits && !context->partsave) {
free(context->fullsave); free(context->filename);
if (context->fullsave) {
free(context->fullsave);
}
free(context); free(context);
return E_NO_MEM; return E_NO_MEM;
} }
@@ -175,6 +196,7 @@ int PopOmitContext(ParsePtr p)
/* Free memory used by the saved context */ /* Free memory used by the saved context */
if (c->partsave) free(c->partsave); if (c->partsave) free(c->partsave);
if (c->fullsave) free(c->fullsave); if (c->fullsave) free(c->fullsave);
if (c->filename) free(c->filename);
free(c); free(c);
return VerifyEoln(p); return VerifyEoln(p);
@@ -251,7 +273,7 @@ int IsOmitted(int dse, int localomit, char const *omitfunc, int *omit)
/* element is found, 0 otherwise. */ /* element is found, 0 otherwise. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int BexistsIntArray(int array[], int num, int key) static int BexistsIntArray(int const array[], int num, int key)
{ {
int top=num-1, bot=0, mid; int top=num-1, bot=0, mid;
@@ -379,6 +401,8 @@ int DoOmit(ParsePtr p)
default: default:
if (tok.type == T_Until) { if (tok.type == T_Until) {
Eprint("OMIT: UNTIL not allowed; did you mean THROUGH?"); Eprint("OMIT: UNTIL not allowed; did you mean THROUGH?");
} else if (tok.type == T_Illegal && tok.val < 0) {
Eprint("%s: `%s'", ErrMsg[-tok.val], DBufValue(&buf));
} else { } else {
Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN], Eprint("%s: `%s' (OMIT)", ErrMsg[E_UNKNOWN_TOKEN],
DBufValue(&buf)); DBufValue(&buf));
@@ -445,6 +469,9 @@ int DoOmit(ParsePtr p)
if (!BexistsIntArray(PartialOmitArray, NumPartialOmits, syndrome)) { if (!BexistsIntArray(PartialOmitArray, NumPartialOmits, syndrome)) {
InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome); InsertIntoSortedArray(PartialOmitArray, NumPartialOmits, syndrome);
NumPartialOmits++; NumPartialOmits++;
if (NumPartialOmits == 366) {
Wprint("You have OMITted everything! The space-time continuum is at risk.");
}
} }
if (mc == m[1] && dc == d[1]) { if (mc == m[1] && dc == d[1]) {
break; break;

View File

@@ -5,7 +5,7 @@
/* Function Prototypes. */ /* Function Prototypes. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -13,6 +13,18 @@
/* Suppress unused variable warnings */ /* Suppress unused variable warnings */
#define UNUSED(x) (void) x #define UNUSED(x) (void) x
#ifdef HAVE_STRDUP
#define StrDup strdup
#endif
#ifdef HAVE_STRNCASECMP
#define StrinCmp strncasecmp
#endif
#ifdef HAVE_STRCASECMP
#define StrCmpi strcasecmp
#endif
/* Define a string assignment macro - be careful!!! */ /* Define a string assignment macro - be careful!!! */
#define STRSET(x, str) { if (x) free(x); (x) = StrDup(str); } #define STRSET(x, str) { if (x) free(x); (x) = StrDup(str); }
@@ -22,28 +34,41 @@
/* Characters to ignore */ /* Characters to ignore */
#define isempty(c) (isspace(c) || ((c) == '\\')) #define isempty(c) (isspace(c) || ((c) == '\\'))
#define IsServerMode() (Daemon < 0)
#define ShouldFork (!DontFork)
#include "dynbuf.h" #include "dynbuf.h"
#include <ctype.h> #include <ctype.h>
int CallUserFunc (char const *name, int nargs, ParsePtr p); int CallUserFunc (char const *name, int nargs, ParsePtr p);
int DoFset (ParsePtr p); int DoFset (ParsePtr p);
int DoFunset (ParsePtr p); int DoFunset (ParsePtr p);
void UnsetAllUserFuncs(void);
void ProduceCalendar (void); void ProduceCalendar (void);
char const *SimpleTime (int tim); char const *SimpleTime (int tim);
char const *CalendarTime (int tim, int duration); char const *CalendarTime (int tim, int duration);
int DoRem (ParsePtr p); int DoRem (ParsePtr p);
int DoFlush (ParsePtr p); int DoFlush (ParsePtr p);
void DoExit (ParsePtr p); void DoExit (ParsePtr p);
int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals); int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim);
int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued); int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output);
int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err); int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err);
int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode); int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode);
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim); int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim);
int ParseLiteralDate (char const **s, int *dse, int *tim); int ParseLiteralDateOrTime (char const **s, int *dse, int *tim);
int ParseLiteralTime (char const **s, int *tim); int ParseLiteralTime (char const **s, int *tim);
expr_node *parse_expression(char const **e, int *r, Var *locals);
int evaluate_expression(expr_node *node, Value *locals, Value *ans, int *nonconst);
int evaluate_expr_node(expr_node *node, Value *locals, Value *ans, int *nonconst);
int truthy(Value const *v);
void print_expr_tree(expr_node *node, FILE *fp);
void unlimit_execution_time(void);
expr_node *free_expr_tree(expr_node *node);
int EvalExpr (char const **e, Value *v, ParsePtr p); int EvalExpr (char const **e, Value *v, ParsePtr p);
int DoCoerce (char type, Value *v); int DoCoerce (char type, Value *v);
void PrintValue (Value *v, FILE *fp); char const *PrintValue (Value *v, FILE *fp);
int CopyValue (Value *dest, const Value *src); int CopyValue (Value *dest, const Value *src);
int ReadLine (void); int ReadLine (void);
int OpenFile (char const *fname); int OpenFile (char const *fname);
@@ -62,8 +87,9 @@ int JulianToGregorianOffset(int y, int m);
int ParseChar (ParsePtr p, int *err, int peek); int ParseChar (ParsePtr p, int *err, int peek);
int ParseToken (ParsePtr p, DynamicBuffer *dbuf); int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf); int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
expr_node * ParseExpr(ParsePtr p, int *r);
void print_expr_nodes_stats(void);
int EvaluateExpr (ParsePtr p, Value *v); int EvaluateExpr (ParsePtr p, Value *v);
int Evaluate (char const **s, Var *locals, ParsePtr p);
int FnPopValStack (Value *val); int FnPopValStack (Value *val);
void Eprint (char const *fmt, ...); void Eprint (char const *fmt, ...);
void Wprint (char const *fmt, ...); void Wprint (char const *fmt, ...);
@@ -71,7 +97,7 @@ void OutputLine (FILE *fp);
void CreateParser (char const *s, ParsePtr p); void CreateParser (char const *s, ParsePtr p);
void DestroyParser (ParsePtr p); void DestroyParser (ParsePtr p);
int PushToken (char const *tok, ParsePtr p); int PushToken (char const *tok, ParsePtr p);
long SystemTime (int realtime); int SystemTime (int realtime);
int MinutesPastMidnight (int realtime); int MinutesPastMidnight (int realtime);
int SystemDate (int *y, int *m, int *d); int SystemDate (int *y, int *m, int *d);
int DoIf (ParsePtr p); int DoIf (ParsePtr p);
@@ -83,10 +109,11 @@ int VerifyEoln (ParsePtr p);
int DoDebug (ParsePtr p); int DoDebug (ParsePtr p);
int DoBanner (ParsePtr p); int DoBanner (ParsePtr p);
int DoRun (ParsePtr p); int DoRun (ParsePtr p);
int DoExpr (ParsePtr p);
int DoErrMsg (ParsePtr p); int DoErrMsg (ParsePtr p);
int ClearGlobalOmits (void); int ClearGlobalOmits (void);
int DoClear (ParsePtr p); int DoClear (ParsePtr p);
int DestroyOmitContexts (void); int DestroyOmitContexts (int print_unmatched);
int PushOmitContext (ParsePtr p); int PushOmitContext (ParsePtr p);
int PopOmitContext (ParsePtr p); int PopOmitContext (ParsePtr p);
int IsOmitted (int dse, int localomit, char const *omitfunc, int *omit); int IsOmitted (int dse, int localomit, char const *omitfunc, int *omit);
@@ -100,13 +127,26 @@ int ComputeTrigger (int today, Trigger *trig, TimeTrig *tim, int *err, int save_
int ComputeTriggerNoAdjustDuration (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals, int duration_days); 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 AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int save_in_globals);
char *StrnCpy (char *dest, char const *source, int n); char *StrnCpy (char *dest, char const *source, int n);
#ifndef HAVE_STRNCASECMP
int StrinCmp (char const *s1, char const *s2, int n); int StrinCmp (char const *s1, char const *s2, int n);
#endif
#ifndef HAVE_STRDUP
char *StrDup (char const *s); char *StrDup (char const *s);
#endif
#ifndef HAVE_STRCASECMP
int StrCmpi (char const *s1, char const *s2); int StrCmpi (char const *s1, char const *s2);
#endif
void strtolower(char *s);
Var *FindVar (char const *str, int create); Var *FindVar (char const *str, int create);
SysVar *FindSysVar (char const *name);
int DeleteVar (char const *str); int DeleteVar (char const *str);
int SetVar (char const *str, Value *val); int SetVar (char const *str, Value const *val);
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p); int GetVarValue (char const *str, Value *val);
int DoSet (Parser *p); int DoSet (Parser *p);
int DoUnset (Parser *p); int DoUnset (Parser *p);
int DoDump (ParsePtr p); int DoDump (ParsePtr p);
@@ -115,14 +155,14 @@ void DestroyVars (int all);
int PreserveVar (char const *name); int PreserveVar (char const *name);
int DoPreserve (Parser *p); int DoPreserve (Parser *p);
int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p); int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p);
int DoMsgCommand (char const *cmd, char const *msg); int DoMsgCommand (char const *cmd, char const *msg, int is_queued);
int ParseNonSpaceChar (ParsePtr p, int *err, int peek); int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
unsigned int HashVal (char const *str); unsigned int HashVal (char const *str);
int DateOK (int y, int m, int d); int DateOK (int y, int m, int d);
Operator *FindOperator (char const *name, Operator where[], int num); BuiltinFunc *FindBuiltinFunc (char const *name);
BuiltinFunc *FindFunc (char const *name, BuiltinFunc where[], int num);
int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio); int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio);
void IssueSortedReminders (void); void IssueSortedReminders (void);
UserFunc *FindUserFunc(char const *name);
int UserFuncExists (char const *fn); int UserFuncExists (char const *fn);
void DSEToHeb (int dse, int *hy, int *hm, int *hd); void DSEToHeb (int dse, int *hy, int *hm, int *hd);
int HebNameToNum (char const *mname); int HebNameToNum (char const *mname);
@@ -139,7 +179,7 @@ int GetSysVar (char const *name, Value *val);
int SetSysVar (char const *name, Value *val); int SetSysVar (char const *name, Value *val);
void DumpSysVarByName (char const *name); void DumpSysVarByName (char const *name);
int CalcMinsFromUTC (int dse, int tim, int *mins, int *isdst); int CalcMinsFromUTC (int dse, int tim, int *mins, int *isdst);
void FillParagraph (char const *s); void FillParagraph (char const *s, DynamicBuffer *output);
void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime); void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime);
void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime); void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime);
int MoonPhase (int date, int time); int MoonPhase (int date, int time);
@@ -165,23 +205,45 @@ void PrintJSONKeyPairString(char const *name, char const *val);
void PrintJSONKeyPairDate(char const *name, int dse); void PrintJSONKeyPairDate(char const *name, int dse);
void PrintJSONKeyPairDateTime(char const *name, int dt); void PrintJSONKeyPairDateTime(char const *name, int dt);
void PrintJSONKeyPairTime(char const *name, int t); void PrintJSONKeyPairTime(char const *name, int t);
void System(char const *cmd); void System(char const *cmd, int queued);
int ShellEscape(char const *in, DynamicBuffer *out); int ShellEscape(char const *in, DynamicBuffer *out);
int AddGlobalOmit(int dse); int AddGlobalOmit(int dse);
void set_lat_and_long_from_components(void); void set_lat_and_long_from_components(void);
void set_components_from_lat_and_long(void); void set_components_from_lat_and_long(void);
void DebugExitFunc(void);
int GetTerminalBackground(void);
char const *get_day_name(int wkday); char const *get_day_name(int wkday);
char const *get_month_name(int mon); char const *get_month_name(int mon);
void set_cloexec(FILE *fp);
int push_call(char const *filename, char const *func, int lineno); int push_call(char const *filename, char const *func, int lineno);
void clear_callstack(void); void clear_callstack(void);
int print_callstack(FILE *fp); int print_callstack(FILE *fp);
int have_callstack(void);
void pop_call(void); void pop_call(void);
void FixSpecialType(Trigger *trig); void FixSpecialType(Trigger *trig);
void WriteJSONTrigger(Trigger const *t, int include_tags, int today);
void WriteJSONTimeTrigger(TimeTrig const *tt);
int GetOnceDate(void);
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#include <wctype.h> #include <wctype.h>
#include <wchar.h> #include <wchar.h>
void PutWideChar(wchar_t const wc); void PutWideChar(wchar_t const wc, DynamicBuffer *output);
#endif #endif
/* These functions are in utils.c and are used to detect overflow
in various arithmetic operators. They have to be in separate
functions with extern linkage to defeat compiler optimizations
that would otherwise break the overflow checks. */
extern int _private_mul_overflow(int a, int b);
extern int _private_add_overflow(int a, int b);
extern int _private_sub_overflow(int a, int b);
/* Utility functions for dumping tokens */
void print_sysvar_tokens(void);
void print_builtinfunc_tokens(void);
void print_remind_tokens(void);

View File

@@ -5,7 +5,7 @@
/* Queue up reminders for subsequent execution. */ /* Queue up reminders for subsequent execution. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -24,16 +24,33 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h>
#include <sys/select.h> #include <sys/select.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h>
#include "types.h" #include "types.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#undef USE_INOTIFY
#if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1)
#define USE_INOTIFY 1
#include <sys/inotify.h>
int watch_fd = -1;
static void consume_inotify_events(int fd);
static int setup_inotify_watch(void);
#endif
/* A list of filenames associated with queued reminders */
typedef struct queuedfname {
struct queuedfname *next;
char const *fname;
} QueuedFilename;
/* List structure for holding queued reminders */ /* List structure for holding queued reminders */
typedef struct queuedrem { typedef struct queuedrem {
@@ -42,16 +59,19 @@ typedef struct queuedrem {
int RunDisabled; int RunDisabled;
int ntrig; int ntrig;
char const *text; char const *text;
char const *fname;
int lineno;
char passthru[PASSTHRU_LEN+1]; char passthru[PASSTHRU_LEN+1];
char sched[VAR_NAME_LEN+1]; char sched[VAR_NAME_LEN+1];
DynamicBuffer tags;
Trigger t; Trigger t;
TimeTrig tt; TimeTrig tt;
int red, green, blue;
} QueuedRem; } QueuedRem;
/* Global variables */ /* Global variables */
static QueuedRem *QueueHead; static QueuedRem *QueueHead = NULL;
static QueuedFilename *Files = NULL;
static time_t FileModTime; static time_t FileModTime;
static struct stat StatBuf; static struct stat StatBuf;
@@ -59,9 +79,108 @@ static void CheckInitialFile (void);
static int CalculateNextTime (QueuedRem *q); static int CalculateNextTime (QueuedRem *q);
static QueuedRem *FindNextReminder (void); static QueuedRem *FindNextReminder (void);
static int CalculateNextTimeUsingSched (QueuedRem *q); static int CalculateNextTimeUsingSched (QueuedRem *q);
static void DaemonWait (struct timeval *sleep_tv); static void ServerWait (struct timeval *sleep_tv);
static void reread (void); static void reread (void);
static void PrintQueue(void); static void PrintQueue(void);
static char const *QueueFilename(char const *fname);
static void chomp(DynamicBuffer *buf)
{
char *s = DBufValue(buf);
int l = DBufLen(buf);
while (l) {
if (s[l-1] == '\n') {
s[l-1] = 0;
DBufLen(buf)--;
l--;
} else {
break;
}
}
}
char const *SimpleTimeNoSpace(int tim)
{
char *s = (char *) SimpleTime(tim);
if (s && *s) {
size_t l = strlen(s);
if (l > 0 && s[l-1] == ' ') {
s[l-1] = 0;
}
}
return s;
}
/***************************************************************/
/* */
/* QueueFilename */
/* */
/* Add fname to the list of queued filenames if it's not */
/* already present. Either way, return a pointer to the */
/* filename. Returns NULL if out of memory */
/* */
/***************************************************************/
static QueuedFilename *last_file_found = NULL;
static char const *QueueFilename(char const *fname)
{
QueuedFilename *elem = Files;
/* Optimization: We are very likely in the same file as
before... */
if (last_file_found && !strcmp(fname, last_file_found->fname)) {
return last_file_found->fname;
}
/* No such luck; search the list */
while(elem) {
if (!strcmp(elem->fname, fname)) {
last_file_found = elem;
return elem->fname;
}
elem = elem->next;
}
/* Not found... queue it */
elem = NEW(QueuedFilename);
if (!elem) return NULL;
elem->fname = StrDup(fname);
if (!elem->fname) {
free(elem);
return NULL;
}
elem->next = Files;
Files = elem;
last_file_found = elem;
return elem->fname;
}
static void del_reminder(QueuedRem *qid)
{
QueuedRem *q = QueueHead;
QueuedRem *next;
if (!q) {
return;
}
if (q == qid) {
QueueHead = q->next;
if (q->text) free((void *) q->text);
free(q);
return;
}
while(q->next) {
next = q->next;
if (q->next == qid) {
q->next = q->next->next;
if (next->text) free((void *) next->text);
free(next);
return;
}
q = q->next;
}
}
static void del_reminder_ul(unsigned long qid) {
del_reminder((QueuedRem *) qid);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -72,7 +191,7 @@ static void PrintQueue(void);
/* */ /* */
/***************************************************************/ /***************************************************************/
int QueueReminder(ParsePtr p, Trigger *trig, int QueueReminder(ParsePtr p, Trigger *trig,
TimeTrig *tim, char const *sched) TimeTrig *tim, char const *sched)
{ {
QueuedRem *qelem; QueuedRem *qelem;
@@ -87,26 +206,36 @@ int QueueReminder(ParsePtr p, Trigger *trig,
if (!qelem) { if (!qelem) {
return E_NO_MEM; return E_NO_MEM;
} }
qelem->red = DefaultColorR;
qelem->green = DefaultColorG;
qelem->blue = DefaultColorB;
qelem->text = StrDup(p->pos); /* Guaranteed that parser is not nested. */ qelem->text = StrDup(p->pos); /* Guaranteed that parser is not nested. */
if (!qelem->text) { if (!qelem->text) {
free(qelem); free(qelem);
return E_NO_MEM; return E_NO_MEM;
} }
qelem->fname = QueueFilename(FileName);
if (!qelem->fname) {
free((void *) qelem->text);
free(qelem);
return E_NO_MEM;
}
qelem->lineno = LineNo;
NumQueued++; NumQueued++;
qelem->typ = trig->typ; qelem->typ = trig->typ;
strcpy(qelem->passthru, trig->passthru); strcpy(qelem->passthru, trig->passthru);
qelem->tt = *tim; qelem->tt = *tim;
qelem->t = *trig; qelem->t = *trig;
DBufInit(&(qelem->t.tags)); DBufInit(&(qelem->t.tags));
DBufPuts(&(qelem->t.tags), DBufValue(&(trig->tags)));
if (SynthesizeTags) {
AppendTag(&(qelem->t.tags), SynthesizeTag());
}
qelem->next = QueueHead; qelem->next = QueueHead;
qelem->RunDisabled = RunDisabled; qelem->RunDisabled = RunDisabled;
qelem->ntrig = 0; qelem->ntrig = 0;
strcpy(qelem->sched, sched); strcpy(qelem->sched, sched);
DBufInit(&(qelem->tags));
DBufPuts(&(qelem->tags), DBufValue(&(trig->tags)));
if (SynthesizeTags) {
AppendTag(&(qelem->tags), SynthesizeTag());
}
QueueHead = qelem; QueueHead = qelem;
return OK; return OK;
} }
@@ -141,6 +270,28 @@ SigContHandler(int d)
UNUSED(d); UNUSED(d);
} }
static void
print_num_queued(void)
{
int nqueued = 0;
QueuedRem *q = QueueHead;
while(q) {
if (q->tt.nexttime != NO_TIME) {
nqueued++;
}
q = q->next;
}
if (DaemonJSON) {
printf("{");
PrintJSONKeyPairString("response", "queued");
PrintJSONKeyPairInt("nqueued", nqueued);
printf("\"command\":\"STATUS\"}\n");
} else {
printf("NOTE queued %d\n", nqueued);
}
fflush(stdout);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* HandleQueuedReminders */ /* HandleQueuedReminders */
@@ -154,17 +305,26 @@ void HandleQueuedReminders(void)
int TimeToSleep; int TimeToSleep;
unsigned SleepTime; unsigned SleepTime;
Parser p; Parser p;
Trigger trig;
struct timeval tv; struct timeval tv;
struct timeval sleep_tv; struct timeval sleep_tv;
struct sigaction sa; struct sigaction sa;
char qid[64];
/* Suppress the BANNER from being issued */ /* Disable any potential pending SIGALRMs */
DidMsgReminder = 1; alarm(0);
/* Un-limit execution time */
unlimit_execution_time();
/* Turn off sorting -- otherwise, TriggerReminder has no effect! */ /* Turn off sorting -- otherwise, TriggerReminder has no effect! */
SortByDate = 0; SortByDate = 0;
/* Free FileName if necessary */
if (FileName) {
free(FileName);
FileName = NULL;
}
/* If we are not connected to a tty, then we must close the /* If we are not connected to a tty, then we must close the
* standard file descriptors. This is to prevent someone * standard file descriptors. This is to prevent someone
* doing: * doing:
@@ -174,7 +334,7 @@ void HandleQueuedReminders(void)
* processed correctly are RUN commands, provided they mail * processed correctly are RUN commands, provided they mail
* the result back or use their own resource (as a window). * the result back or use their own resource (as a window).
*/ */
if (!DontFork) { if (ShouldFork) {
maybe_close(STDIN_FILENO); maybe_close(STDIN_FILENO);
maybe_close(STDOUT_FILENO); maybe_close(STDOUT_FILENO);
maybe_close(STDERR_FILENO); maybe_close(STDERR_FILENO);
@@ -197,14 +357,18 @@ void HandleQueuedReminders(void)
q = q->next; q = q->next;
} }
if (!DontFork || Daemon) { if (ShouldFork || Daemon) {
sa.sa_handler = SigIntHandler; sa.sa_handler = SigIntHandler;
sa.sa_flags = 0; sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
(void) sigaction(SIGINT, &sa, NULL); (void) sigaction(SIGINT, &sa, NULL);
sa.sa_handler = SigContHandler; sa.sa_handler = SigContHandler;
(void) sigaction(SIGCONT, &sa, NULL); (void) sigaction(SIGCONT, &sa, NULL);
} }
#ifdef USE_INOTIFY
watch_fd = setup_inotify_watch();
#endif
/* Sit in a loop, issuing reminders when necessary */ /* Sit in a loop, issuing reminders when necessary */
while(1) { while(1) {
q = FindNextReminder(); q = FindNextReminder();
@@ -213,7 +377,7 @@ void HandleQueuedReminders(void)
if (!q && !Daemon) break; if (!q && !Daemon) break;
if (Daemon && !q) { if (Daemon && !q) {
if (Daemon < 0) { if (IsServerMode()) {
/* Sleep until midnight */ /* Sleep until midnight */
TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1); TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1);
} else { } else {
@@ -230,9 +394,7 @@ void HandleQueuedReminders(void)
SleepTime = 60*Daemon; SleepTime = 60*Daemon;
} }
/* Wake up once a minute to recalibrate sleep time in if (IsServerMode()) {
case of laptop hibernation */
if (Daemon < 0) {
/* Wake up on the next exact minute */ /* Wake up on the next exact minute */
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
sleep_tv.tv_sec = 60 - (tv.tv_sec % 60); sleep_tv.tv_sec = 60 - (tv.tv_sec % 60);
@@ -242,7 +404,9 @@ void HandleQueuedReminders(void)
} else { } else {
sleep_tv.tv_usec = 0; sleep_tv.tv_usec = 0;
} }
DaemonWait(&sleep_tv); ServerWait(&sleep_tv);
/* A DEL command might have deleted our queued reminder! */
q = FindNextReminder();
} else { } else {
sleep(SleepTime); sleep(SleepTime);
} }
@@ -260,10 +424,12 @@ void HandleQueuedReminders(void)
} }
} }
if (Daemon > 0 && SleepTime) CheckInitialFile(); if (Daemon > 0 && SleepTime) {
CheckInitialFile();
}
if (Daemon && !q) { if (Daemon && !q) {
if (Daemon < 0) { if (IsServerMode()) {
/* Sleep until midnight */ /* Sleep until midnight */
TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1); TimeToSleep = MINUTES_PER_DAY*60 - SystemTime(1);
} else { } else {
@@ -286,25 +452,52 @@ void HandleQueuedReminders(void)
(MaxLateMinutes == 0 || SystemTime(1) - (q->tt.nexttime * 60) <= 60 * MaxLateMinutes))) { (MaxLateMinutes == 0 || SystemTime(1) - (q->tt.nexttime * 60) <= 60 * MaxLateMinutes))) {
/* Trigger the reminder */ /* Trigger the reminder */
CreateParser(q->text, &p); CreateParser(q->text, &p);
trig.typ = q->typ;
strcpy(trig.passthru, q->passthru);
RunDisabled = q->RunDisabled; RunDisabled = q->RunDisabled;
if (Daemon < 0) { if (IsServerMode() && q->typ != RUN_TYPE) {
printf("NOTE reminder %s", if (DaemonJSON) {
SimpleTime(q->tt.ttime)); printf("{\"response\":\"reminder\",");
printf("%s", SimpleTime(MinutesPastMidnight(1))); snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
if (!*DBufValue(&q->tags)) { PrintJSONKeyPairString("qid", qid);
printf("*\n"); PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
} else { PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
printf("%s\n", DBufValue(&(q->tags))); PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
} } else {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(MinutesPastMidnight(1)));
if (!*DBufValue(&q->t.tags)) {
printf("*\n");
} else {
printf("%s\n", DBufValue(&(q->t.tags)));
}
}
} }
/* Set up global variables so some functions like trigdate() /* Set up global variables so some functions like trigdate()
and trigtime() work correctly */ and trigtime() work correctly */
SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1); SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1);
(void) TriggerReminder(&p, &trig, &q->tt, DSEToday, 1); FileName = (char *) q->fname;
if (Daemon < 0) { DefaultColorR = q->red;
DefaultColorG = q->green;
DefaultColorB = q->blue;
/* Make a COPY of q->t because TriggerReminder can change q->t.typ */
Trigger tcopy = q->t;
if (DaemonJSON) {
DynamicBuffer out;
DBufInit(&out);
(void) TriggerReminder(&p, &tcopy, &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, &tcopy, &q->tt, DSEToday, 1, NULL);
}
FileName = NULL;
if (IsServerMode() && !DaemonJSON && q->typ != RUN_TYPE) {
printf("NOTE endreminder\n"); printf("NOTE endreminder\n");
} }
fflush(stdout); fflush(stdout);
@@ -314,14 +507,23 @@ void HandleQueuedReminders(void)
/* Calculate the next trigger time */ /* Calculate the next trigger time */
q->tt.nexttime = CalculateNextTime(q); q->tt.nexttime = CalculateNextTime(q);
/* If trigger time is way in the past because computer has been
suspended or hibernated, remove from queue */
if (q->tt.nexttime != NO_TIME) { 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 && if (q->tt.ttime < MinutesPastMidnight(1) - MaxLateMinutes &&
q->tt.nexttime < MinutesPastMidnight(1) - MaxLateMinutes) { q->tt.nexttime < MinutesPastMidnight(1) - MaxLateMinutes) {
q->tt.nexttime = NO_TIME; 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(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
@@ -441,7 +643,25 @@ static void CheckInitialFile(void)
/* If date has rolled around, or file has changed, spawn a new version. */ /* If date has rolled around, or file has changed, spawn a new version. */
time_t tim = FileModTime; time_t tim = FileModTime;
int y, m, d; int y, m, d;
#ifdef USE_INOTIFY
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
int n;
#endif
#ifdef USE_INOTIFY
/* If there are any inotify events, reread */
if (watch_fd >= 0) {
while(1) {
n = read(watch_fd, buf, sizeof(buf));
if (n < 0 && errno == EINTR) continue;
if (n > 0) {
close(watch_fd);
reread();
}
break;
}
}
#endif
if (stat(InitialFile, &StatBuf) == 0) tim = StatBuf.st_mtime; if (stat(InitialFile, &StatBuf) == 0) tim = StatBuf.st_mtime;
if (tim != FileModTime || if (tim != FileModTime ||
RealToday != SystemDate(&y, &m, &d)) { RealToday != SystemDate(&y, &m, &d)) {
@@ -519,7 +739,11 @@ static void
json_queue(QueuedRem const *q) json_queue(QueuedRem const *q)
{ {
int done = 0; int done = 0;
if (DaemonJSON) {
printf("{\"response\":\"queue\",\"queue\":");
}
printf("["); printf("[");
char idbuf[64];
while(q) { while(q) {
if (q->tt.nexttime == NO_TIME) { if (q->tt.nexttime == NO_TIME) {
q = q->next; q = q->next;
@@ -530,6 +754,14 @@ json_queue(QueuedRem const *q)
} }
done = 1; done = 1;
printf("{"); printf("{");
WriteJSONTrigger(&(q->t), 1, DSEToday);
WriteJSONTimeTrigger(&(q->tt));
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
PrintJSONKeyPairString("qid", idbuf);
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairString("filename", q->fname);
PrintJSONKeyPairInt("lineno", q->lineno);
switch(q->typ) { switch(q->typ) {
case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break; case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break;
case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break; case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break;
@@ -539,29 +771,12 @@ json_queue(QueuedRem const *q)
case PS_TYPE: PrintJSONKeyPairString("type", "PS_TYPE"); break; case PS_TYPE: PrintJSONKeyPairString("type", "PS_TYPE"); break;
case PSF_TYPE: PrintJSONKeyPairString("type", "PSF_TYPE"); break; case PSF_TYPE: PrintJSONKeyPairString("type", "PSF_TYPE"); break;
case MSF_TYPE: PrintJSONKeyPairString("type", "MSF_TYPE"); break; case MSF_TYPE: PrintJSONKeyPairString("type", "MSF_TYPE"); break;
case PASSTHRU_TYPE: PrintJSONKeyPairString("type", "PASSTHRU_TYPE"); break; case PASSTHRU_TYPE:
PrintJSONKeyPairString("type", "PASSTHRU_TYPE");
PrintJSONKeyPairString("passthru", q->passthru);
break;
default: PrintJSONKeyPairString("type", "?"); break; default: PrintJSONKeyPairString("type", "?"); break;
} }
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairTime("ttime", q->tt.ttime);
PrintJSONKeyPairTime("nextttime", q->tt.nexttime);
PrintJSONKeyPairInt("delta", q->tt.delta);
if (q->tt.rep != NO_TIME) {
PrintJSONKeyPairInt("rep", q->tt.rep);
}
if (q->tt.duration != NO_TIME) {
PrintJSONKeyPairInt("duration", q->tt.duration);
}
if (q->passthru[0]) {
PrintJSONKeyPairString("passthru", q->passthru);
}
if (q->sched[0]) {
PrintJSONKeyPairString("sched", q->sched);
}
if (DBufLen(&(q->tags))) {
PrintJSONKeyPairString("tags", DBufValue(&(q->tags)));
}
/* Last one is a special case - no trailing comma */ /* Last one is a special case - no trailing comma */
printf("\""); printf("\"");
@@ -575,30 +790,48 @@ json_queue(QueuedRem const *q)
printf("\"}"); printf("\"}");
q = q->next; q = q->next;
} }
printf("]\n"); printf("]");
if (DaemonJSON) {
printf(",\"command\":\"QUEUE\"}\n");
} else {
printf("\n");
}
} }
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DaemonWait */ /* ServerWait */
/* */ /* */
/* Sleep or read command from stdin in "daemon -1" mode */ /* Sleep or read command from stdin in server mode */
/* */ /* */
/***************************************************************/ /***************************************************************/
static void DaemonWait(struct timeval *sleep_tv) static void ServerWait(struct timeval *sleep_tv)
{ {
fd_set readSet; fd_set readSet;
int retval; int retval;
int y, m, d; int y, m, d;
int max = 1;
char cmdLine[256]; char cmdLine[256];
FD_ZERO(&readSet); FD_ZERO(&readSet);
FD_SET(0, &readSet); FD_SET(0, &readSet);
retval = select(1, &readSet, NULL, NULL, sleep_tv);
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
FD_SET(watch_fd, &readSet);
if (watch_fd > max-1)
max = watch_fd+1;
}
#endif
retval = select(max, &readSet, NULL, NULL, sleep_tv);
/* If date has rolled around, restart */ /* If date has rolled around, restart */
if (RealToday != SystemDate(&y, &m, &d)) { if (RealToday != SystemDate(&y, &m, &d)) {
printf("NOTE newdate\nNOTE reread\n"); if (DaemonJSON) {
printf("{\"response\":\"newdate\"}\n{\"response\":\"reread\",\"command\":\"newdate\"}\n");
} else {
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout); fflush(stdout);
reread(); reread();
} }
@@ -606,6 +839,24 @@ static void DaemonWait(struct timeval *sleep_tv)
/* If nothing readable or interrupted system call, return */ /* If nothing readable or interrupted system call, return */
if (retval <= 0) return; if (retval <= 0) return;
/* If inotify watch descriptor is readable, handle it */
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
if (FD_ISSET(watch_fd, &readSet)) {
consume_inotify_events(watch_fd);
if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"inotify\"}\n");
} else {
/* In deprecated server mode, we need to spit out
a NOTE newdate to force the front-end to redraw
the calendar */
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout);
reread();
}
}
#endif
/* If stdin not readable, return */ /* If stdin not readable, return */
if (!FD_ISSET(0, &readSet)) return; if (!FD_ISSET(0, &readSet)) return;
@@ -622,54 +873,74 @@ static void DaemonWait(struct timeval *sleep_tv)
if (!strcmp(cmdLine, "EXIT\n")) { if (!strcmp(cmdLine, "EXIT\n")) {
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} else if (!strcmp(cmdLine, "STATUS\n")) { } else if (!strcmp(cmdLine, "STATUS\n")) {
int nqueued = 0; print_num_queued();
QueuedRem *q = QueueHead;
while(q) {
if (q->tt.nexttime != NO_TIME) {
nqueued++;
}
q = q->next;
}
printf("NOTE queued %d\n", nqueued);
fflush(stdout);
} else if (!strcmp(cmdLine, "QUEUE\n")) { } else if (!strcmp(cmdLine, "QUEUE\n")) {
printf("NOTE queue\n"); if (DaemonJSON) {
QueuedRem *q = QueueHead; json_queue(QueueHead);
while (q) { } else {
if (q->tt.nexttime != NO_TIME) { printf("NOTE queue\n");
switch (q->typ) { QueuedRem *q = QueueHead;
case NO_TYPE: printf("NO_TYPE "); break; while (q) {
case MSG_TYPE: printf("MSG_TYPE "); break; if (q->tt.nexttime != NO_TIME) {
case RUN_TYPE: printf("RUN_TYPE "); break; switch (q->typ) {
case CAL_TYPE: printf("CAL_TYPE "); break; case NO_TYPE: printf("NO_TYPE"); break;
case SAT_TYPE: printf("SAT_TYPE "); break; case MSG_TYPE: printf("MSG_TYPE"); break;
case PS_TYPE: printf("PS_TYPE "); break; case RUN_TYPE: printf("RUN_TYPE"); break;
case PSF_TYPE: printf("PSF_TYPE "); break; case CAL_TYPE: printf("CAL_TYPE"); break;
case MSF_TYPE: printf("MSF_TYPE "); break; case SAT_TYPE: printf("SAT_TYPE"); break;
case PASSTHRU_TYPE: printf("PASSTHRU_TYPE "); break; case PS_TYPE: printf("PS_TYPE"); break;
default: printf("? "); break; case PSF_TYPE: printf("PSF_TYPE"); break;
} case MSF_TYPE: printf("MSF_TYPE"); break;
printf("RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1)); case PASSTHRU_TYPE: printf("PASSTHRU_TYPE"); break;
printf("%s %s %s\n", default: printf("?"); break;
(q->passthru[0] ? q->passthru : "*"), }
(q->sched[0] ? q->sched : "*"), printf(" RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1));
q->text ? q->text : "NULL"); printf("%s %s %s\n",
(q->passthru[0] ? q->passthru : "*"),
(q->sched[0] ? q->sched : "*"),
q->text ? q->text : "NULL");
}
q = q->next;
} }
q = q->next; printf("NOTE endqueue\n");
} }
printf("NOTE endqueue\n");
fflush(stdout); fflush(stdout);
} else if (!strcmp(cmdLine, "JSONQUEUE\n")) { } else if (!strcmp(cmdLine, "JSONQUEUE\n")) {
printf("NOTE JSONQUEUE\n"); if (!DaemonJSON) {
printf("NOTE JSONQUEUE\n");
}
json_queue(QueueHead); json_queue(QueueHead);
printf("NOTE ENDJSONQUEUE\n"); if (!DaemonJSON) {
printf("NOTE ENDJSONQUEUE\n");
}
fflush(stdout); fflush(stdout);
} else if (!strcmp(cmdLine, "REREAD\n")) { } else if (!strcmp(cmdLine, "REREAD\n")) {
printf("NOTE reread\n"); if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"REREAD\"}\n");
} else {
printf("NOTE reread\n");
}
fflush(stdout); fflush(stdout);
reread(); reread();
} else if (!strncmp(cmdLine, "DEL ", 4)) {
unsigned long qid;
if (sscanf(cmdLine, "DEL %lx", &qid) == 1) {
del_reminder_ul(qid);
}
print_num_queued();
fflush(stdout);
} else { } else {
printf("ERR Invalid daemon command: %s", cmdLine); if (DaemonJSON) {
size_t l = strlen(cmdLine);
if (l && cmdLine[l-1] == '\n') {
cmdLine[l-1] = 0;
}
printf("{\"response\":\"error\",\"error\":\"Unknown command\",\"command\":\"");
PrintJSONString(cmdLine);
printf("\"}\n");
} else {
printf("ERR Invalid daemon command: %s", cmdLine);
}
fflush(stdout); fflush(stdout);
} }
} }
@@ -686,3 +957,56 @@ static void reread(void)
execvp(ArgV[0], (char **) ArgV); execvp(ArgV[0], (char **) ArgV);
} }
#ifdef USE_INOTIFY
static void consume_inotify_events(int fd)
{
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
int n;
struct timespec sleeptime;
int slept = 0;
/* Consume all the inotify events */
while(1) {
n = read(fd, buf, sizeof(buf));
if (n > 0) {
/* Something new since we slept */
slept = 0;
}
if (n < 0) {
if (errno == EINTR) continue;
if (slept) {
/* Nothing new since we slept */
return;
}
slept = 1;
/* HACK: sleep for 0.2 seconds to let multiple events queue up so we
only do a single reread */
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 200000000;
nanosleep(&sleeptime, NULL);
}
}
}
static int setup_inotify_watch(void)
{
int fd;
/* Don't inotify_watch stdin */
if (!strcmp(InitialFile, "-")) {
return -1;
}
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd < 0) {
return fd;
}
if (inotify_add_watch(fd, InitialFile, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) {
close(fd);
return -1;
}
return fd;
}
#endif

View File

@@ -5,7 +5,7 @@
/* Print a PostScript calendar. */ /* Print a PostScript calendar. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -350,7 +350,7 @@ int main(int argc, char *argv[])
!strcmp(DBufValue(&buf), PSBEGIN2)) { !strcmp(DBufValue(&buf), PSBEGIN2)) {
if (!validfile) { if (!validfile) {
if (Verbose) { if (Verbose) {
fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2023 by Dianne Skoll\n\n", VERSION); fprintf(stderr, "Rem2PS: Version %s Copyright 1992-2024 by Dianne Skoll\n\n", VERSION);
fprintf(stderr, "Generating PostScript calendar\n"); fprintf(stderr, "Generating PostScript calendar\n");
} }
} }

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
/* classifying the tokens parsed. */ /* classifying the tokens parsed. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -39,23 +39,24 @@ while (isdigit(*(string))) { \
Token TokArray[] = { Token TokArray[] = {
/* NAME MINLEN TYPE VALUE */ /* NAME MINLEN TYPE VALUE */
{ "addomit", 7, T_AddOmit, 0 }, { "addomit", 7, T_AddOmit, 0 },
{ "after", 3, T_Skip, AFTER_SKIP }, { "after", 5, T_Skip, AFTER_SKIP },
{ "april", 3, T_Month, 3 }, { "april", 3, T_Month, 3 },
{ "at", 2, T_At, 0 }, { "at", 2, T_At, 0 },
{ "august", 3, T_Month, 7 }, { "august", 3, T_Month, 7 },
{ "banner", 3, T_Banner, 0 }, { "banner", 3, T_Banner, 0 },
{ "before", 3, T_Skip, BEFORE_SKIP }, { "before", 6, T_Skip, BEFORE_SKIP },
{ "cal", 3, T_RemType, CAL_TYPE }, { "cal", 3, T_RemType, CAL_TYPE },
{ "clear-omit-context", 5, T_Clr, 0 }, { "clear-omit-context", 5, T_Clr, 0 },
{ "debug", 5, T_Debug, 0 }, { "debug", 5, T_Debug, 0 },
{ "december", 3, T_Month, 11 }, { "december", 3, T_Month, 11 },
{ "do", 2, T_IncludeR, 0 }, { "do", 2, T_IncludeR, 0 },
{ "dumpvars", 4, T_Dumpvars, 0 }, { "dumpvars", 4, T_Dumpvars, 0 },
{ "duration", 3, T_Duration, 0 }, { "duration", 8, T_Duration, 0 },
{ "else", 4, T_Else, 0 }, { "else", 4, T_Else, 0 },
{ "endif", 5, T_EndIf, 0 }, { "endif", 5, T_EndIf, 0 },
{ "errmsg", 6, T_ErrMsg, 0 }, { "errmsg", 6, T_ErrMsg, 0 },
{ "exit", 4, T_Exit, 0 }, { "exit", 4, T_Exit, 0 },
{ "expr", 4, T_Expr, 0 },
{ "february", 3, T_Month, 1 }, { "february", 3, T_Month, 1 },
{ "first", 5, T_Ordinal, 0 }, { "first", 5, T_Ordinal, 0 },
{ "flush", 5, T_Flush, 0 }, { "flush", 5, T_Flush, 0 },
@@ -84,9 +85,9 @@ Token TokArray[] = {
{ "noqueue", 7, T_NoQueue, 0 }, { "noqueue", 7, T_NoQueue, 0 },
{ "november", 3, T_Month, 10 }, { "november", 3, T_Month, 10 },
{ "october", 3, T_Month, 9 }, { "october", 3, T_Month, 9 },
{ "omit", 3, T_Omit, 0 }, { "omit", 4, T_Omit, 0 },
{ "omitfunc", 8, T_OmitFunc, 0 }, { "omitfunc", 8, T_OmitFunc, 0 },
{ "once", 3, T_Once, 0 }, { "once", 4, T_Once, 0 },
{ "pop-omit-context", 3, T_Pop, 0 }, { "pop-omit-context", 3, T_Pop, 0 },
{ "preserve", 8, T_Preserve, 0 }, { "preserve", 8, T_Preserve, 0 },
{ "priority", 8, T_Priority, 0 }, { "priority", 8, T_Priority, 0 },
@@ -102,7 +103,7 @@ Token TokArray[] = {
{ "second", 6, T_Ordinal, 1 }, { "second", 6, T_Ordinal, 1 },
{ "september", 3, T_Month, 8 }, { "september", 3, T_Month, 8 },
{ "set", 3, T_Set, 0 }, { "set", 3, T_Set, 0 },
{ "skip", 3, T_Skip, SKIP_SKIP }, { "skip", 4, T_Skip, SKIP_SKIP },
{ "special", 7, T_RemType, PASSTHRU_TYPE }, { "special", 7, T_RemType, PASSTHRU_TYPE },
{ "sunday", 3, T_WkDay, 6 }, { "sunday", 3, T_WkDay, 6 },
{ "tag", 3, T_Tag, 0 }, { "tag", 3, T_Tag, 0 },
@@ -111,13 +112,28 @@ Token TokArray[] = {
{ "thursday", 3, T_WkDay, 3 }, { "thursday", 3, T_WkDay, 3 },
{ "tuesday", 3, T_WkDay, 1 }, { "tuesday", 3, T_WkDay, 1 },
{ "unset", 5, T_UnSet, 0 }, { "unset", 5, T_UnSet, 0 },
{ "until", 3, T_Until, 0 }, { "until", 5, T_Until, 0 },
{ "warn", 4, T_Warn, 0 }, { "warn", 4, T_Warn, 0 },
{ "wednesday", 3, T_WkDay, 2 } { "wednesday", 3, T_WkDay, 2 }
}; };
static int TokStrCmp (Token const *t, char const *s); static int TokStrCmp (Token const *t, char const *s);
static void
init_token(Token *t)
{
t->name = NULL;
t->type = T_Illegal;
t->val = 0;
}
static void
token_error(Token *t, int errcode)
{
t->type = T_Illegal;
t->val = -errcode;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* FindInitialToken */ /* FindInitialToken */
@@ -131,7 +147,7 @@ char const *FindInitialToken(Token *tok, char const *s)
DynamicBuffer buf; DynamicBuffer buf;
DBufInit(&buf); DBufInit(&buf);
tok->type = T_Illegal; init_token(tok);
while (isempty(*s)) s++; while (isempty(*s)) s++;
@@ -158,7 +174,7 @@ void FindToken(char const *s, Token *tok)
int top, bot, mid, r, max; int top, bot, mid, r, max;
int l; int l;
tok->type = T_Illegal; init_token(tok);
if (! *s) { if (! *s) {
tok->type = T_Empty; tok->type = T_Empty;
return; return;
@@ -232,9 +248,9 @@ void FindNumericToken(char const *s, Token *t)
int mult = 1, hour, min; int mult = 1, hour, min;
char const *s_orig = s; char const *s_orig = s;
int ampm = 0; int ampm = 0;
int r;
t->type = T_Illegal; init_token(t);
t->val = 0;
if (isdigit(*s)) { if (isdigit(*s)) {
PARSENUM(t->val, s); PARSENUM(t->val, s);
@@ -242,22 +258,37 @@ void FindNumericToken(char const *s, Token *t)
if (*s == '-' || *s == '/') { if (*s == '-' || *s == '/') {
char const *p = s_orig; char const *p = s_orig;
int dse, tim; int dse, tim;
if (ParseLiteralDate(&p, &dse, &tim) == OK) { r = ParseLiteralDateOrTime(&p, &dse, &tim);
if (*p) return; if (r == OK) {
if (*p) {
if (tim == NO_TIME) {
t->val = -E_BAD_DATE;
} else {
t->val = -E_BAD_TIME;
}
return;
}
if (tim == NO_TIME) { if (tim == NO_TIME) {
t->type = T_Date; t->type = T_Date;
t->val = dse; t->val = dse;
return; return;
} }
if (dse == NO_DATE) {
t->type = T_Time;
t->val = tim;
return;
}
t->type = T_DateTime; t->type = T_DateTime;
t->val = MINUTES_PER_DAY * dse + tim; t->val = MINUTES_PER_DAY * dse + tim;
} } else {
token_error(t, r);
}
return; return;
} }
/* If we hit a comma, swallow it. This allows stuff /* If we hit a comma, swallow it. This allows stuff
like Jan 6, 1998 */ like Jan 6, 1998 */
if (*s == ',') { if (*s == ',' && *(s+1) == 0) {
/* Classify the number we've got */ /* Classify the number we've got */
if (t->val >= BASE && t->val <= BASE+YR_RANGE) t->type = T_Year; 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; else if (t->val >= 1 && t->val <= 31) t->type = T_Day;
@@ -268,8 +299,16 @@ void FindNumericToken(char const *s, Token *t)
if (*s == ':' || *s == '.' || *s == TimeSep) { if (*s == ':' || *s == '.' || *s == TimeSep) {
s++; s++;
hour = t->val; hour = t->val;
if (!isdigit(*s)) {
token_error(t, E_BAD_TIME);
return;
}
PARSENUM(min, s); PARSENUM(min, s);
if (min > 59) return; /* Illegal time */ if (min > 59) {
/* Illegal time */
token_error(t, E_BAD_TIME);
return;
}
/* Check for p[m] or a[m] */ /* Check for p[m] or a[m] */
if (*s == 'A' || *s == 'a' || *s == 'P' || *s == 'p') { if (*s == 'A' || *s == 'a' || *s == 'P' || *s == 'p') {
ampm = tolower(*s); ampm = tolower(*s);
@@ -278,9 +317,15 @@ void FindNumericToken(char const *s, Token *t)
s++; s++;
} }
} }
if (*s) return; /* Illegal time */ if (*s) {
token_error(t, E_BAD_TIME);
return;
}
if (ampm) { if (ampm) {
if (hour < 1 || hour > 12) return; if (hour < 1 || hour > 12) {
token_error(t, E_BAD_TIME);
return;
}
if (ampm == 'a') { if (ampm == 'a') {
if (hour == 12) { if (hour == 12) {
hour = 0; hour = 0;
@@ -302,45 +347,70 @@ void FindNumericToken(char const *s, Token *t)
} }
/* If we hit a non-digit, error! */ /* If we hit a non-digit, error! */
if (*s) return; if (*s) {
token_error(t, E_BAD_NUMBER);
return;
}
/* Classify the number we've got */ /* Classify the number we've got */
if (t->val >= BASE && t->val <= BASE+YR_RANGE) t->type = T_Year; 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; else if (t->val >= 1 && t->val <= 31) t->type = T_Day;
else t->type = T_Number; else t->type = T_Number;
return; return;
} else if (*s == '*') { }
switch (*s) {
case '*':
s++; s++;
PARSENUM(t->val, s); PARSENUM(t->val, s);
if (*s) return; /* Illegal token if followed by non-numeric char */ if (*s) {
/* Illegal token if followed by non-numeric char */
token_error(t, E_BAD_NUMBER);
return;
}
t->type = T_Rep; t->type = T_Rep;
return; return;
} else if (*s == '+') {
case '+':
s++; s++;
if (*s == '+') { mult = -1; s++; } if (*s == '+') { mult = -1; s++; }
PARSENUM(t->val, s); PARSENUM(t->val, s);
if (*s) return; /* Illegal token if followed by non-numeric char */ if (*s) {
/* Illegal token if followed by non-numeric char */
token_error(t, E_BAD_NUMBER);
return;
}
t->type = T_Delta; t->type = T_Delta;
t->val *= mult; t->val *= mult;
return; return;
} else if (*s == '-') { case '-':
s++; s++;
if (*s == '-') { mult = -1; s++; } if (*s == '-') { mult = -1; s++; }
PARSENUM(t->val, s); PARSENUM(t->val, s);
if (*s) return; /* Illegal token if followed by non-numeric char */ if (*s) {
/* Illegal token if followed by non-numeric char */
token_error(t, E_BAD_NUMBER);
return;
}
t->type = T_Back; t->type = T_Back;
t->val *= mult; t->val *= mult;
return; return;
} else if (*s == '~') {
case '~':
s++; s++;
if (*s == '~') { mult = -1; s++; } if (*s == '~') { mult = -1; s++; }
PARSENUM(t->val, s); PARSENUM(t->val, s);
if (*s) return; /* Illegal token if followed by non-numeric char */ if (*s) {
/* Illegal token if followed by non-numeric char */
token_error(t, E_BAD_NUMBER);
return;
}
t->type = T_BackAdj; t->type = T_BackAdj;
t->val *= mult; t->val *= mult;
return; return;
default: return;
} }
return; /* Unknown token type */
} }
@@ -367,3 +437,43 @@ static int TokStrCmp(Token const *t, char const *s)
if (!*s || (*s == ',' && !*(s+1))) return 0; if (!*s || (*s == ',' && !*(s+1))) return 0;
return (*tk - tolower(*s)); return (*tk - tolower(*s));
} }
static void
print_token(Token *tok)
{
if (tok->MinLen < (int) strlen(tok->name)) {
printf("%.*s\n", tok->MinLen, tok->name);
}
printf("%s\n", tok->name);
}
void
print_remind_tokens(void)
{
int i;
Token *tok;
int num = (int) (sizeof(TokArray) / sizeof(TokArray[0]));
printf("# Remind Tokens\n\n");
for (i=0; i<num; i++) {
tok = &TokArray[i];
if (tok->type != T_Month && tok->type != T_WkDay) {
print_token(tok);
}
}
printf("\n# Month Names\n\n");
for (i=0; i<num; i++) {
tok = &TokArray[i];
if (tok->type == T_Month) {
print_token(tok);
}
}
printf("\n# Weekdays\n\n");
for (i=0; i<num; i++) {
tok = &TokArray[i];
if (tok->type == T_WkDay) {
print_token(tok);
}
}
}

View File

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

View File

@@ -5,7 +5,7 @@
/* Type definitions all dumped here. */ /* Type definitions all dumped here. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -13,6 +13,22 @@
#include <limits.h> #include <limits.h>
#include "dynbuf.h" #include "dynbuf.h"
typedef struct udf_struct UserFunc;
/* 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 CONST_INT_TYPE 7 /* Only for system variables */
#define BEG_OF_EXPR '['
#define END_OF_EXPR ']'
#define COMMA ','
/* Values */ /* Values */
typedef struct { typedef struct {
char type; char type;
@@ -22,29 +38,62 @@ typedef struct {
} v; } v;
} Value; } Value;
/* Define the type of operators */ /* New-style expr_node structure and constants */
typedef struct { enum expr_node_type
char const *name; {
char prec; N_FREE,
char type; N_ERROR,
int (*func)(void); N_CONSTANT,
} Operator; N_SHORT_STR,
N_LOCAL_VAR,
N_SHORT_VAR,
N_VARIABLE,
N_SHORT_SYSVAR,
N_SYSVAR,
N_BUILTIN_FUNC,
N_SHORT_USER_FUNC,
N_USER_FUNC,
N_OPERATOR,
};
/* Structure for passing in Nargs and out RetVal from functions */ /* Structure for passing in Nargs and out RetVal from functions */
typedef struct { typedef struct {
int nargs; int nargs;
Value *args;
Value retval; Value retval;
} func_info; } func_info;
/* Forward reference */
typedef struct expr_node_struct expr_node;
/* Define the type of user-functions */ /* Define the type of user-functions */
typedef struct { typedef struct {
char const *name; char const *name;
char minargs; char minargs;
char maxargs; char maxargs;
char is_constant; char is_constant;
/* Old-style function calling convention */
int (*func)(func_info *); int (*func)(func_info *);
/* New-style function calling convention */
int (*newfunc)(expr_node *node, Value *locals, Value *ans, int *nonconst);
} BuiltinFunc; } BuiltinFunc;
#define SHORT_NAME_BUF 16
typedef struct expr_node_struct {
struct expr_node_struct *child;
struct expr_node_struct *sibling;
enum expr_node_type type;
int num_kids;
union {
Value value;
int arg;
BuiltinFunc *builtin_func;
char name[SHORT_NAME_BUF];
int (*operator_func) (struct expr_node_struct *node, Value *locals, Value *ans, int *nonconst);
} u;
} expr_node;
/* Define the structure of a variable */ /* Define the structure of a variable */
typedef struct var { typedef struct var {
struct var *next; struct var *next;
@@ -142,6 +191,8 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define MSF_TYPE 7 #define MSF_TYPE 7
#define PASSTHRU_TYPE 8 #define PASSTHRU_TYPE 8
/* For function arguments */
#define NO_MAX 127
/* DEFINES for debugging flags */ /* DEFINES for debugging flags */
#define DB_PRTLINE 1 #define DB_PRTLINE 1
@@ -150,6 +201,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_DUMP_VARS 8 #define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16 #define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32 #define DB_TRACE_FILES 32
#define DB_PARSE_EXPR 64
/* Enumeration of the tokens */ /* Enumeration of the tokens */
enum TokTypes enum TokTypes
@@ -183,7 +235,8 @@ enum TokTypes
T_MaybeUncomputable, T_MaybeUncomputable,
T_Ordinal, T_Ordinal,
T_In, T_In,
T_LastBack T_LastBack,
T_Expr
}; };
/* The structure of a token */ /* The structure of a token */
@@ -243,3 +296,27 @@ typedef struct {
#define TERMINAL_BACKGROUND_UNKNOWN -1 #define TERMINAL_BACKGROUND_UNKNOWN -1
#define TERMINAL_BACKGROUND_DARK 0 #define TERMINAL_BACKGROUND_DARK 0
#define TERMINAL_BACKGROUND_LIGHT 1 #define TERMINAL_BACKGROUND_LIGHT 1
typedef int (*SysVarFunc)(int, Value *);
/* The structure of a system variable */
typedef struct {
char const *name;
char modifiable;
int type;
void *value;
int min; /* Or const-value */
int max;
} SysVar;
/* Define the data structure used to hold a user-defined function */
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
expr_node *node;
char **args;
int nargs;
char const *filename;
int lineno;
int recurse_flag;
} UserFunc;

View File

@@ -6,7 +6,7 @@
/* functions. */ /* functions. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -16,43 +16,46 @@
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "types.h" #include "types.h"
#include "globals.h" #include "globals.h"
#include "protos.h" #include "protos.h"
#include "err.h" #include "err.h"
#include "expr.h"
#define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */ #define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */
/* Define the data structure used to hold a user-defined function */
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
char const *text;
Var *locals;
char IsActive;
int nargs;
char const *filename;
int lineno;
} UserFunc;
/* The hash table */ /* The hash table */
static UserFunc *FuncHash[FUNC_HASH_SIZE]; static UserFunc *FuncHash[FUNC_HASH_SIZE];
/* Access to built-in functions */
extern int NumFuncs;
extern BuiltinFunc Func[];
/* We need access to the expression evaluation stack */
extern Value ValStack[];
extern int ValStackPtr;
static void DestroyUserFunc (UserFunc *f); static void DestroyUserFunc (UserFunc *f);
static void FUnset (char const *name); static void FUnset (char const *name);
static void FSet (UserFunc *f); static void FSet (UserFunc *f);
static int SetUpLocalVars (UserFunc *f);
static void DestroyLocalVals (UserFunc *f); /***************************************************************/
/* */
/* HashVal */
/* Given a string, compute the hash value. */
/* */
/***************************************************************/
unsigned int HashVal_nocase(char const *str)
{
register unsigned int i=0;
register unsigned int j=1;
register unsigned int len=0;
while(*str && len < VAR_NAME_LEN) {
i += j * (*str);
str++;
len++;
j = 3-j;
}
return i;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -75,6 +78,7 @@ int DoFunset(ParsePtr p)
break; break;
} }
seen_one = 1; seen_one = 1;
strtolower(DBufValue(&buf));
FUnset(DBufValue(&buf)); FUnset(DBufValue(&buf));
DBufFree(&buf); DBufFree(&buf);
} }
@@ -93,8 +97,11 @@ int DoFset(ParsePtr p)
{ {
int r; int r;
int c; int c;
int i;
UserFunc *func; UserFunc *func;
Var *v; UserFunc *existing;
Var *locals = NULL;
Var local_array[MAX_FUNC_ARGS];
int orig_namelen; int orig_namelen;
DynamicBuffer buf; DynamicBuffer buf;
@@ -108,6 +115,25 @@ int DoFset(ParsePtr p)
} }
orig_namelen = buf.len; orig_namelen = buf.len;
/* Convert to lower-case */
strtolower(DBufValue(&buf));
/* If the function exists and was defined at the same line of the same
file, do nothing */
existing = FindUserFunc(DBufValue(&buf));
if (existing) {
if (!strcmp(existing->filename, FileName) &&
strcmp(existing->filename, "[cmdline]") &&
existing->lineno == LineNo) {
DBufFree(&buf);
/* We already have it! Our work here is done. */
return OK;
}
/* Warn about redefinition */
Wprint("Function %s redefined (previously defined at %s:%d)",
existing->name, existing->filename, existing->lineno);
}
/* Should be followed by '(' */ /* Should be followed by '(' */
c = ParseNonSpaceChar(p, &r, 0); c = ParseNonSpaceChar(p, &r, 0);
if (r) { if (r) {
@@ -134,28 +160,26 @@ int DoFset(ParsePtr p)
return E_NO_MEM; return E_NO_MEM;
} }
func->lineno = LineNo; func->lineno = LineNo;
func->recurse_flag = 0;
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf); DBufFree(&buf);
if (!Hush) { if (!Hush) {
if (FindFunc(func->name, Func, NumFuncs)) { if (FindBuiltinFunc(func->name)) {
Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name); Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name);
} }
} }
func->locals = NULL; func->node = NULL;
func->text = NULL;
func->IsActive = 0;
func->nargs = 0; func->nargs = 0;
func->args = NULL;
/* Get the local variables - we insert the local variables in REVERSE /* Get the local variables */
order, but that's OK, because we pop them off the stack in reverse
order, too, so everything works out just fine. */
c=ParseNonSpaceChar(p, &r, 1); c=ParseNonSpaceChar(p, &r, 1);
if (r) return r; if (r) return r;
if (c == ')') { if (c == ')') {
(void) ParseNonSpaceChar(p, &r, 0); (void) ParseNonSpaceChar(p, &r, 0);
} } else {
else { locals = local_array;
while(1) { while(1) {
if ( (r=ParseIdentifier(p, &buf)) ) return r; if ( (r=ParseIdentifier(p, &buf)) ) return r;
if (*DBufValue(&buf) == '$') { if (*DBufValue(&buf) == '$') {
@@ -163,19 +187,31 @@ int DoFset(ParsePtr p)
DestroyUserFunc(func); DestroyUserFunc(func);
return E_BAD_ID; return E_BAD_ID;
} }
v = NEW(Var); /* If we've already seen this local variable, error */
if (!v) { for (i=0; i<func->nargs; i++) {
DBufFree(&buf); if (!StrinCmp(DBufValue(&buf), local_array[i].name, VAR_NAME_LEN)) {
DestroyUserFunc(func); DBufFree(&buf);
return E_NO_MEM; DestroyUserFunc(func);
} return E_REPEATED_ARG;
}
}
i = func->nargs;
if (i >= MAX_FUNC_ARGS-1) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_2MANY_ARGS;
}
local_array[i].v.type = ERR_TYPE;
StrnCpy(local_array[i].name, DBufValue(&buf), VAR_NAME_LEN);
local_array[i].next = &(local_array[i+1]);
local_array[i+1].next = NULL;
func->nargs++; func->nargs++;
v->v.type = ERR_TYPE;
StrnCpy(v->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
v->next = func->locals;
func->locals = v;
c = ParseNonSpaceChar(p, &r, 0); c = ParseNonSpaceChar(p, &r, 0);
if (r) {
DBufFree(&buf);
DestroyUserFunc(func);
return r;
}
if (c == ')') break; if (c == ')') break;
else if (c != ',') { else if (c != ',') {
DestroyUserFunc(func); DestroyUserFunc(func);
@@ -186,20 +222,50 @@ int DoFset(ParsePtr p)
/* Allow an optional = sign: FSET f(x) = x*x */ /* Allow an optional = sign: FSET f(x) = x*x */
c = ParseNonSpaceChar(p, &r, 1); c = ParseNonSpaceChar(p, &r, 1);
if (r) {
DestroyUserFunc(func);
return r;
}
if (c == '=') { if (c == '=') {
(void) ParseNonSpaceChar(p, &r, 0); (void) ParseNonSpaceChar(p, &r, 0);
} }
/* Copy the text over */
if (p->isnested) { if (p->isnested) {
Eprint("%s", ErrMsg[E_CANTNEST_FDEF]); Eprint("%s", ErrMsg[E_CANTNEST_FDEF]);
DestroyUserFunc(func); DestroyUserFunc(func);
return E_PARSE_ERR; return E_PARSE_ERR;
} }
func->text = StrDup(p->pos); while(*(p->pos) && isspace(*(p->pos))) {
if (!func->text) { p->pos++;
DestroyUserFunc(func); }
return E_NO_MEM; if (!*(p->pos)) {
DestroyUserFunc(func);
return E_EOLN;
}
/* Parse the expression */
func->node = parse_expression(&(p->pos), &r, locals);
if (!func->node) {
DestroyUserFunc(func);
return r;
}
c = ParseNonSpaceChar(p, &r, 1);
if (c != 0 || r != 0) {
DestroyUserFunc(func);
if (r != 0) return r;
return E_EXPECTING_EOL;
}
/* Save the argument names */
if (func->nargs) {
func->args = calloc(func->nargs, sizeof(char *));
for (i=0; i<func->nargs; i++) {
func->args[i] = StrDup(local_array[i].name);
if (!func->args[i]) {
DestroyUserFunc(func);
return E_NO_MEM;
}
}
} }
/* If an old definition of this function exists, destroy it */ /* If an old definition of this function exists, destroy it */
@@ -223,23 +289,22 @@ int DoFset(ParsePtr p)
/***************************************************************/ /***************************************************************/
static void DestroyUserFunc(UserFunc *f) static void DestroyUserFunc(UserFunc *f)
{ {
Var *v, *prev; int i;
/* Free the local variables first */
v = f->locals;
while(v) {
DestroyValue(v->v);
prev = v;
v = v->next;
free(prev);
}
/* Free the function definition */ /* Free the function definition */
if (f->text) free( (char *) f->text); if (f->node) free_expr_tree(f->node);
/* Free the filename */ /* Free the filename */
if (f->filename) free( (char *) f->filename); if (f->filename) free( (char *) f->filename);
/* Free arg names */
if (f->args) {
for (i=0; i<f->nargs; i++) {
if (f->args[i]) free(f->args[i]);
}
free(f->args);
}
/* Free the data structure itself */ /* Free the data structure itself */
free(f); free(f);
} }
@@ -257,12 +322,12 @@ static void FUnset(char const *name)
UserFunc *cur, *prev; UserFunc *cur, *prev;
int h; int h;
h = HashVal(name) % FUNC_HASH_SIZE; h = HashVal_nocase(name) % FUNC_HASH_SIZE;
cur = FuncHash[h]; cur = FuncHash[h];
prev = NULL; prev = NULL;
while(cur) { while(cur) {
if (! StrinCmp(name, cur->name, VAR_NAME_LEN)) break; if (! strncmp(name, cur->name, VAR_NAME_LEN)) break;
prev = cur; prev = cur;
cur = cur->next; cur = cur->next;
} }
@@ -280,132 +345,22 @@ static void FUnset(char const *name)
/***************************************************************/ /***************************************************************/
static void FSet(UserFunc *f) static void FSet(UserFunc *f)
{ {
int h = HashVal(f->name) % FUNC_HASH_SIZE; int h = HashVal_nocase(f->name) % FUNC_HASH_SIZE;
f->next = FuncHash[h]; f->next = FuncHash[h];
FuncHash[h] = f; FuncHash[h] = f;
} }
/***************************************************************/ UserFunc *FindUserFunc(char const *name)
/* */
/* CallUserFunc */
/* */
/* Call a user-defined function. */
/* */
/***************************************************************/
int CallUserFunc(char const *name, int nargs, ParsePtr p)
{ {
UserFunc *f; UserFunc *f;
int h = HashVal(name) % FUNC_HASH_SIZE; int h = HashVal_nocase(name) % FUNC_HASH_SIZE;
int i;
char const *s;
/* Search for the function */ /* Search for the function */
f = FuncHash[h]; f = FuncHash[h];
while (f && StrinCmp(name, f->name, VAR_NAME_LEN)) f = f->next; while (f && strncmp(name, f->name, VAR_NAME_LEN)) f = f->next;
if (!f) { return f;
Eprint("%s: `%s'", ErrMsg[E_UNDEF_FUNC], name);
return E_UNDEF_FUNC;
}
/* Debugging stuff */
if (DebugFlag & DB_PRTEXPR) {
fprintf(ErrFp, "%s %s(", ErrMsg[E_ENTER_FUN], f->name);
for (i=0; i<nargs; i++) {
PrintValue(&ValStack[ValStackPtr - nargs + i], ErrFp);
if (i<nargs-1) fprintf(ErrFp, ", ");
}
fprintf(ErrFp, ")\n");
}
/* Detect illegal recursive call */
if (f->IsActive) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n", ErrMsg[E_RECURSIVE]);
}
return E_RECURSIVE;
}
/* Check number of args */
if (nargs != f->nargs) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n",
ErrMsg[(nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS]);
}
return (nargs < f->nargs) ? E_2FEW_ARGS : E_2MANY_ARGS;
}
/* Found the function - set up a local variable frame */
h = SetUpLocalVars(f);
if (h) {
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
fprintf(ErrFp, "%s\n", ErrMsg[h]);
}
return h;
}
/* Evaluate the expression */
f->IsActive = 1;
s = f->text;
/* Skip the opening bracket, if there's one */
while (isempty(*s)) s++;
if (*s == BEG_OF_EXPR) s++;
push_call(f->filename, f->name, f->lineno);
h = Evaluate(&s, f->locals, p);
if (h == OK) {
pop_call();
}
f->IsActive = 0;
DestroyLocalVals(f);
if (DebugFlag &DB_PRTEXPR) {
fprintf(ErrFp, "%s %s() => ", ErrMsg[E_LEAVE_FUN], name);
if (h) fprintf(ErrFp, "%s\n", ErrMsg[h]);
else {
PrintValue(&ValStack[ValStackPtr-1], ErrFp);
fprintf(ErrFp, "\n");
}
}
return h;
} }
/***************************************************************/
/* */
/* SetUpLocalVars */
/* */
/* Set up the local variables from the stack frame. */
/* */
/***************************************************************/
static int SetUpLocalVars(UserFunc *f)
{
int i, r;
Var *var;
for (i=0, var=f->locals; var && i<f->nargs; var=var->next, i++) {
if ( (r=FnPopValStack(&(var->v))) ) {
DestroyLocalVals(f);
return r;
}
}
return OK;
}
/***************************************************************/
/* */
/* DestroyLocalVals */
/* */
/* Destroy the values of all local variables after evaluating */
/* the function. */
/* */
/***************************************************************/
static void DestroyLocalVals(UserFunc *f)
{
Var *v = f->locals;
while(v) {
DestroyValue(v->v);
v = v->next;
}
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* UserFuncExists */ /* UserFuncExists */
@@ -416,12 +371,33 @@ static void DestroyLocalVals(UserFunc *f)
/***************************************************************/ /***************************************************************/
int UserFuncExists(char const *fn) int UserFuncExists(char const *fn)
{ {
UserFunc *f; UserFunc *f = FindUserFunc(fn);
int h = HashVal(fn) % FUNC_HASH_SIZE;
f = FuncHash[h];
while (f && StrinCmp(fn, f->name, VAR_NAME_LEN)) f = f->next;
if (!f) return -1; if (!f) return -1;
else return f->nargs; else return f->nargs;
} }
/***************************************************************/
/* */
/* UnsetAllUserFuncs */
/* */
/* Call FUNSET on all user funcs. Used with -ds flag to */
/* ensure no expr_node memory leaks. */
/* */
/***************************************************************/
void
UnsetAllUserFuncs(void)
{
UserFunc *f;
UserFunc *next;
int i;
for (i=0; i<FUNC_HASH_SIZE; i++) {
f = FuncHash[i];
while(f) {
next = f->next;
DestroyUserFunc(f);
f = next;
}
FuncHash[i] = NULL;
}
}

View File

@@ -5,7 +5,7 @@
/* Useful utility functions. */ /* Useful utility functions. */
/* */ /* */
/* This file is part of REMIND. */ /* This file is part of REMIND. */
/* Copyright (C) 1992-2023 by Dianne Skoll */ /* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -17,6 +17,11 @@ static char const DontEscapeMe[] =
#include "err.h" #include "err.h"
#include <string.h> #include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
@@ -36,11 +41,17 @@ char *StrnCpy(char *dest, char const *source, int n)
{ {
char *odest = dest; char *odest = dest;
if (n <= 0) {
*dest = 0;
return dest;
}
while (n-- && (*dest++ = *source++)) ; while (n-- && (*dest++ = *source++)) ;
if (*(dest-1)) *dest = 0; if (*(dest-1)) *dest = 0;
return odest; return odest;
} }
#ifndef HAVE_STRNCASECMP
/***************************************************************/ /***************************************************************/
/* */ /* */
/* StrinCmp - compare strings, case-insensitive */ /* StrinCmp - compare strings, case-insensitive */
@@ -59,6 +70,9 @@ int StrinCmp(char const *s1, char const *s2, int n)
if (n) return (toupper(*s1) - toupper(*s2)); else return 0; if (n) return (toupper(*s1) - toupper(*s2)); else return 0;
} }
#endif
#ifndef HAVE_STRDUP
/***************************************************************/ /***************************************************************/
/* */ /* */
/* StrDup */ /* StrDup */
@@ -74,6 +88,9 @@ char *StrDup(char const *s)
return ret; return ret;
} }
#endif
#ifndef HAVE_STRCASECMP
/***************************************************************/ /***************************************************************/
/* */ /* */
/* StrCmpi */ /* StrCmpi */
@@ -93,6 +110,8 @@ int StrCmpi(char const *s1, char const *s2)
return toupper(*s1) - toupper(*s2); return toupper(*s1) - toupper(*s2);
} }
#endif
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DateOK */ /* DateOK */
@@ -108,7 +127,15 @@ int DateOK(int y, int m, int d)
m > 11 || m > 11 ||
y > BASE + YR_RANGE || y > BASE + YR_RANGE ||
d > DaysInMonth(m, y) ) return 0; d > DaysInMonth(m, y) ) return 0;
else return 1; return 1;
}
void strtolower(char *s)
{
while (*s) {
*s = tolower(*s);
s++;
}
} }
/* Functions designed to defeat gcc optimizer */ /* Functions designed to defeat gcc optimizer */
@@ -146,11 +173,15 @@ int _private_sub_overflow(int a, int b)
int int
ShellEscape(char const *in, DynamicBuffer *out) ShellEscape(char const *in, DynamicBuffer *out)
{ {
while(*in) { unsigned char const *i = (unsigned char const *) in;
if (!strchr(DontEscapeMe, *in)) { while(*i) {
if (DBufPutc(out, '\\') != OK) return E_NO_MEM; /* Don't escape chars with high bit set. That will mangle UTF-8 */
if (! (*i & 0x80) ) {
if (!strchr(DontEscapeMe, *i)) {
if (DBufPutc(out, '\\') != OK) return E_NO_MEM;
}
} }
if (DBufPutc(out, *in++) != OK) return E_NO_MEM; if (DBufPutc(out, *i++) != OK) return E_NO_MEM;
} }
return OK; return OK;
} }
@@ -164,31 +195,33 @@ typedef struct cs_s {
} cs; } cs;
static cs *callstack = NULL; static cs *callstack = NULL;
static cs *freecs = NULL;
static void static void
destroy_cs(cs *entry) destroy_cs(cs *entry)
{ {
if (entry->filename) free( (void *) entry->filename); entry->next = freecs;
if (entry->func) free( (void *) entry->func); freecs = entry;
free( (void *) entry);
} }
int int
push_call(char const *filename, char const *func, int lineno) push_call(char const *filename, char const *func, int lineno)
{ {
cs *entry = NEW(cs); cs *entry;
if (!entry) { if (freecs) {
return E_NO_MEM; entry = freecs;
freecs = freecs->next;
} else {
entry = NEW(cs);
if (!entry) {
return E_NO_MEM;
}
} }
entry->next = NULL; entry->next = NULL;
entry->filename = StrDup(filename); entry->filename = filename;
entry->func = StrDup(func); entry->func = func;
entry->lineno = lineno; entry->lineno = lineno;
if (!entry->filename || !entry->func) {
destroy_cs(entry);
return E_NO_MEM;
}
entry->next = callstack; entry->next = callstack;
callstack = entry; callstack = entry;
return OK; return OK;
@@ -210,11 +243,35 @@ clear_callstack(void)
static void static void
print_callstack_aux(FILE *fp, cs *entry) print_callstack_aux(FILE *fp, cs *entry)
{ {
if (entry) { int i = 0;
print_callstack_aux(fp, entry->next); char const *in = "In";
fprintf(fp, "\n"); cs *prev = NULL;
(void) fprintf(fp, "%s(%d): In function `%s'", entry->filename, entry->lineno, entry->func); while(entry) {
if (prev) {
in = "Called from";
}
if (!prev || strcmp(prev->func, entry->func) || strcmp(prev->filename, entry->filename) || prev->lineno != entry->lineno) {
if (prev) {
fprintf(fp, "\n");
}
(void) fprintf(fp, " %s(%d): [#%d] %s function `%s'", entry->filename, entry->lineno, i, in, entry->func);
}
prev = entry;
entry = entry->next;
i++;
if (i > 10) {
break;
}
} }
if (entry) {
(void) fprintf(fp, "\n [remaining call frames omitted]");
}
}
int
have_callstack(void)
{
return (callstack != NULL);
} }
int int

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