Compare commits

...

186 Commits

Author SHA1 Message Date
Dianne Skoll 5b7d4a07ec More explicit error messages.
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.
Remind unit tests / tests (push) Successful in 31s
2024-09-05 12:44:17 -04:00
Dianne Skoll 5d7f55c8d5 Make error messages more consistent.
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.
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.
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.
Remind unit tests / tests (push) Successful in 31s
2024-09-01 22:39:49 -04:00
Dianne Skoll ce2b2e80da Remove unnecessary _h() function definition.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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
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.
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.)
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
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
72 changed files with 7378 additions and 2723 deletions
+1
View File
@@ -33,3 +33,4 @@ src/version.h
tests/test.out tests/test.out
www/Makefile www/Makefile
gmon.out gmon.out
tests/once.timestamp
+5
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:
Vendored
+9 -9
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 for remind 04.03.07. # Generated by GNU Autoconf 2.71 for remind 05.00.05.
# #
# #
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -608,8 +608,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='remind' PACKAGE_NAME='remind'
PACKAGE_TARNAME='remind' PACKAGE_TARNAME='remind'
PACKAGE_VERSION='04.03.07' PACKAGE_VERSION='05.00.05'
PACKAGE_STRING='remind 04.03.07' PACKAGE_STRING='remind 05.00.05'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
PACKAGE_URL='https://dianne.skoll.ca/projects/remind/' PACKAGE_URL='https://dianne.skoll.ca/projects/remind/'
@@ -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 remind 04.03.07 to adapt to many kinds of systems. \`configure' configures remind 05.00.05 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1326,7 +1326,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of remind 04.03.07:";; short | recursive ) echo "Configuration of remind 05.00.05:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@@ -1414,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
remind configure 04.03.07 remind configure 05.00.05
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.
@@ -1864,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 remind $as_me 04.03.07, which was It was created by remind $as_me 05.00.05, 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
@@ -4703,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 remind $as_me 04.03.07, which was This file was extended by remind $as_me 05.00.05, 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
@@ -4768,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="\\
remind config.status 04.03.07 remind config.status 05.00.05
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\\"
+1 -1
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(remind, 04.03.07, , , https://dianne.skoll.ca/projects/remind/) AC_INIT(remind, 05.00.05, , , https://dianne.skoll.ca/projects/remind/)
AC_CONFIG_SRCDIR([src/queue.c]) AC_CONFIG_SRCDIR([src/queue.c])
cat <<'EOF' cat <<'EOF'
+3
View File
@@ -0,0 +1,3 @@
The upstream GitHub project for ical2rem is:
https://github.com/jbalcorn/ical2rem
+21
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.
+103
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.
+82
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 :
+45
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
+21
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
@@ -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
@@ -57,10 +58,17 @@
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)
--iso8601 Use YYYY-MM-DD date format
--locations, --no-locations Include location? (Default: Yes)
--end-times, --no-end-times Include event end times in reminder text
(Default: No)
--heading Define a priority for static entries --heading Define a priority for static entries
--help Usage --help Usage
--debug Enable debug output
--man Complete man page --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
@@ -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;
@@ -110,17 +127,29 @@ 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 :
+43 -35
View File
@@ -111,11 +111,11 @@
(sort (sort
(list "ADDOMIT" "AFTER" "AT" "BANNER" "BEFORE" (list "ADDOMIT" "AFTER" "AT" "BANNER" "BEFORE"
"CAL" "CLEAR-OMIT-CONTEXT" "DEBUG" "DO" "DUMPVARS" "CAL" "CLEAR-OMIT-CONTEXT" "DEBUG" "DO" "DUMPVARS"
"DURATION" "ELSE" "ENDIF" "ERRMSG" "EXIT" "FIRST" "DURATION" "ELSE" "ENDIF" "ERRMSG" "EXIT" "EXPR" "FIRST"
"FLUSH" "FOURTH" "FROM" "FSET" "IF" "IFTRIG" "IN" "FLUSH" "FOURTH" "FROM" "FSET" "FUNSET" "IF" "IFTRIG" "IN"
"INCLUDE" "INCLUDECMD" "LAST" "LASTDAY" "INCLUDE" "INCLUDECMD" "LAST" "LASTDAY"
"LASTWORKDAY" "MAYBE-UNCOMPUTABLE" "MSF" "LASTWORKDAY" "MAYBE-UNCOMPUTABLE" "MSF"
"MSG" "OMIT" "OMITFUNC" "ONCE" "MSG" "NOQUEUE" "OMIT" "OMITFUNC" "ONCE"
"POP-OMIT-CONTEXT" "PRESERVE" "PRIORITY" "PS" "PSFILE" "POP-OMIT-CONTEXT" "PRESERVE" "PRIORITY" "PS" "PSFILE"
"PUSH-OMIT-CONTEXT" "REM" "RUN" "SATISFY" "SCANFROM" "PUSH-OMIT-CONTEXT" "REM" "RUN" "SATISFY" "SCANFROM"
"SCHED" "SECOND" "SET" "SKIP" "SPECIAL" "SCHED" "SECOND" "SET" "SKIP" "SPECIAL"
@@ -131,50 +131,58 @@
(defconst remind-builtin-variables (defconst remind-builtin-variables
(sort (sort
(list "$Ago" "$Am" "$And" "$April" "$At" "$August" "$CalcUTC" "$CalMode" "$Daemon" "$DateSep" (list
"$DateTimeSep" "$December" "$DefaultColor" "$DefaultPrio" "$AddBlankLines" "$Ago" "$Am" "$And" "$April" "$At" "$August"
"$DefaultTDelta" "$DeltaOffset" "$DontFork" "$DontQueue" "$CalMode" "$CalcUTC" "$Daemon" "$DateSep" "$DateTimeSep" "$December"
"$DontTrigAts" "$EndSent" "$EndSentIg" "$February" "$FirstIndent" "$DefaultColor" "$DefaultPrio" "$DefaultTDelta" "$DeltaOverride"
"$FoldYear" "$FormWidth" "$Friday" "$Fromnow" "$Hour" "$Hplu" "$HushMode" "$IgnoreOnce" "$DontFork" "$DontQueue" "$DontTrigAts" "$EndSent" "$EndSentIg"
"$InfDelta" "$IntMax" "$IntMin" "$Is" "$January" "$July" "$June" "$LatDeg" "$ExpressionTimeLimit" "$February" "$FirstIndent" "$FoldYear"
"$Latitude" "$LatMin" "$LatSec" "$Location" "$LongDeg" "$Longitude" "$FormWidth" "$Friday" "$Fromnow" "$Hour" "$Hplu" "$HushMode"
"$LongMin" "$LongSec" "$March" "$MaxSatIter" "$MaxStringLen" "$May" "$IgnoreOnce" "$InfDelta" "$IntMax" "$IntMin" "$Is" "$January" "$July"
"$MinsFromUTC" "$Minute" "$Monday" "$Mplu" "$NextMode" "$November" "$Now" "$NumQueued" "$June" "$LatDeg" "$LatMin" "$LatSec" "$Latitude" "$Location"
"$NumTrig" "$October" "$On" "$Pm" "$PrefixLineNo" "$PSCal" "$RunOff" "$Saturday" "$LongDeg" "$LongMin" "$LongSec" "$Longitude" "$March" "$MaxFullOmits"
"$September" "$SimpleCal" "$SortByDate" "$SortByPrio" "$SortByTime" "$MaxLateMinutes" "$MaxPartialOmits" "$MaxSatIter" "$MaxStringLen"
"$SubsIndent" "$Sunday" "$SysInclude" "$T" "$Td" "$Thursday" "$TimeSep" "$Tm" "$May" "$MinsFromUTC" "$Minute" "$Monday" "$Mplu" "$NextMode"
"$Today" "$Tomorrow" "$Tuesday" "$Tw" "$Ty" "$U" "$Ud" "$Um" "$UntimedFirst" "$Uw" "$Uy" "$November" "$Now" "$NumFullOmits" "$NumPartialOmits" "$NumQueued"
"$Was" "$Wednesday") "$NumTrig" "$October" "$On" "$OnceFile" "$PSCal" "$ParseUntriggered"
"$Pm" "$PrefixLineNo" "$RunOff" "$Saturday" "$September" "$SimpleCal"
"$SortByDate" "$SortByPrio" "$SortByTime" "$SubsIndent" "$Sunday"
"$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
"Jan" "January" "Feb" "Mar" "Apr" "Jun" "Jul" "Aug" "Sept" "Sep" "Oct" "Nov" "Dec"
"February" "March" "April" "May" "June" "July" "August" "September" "October" "February" "March" "April" "May" "June" "July" "August" "September" "October"
"November" "December" "Mon" "Monday" "Tue" "Tues" "Tuesday" "Wed" "Wednesday" "November" "December" "Mon" "Monday" "Tue" "Tues" "Tuesday" "Wed" "Wednesday"
"Thu" "Thursday" "Thurs" "Fri" "Friday" "Saturday" "Sat" "Sun" "Sunday") "Thu" "Thursday" "Thurs" "Fri" "Friday" "Saturday" "Sat" "Sun" "Sunday")
#'(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
+184 -1
View File
@@ -1,5 +1,188 @@
CHANGES TO REMIND CHANGES TO REMIND
* 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 * VERSION 4.3 Patch 7 - 2024-04-29
* IMPROVEMENT: build.tk: Add a note if build.tk obtains default settings * IMPROVEMENT: build.tk: Add a note if build.tk obtains default settings
@@ -8,7 +191,7 @@ CHANGES TO REMIND
* IMPROVEMENT: configure: Pass all args to AC_INIT including the Remind * IMPROVEMENT: configure: Pass all args to AC_INIT including the Remind
home page. Remove some unused autoconf cruft. home page. Remove some unused autoconf cruft.
* IMPROVEMENT: Use standard C library versions of strdup, strcasemp and * IMPROVEMENT: Use standard C library versions of strdup, strcasecmp and
strncasecmp where available, rather than using our own versions. strncasecmp where available, rather than using our own versions.
* MINOR FEATURE: remind: Make $Tt a synonym for trigtime(). * MINOR FEATURE: remind: Make $Tt a synonym for trigtime().
+46 -41
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> #
@@ -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:
+1 -1
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
+35 -40
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
+13 -9
View File
@@ -1,17 +1,20 @@
# 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
@@ -19,6 +22,7 @@ IF autolang != ""
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
+2
View File
@@ -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
+2
View File
@@ -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
+3 -3
View File
@@ -525,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
+235 -162
View File
@@ -11,8 +11,8 @@ reminder or alarm can consist of a message sent to standard output, or
a program to be executed. a program to be executed.
.PP .PP
If \fIfilename\fR is specified as a single dash '-', then \fBRemind\fR If \fIfilename\fR is specified as a single dash '-', then \fBRemind\fR
takes its input from standard input. This also implicitly enables takes its input from standard input.
the \fB\-o\fR option, described below.
.PP .PP
If \fIfilename\fR happens to If \fIfilename\fR happens to
be a directory rather than a plain file, then \fBRemind\fR reads all of be a directory rather than a plain file, then \fBRemind\fR reads all of
@@ -28,15 +28,12 @@ Anything after the __EOF__ marker is completely ignored.
\fBRemind\fR has a slew of options. If you're new to the program, \fBRemind\fR has a slew of options. If you're new to the program,
ignore them for now and skip to the section "REMINDER FILES". ignore them for now and skip to the section "REMINDER FILES".
.TP .TP
.B \-\-version
The \fB\-\-version\fR option causes \fBRemind\fR to print its version number
to standard output and then exit.
.TP
.B \-n .B \-n
The \fB\-n\fR option causes \fBRemind\fR to print the \fBnext\fR occurrence The \fB\-n\fR option causes \fBRemind\fR to print the \fBnext\fR occurrence
of each reminder in a simple calendar format. You can sort this by of each reminder in a simple calendar format. You can sort this by
date by piping the output through \fBsort(1)\fR. Note that the \fB\-n\fR date by piping the output through \fBsort(1)\fR. Note that the \fB\-n\fR
option causes any \fB\-g\fR option to be \fIignored\fR. option causes any \fB\-g\fR option to be \fIignored\fR and also implicitly
enables the \fB\-o\fR option.
.TP .TP
.B \-j\fR[\fIn\fR] .B \-j\fR[\fIn\fR]
Runs \fBRemind\fR in "purge" mode to get rid of expired reminders. Runs \fBRemind\fR in "purge" mode to get rid of expired reminders.
@@ -51,7 +48,7 @@ The \fB\-c\fR option causes \fBRemind\fR to produce a calendar that is
sent to standard output. If you supply a number \fIn\fR, then a sent to standard output. If you supply a number \fIn\fR, then a
calendar will be generated for \fIn\fR months, starting with the calendar will be generated for \fIn\fR months, starting with the
current month. By default, a calendar for only the current month is current month. By default, a calendar for only the current month is
produced. produced. This option implicitly enables the \fB\-o\fR option.
.RS .RS
.PP .PP
You can precede \fIn\fR (if any) with a set of flags. The flags You can precede \fIn\fR (if any) with a set of flags. The flags
@@ -177,7 +174,7 @@ The \fB\-s\fR option is very similar to the \fB\-c\fR option, except
that the output calendar is not formatted. It is listed in a "simple that the output calendar is not formatted. It is listed in a "simple
format" that can be used as input for more sophisticated calendar-drawing format" that can be used as input for more sophisticated calendar-drawing
programs. If \fIn\fR starts with "+", then it is interpreted as a number programs. If \fIn\fR starts with "+", then it is interpreted as a number
of weeks. of weeks. This option also implicitly enables the \fB\-o\fR option.
If you immediately follow the \fBs\fR with the letter If you immediately follow the \fBs\fR with the letter
\fBa\fR, then \fBRemind\fR displays reminders on the calendar on the \fBa\fR, then \fBRemind\fR displays reminders on the calendar on the
@@ -204,6 +201,9 @@ letter with this option, then the normal calendar-mode substitution filter
is disabled and the %"...%" sequences are preserved in the output. is disabled and the %"...%" sequences are preserved in the output.
.RS .RS
.PP .PP
The \fB\-p\fR, \fB\-pp\fR and \fB\-ppp\fR options implicitly enable
the \fB\-o\fR option.
.PP
Note that the \fB\-pp\fR or \fB\-ppp\fR options also enable the \fB\-l\fR Note that the \fB\-pp\fR or \fB\-ppp\fR options also enable the \fB\-l\fR
option. option.
.RE .RE
@@ -227,7 +227,11 @@ error, and to print a security message if a script tests the
$RunOff system variable. $RunOff system variable.
.TP .TP
.B \-o .B \-o
The \fB\-o\fR option causes \fBRemind\fR to ignore all \fBONCE\fR directives. The \fB\-o\fR option causes \fBRemind\fR to ignore all \fBONCE\fR
directives. Note that \fBONCE\fR is also ignored if any of the
\fB\-c\fR, \fB\-n\fR, \fB\-p\fR, or \fB\-s\fR options is used, if a
repetition factor \fB*n\fR is used, or if a date other than today's
date is specified on the command-line.
.TP .TP
.B \-t .B \-t
The \fB\-t\fR option causes \fBRemind\fR to trigger all non-expired reminders, The \fB\-t\fR option causes \fBRemind\fR to trigger all non-expired reminders,
@@ -236,13 +240,13 @@ regardless of the \fIdelta\fR supplied for each reminder.
.B \-t\fR\fIn\fR .B \-t\fR\fIn\fR
If you supply a number \fIn\fR after the \fB\-t\fR option, then If you supply a number \fIn\fR after the \fB\-t\fR option, then
\fBRemind\fR pretends that echo \fBREM\fR command has a delta \fBRemind\fR pretends that echo \fBREM\fR command has a delta
of \+\+\fIn\fR, regardless of any existing delta. of ++\fIn\fR, regardless of any existing delta.
.TP .TP
.B \-tz\fR .B \-tz\fR
If you supply the letter \fBz\fR after the \fB\-t\fR option, then If you supply the letter \fBz\fR after the \fB\-t\fR option, then
\fBRemind\fR sets all REM statements' deltas to zero, regardless of the \fBRemind\fR sets all REM statements' deltas to zero, regardless of the
value supplied in the REM statement itself. In effect, this disables value supplied in the REM statement itself. In effect, this disables
all deltas of the form \fB\+\fIn\fR and \fB\+\+\fIn\fR. all deltas of the form \fB+\fIn\fR and \fB++\fIn\fR.
.TP .TP
.B \-tt\fR[\fIn\fR] .B \-tt\fR[\fIn\fR]
The \fB-tt\fR option causes \fBRemind\fR to assume a default delta of The \fB-tt\fR option causes \fBRemind\fR to assume a default delta of
@@ -302,9 +306,9 @@ Echo lines when displaying error messages
Trace the reading of reminder files Trace the reading of reminder files
.TP .TP
.B s .B s
Upon exit, print the high-water mark of the operator and value stacks Trace expression parsing and display the internal expression node
used for expression-parsing. This is unlikely to be useful unless tree. This is unlikely to be useful unless you are working on
you're intimately familiar with Remind's source code. \fBRemind\fR's expression evaluation engine.
.RE .RE
.TP .TP
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]] \fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
@@ -420,7 +424,9 @@ TAG clause.
\fB\-i\fR\fIvar\fR\fB=\fR\fIexpr\fR \fB\-i\fR\fIvar\fR\fB=\fR\fIexpr\fR
Sets the value of the specified \fIvar\fR to \fIexpr\fR, and \fBpreserves\fR Sets the value of the specified \fIvar\fR to \fIexpr\fR, and \fBpreserves\fR
\fIvar\fR. \fIExpr\fR can be any valid \fBRemind\fR expression. See the \fIvar\fR. \fIExpr\fR can be any valid \fBRemind\fR expression. See the
section "INITIALIZING VARIABLES ON THE COMMAND LINE" for more details. section "INITIALIZING VARIABLES ON THE COMMAND LINE" for more details. If
you omit the \fB=\fR\fIexpr\fR part, then \fIvar\fR is initialized to 0.
In other words, \fB\-i\fIvar\fR is exactly the same as \fB\-i\fIvar\fR\fB=\fR0.
.TP .TP
\fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR \fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR
Allows you to define a function on the command line. Allows you to define a function on the command line.
@@ -451,6 +457,30 @@ with the date incrementing on each iteration. You may have to enclose
the parameter in quotes to avoid shell expansion. See the subsection the parameter in quotes to avoid shell expansion. See the subsection
"Repeated Execution" in the section "CALENDAR MODE" for more "Repeated Execution" in the section "CALENDAR MODE" for more
information. information.
.SH LONG OPTIONS
\fBRemind\fR supports the following long options, which \fIare\fR
case-sensitive:
.PP
.B \-\-version
The \fB\-\-version\fR option causes \fBRemind\fR to print its version number
to standard output and then exit.
.TP
.B \-\-max-execution-time\fR=\fIn\fR
Limit the total execution time (as measured by the wall clock) to
\fIn\fR seconds. This is useful if \fBRemind\fR is invoked on
potentially-untrustworthy files that could attempt to use a lot of
resources. Note that the limit \fIn\fR is approximate and
\fBRemind\fR might execute for one or two more seconds before it is
killed. If \fIn\fR is specified as zero, then no limit is applied, just
as if the option had not been used at all.
.PP
If a limit is applied, it applies only to the foreground run of \fBRemind\fR.
If \fBRemind\fR finishes processing the script and then starts handling
queued reminders, the time limit is reset to no limit.
.PP
.SH REMINDER FILES .SH REMINDER FILES
.PP .PP
\fBRemind\fR uses scripts to control its operation. You can use any \fBRemind\fR uses scripts to control its operation. You can use any
@@ -525,12 +555,15 @@ Its syntax is:
The parts of the \fBREM\fR command can be specified in any order, except The parts of the \fBREM\fR command can be specified in any order, except
that the \fIbody\fR must come immediately after the \fBMSG\fR, that the \fIbody\fR must come immediately after the \fBMSG\fR,
\fBRUN\fR, \fBCAL\fR, \fBPS\fR, \fBPSFILE\fR or \fBSATISFY\fR keyword. \fBRUN\fR, \fBCAL\fR, \fBPS\fR, \fBPSFILE\fR or \fBSATISFY\fR keyword.
The portion of the \fBREM\fR command before the \fBMSG\fR, \fBMSF\fR
\fBRUN\fR, \fBCAL\fR or \fBSATISFY\fR clause is called a
\fItrigger\fR.
.PP .PP
The \fBREM\fR token is optional, providing that the remainder In earlier versions of \fBRemind\fR, the \fBREM\fR token was optional
of the command cannot be mistaken for another \fBRemind\fR command providing that the remainder of the command cannot be mistaken for
such as \fBOMIT\fR or \fBRUN\fR. The portion of the \fBREM\fR command another \fBRemind\fR command. However, this use is deprecated and will
before the \fBMSG\fR, \fBMSF\fR \fBRUN\fR, \fBCAL\fR or \fBSATISFY\fR clause now cause a warning to be issued. All of your reminder lines should
is called a \fItrigger\fR. be written to start with the REM command.
.PP .PP
.B "MSG, MSF, RUN, CAL, SPECIAL, PS and PSFILE" .B "MSG, MSF, RUN, CAL, SPECIAL, PS and PSFILE"
.PP .PP
@@ -543,14 +576,16 @@ used the \fB\-k\fR command-line option, then \fBMSG\fR-type reminders are
passed to the appropriate program. Note that the options \fB\-c\fR, passed to the appropriate program. Note that the options \fB\-c\fR,
\fB\-s\fR, \fB\-p\fR and \fB\-n\fR disable the \fB\-k\fR option. \fB\-s\fR, \fB\-p\fR and \fB\-n\fR disable the \fB\-k\fR option.
.PP .PP
Note that you can omit the reminder type, in which case it Earlier versions of \fBRemind\fR let you omit the reminder type,
defaults to \fBMSG\fR. So you can write: in which case it defaulted to \fBMSG\fR. However, this usage is
deprecated and will cause a warning. Something like:
.PP .PP
.nf .nf
6 January Dianne's Birthday REM 6 January Dianne's Birthday
.fi .fi
.PP .PP
although this is not recommended. will issue the warning "Missing REM type; assuming MSG"
.PP .PP
The \fBMSF\fR keyword is almost the same as the \fBMSG\fR keyword, The \fBMSF\fR keyword is almost the same as the \fBMSG\fR keyword,
except that the reminder is formatted to fit into a paragraph-like except that the reminder is formatted to fit into a paragraph-like
@@ -997,26 +1032,35 @@ overrides that.
.B THE ONCE KEYWORD .B THE ONCE KEYWORD
.PP .PP
Sometimes, it is necessary to ensure that reminders are run only once Sometimes, it is necessary to ensure that reminders are run only once
on a given day. For example, if you have a reminder that makes a backup on a given day. For example, compare the following two reminders:
of your files every Friday:
.PP .PP
.nf .nf
REM Fri RUN do_backup REM Fri RUN do_backup
REM Fri ONCE RUN do_backup
.fi .fi
.PP .PP
(Here, \fIdo_backup\fR is assumed to be a program or shell script that The first will be run every time you invoke \fBRemind\fR on a Friday,
does the work.) If you run \fBRemind\fR from your .login script, for whereas the second will be run only the first time you invoke
example, and log in several times per day, the \fIdo_backup\fR program \fBRemind\fR on a given Friday.
will be run each time you log in. If, however, you use the \fBONCE\fR .PP
keyword in the reminder, the \fBRemind\fR checks the last access date of If you run \fBRemind\fR from your .login script, for example, and log
the reminder script. If it is the same as the current date, \fBRemind\fR in several times per day, the \fIdo_backup\fR program in the first
assumes that it has already been run, and will not issue reminders containing reminder will be run each time you log in. If, however, you use the
the \fBONCE\fR keyword. \fBONCE\fR keyword in the reminder, the \fBRemind\fR checks the last
access date of the reminder script. If it is the same as the current
date, \fBRemind\fR assumes that it has already been run, and will not
issue reminders containing the \fBONCE\fR keyword.
.PP .PP
Note that if you view or edit your reminder script, the last access date Note that if you view or edit your reminder script, the last access date
will be updated, and the \fBONCE\fR keyword will not operate properly. will be updated, and the \fBONCE\fR keyword will not operate properly.
You can fix this by setting a timestamp file for \fBRemind\fR to track
the last-run date; see the documentation of \fB$OnceFile\fR in the
\fBSYSTEM VARIABLES\fR section. If you use standard input as your
\fBRemind\fR input file, then you \fImust\fR use \fB$OnceFile\fR for the
\fBONCE\fR keyword to work properly.
.PP
If you start \fBRemind\fR with the \fB\-o\fR option, then the \fBONCE\fR If you start \fBRemind\fR with the \fB\-o\fR option, then the \fBONCE\fR
keyword will be ignored. keyword will be ignored and any \fB$OnceFile\fR will be ignored.
.PP .PP
.B LOCALLY OMITTING WEEKDAYS .B LOCALLY OMITTING WEEKDAYS
.PP .PP
@@ -1526,7 +1570,35 @@ is replaced with "\fIyy\fR", the last two digits of the year.
.TP .TP
.B %_ .B %_
(percent-underscore) is replaced with a newline. You can use this to (percent-underscore) is replaced with a newline. You can use this to
achieve multi-line reminders. achieve multi-line reminders. Note that calendar back-ends vary in
how they handle multi-line reminders:
.RS
.TP
.B o
Running \fBremind -c\fR preserves newlines in the terminal calendar output.
.TP
.B o
\fBrem2pdf\fR preserves newlines if \fBremind\fR is invoked with the \fB\-pp\fR
or \fB\-ppp\fR option.
.TP
.B o
\fBrem2html\fR preserves newlines if \fBremind\fR is invoked with the
\fB\-pp\fR option.
.TP
.B o
\fBtkremind\fR preserves newlines.\fR
.TP
.B o
\fBrem2ps\fR converts newlines to spaces. But \fBrem2ps\fR is deprecated;
use \fBrem2pdf\fR instead.
.TP
.B o
The "simple calendar" formats (ie, \fBremind\fR's \fB\-s\fR, \fB\-n\fR and
\fB\-p\fR options) convert newlines to spaces.
.PP
All calendar back-ends collapse multiple spaces to a single space and
multiple newlines to a single newline.
.RE
.TP .TP
.B %1 .B %1
is replaced with "now", "\fIm\fR minutes from now", "\fIm\fR minutes ago", is replaced with "now", "\fIm\fR minutes from now", "\fIm\fR minutes ago",
@@ -1660,7 +1732,7 @@ or:
The \fBOMIT\fR command is used to "globally" omit certain days The \fBOMIT\fR command is used to "globally" omit certain days
(usually holidays). These globally-omitted days are skipped by the (usually holidays). These globally-omitted days are skipped by the
"\-" and "+" forms of \fIback\fR and \fIdelta\fR, but not by the "\-" and "+" forms of \fIback\fR and \fIdelta\fR, but not by the
"\-\-" and "\+\+" forms. Some examples: "\-\-" and "++" forms. Some examples:
.PP .PP
.nf .nf
OMIT Saturday Sunday OMIT Saturday Sunday
@@ -1880,24 +1952,6 @@ If you run \fBRemind\fR with the \fB\-r\fR command-line option,
regardless of any \fBRUN\fR commands in the reminder script. However, regardless of any \fBRUN\fR commands in the reminder script. However,
any command supplied with the \fB\-k\fR option will still be executed. any command supplied with the \fB\-k\fR option will still be executed.
.PP .PP
One use of the \fBRUN\fR command is to provide a secure interface
between \fBRemind\fR and the \fBElm\fR mail system. The \fBElm\fR
system can automatically scan incoming mail for reminder or calendar
entries, and place them in your calendar file. To use this feature,
you should set the calendar filename option under \fBElm\fR to be something
like "~/.reminders.in", \fInot\fR your main reminder file! This is
so that any \fBRUN ON\fR commands mailed to you can never be activated.
.PP
Then, you can use the \fBElm\fR \fIscan message for calendar entries\fR
command to place reminders prefaced by "->" into .reminders.in. In
your main .reminders file, include the following lines:
.PP
.nf
RUN OFF # Disable RUN
INCLUDE .reminders.in
RUN ON # Re-enable RUN
.fi
.PP
In addition, \fBRemind\fR contains a few other security In addition, \fBRemind\fR contains a few other security
features. It will not read a file that is group- or world-writable. features. It will not read a file that is group- or world-writable.
It will not run set-uid. If it reads a file you don't own, it will It will not run set-uid. If it reads a file you don't own, it will
@@ -1909,6 +1963,30 @@ attempt to check the ownership of standard input, even if it is
coming from a file, and hence does \fInot\fR disable RUN and shell() coming from a file, and hence does \fInot\fR disable RUN and shell()
in this situation. in this situation.
.PP
.SH THE EXPR COMMAND
.PP
\fBRemind\fR lets you completely disable expression evaluation. This
could be useful if you are running Remind on a somewhat-untrustworthy
file that is not expected to contain expressions. To disable
expression evaluation, use:
.PP
.nf
EXPR OFF
.fi
.PP
If \fBRemind\fR encounters an expression while EXPR OFF is in effect, it
returns an error
.PP
To re-enable expression evaluation, use:
.PP
.nf
EXPR ON
.fi
.PP
As with \fBRUN ON\fB, \fBEXPR ON\fR can be used only in the top-level
script, not in an included file.
.PP
.SH THE INCLUDECMD COMMAND .SH THE INCLUDECMD COMMAND
.PP .PP
\fBRemind\fR allows you to execute a shell command and evaluate the \fBRemind\fR allows you to execute a shell command and evaluate the
@@ -1950,6 +2028,10 @@ your INCLUDECMD uses expression-pasting that results in differences depending
on the value of \fBtoday()\fR, then each \fIunique\fR version of the on the value of \fBtoday()\fR, then each \fIunique\fR version of the
command will be executed once. command will be executed once.
.PP .PP
If a given reminder file contains more than one identical
\fBINCLUDECMD\fR, only the first one will actually be executed. All
subsequent identical ones will use the cached output from the first one.
.PP
.SH THE BANNER COMMAND .SH THE BANNER COMMAND
.PP .PP
@@ -2133,11 +2215,11 @@ and time separator characters for \fBDATE\fR and \fBTIME\fR constants apply
also to \fBDATETIME\fR constants. also to \fBDATETIME\fR constants.
.RE .RE
.PP .PP
.B ZERO VALUES .B ZERO VALUES AND TRUE/FALSE
.PP .PP
The non-string types all have an associated \fIzero\fR value, which is All types have an associated \fIzero value\fR, which is treated as
treated as "false" by the IF command and the logical operators. The \fIfalse\fR by the IF command, the IIF function, and the logical
zero values are: operators. The zero values are:
.PP .PP
.RS .RS
.PP .PP
@@ -2148,10 +2230,11 @@ zero values are:
\fBTIME\fR - 00:00 \fBTIME\fR - 00:00
.PP .PP
\fBDATETIME\fR - '1990-01-01@00:00' \fBDATETIME\fR - '1990-01-01@00:00'
.PP
\fBSTRING\fR - "" (the empty string)
.RE .RE
.PP .PP
Additionally, for the purpose of the IF command (but \fInot\fR the Any value other than the \fIzero value\fR is treated as \fItrue\fR.
logical operators) the empty string "" is considered a false value.
.PP .PP
.B OPERATORS .B OPERATORS
.PP .PP
@@ -2162,20 +2245,20 @@ C operators.
.PP .PP
.nf .nf
! - (unary logical negation and arithmetic negation) ! - (unary logical negation and arithmetic negation)
* / % * / % (multiplication, division, modulus)
+ - + - (addition/concatenation, subtraction)
< <= > >= < <= > >= (comparisons)
== != == != (equality and inequality tests)
&& && (logical AND)
|| || (logical OR)
.fi .fi
.PP .PP
.B DESCRIPTION OF OPERATORS .B DESCRIPTION OF OPERATORS
.PP .PP
.TP .TP
.B ! .B !
Logical negation. Can be applied to an \fBINT\fR type. If the operand Logical negation. Can be applied to any type. If the operand
is non-zero, returns zero. Otherwise, returns 1. is non-zero, returns 0. Otherwise, returns 1.
.TP .TP
.B \- .B \-
Unary minus. Can be applied to an \fBINT\fR. Returns the negative Unary minus. Can be applied to an \fBINT\fR. Returns the negative
@@ -2279,16 +2362,16 @@ If the operands are not of the same type, == returns 0 and != returns
.RE .RE
.TP .TP
.B && .B &&
This is the logical AND operator. Both of its operands must be of the This is the logical AND operator. Returns the second operand if both
same type and must not be \fBSTRING\fR type. Returns the second operands are non-zero. Otherwise, returns whichever operand is zero.
operand if both operands are non-zero. Otherwise, returns a zero Operands can be any type and "zero" is interpreted as appropriate for
of the same type as the operands. each operand's type.
.TP .TP
.B || .B ||
This is the logical OR operator. Both of its operands must be of This is the logical OR operator. It returns the first operand that is
the same type and must not be of \fBSTRING\fR type. It returns non-zero; if both operands are zero, then returns the second operand.
the first operand that is non-zero; if both operands are zero, then Operands can be any type and "zero" is interpreted as appropriate for
returns a zero of the same type as the operands. each operand's type.
.PP .PP
.B NOTES .B NOTES
.PP .PP
@@ -2309,15 +2392,10 @@ For example:
12:59 + (1 + "test") yields "12:591test" 12:59 + (1 + "test") yields "12:591test"
.fi .fi
.PP .PP
The logical operators are \fInot\fR so-called short-circuit operators, as The logical operators are so-called short-circuit operators, as
they are in C. Both operands are always evaluated. Thus, an expression they are in C. This means that if the first operand of || is true,
such as: then the second operand is \fInot\fR evaluated. Similarly, if the first
.PP operand of && is false, then the second operand is \fInot\fR evaluated.
.nf
(f!=0) && (100/f <= 3)
.fi
.PP
will cause an error if f is zero.
.PP .PP
.B VARIABLES .B VARIABLES
.PP .PP
@@ -2474,6 +2552,14 @@ because the final parenthesis and quote are ignored (for the purposes
of spacing) when they follow a period. of spacing) when they follow a period.
.RE .RE
.TP .TP
.B $ExpressionTimeLimit
If set to a non-zero value \fIn\fR, than any expression that takes longer than
\fIn\fR seconds to evaluate will be aborted and an error returned. This is
to prevent maliciously-crafted expressions for creating a denial-of-service.
In an included file, $ExpressionTimeLimit can only be lowered from its
current value. In the top-level file, it can be set to any value, including
zero to disable the time limit.
.TP
.B $FirstIndent .B $FirstIndent
The number of spaces by which to indent the first line of a \fBMSF\fR-type The number of spaces by which to indent the first line of a \fBMSF\fR-type
reminder. The default is 0. reminder. The default is 0.
@@ -2502,9 +2588,9 @@ truncated - the width limit will be ignored.
If non-zero, then the \fB\-h\fR option was supplied on the command line. If non-zero, then the \fB\-h\fR option was supplied on the command line.
.TP .TP
.B $IgnoreOnce (read-only) .B $IgnoreOnce (read-only)
If non-zero, then the \fB\-o\fR option was supplied on the command line, If non-zero, then the \fB\-o\fR option was supplied on the command
or a date different from today's true date was supplied. If non-zero, line, or implicitly enabled for some other reason. In this case,
then \fBONCE\fR directives will be ignored. \fBONCE\fR directives will be ignored.
.TP .TP
.B $InfDelta (read-only) .B $InfDelta (read-only)
If non-zero, then the \fB\-t\fR option was supplied on the command line, If non-zero, then the \fB\-t\fR option was supplied on the command line,
@@ -2661,6 +2747,20 @@ by \fBREM\fR commands; triggers in \fBIFTRIG\fR commands do
not affect it. not affect it.
.RE .RE
.TP .TP
.B $OnceFile (STRING type)
If you set this variable to a non-empty string, then rather than using
the file access date to determine whether or not to run a ONCE-type
reminder, \fBRemind\fR will maintain a timestamp in the file \fB$OnceFile\fR.
This is more reliable than using the access date of the reminder file.
.RS
.PP
If \fB$OnceFile\fR does not exist, then it will be created the first time
a \fBONCE\fR keyword is processed. The file must be writable by the
current user. If you try to set \fB$OnceFile\fR \fIafter\fR a \fBONCE\fR
reminder has already been processed, \fBRemind\fR will issue a warning
and ignore the attempt to set \fB$OnceFile\fR.
.RE
.TP
.B $ParseUntriggered .B $ParseUntriggered
A flag indicating whether or not \fBRemind\fR should fully parse \fBREM\fR A flag indicating whether or not \fBRemind\fR should fully parse \fBREM\fR
statements that are not triggered. 0 means to skip parsing them and 1 statements that are not triggered. 0 means to skip parsing them and 1
@@ -2839,18 +2939,25 @@ is supplied, only the date component is used.
Returns the time of "astronomical twilight" on the specified \fIdate\fR. If Returns the time of "astronomical twilight" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR. \fIdate\fR is omitted, defaults to \fBtoday()\fR.
.TP .TP
.B ampm(tq_time [,s_am [,s_pm]]) .B ampm(tq_time [,s_am [,s_pm [,i_lz]]])
Returns a \fBSTRING\fR that is the result of converting \fItime\fR Returns a \fBSTRING\fR that is the result of converting \fItime\fR
(which is either a \fBTIME\fR or a \fBDATETIME\fR object) to "AM/PM" (which is either a \fBTIME\fR or a \fBDATETIME\fR object) to "AM/PM"
format. The optional arguments \fIam\fR and \fIpm\fR are the strings format. The optional arguments \fIam\fR and \fIpm\fR are the strings
to append in the AM and PM case, respectively; they default to "AM" to append in the AM and PM case, respectively; they default to "AM"
and "PM". The function obeys the system variables $DateSep, and "PM". The optional argument \fIlz\fR specifies whether or not
$TimeSep and $DateTimeSep when formatting its output. For example: the hour should be padded to two digits with a leading zero. If \fIlz\fR is
zero, then a leading 0 is not added; otherwise, the hour is padded out to
two digits with a leading zero. If not supplied, \fIlz\fR defaults to zero.
.RS .RS
.PP .PP
The function obeys the system variables $DateSep, $TimeSep and
$DateTimeSep when formatting its output. Here are some examples of
its output:
.PP
.nf .nf
ampm(0:22) returns "12:22AM" ampm(0:22) returns "12:22AM"
ampm(17:45, "am", "pm") returns "5:45pm" ampm(17:45, "am", "pm") returns "5:45pm"
ampm(17:45, "am", "pm", 1) returns "05:45pm"
ampm('2020-03-14@21:34') returns "2020-03-14@9:34PM" ampm('2020-03-14@21:34') returns "2020-03-14@9:34PM"
.fi .fi
.PP .PP
@@ -3210,14 +3317,14 @@ out. The stripping algorithm is fairly naive; the function starts
stripping characters when it encounters a "<" and it stops stripping stripping characters when it encounters a "<" and it stops stripping
when it encounters a ">". when it encounters a ">".
.TP .TP
.B iif(si_test1, x_arg1, [si_test2, x_arg2,...], x_default) .B iif(x_test1, x_arg1, [x_test2, x_arg2,...], x_default)
If \fItest1\fR is not zero or the null string, returns \fIarg1\fR. If \fItest1\fR is true, returns \fIarg1\fR. Otherwise, if \fItest2\fR
Otherwise, if \fItest2\fR is not zero or the null string, returns is true, returns \fIarg2\fR, and so on. If all of the \fItest\fR
\fIarg2\fR, and so on. If all of the \fItest\fR arguments are false, arguments are false, returns \fIdefault\fR. Note that all arguments
returns \fIdefault\fR. Note that all arguments are \fIalways\fR evaluated. are \fIalways\fR evaluated. This function accepts an odd number of
This function accepts an odd number of arguments - note that prior to version arguments - note that prior to version 03.00.05 of \fBRemind\fR, it
03.00.05 of \fBRemind\fR, it accepted 3 arguments only. The 3-argument accepted 3 arguments only. The 3-argument version of \fBiif()\fR is
version of \fBiif()\fR is compatible with previous versions of \fBRemind\fR. compatible with previous versions of \fBRemind\fR.
.TP .TP
.B index(s_search, s_target [,i_start) .B index(s_search, s_target [,i_start)
Returns an \fBINT\fR that is the location of \fItarget\fR in the Returns an \fBINT\fR that is the location of \fItarget\fR in the
@@ -3838,7 +3945,7 @@ function for advance warning to work properly. This is because
\fBtrig\fR returns a date constant (the trigger date) and the \fBtrig\fR returns a date constant (the trigger date) and the
REM command does not know the details of \fBtrig\fR's arguments. REM command does not know the details of \fBtrig\fR's arguments.
.PP .PP
Note that because \fBRemind\fR does not have short-circuit logical Note that because \fBRemind\fR has short-circuit logical
operators, something like: operators, something like:
.PP .PP
.nf .nf
@@ -3846,58 +3953,9 @@ operators, something like:
.fi .fi
would set the value of trig() to the date of the following would set the value of trig() to the date of the following
Thursday. Even though trig("Mon +7") always returns true, Monday. Because trig("Mon +7") always returns true,
the logical-OR operator still evaluates trig("Fri +7") which the logical-OR operator does not bother evaluating trig("Fri +7") which
\fIalso\fR returns true and sets \fBtrig()\fR. therefore does not set \fBtrig()\fR.
.PP
You can work around the lack of a short-circuit logical-OR as follows:
If \fBtrig\fR returns a true value, the specific value it returns
can be coerced to a DATE which is the trigger date. So the following code:
.PP
.nf
SET a trig("Mon +4") || trig("Fri +4")
IF a
REM [a] +4 MSG [wkday($T)] %b.
ENDIF
.fi
.PP
would operate as follows:
.PP
.nf
On Monday: Monday today.
On Tuesday: Friday in 3 days' time.
On Wednesday: Friday in 2 days' time.
On Thursday: Monday in 4 days' time.
On Friday: Monday in 3 days' time.
On Saturday: Monday in 2 days' time.
On Sunday: Monday tomorrow.
.fi
.PP
Compare with the following:
.PP
.nf
SET a trig("Mon +4") || trig("Fri +4")
IF a
REM [trig()] +4 MSG [wkday($T)] %b.
ENDIF
.fi
.PP
which yields:
.PP
.nf
On Monday: Friday in 4 days' time.
On Tuesday: Friday in 3 days' time.
On Wednesday: Friday in 2 days' time.
On Thursday: Friday tomorrow.
On Friday: Friday today.
On Saturday: Monday in 2 days' time.
On Sunday: Monday tomorrow.
.fi
.PP
That is because \fBtrig()\fR returns the trigger date of
the \fIlast\fR trig function that returns true,
whereas the value of \fBa\fR is the trigger date of the \fIfirst\fR
trig function that returns true.
.PP .PP
\fBImportant Note\fR: Because \fBtrig()\fR always returns an absolute \fBImportant Note\fR: Because \fBtrig()\fR always returns an absolute
date, it will \fBnot\fR work properly with a \fBSATISFY\fR clause. date, it will \fBnot\fR work properly with a \fBSATISFY\fR clause.
@@ -4237,6 +4295,14 @@ with square brackets. For example:
This evaluates the expression "mydate", where "mydate" is This evaluates the expression "mydate", where "mydate" is
presumably some pre-computed variable, and then "pastes" the result presumably some pre-computed variable, and then "pastes" the result
into the command-line for the parser to process. into the command-line for the parser to process.
.PP
If you want a literal "[" character for some reason, simply use "[[". For
example:
.PP
.nf
REM MSG Here are [[square] brackets!
.fi
.PP .PP
A formal description of this is: When \fBRemind\fR encounters a A formal description of this is: When \fBRemind\fR encounters a
"pasted-in" expression, it evaluates the expression, and coerces the "pasted-in" expression, it evaluates the expression, and coerces the
@@ -4473,11 +4539,10 @@ The above sequence sets y to 1, which is the global value of x.
.TP .TP
o o
User-defined functions may call other functions, including other user-defined User-defined functions may call other functions, including other user-defined
functions. However, recursive calls are not allowed. functions. Recursive calls are allowed, but they must terminate (for
.TP example, by using a short-circuit operator or function that breaks the
o recursion) or an error will result after a certain maximum number of
User-defined functions are not syntax-checked when they are defined; parsing recursive calls (by default, 1000.)
occurs only when they are called.
.TP .TP
o o
If a user-defined function has the same name as a built-in function, If a user-defined function has the same name as a built-in function,
@@ -4499,6 +4564,11 @@ or thirdfunc will exist. \fBRemind\fR does not issue an error if you
try to \fBFUNSET\fR a nonexistent user-defined function; it simply try to \fBFUNSET\fR a nonexistent user-defined function; it simply
does nothing in that case. does nothing in that case.
.PP .PP
If you define a user-defined function and then later on redefine it,
\fBRemind\fR will issue a warning. If you do not want this warning,
then use \fBFUNSET\fR to remove the existing definition before you
redefine the function.
.PP
.SH PRECISE SCHEDULING .SH PRECISE SCHEDULING
.PP .PP
The \fBWARN\fR keyword allows precise control over advance warning in The \fBWARN\fR keyword allows precise control over advance warning in
@@ -4632,8 +4702,11 @@ the error message "Can't compute trigger" is issued. Otherwise,
\fBtrigvalid()\fR is set to 1. \fBtrigvalid()\fR is set to 1.
.PP .PP
This is really useful only if \fIexpr\fR involves a call to the This is really useful only if \fIexpr\fR involves a call to the
\fBtrigdate()\fR or related functions; otherwise, \fIexpr\fR will not change as \fBtrigdate()\fR or related functions or system variables; otherwise,
\fBRemind\fR iterates. \fIexpr\fR will not change as \fBRemind\fR iterates. In fact, if
\fIexpr\fR is not a constant and does not call \fBtrigdate()\fR or
related functions or system variables, then \fBRemind\fR will issue a
warning.
.PP .PP
An example of the usefulness of \fBSATISFY\fR: Suppose you wish to An example of the usefulness of \fBSATISFY\fR: Suppose you wish to
be warned of every Friday the 13th. Your first attempt may be: be warned of every Friday the 13th. Your first attempt may be:
@@ -4784,7 +4857,7 @@ under program control. The format is:
.PP .PP
\fBDEBUG\fR [+\fIflagson\fR] [\-\fIflagsoff\fR] \fBDEBUG\fR [+\fIflagson\fR] [\-\fIflagsoff\fR]
.PP .PP
\fIFlagson\fR and \fIflagsoff\fR consist of strings of the characters "extvlf" \fIFlagson\fR and \fIflagsoff\fR consist of strings of the characters "extvlfs"
that correspond to the debugging options discussed in the command-line that correspond to the debugging options discussed in the command-line
options section. If preceded with a "+", the corresponding group of options section. If preceded with a "+", the corresponding group of
debugging options is switched on. Otherwise, they are switched off. debugging options is switched on. Otherwise, they are switched off.
+1 -1
View File
@@ -266,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
+21 -3
View File
@@ -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) = @_;
+85 -5
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;
if ($settings->{svg}) {
$surface = Cairo::SvgSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height}); $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
+10
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);
} }
} }
+12
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);
+16 -5
View File
@@ -319,7 +319,7 @@ set Option(PrintMargins) 36pt
set OptDescr(PrintSmallCalendars) "(0/1) If 1, print small calendars in PostScript output" set OptDescr(PrintSmallCalendars) "(0/1) If 1, print small calendars in PostScript output"
set Option(PrintSmallCalendars) 1 set Option(PrintSmallCalendars) 1
set OptDescr(PrintFormat) "Print format: pdf or ps" set OptDescr(PrintFormat) "Print format: pdf, ps or ps1 - ps1 means PostScript using rem2pdf"
set Option(PrintFormat) ps set Option(PrintFormat) ps
set WarningHeaders [list "# Lines starting with REM TAG TKTAGnnn ... were created by tkremind" "# Do not edit them by hand or results may be unpredictable."] set WarningHeaders [list "# Lines starting with REM TAG TKTAGnnn ... were created by tkremind" "# Do not edit them by hand or results may be unpredictable."]
@@ -1217,6 +1217,13 @@ proc FillCalWindow {} {
continue continue
} }
.cal.t$n configure -state normal .cal.t$n configure -state normal
# Canonicalize spaces and newlines
set stuff [regsub -all {[ \t]+} $stuff " "]
set stuff [regsub -all {[ \t]+\n} $stuff "\n"]
set stuff [regsub -all {\n[ \t]} $stuff "\n"]
set stuff [regsub -all {\n+} $stuff "\n"]
if {[regexp {TKTAG([0-9]+)} $tag all tagno] && "$fntag" != "x"} { if {[regexp {TKTAG([0-9]+)} $tag all tagno] && "$fntag" != "x"} {
.cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag] .cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" "date_$date" $extratags $fntag]
.cal.t$n tag bind "TKTAG$tagno" <Enter> "TaggedEnter .cal.t$n" .cal.t$n tag bind "TKTAG$tagno" <Enter> "TaggedEnter .cal.t$n"
@@ -1333,7 +1340,8 @@ proc DoPrint {} {
frame .p.ff -relief sunken -bd 2 frame .p.ff -relief sunken -bd 2
label .p.format -text "Output Format:" label .p.format -text "Output Format:"
radiobutton .p.pdf -text "PDF" -variable Option(PrintFormat) -value pdf radiobutton .p.pdf -text "PDF" -variable Option(PrintFormat) -value pdf
radiobutton .p.ps -text "PostScript" -variable Option(PrintFormat) -value ps radiobutton .p.ps -text "PostScript (using rem2ps)" -variable Option(PrintFormat) -value ps
radiobutton .p.ps1 -text "PostScript (using rem2pdf)" -variable Option(PrintFormat) -value ps1
} }
label .p.size -text "Paper Size:" label .p.size -text "Paper Size:"
@@ -1350,7 +1358,7 @@ proc DoPrint {} {
radiobutton .p.portrait -text "Portrait" -variable Option(PrintOrient) -value portrait radiobutton .p.portrait -text "Portrait" -variable Option(PrintOrient) -value portrait
checkbutton .p.fill -text "Fill page" -variable Option(PrintFill) checkbutton .p.fill -text "Fill page" -variable Option(PrintFill)
checkbutton .p.wrap -text "Use at most 5 rows (PDF only)" -variable Option(WrapCal) checkbutton .p.wrap -text "Use at most 5 rows (rem2pdf only)" -variable Option(WrapCal)
checkbutton .p.right -text "Day numbers at top-right" -variable Option(PrintDaysRight) checkbutton .p.right -text "Day numbers at top-right" -variable Option(PrintDaysRight)
checkbutton .p.encoding -text "ISO 8859-1 PostScript encoding" -variable Option(PrintEncoding) checkbutton .p.encoding -text "ISO 8859-1 PostScript encoding" -variable Option(PrintEncoding)
checkbutton .p.calendars -text "Print small calendars" -variable Option(PrintSmallCalendars) checkbutton .p.calendars -text "Print small calendars" -variable Option(PrintSmallCalendars)
@@ -1374,7 +1382,7 @@ proc DoPrint {} {
pack .p.tofile .p.filename .p.browse -in .p.f11 -side left -fill none -expand 0 -anchor w pack .p.tofile .p.filename .p.browse -in .p.f11 -side left -fill none -expand 0 -anchor w
pack .p.tocmd .p.command -in .p.f12 -side left -fill none -expand 0 -anchor w pack .p.tocmd .p.command -in .p.f12 -side left -fill none -expand 0 -anchor w
if { $HaveRem2PDF } { if { $HaveRem2PDF } {
pack .p.format .p.pdf .p.ps -in .p.ff -side top -fill none -expand 0 -anchor w pack .p.format .p.pdf .p.ps .p.ps1 -in .p.ff -side top -fill none -expand 0 -anchor w
} }
pack .p.size .p.letter .p.a4 -in .p.f2 -side top -fill none -expand 0 -anchor w pack .p.size .p.letter .p.a4 -in .p.f2 -side top -fill none -expand 0 -anchor w
pack .p.margin .p.24pt .p.36pt .p.48pt -in .p.f2a -side top -anchor w -fill none -expand 0 pack .p.margin .p.24pt .p.36pt .p.48pt -in .p.f2a -side top -anchor w -fill none -expand 0
@@ -1422,6 +1430,9 @@ proc DoPrint {} {
if {$HaveRem2PDF && $Option(PrintFormat) == "pdf"} { if {$HaveRem2PDF && $Option(PrintFormat) == "pdf"} {
set p [regsub @EXTRA@ $PSCmd "-itkpdf=1 $Option(ExtraRemindArgs)"] set p [regsub @EXTRA@ $PSCmd "-itkpdf=1 $Option(ExtraRemindArgs)"]
set cmd "$p 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PDF" set cmd "$p 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PDF"
} elseif {$HaveRem2PDF && $Option(PrintFormat) == "ps1"} {
set p [regsub @EXTRA@ $PSCmd "-itkpdf=1 $Option(ExtraRemindArgs)"]
set cmd "$p 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PDF --ps"
} else { } else {
set p [regsub @EXTRA@ $PSCmd $Option(ExtraRemindArgs)] set p [regsub @EXTRA@ $PSCmd $Option(ExtraRemindArgs)]
set cmd "$p 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PS" set cmd "$p 1 [lindex $MonthNames $CurMonth] $CurYear | $Rem2PS"
@@ -1463,7 +1474,7 @@ proc DoPrint {} {
} }
if {$Option(WrapCal)} { if {$Option(WrapCal)} {
if {$Option(PrintFormat) == "pdf"} { if {$Option(PrintFormat) == "pdf" || $Option(PrintFormat) == "ps1"} {
append cmd " --wrap" append cmd " --wrap"
} }
} }
+1 -1
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)
+49 -26
View File
@@ -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"
@@ -339,6 +338,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 +353,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)
@@ -527,9 +528,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);
@@ -543,10 +544,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)
{ {
@@ -759,13 +758,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);
@@ -903,13 +900,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);
@@ -1125,13 +1126,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();
@@ -1199,15 +1204,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++) {
@@ -1226,13 +1233,16 @@ 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) {
if (i + wcwidth(*ws) > width) {
break;
}
i += wcwidth(*ws);
PutWideChar(*ws++, NULL); PutWideChar(*ws++, NULL);
i+= wcwidth(*ws);
} else { } else {
break; break;
} }
@@ -1245,7 +1255,10 @@ static void PrintLeft(char const *s, int width, char pad)
/* 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
@@ -1266,7 +1279,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(' ');
@@ -1310,13 +1323,14 @@ 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++, NULL); 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;
} }
@@ -1328,7 +1342,10 @@ static void PrintCentered(char const *s, int width, char *pad)
/* 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
} }
@@ -1415,7 +1432,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;
} }
@@ -1521,7 +1538,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++;
} }
@@ -1670,6 +1687,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;
@@ -1683,7 +1701,10 @@ 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:
Wprint("Unrecognized command; interpreting as REM");
WarnedAboutImplicit = 1;
CreateParser(CurLine, &p);
r=DoCalRem(&p, col); r=DoCalRem(&p, col);
break; break;
} }
@@ -2142,7 +2163,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) {
+4 -16
View File
@@ -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 100
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* 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"
+4 -16
View File
@@ -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 100
/*---------------------------------------------------------------------*/ /*---------------------------------------------------------------------*/
/* 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"
+191 -14
View File
@@ -23,7 +23,6 @@
#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); static int ParseTimeTrig (ParsePtr s, TimeTrig *tim);
static int ParseLocalOmit (ParsePtr s, Trigger *t); static int ParseLocalOmit (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)
{ {
@@ -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) {
@@ -473,7 +619,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
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;
@@ -484,6 +630,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
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;
@@ -525,6 +672,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
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;
@@ -538,6 +686,10 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
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) {
Wprint("Missing REM type; assuming MSG");
WarnedAboutImplicit = 1;
}
parsing = 0; parsing = 0;
break; break;
} }
@@ -589,6 +741,11 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
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;
} }
@@ -860,6 +1017,9 @@ 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:
@@ -1186,7 +1346,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;
@@ -1268,20 +1428,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) {
@@ -1294,13 +1466,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);
@@ -1325,17 +1502,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;
} }
+40 -15
View File
@@ -12,15 +12,13 @@
/***************************************************************/ /***************************************************************/
#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 */
@@ -69,6 +80,7 @@ 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);
@@ -102,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);
@@ -131,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);
@@ -142,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]);
} }
@@ -155,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);
@@ -166,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]);
} }
@@ -242,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++;
} }
@@ -250,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)",
@@ -269,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) {
@@ -344,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) {
@@ -797,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), " ");
+3 -1
View File
@@ -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);
} }
+111 -105
View File
@@ -122,6 +122,9 @@
#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_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
@@ -140,111 +143,114 @@ 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 (max. " STR(INCLUDE_NEST) ")", /* 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 (max. " STR(MAX_PARTIAL_OMITS) ")", /* E_2MANY_PART */ "Too many partial OMITs (max. " STR(MAX_PARTIAL_OMITS) ")",
"Too many full OMITs (max. " STR(MAX_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",
"Expecting weekday name" /* 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 */
; ;
+2675 -1065
View File
File diff suppressed because it is too large Load Diff
-67
View File
@@ -1,67 +0,0 @@
/***************************************************************/
/* */
/* EXPR.H */
/* */
/* Contains a few definitions used by expression evaluator. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-2024 by Dianne Skoll */
/* SPDX-License-Identifier: GPL-2.0-only */
/* */
/***************************************************************/
/* Define the types of values */
#define ERR_TYPE 0
#define INT_TYPE 1
#define TIME_TYPE 2
#define DATE_TYPE 3
#define STR_TYPE 4
#define DATETIME_TYPE 5
#define SPECIAL_TYPE 6 /* Only for system variables */
#define CONST_INT_TYPE 7 /* 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) \
do { if (OpStackPtr >= OP_STACK_SIZE) return E_OP_STK_OVER; \
else { OpStack[OpStackPtr++] = (op); if (OpStackPtr > OpStackHiWater) OpStackHiWater = OpStackPtr; } } while(0)
#define PopOpStack(op) \
if (OpStackPtr <= 0) \
return E_OP_STK_UNDER; \
else \
(op) = OpStack[--OpStackPtr]
#define PushValStack(val) \
do { if (ValStackPtr >= VAL_STACK_SIZE) { \
DestroyValue(val); \
return E_VA_STK_OVER; \
} else { \
ValStack[ValStackPtr++] = (val); \
if (ValStackPtr > ValStackHiWater) ValStackHiWater = ValStackPtr; \
} } while (0);
#define PopValStack(val) \
if (ValStackPtr <= 0) \
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);
+22 -5
View File
@@ -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;
@@ -101,6 +102,13 @@ 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) void set_cloexec(FILE *fp)
{ {
int flags; int flags;
@@ -181,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;
@@ -282,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;
@@ -526,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];
@@ -547,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;
@@ -841,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];
@@ -871,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;
@@ -959,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];
@@ -973,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;
+376 -267
View File
@@ -54,12 +54,16 @@
#include "globals.h" #include "globals.h"
#include "protos.h" #include "protos.h"
#include "err.h" #include "err.h"
#include "expr.h"
/* Defines that used to be static variables */ /* Defines that used to be static variables */
#define Nargs (info->nargs) #define Nargs (info->nargs)
#define RetVal (info->retval) #define RetVal (info->retval)
#define DBG(x) do { if (DebugFlag & DB_PRTEXPR) { x; } } while(0)
/* Debugging helpers for "choose()", "iif(), etc. */
#define PUT(x) DBufPuts(&DebugBuf, x)
#define OUT() do { fprintf(ErrFp, "%s\n", DBufValue(&DebugBuf)); DBufFree(&DebugBuf); } while(0)
static int static int
solstice_equinox_for_year(int y, int which); solstice_equinox_for_year(int y, int which);
@@ -74,7 +78,7 @@ static int FArgs (func_info *);
static int FAsc (func_info *); static int FAsc (func_info *);
static int FBaseyr (func_info *); static int FBaseyr (func_info *);
static int FChar (func_info *); static int FChar (func_info *);
static int FChoose (func_info *); static int FChoose (expr_node *, Value *, Value *, int *);
static int FCoerce (func_info *); static int FCoerce (func_info *);
static int FColumns (func_info *); static int FColumns (func_info *);
static int FCurrent (func_info *); static int FCurrent (func_info *);
@@ -101,9 +105,9 @@ static int FHebyear (func_info *);
static int FHour (func_info *); static int FHour (func_info *);
static int FHtmlEscape (func_info *); static int FHtmlEscape (func_info *);
static int FHtmlStriptags (func_info *); static int FHtmlStriptags (func_info *);
static int FIif (func_info *); static int FIif (expr_node *, Value *, Value *, int *);
static int FIndex (func_info *); static int FIndex (func_info *);
static int FIsAny (func_info *); static int FIsAny (expr_node *, Value *, Value *, int *);
static int FIsdst (func_info *); static int FIsdst (func_info *);
static int FIsleap (func_info *); static int FIsleap (func_info *);
static int FIsomitted (func_info *); static int FIsomitted (func_info *);
@@ -180,14 +184,9 @@ static int FWkday (func_info *);
static int FWkdaynum (func_info *); static int FWkdaynum (func_info *);
static int FYear (func_info *); static int FYear (func_info *);
static int CleanUpAfterFunc (func_info *);
static int CheckArgs (BuiltinFunc *f, int nargs);
static int SunStuff (int rise, double cosz, int dse); static int SunStuff (int rise, double cosz, int dse);
static int tz_set_tz (char const *tz); static int tz_set_tz (char const *tz);
/* "Overload" the struct Operator definition */
#define NO_MAX 127
/* Caches for extracting months, days, years from dates - may /* Caches for extracting months, days, years from dates - may
improve performance slightly. */ improve performance slightly. */
static int CacheDse = -1; static int CacheDse = -1;
@@ -196,14 +195,9 @@ static int CacheYear, CacheMon, CacheDay;
static int CacheHebDse = -1; static int CacheHebDse = -1;
static int CacheHebYear, CacheHebMon, CacheHebDay; static int CacheHebYear, CacheHebMon, CacheHebDay;
/* We need access to the value stack */
extern Value ValStack[];
extern int ValStackPtr;
extern int ValStackHiWater;
/* Macro for accessing arguments from the value stack - args are numbered /* Macro for accessing arguments from the value stack - args are numbered
from 0 to (Nargs - 1) */ from 0 to (Nargs - 1) */
#define ARG(x) (ValStack[ValStackPtr - Nargs + (x)]) #define ARG(x) (info->args[x])
#define ARGV(x) ARG(x).v.val #define ARGV(x) ARG(x).v.val
#define ARGSTR(x) ARG(x).v.str #define ARGSTR(x) ARG(x).v.str
@@ -231,215 +225,127 @@ extern int ValStackHiWater;
/* The array holding the built-in functions. */ /* The array holding the built-in functions. */
BuiltinFunc Func[] = { BuiltinFunc Func[] = {
/* Name minargs maxargs is_constant func */ /* Name minargs maxargs is_constant func newfunc*/
{ "abs", 1, 1, 1, FAbs }, { "abs", 1, 1, 1, FAbs, NULL },
{ "access", 2, 2, 0, FAccess }, { "access", 2, 2, 0, FAccess, NULL },
{ "adawn", 0, 1, 0, FADawn}, { "adawn", 0, 1, 0, FADawn, NULL},
{ "adusk", 0, 1, 0, FADusk}, { "adusk", 0, 1, 0, FADusk, NULL},
{ "ampm", 1, 3, 1, FAmpm }, { "ampm", 1, 4, 1, FAmpm, NULL },
{ "ansicolor", 1, 5, 1, FAnsicolor }, { "ansicolor", 1, 5, 1, FAnsicolor, NULL },
{ "args", 1, 1, 0, FArgs }, { "args", 1, 1, 0, FArgs, NULL },
{ "asc", 1, 1, 1, FAsc }, { "asc", 1, 1, 1, FAsc, NULL },
{ "baseyr", 0, 0, 1, FBaseyr }, { "baseyr", 0, 0, 1, FBaseyr, NULL },
{ "char", 1, NO_MAX, 1, FChar }, { "char", 1, NO_MAX, 1, FChar, NULL },
{ "choose", 2, NO_MAX, 1, FChoose }, { "choose", 2, NO_MAX, 1, NULL, FChoose }, /*NEW-STYLE*/
{ "coerce", 2, 2, 1, FCoerce }, { "coerce", 2, 2, 1, FCoerce, NULL },
{ "columns", 0, 1, 0, FColumns }, { "columns", 0, 1, 0, FColumns, NULL },
{ "current", 0, 0, 0, FCurrent }, { "current", 0, 0, 0, FCurrent, NULL },
{ "date", 3, 3, 1, FDate }, { "date", 3, 3, 1, FDate, NULL },
{ "datepart", 1, 1, 1, FDatepart }, { "datepart", 1, 1, 1, FDatepart, NULL },
{ "datetime", 2, 5, 1, FDateTime }, { "datetime", 2, 5, 1, FDateTime, NULL },
{ "dawn", 0, 1, 0, FDawn}, { "dawn", 0, 1, 0, FDawn, NULL },
{ "day", 1, 1, 1, FDay }, { "day", 1, 1, 1, FDay, NULL },
{ "daysinmon", 2, 2, 1, FDaysinmon }, { "daysinmon", 2, 2, 1, FDaysinmon, NULL },
{ "defined", 1, 1, 0, FDefined }, { "defined", 1, 1, 0, FDefined, NULL },
{ "dosubst", 1, 3, 0, FDosubst }, { "dosubst", 1, 3, 0, FDosubst, NULL },
{ "dusk", 0, 1, 0, FDusk }, { "dusk", 0, 1, 0, FDusk, NULL },
{ "easterdate", 0, 1, 0, FEasterdate }, { "easterdate", 0, 1, 0, FEasterdate, NULL },
{ "evaltrig", 1, 2, 0, FEvalTrig }, { "evaltrig", 1, 2, 0, FEvalTrig, NULL },
{ "filedate", 1, 1, 0, FFiledate }, { "filedate", 1, 1, 0, FFiledate, NULL },
{ "filedatetime", 1, 1, 0, FFiledatetime }, { "filedatetime", 1, 1, 0, FFiledatetime, NULL },
{ "filedir", 0, 0, 0, FFiledir }, { "filedir", 0, 0, 0, FFiledir, NULL },
{ "filename", 0, 0, 0, FFilename }, { "filename", 0, 0, 0, FFilename, NULL },
{ "getenv", 1, 1, 0, FGetenv }, { "getenv", 1, 1, 0, FGetenv, NULL },
{ "hebdate", 2, 5, 0, FHebdate }, { "hebdate", 2, 5, 0, FHebdate, NULL },
{ "hebday", 1, 1, 0, FHebday }, { "hebday", 1, 1, 0, FHebday, NULL },
{ "hebmon", 1, 1, 0, FHebmon }, { "hebmon", 1, 1, 0, FHebmon, NULL },
{ "hebyear", 1, 1, 0, FHebyear }, { "hebyear", 1, 1, 0, FHebyear, NULL },
{ "hour", 1, 1, 1, FHour }, { "hour", 1, 1, 1, FHour, NULL },
{ "htmlescape", 1, 1, 1, FHtmlEscape }, { "htmlescape", 1, 1, 1, FHtmlEscape, NULL },
{ "htmlstriptags",1, 1, 1, FHtmlStriptags }, { "htmlstriptags",1, 1, 1, FHtmlStriptags, NULL },
{ "iif", 1, NO_MAX, 1, FIif }, { "iif", 1, NO_MAX, 1, NULL, FIif }, /*NEW-STYLE*/
{ "index", 2, 3, 1, FIndex }, { "index", 2, 3, 1, FIndex, NULL },
{ "isany", 1, NO_MAX, 1, FIsAny }, { "isany", 1, NO_MAX, 1, NULL, FIsAny }, /*NEW-STYLE*/
{ "isdst", 0, 2, 0, FIsdst }, { "isdst", 0, 2, 0, FIsdst, NULL },
{ "isleap", 1, 1, 1, FIsleap }, { "isleap", 1, 1, 1, FIsleap, NULL },
{ "isomitted", 1, 1, 0, FIsomitted }, { "isomitted", 1, 1, 0, FIsomitted, NULL },
{ "language", 0, 0, 1, FLanguage }, { "language", 0, 0, 1, FLanguage, NULL },
{ "localtoutc", 1, 1, 1, FLocalToUTC }, { "localtoutc", 1, 1, 1, FLocalToUTC, NULL },
{ "lower", 1, 1, 1, FLower }, { "lower", 1, 1, 1, FLower, NULL },
{ "max", 1, NO_MAX, 1, FMax }, { "max", 1, NO_MAX, 1, FMax, NULL },
{ "min", 1, NO_MAX, 1, FMin }, { "min", 1, NO_MAX, 1, FMin, NULL },
{ "minsfromutc", 0, 2, 0, FMinsfromutc }, { "minsfromutc", 0, 2, 0, FMinsfromutc, NULL },
{ "minute", 1, 1, 1, FMinute }, { "minute", 1, 1, 1, FMinute, NULL },
{ "mon", 1, 1, 1, FMon }, { "mon", 1, 1, 1, FMon, NULL },
{ "monnum", 1, 1, 1, FMonnum }, { "monnum", 1, 1, 1, FMonnum, NULL },
{ "moondate", 1, 3, 0, FMoondate }, { "moondate", 1, 3, 0, FMoondate, NULL },
{ "moondatetime", 1, 3, 0, FMoondatetime }, { "moondatetime", 1, 3, 0, FMoondatetime, NULL },
{ "moonphase", 0, 2, 0, FMoonphase }, { "moonphase", 0, 2, 0, FMoonphase, NULL },
{ "moontime", 1, 3, 0, FMoontime }, { "moontime", 1, 3, 0, FMoontime, NULL },
{ "multitrig", 1, NO_MAX, 0, FMultiTrig }, { "multitrig", 1, NO_MAX, 0, FMultiTrig, NULL },
{ "ndawn", 0, 1, 0, FNDawn}, { "ndawn", 0, 1, 0, FNDawn, NULL },
{ "ndusk", 0, 1, 0, FNDusk}, { "ndusk", 0, 1, 0, FNDusk, NULL },
{ "nonomitted", 2, NO_MAX, 0, FNonomitted }, { "nonomitted", 2, NO_MAX, 0, FNonomitted, NULL },
{ "now", 0, 0, 0, FNow }, { "now", 0, 0, 0, FNow, NULL },
{ "ord", 1, 1, 1, FOrd }, { "ord", 1, 1, 1, FOrd, NULL },
{ "orthodoxeaster",0, 1, 0, FOrthodoxeaster }, { "orthodoxeaster",0, 1, 0, FOrthodoxeaster, NULL },
{ "ostype", 0, 0, 1, FOstype }, { "ostype", 0, 0, 1, FOstype, NULL },
{ "pad", 3, 4, 1, FPad }, { "pad", 3, 4, 1, FPad, NULL },
{ "plural", 1, 3, 1, FPlural }, { "plural", 1, 3, 1, FPlural, NULL },
{ "psmoon", 1, 4, 1, FPsmoon}, { "psmoon", 1, 4, 1, FPsmoon, NULL },
{ "psshade", 1, 3, 1, FPsshade}, { "psshade", 1, 3, 1, FPsshade, NULL },
{ "realcurrent", 0, 0, 0, FRealCurrent}, { "realcurrent", 0, 0, 0, FRealCurrent, NULL },
{ "realnow", 0, 0, 0, FRealnow}, { "realnow", 0, 0, 0, FRealnow, NULL },
{ "realtoday", 0, 0, 0, FRealtoday }, { "realtoday", 0, 0, 0, FRealtoday, NULL },
{ "rows", 0, 0, 0, FRows }, { "rows", 0, 0, 0, FRows, NULL },
{ "sgn", 1, 1, 1, FSgn }, { "sgn", 1, 1, 1, FSgn, NULL },
{ "shell", 1, 2, 0, FShell }, { "shell", 1, 2, 0, FShell, NULL },
{ "shellescape", 1, 1, 1, FShellescape }, { "shellescape", 1, 1, 1, FShellescape, NULL },
{ "slide", 2, NO_MAX, 0, FSlide }, { "slide", 2, NO_MAX, 0, FSlide, NULL },
{ "soleq", 1, 2, 0, FSoleq }, { "soleq", 1, 2, 0, FSoleq, NULL },
{ "stdout", 0, 0, 1, FStdout }, { "stdout", 0, 0, 1, FStdout, NULL },
{ "strlen", 1, 1, 1, FStrlen }, { "strlen", 1, 1, 1, FStrlen, NULL },
{ "substr", 2, 3, 1, FSubstr }, { "substr", 2, 3, 1, FSubstr, NULL },
{ "sunrise", 0, 1, 0, FSunrise}, { "sunrise", 0, 1, 0, FSunrise, NULL },
{ "sunset", 0, 1, 0, FSunset }, { "sunset", 0, 1, 0, FSunset, NULL },
{ "time", 2, 2, 1, FTime }, { "time", 2, 2, 1, FTime, NULL },
{ "timepart", 1, 1, 1, FTimepart }, { "timepart", 1, 1, 1, FTimepart, NULL },
{ "timezone", 0, 1, 1, FTimezone }, { "timezone", 0, 1, 1, FTimezone, NULL },
{ "today", 0, 0, 0, FToday }, { "today", 0, 0, 0, FToday, NULL },
{ "trig", 0, NO_MAX, 0, FTrig }, { "trig", 0, NO_MAX, 0, FTrig, NULL },
{ "trigback", 0, 0, 0, FTrigback }, { "trigback", 0, 0, 0, FTrigback, NULL },
{ "trigdate", 0, 0, 0, FTrigdate }, { "trigdate", 0, 0, 0, FTrigdate, NULL },
{ "trigdatetime", 0, 0, 0, FTrigdatetime }, { "trigdatetime", 0, 0, 0, FTrigdatetime, NULL },
{ "trigdelta", 0, 0, 0, FTrigdelta }, { "trigdelta", 0, 0, 0, FTrigdelta, NULL },
{ "trigduration", 0, 0, 0, FTrigduration }, { "trigduration", 0, 0, 0, FTrigduration, NULL },
{ "trigeventduration", 0, 0, 0, FTrigeventduration }, { "trigeventduration", 0, 0, 0, FTrigeventduration, NULL },
{ "trigeventstart", 0, 0, 0, FTrigeventstart }, { "trigeventstart", 0, 0, 0, FTrigeventstart, NULL },
{ "trigfrom", 0, 0, 0, FTrigfrom }, { "trigfrom", 0, 0, 0, FTrigfrom, NULL },
{ "trigger", 1, 3, 0, FTrigger }, { "trigger", 1, 3, 0, FTrigger, NULL },
{ "trigpriority", 0, 0, 0, FTrigpriority }, { "trigpriority", 0, 0, 0, FTrigpriority, NULL },
{ "trigrep", 0, 0, 0, FTrigrep }, { "trigrep", 0, 0, 0, FTrigrep, NULL },
{ "trigscanfrom", 0, 0, 0, FTrigscanfrom }, { "trigscanfrom", 0, 0, 0, FTrigscanfrom, NULL },
{ "trigtags", 0, 0, 0, FTrigtags }, { "trigtags", 0, 0, 0, FTrigtags, NULL },
{ "trigtime", 0, 0, 0, FTrigtime }, { "trigtime", 0, 0, 0, FTrigtime, NULL },
{ "trigtimedelta",0, 0, 0, FTrigtimedelta }, { "trigtimedelta",0, 0, 0, FTrigtimedelta, NULL },
{ "trigtimerep", 0, 0, 0, FTrigtimerep }, { "trigtimerep", 0, 0, 0, FTrigtimerep, NULL },
{ "triguntil", 0, 0, 0, FTriguntil }, { "triguntil", 0, 0, 0, FTriguntil, NULL },
{ "trigvalid", 0, 0, 0, FTrigvalid }, { "trigvalid", 0, 0, 0, FTrigvalid, NULL },
{ "typeof", 1, 1, 1, FTypeof }, { "typeof", 1, 1, 1, FTypeof, NULL },
{ "tzconvert", 2, 3, 0, FTzconvert }, { "tzconvert", 2, 3, 0, FTzconvert, NULL },
{ "upper", 1, 1, 1, FUpper }, { "upper", 1, 1, 1, FUpper, NULL },
{ "utctolocal", 1, 1, 1, FUTCToLocal }, { "utctolocal", 1, 1, 1, FUTCToLocal, NULL },
{ "value", 1, 2, 0, FValue }, { "value", 1, 2, 0, FValue, NULL },
{ "version", 0, 0, 1, FVersion }, { "version", 0, 0, 1, FVersion, NULL },
{ "weekno", 0, 3, 1, FWeekno }, { "weekno", 0, 3, 0, FWeekno, NULL },
{ "wkday", 1, 1, 1, FWkday }, { "wkday", 1, 1, 1, FWkday, NULL },
{ "wkdaynum", 1, 1, 1, FWkdaynum }, { "wkdaynum", 1, 1, 1, FWkdaynum, NULL },
{ "year", 1, 1, 1, FYear } { "year", 1, 1, 1, FYear, NULL }
}; };
/* Need a variable here - Func[] array not really visible to outside. */ /* Need a variable here - Func[] array not really visible to outside. */
int NumFuncs = sizeof(Func) / sizeof(Operator) ; int NumFuncs = sizeof(Func) / sizeof(BuiltinFunc) ;
/***************************************************************/
/* */
/* CallFunc */
/* */
/* Call a function given a pointer to it, and the number */
/* of arguments supplied. */
/* */
/***************************************************************/
int CallFunc(BuiltinFunc *f, int nargs)
{
register int r = CheckArgs(f, nargs);
int i;
func_info info_obj;
func_info *info = &info_obj;
Nargs = nargs;
RetVal.type = ERR_TYPE;
if (DebugFlag & DB_PRTEXPR) {
fprintf(ErrFp, "%s(", f->name);
for (i=0; i<nargs; i++) {
PrintValue(&ARG(i), ErrFp);
if (i<nargs-1) fprintf(ErrFp, ", ");
}
fprintf(ErrFp, ") => ");
if (r) {
fprintf(ErrFp, "%s\n", ErrMsg[r]);
return r;
}
}
if (r) {
Eprint("%s(): %s", f->name, ErrMsg[r]);
return r;
}
r = (*(f->func))(info);
if (r) {
DestroyValue(RetVal);
if (DebugFlag & DB_PRTEXPR)
fprintf(ErrFp, "%s\n", ErrMsg[r]);
else
Eprint("%s(): %s", f->name, ErrMsg[r]);
return r;
}
if (DebugFlag & DB_PRTEXPR) {
PrintValue(&RetVal, ErrFp);
fprintf(ErrFp, "\n");
}
r = CleanUpAfterFunc(info);
return r;
}
/***************************************************************/
/* */
/* CheckArgs */
/* */
/* Check that the right number of args have been supplied */
/* for a function. */
/* */
/***************************************************************/
static int CheckArgs(BuiltinFunc *f, int nargs)
{
if (nargs < f->minargs) return E_2FEW_ARGS;
if (nargs > f->maxargs && f->maxargs != NO_MAX) return E_2MANY_ARGS;
return OK;
}
/***************************************************************/
/* */
/* CleanUpAfterFunc */
/* */
/* Clean up the stack after a function call - remove */
/* args and push the new value. */
/* */
/***************************************************************/
static int CleanUpAfterFunc(func_info *info)
{
Value v;
int i;
for (i=0; i<Nargs; i++) {
PopValStack(v);
DestroyValue(v);
}
PushValStack(RetVal);
return OK;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -473,10 +379,9 @@ static int RetStrVal(char const *s, func_info *info)
/***************************************************************/ /***************************************************************/
static int FStrlen(func_info *info) static int FStrlen(func_info *info)
{ {
Value *v = &ARG(0); ASSERT_TYPE(0, STR_TYPE);
if (v->type != STR_TYPE) return E_BAD_TYPE;
RetVal.type = INT_TYPE; RetVal.type = INT_TYPE;
size_t l = strlen(v->v.str); size_t l = strlen(ARGSTR(0));
if (l > INT_MAX) return E_2HIGH; if (l > INT_MAX) return E_2HIGH;
RETVAL = (int) l; RETVAL = (int) l;
return OK; return OK;
@@ -1040,6 +945,8 @@ static int FAmpm(func_info *info)
char const *pm = "PM"; char const *pm = "PM";
char const *ampm = NULL; char const *ampm = NULL;
int include_leading_zero = 0;
char outbuf[128]; char outbuf[128];
if (ARG(0).type != DATETIME_TYPE && ARG(0).type != TIME_TYPE) { if (ARG(0).type != DATETIME_TYPE && ARG(0).type != TIME_TYPE) {
@@ -1054,6 +961,10 @@ static int FAmpm(func_info *info)
if (Nargs >= 3) { if (Nargs >= 3) {
ASSERT_TYPE(2, STR_TYPE); ASSERT_TYPE(2, STR_TYPE);
pm = ARGSTR(2); pm = ARGSTR(2);
if (Nargs >= 4) {
ASSERT_TYPE(3, INT_TYPE);
include_leading_zero = ARGV(3);
}
} }
} }
h = TIMEPART(ARG(0)) / 60; h = TIMEPART(ARG(0)) / 60;
@@ -1068,21 +979,37 @@ static int FAmpm(func_info *info)
} }
} else { } else {
if (ARG(0).type == DATETIME_TYPE) { if (ARG(0).type == DATETIME_TYPE) {
if (include_leading_zero) {
snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%02d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
} else {
snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m); snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
}
} else {
if (include_leading_zero) {
snprintf(outbuf, sizeof(outbuf), "%02d%c%02d", h, TimeSep, m);
} else { } else {
snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m); snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m);
} }
} }
}
ampm = am; ampm = am;
} else { } else {
if (h > 12) { if (h > 12) {
h -= 12; h -= 12;
} }
if (ARG(0).type == DATETIME_TYPE) { if (ARG(0).type == DATETIME_TYPE) {
if (include_leading_zero) {
snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%02d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
} else {
snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m); snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
}
} else {
if (include_leading_zero) {
snprintf(outbuf, sizeof(outbuf), "%02d%c%02d", h, TimeSep, m);
} else { } else {
snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m); snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m);
} }
}
ampm = pm; ampm = pm;
} }
RetVal.type = STR_TYPE; RetVal.type = STR_TYPE;
@@ -1131,8 +1058,8 @@ static int FOrd(func_info *info)
/* */ /* */
/* FPad - Pad a string to min length */ /* FPad - Pad a string to min length */
/* */ /* */
/* pad("1", "0", 4) --> "0004" */ /* pad("1", "0", 4) --> "0001" */
/* pad("1", "0", 4, 1) --> "4000" */ /* pad("1", "0", 4, 1) --> "1000" */
/* pad("foo", "bar", 7) -> "barbfoo" */ /* pad("foo", "bar", 7) -> "barbfoo" */
/* */ /* */
/***************************************************************/ /***************************************************************/
@@ -1256,25 +1183,66 @@ static int FPlural(func_info *info)
/* otherwise. */ /* otherwise. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int FIsAny(func_info *info) static int FIsAny(expr_node *node, Value *locals, Value *ans, int *nonconst)
{ {
int i; DynamicBuffer DebugBuf;
RetVal.type = INT_TYPE; expr_node *cur;
RETVAL = 0; int r;
for (i=1; i<Nargs; i++) {
if (ARG(0).type == ARG(i).type) { Value v;
if (ARG(0).type == STR_TYPE) { Value candidate;
if (!strcmp(ARGSTR(0), ARGSTR(i))) {
RETVAL = 1; ans->type = INT_TYPE;
return OK; ans->v.val = 0;
DBG(DBufInit(&DebugBuf));
DBG(PUT("isany("));
cur = node->child;
r = evaluate_expr_node(cur, locals, &v, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(PrintValue(&v, NULL)));
while(cur->sibling) {
cur = cur->sibling;
r = evaluate_expr_node(cur, locals, &candidate, nonconst);
if (r != OK) {
DestroyValue(v);
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(", "));
DBG(PUT(PrintValue(&candidate, NULL)));
if (candidate.type != v.type) {
DestroyValue(candidate);
continue;
}
if (v.type == STR_TYPE) {
if (strcmp(v.v.str, candidate.v.str)) {
DestroyValue(candidate);
continue;
} }
} else { } else {
if (ARGV(0) == ARGV(i)) { if (v.v.val != candidate.v.val) {
RETVAL = 1; DestroyValue(candidate);
return OK; continue;
} }
} }
DestroyValue(candidate);
ans->v.val = 1;
break;
} }
DestroyValue(v);
if (DebugFlag & DB_PRTEXPR) {
while(cur->sibling) {
cur = cur->sibling;
PUT(", ?");
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
} }
return OK; return OK;
} }
@@ -1287,15 +1255,64 @@ static int FIsAny(func_info *info)
/* from 1. */ /* from 1. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int FChoose(func_info *info) static int FChoose(expr_node *node, Value *locals, Value *ans, int *nonconst)
{ {
int v; DynamicBuffer DebugBuf;
expr_node *cur;
int r;
int n;
int nargs = node->num_kids;
Value v;
DBG(DBufInit(&DebugBuf));
DBG(PUT("choose("));
ASSERT_TYPE(0, INT_TYPE); cur = node->child;
v = ARGV(0); r = evaluate_expr_node(cur, locals, &v, nonconst);
if (v < 1) v = 1; if (r != OK) {
if (v > Nargs-1) v = Nargs-1; DBG(DBufFree(&DebugBuf));
DCOPYVAL(RetVal, ARG(v)); return r;
}
DBG(PUT(PrintValue(&v, NULL)));
if (v.type != INT_TYPE) {
if (DebugFlag & DB_PRTEXPR) {
cur = cur->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(ErrMsg[E_BAD_TYPE]);
OUT();
}
Eprint("choose(): %s", ErrMsg[E_BAD_TYPE]);
return E_BAD_TYPE;
}
n = v.v.val;
if (n < 1) n = 1;
if (n > nargs-1) n = nargs-1;
while(n--) {
cur = cur->sibling;
DBG(if (n) { PUT(", ?"); });
if (!cur) return E_SWERR; /* Should not happen! */
}
r = evaluate_expr_node(cur, locals, ans, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
if (DebugFlag & DB_PRTEXPR) {
PUT(", ");
PUT(PrintValue(ans, NULL));
cur = cur->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return OK; return OK;
} }
@@ -1481,7 +1498,7 @@ static int FValue(func_info *info)
ASSERT_TYPE(0, STR_TYPE); ASSERT_TYPE(0, STR_TYPE);
switch(Nargs) { switch(Nargs) {
case 1: case 1:
return GetVarValue(ARGSTR(0), &RetVal, NULL, NULL); return GetVarValue(ARGSTR(0), &RetVal);
case 2: case 2:
v = FindVar(ARGSTR(0), 0); v = FindVar(ARGSTR(0), 0);
@@ -1953,33 +1970,82 @@ static int FIndex(func_info *info)
/* */ /* */
/* FIif */ /* FIif */
/* */ /* */
/* The IIF function. */ /* The IIF function. Uses new-style evaluation */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int FIif(func_info *info) static int FIif(expr_node *node, Value *locals, Value *ans, int *nonconst)
{ {
int istrue; int r;
int arg; int done;
Value v;
expr_node *cur;
DynamicBuffer DebugBuf;
if (!(Nargs % 2)) return E_IIF_ODD; DBG(DBufInit(&DebugBuf));
DBG(PUT("iif("));
cur = node->child;
for (arg=0; arg<Nargs-1; arg += 2) { if (!(node->num_kids % 2)) {
if (ARG(arg).type != STR_TYPE && ARG(arg).type != INT_TYPE) if (DebugFlag & DB_PRTEXPR) {
return E_BAD_TYPE; r = 0;
while(cur) {
if (ARG(arg).type == INT_TYPE) if (r) PUT(", ");
istrue = ARG(arg).v.val; r=1;
else PUT("?");
istrue = *(ARG(arg).v.str); cur = cur->sibling;
if (istrue) {
DCOPYVAL(RetVal, ARG(arg+1));
return OK;
} }
PUT(") => ");
PUT(ErrMsg[E_IIF_ODD]);
OUT();
}
return E_IIF_ODD;
} }
DCOPYVAL(RetVal, ARG(Nargs-1));
return OK; done = 0;
while(cur->sibling) {
r = evaluate_expr_node(cur, locals, &v, nonconst);
if (r != OK) {
DBG(DBufFree(&DebugBuf));
return r;
}
if (DebugFlag & DB_PRTEXPR) {
if (done) PUT(", ");
done = 1;
PUT(PrintValue(&v, NULL));
}
if (truthy(&v)) {
r = evaluate_expr_node(cur->sibling, locals, ans, nonconst);
if (r == OK && (DebugFlag & DB_PRTEXPR)) {
PUT(", ");
PUT(PrintValue(ans, NULL));
cur = cur->sibling->sibling;
while(cur) {
PUT(", ?");
cur = cur->sibling;
}
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
DBG(DBufFree(&DebugBuf));
return r;
}
DBG(PUT(", ?"));
cur = cur->sibling->sibling;
}
/* Return the last arg */
r = evaluate_expr_node(cur, locals, ans, nonconst);
if (DebugFlag & DB_PRTEXPR) {
if (done) PUT(", ");
PUT(PrintValue(ans, NULL));
PUT(") => ");
PUT(PrintValue(ans, NULL));
OUT();
}
return r;
} }
/***************************************************************/ /***************************************************************/
@@ -2101,6 +2167,7 @@ static int FArgs(func_info *info)
{ {
ASSERT_TYPE(0, STR_TYPE); ASSERT_TYPE(0, STR_TYPE);
RetVal.type = INT_TYPE; RetVal.type = INT_TYPE;
strtolower(ARGSTR(0));
RETVAL = UserFuncExists(ARGSTR(0)); RETVAL = UserFuncExists(ARGSTR(0));
return OK; return OK;
} }
@@ -2373,14 +2440,18 @@ static int FEasterdate(func_info *info)
{ {
int y, m, d; int y, m, d;
int g, c, x, z, e, n; int g, c, x, z, e, n;
int base;
if (Nargs == 0) { if (Nargs == 0) {
base = DSEToday;
FromDSE(DSEToday, &y, &m, &d); FromDSE(DSEToday, &y, &m, &d);
} else { } else {
if (ARG(0).type == INT_TYPE) { if (ARG(0).type == INT_TYPE) {
base = -1;
y = ARGV(0); y = ARGV(0);
if (y < BASE) return E_2LOW; if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH; else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) { } else if (HASDATE(ARG(0))) {
base = DATEPART(ARG(0));
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */ FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE; } else return E_BAD_TYPE;
} }
@@ -2406,7 +2477,7 @@ static int FEasterdate(func_info *info)
RetVal.type = DATE_TYPE; RetVal.type = DATE_TYPE;
RETVAL = DSE(y, m, d); RETVAL = DSE(y, m, d);
y++; } while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0))); y++; } while (base > -1 && RETVAL < base);
return OK; return OK;
} }
@@ -2422,7 +2493,9 @@ static int FOrthodoxeaster(func_info *info)
{ {
int y, m, d; int y, m, d;
int a, b, c, dd, e, f, dse; int a, b, c, dd, e, f, dse;
int base = -1;
if (Nargs == 0) { if (Nargs == 0) {
base = DSEToday;
FromDSE(DSEToday, &y, &m, &d); FromDSE(DSEToday, &y, &m, &d);
} else { } else {
if (ARG(0).type == INT_TYPE) { if (ARG(0).type == INT_TYPE) {
@@ -2430,6 +2503,7 @@ static int FOrthodoxeaster(func_info *info)
if (y < BASE) return E_2LOW; if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH; else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) { } else if (HASDATE(ARG(0))) {
base = DATEPART(ARG(0));
FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */ FromDSE(DATEPART(ARG(0)), &y, &m, &d); /* We just want the year */
} else return E_BAD_TYPE; } else return E_BAD_TYPE;
} }
@@ -2449,7 +2523,7 @@ static int FOrthodoxeaster(func_info *info)
RetVal.type = DATE_TYPE; RetVal.type = DATE_TYPE;
RETVAL = dse; RETVAL = dse;
y++; y++;
} while (HASDATE(ARG(0)) && RETVAL < DATEPART(ARG(0))); } while (base > -1 && RETVAL < base);
return OK; return OK;
} }
@@ -3896,3 +3970,38 @@ FSoleq(func_info *info)
RETVAL = ret; RETVAL = ret;
return OK; return OK;
} }
/* Compare two strings case-insensitively, where we KNOW
that the second string is definitely lower-case */
static int strcmp_lcfirst(char const *s1, char const *s2)
{
int r;
while (*s1 && *s2) {
r = tolower(*s1) - *s2;
if (r) return r;
s1++;
s2++;
}
return tolower(*s1) - *s2;
}
/***************************************************************/
/* */
/* FindBuiltinFunc */
/* */
/* Find a built-in function. */
/* */
/***************************************************************/
BuiltinFunc *FindBuiltinFunc(char const *name)
{
int top=NumFuncs-1, bot=0;
int mid, r;
while (top >= bot) {
mid = (top + bot) / 2;
r = strcmp_lcfirst(name, Func[mid].name);
if (!r) return &Func[mid];
else if (r > 0) bot = mid+1;
else top = mid-1;
}
return NULL;
}
+15 -3
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
@@ -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,10 +75,16 @@ 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( int SysTime, -1); EXTERN INIT( int SysTime, -1);
@@ -110,6 +118,7 @@ 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;
@@ -159,6 +168,9 @@ 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);
extern int NumFullOmits, NumPartialOmits; extern int NumFullOmits, NumPartialOmits;
/* List of months */ /* List of months */
+85 -4
View File
@@ -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;
@@ -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':
@@ -458,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 */
@@ -502,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') {
@@ -528,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' ||
@@ -605,7 +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_EXPR_STACKS; break; 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;
@@ -721,6 +723,7 @@ void InitRemind(int argc, char const *argv[])
if (rep > 0) { if (rep > 0) {
Iterations = rep; Iterations = rep;
IgnoreOnce = 1;
DontQueue = 1; DontQueue = 1;
Daemon = 0; Daemon = 0;
} }
@@ -926,7 +929,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) {
if (isalpha(*str) || *str == '_' || (r > 0 && *str == '(') || (r == 0 && *str == '$') || (r > 0 && isdigit(*str))) {
varname[r++] = *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 */
@@ -936,10 +944,28 @@ static void InitializeVar(char const *str)
str++; str++;
} }
varname[r] = 0; varname[r] = 0;
if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return;
}
if (!*str) { if (!*str) {
/* Setting a system var does require =expr on the commandline */
if (*varname == '$') {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]); fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]);
return; 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;
@@ -992,13 +1018,65 @@ 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 (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);
} }
@@ -1041,6 +1119,8 @@ guess_terminal_background(int *r, int *g, int *b)
if (n != 8) { if (n != 8) {
/* write failed... WTF? Not much we can do */ /* write failed... WTF? Not much we can do */
tty_reset(ttyfd);
close(ttyfd);
return; return;
} }
@@ -1064,6 +1144,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 */
+5 -2
View File
@@ -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",
@@ -246,7 +246,10 @@ EXTERN char *ErrMsg[] =
"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" "Odotettu viikonpäivän nimi",
"Päällekkäinen argumentin nimi",
"Lausekkeiden arviointi on poistettu käytöstä",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
+4 -1
View File
@@ -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",
@@ -221,6 +221,9 @@ EXTERN char *ErrMsg[] =
"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 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 */
+4 -2
View File
@@ -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",
@@ -236,7 +236,9 @@ EXTERN char *ErrMsg[] =
"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" "Oczekiwana nazwa dnia tygodnia",
"Zduplikowana nazwa argumentu",
"Ocena wyrażeń jest wyłączona",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
+3 -1
View File
@@ -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",
@@ -246,6 +246,8 @@ EXTERN char *ErrMsg[] =
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT", "Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana", "Esperando nome do dia da semana",
"Nome de argumento duplicado",
"A avaliação da expressão está desabilitada",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
+243 -43
View File
@@ -46,7 +46,6 @@
#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"
@@ -55,6 +54,40 @@ static void DoReminders(void);
/* Macro for simplifying common block so as not to litter code */ /* 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) #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));
}
/***************************************************************/ /***************************************************************/
/***************************************************************/ /***************************************************************/
/** **/ /** **/
@@ -66,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
@@ -78,10 +113,29 @@ 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(DebugExitFunc); atexit(exitfunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) { if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar(); ProduceCalendar();
@@ -106,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]);
@@ -159,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;
@@ -188,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) {
@@ -286,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;
@@ -296,11 +353,16 @@ 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:
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)) {
@@ -458,6 +520,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);
@@ -466,8 +538,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) {
if (*p->pos) {
*err = E_PARSE_ERR;
} else {
*err = E_MISS_END; *err = E_MISS_END;
}
DestroyParser(p); DestroyParser(p);
DestroyValue(val); DestroyValue(val);
return 0; return 0;
@@ -582,6 +661,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 */
@@ -593,21 +723,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) {
if (!p->pos) return E_PARSE_ERR; /* Missing expression */ return r;
while (isempty(*p->pos)) (p->pos)++;
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;
} }
@@ -643,36 +774,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;
} }
/***************************************************************/ /***************************************************************/
@@ -841,6 +972,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);
@@ -1025,8 +1157,8 @@ int DoDebug(ParsePtr p)
case 's': case 's':
case 'S': case 'S':
if (val) DebugFlag |= DB_EXPR_STACKS; if (val) DebugFlag |= DB_PARSE_EXPR;
else DebugFlag &= ~DB_EXPR_STACKS; else DebugFlag &= ~DB_PARSE_EXPR;
break; break;
case 'x': case 'x':
@@ -1106,7 +1238,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)
{ {
@@ -1133,6 +1264,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 */
@@ -1738,3 +1901,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;
}
-15
View File
@@ -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
+24 -4
View File
@@ -14,13 +14,12 @@
#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 const 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);
@@ -37,6 +36,8 @@ 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,16 +123,28 @@ 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->filename);
free(context->fullsave); free(context->fullsave);
free(context); free(context);
return E_NO_MEM; return E_NO_MEM;
@@ -175,6 +194,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);
+31 -6
View File
@@ -43,6 +43,7 @@
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);
@@ -56,9 +57,18 @@ int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
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 ParseLiteralDate (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);
@@ -77,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, ...);
@@ -98,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);
@@ -128,10 +140,13 @@ char *StrDup (char const *s);
int StrCmpi (char const *s1, char const *s2); int StrCmpi (char const *s1, char const *s2);
#endif #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 const *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);
@@ -144,10 +159,10 @@ 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);
@@ -207,13 +222,23 @@ 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 WriteJSONTrigger(Trigger const *t, int include_tags, int today);
void WriteJSONTimeTrigger(TimeTrig const *tt); 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, DynamicBuffer *output); 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);
+8 -2
View File
@@ -34,7 +34,6 @@
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#undef USE_INOTIFY #undef USE_INOTIFY
#if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1) #if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1)
@@ -311,6 +310,12 @@ void HandleQueuedReminders(void)
struct sigaction sa; struct sigaction sa;
char qid[64]; char qid[64];
/* Disable any potential pending SIGALRMs */
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;
@@ -354,7 +359,8 @@ void HandleQueuedReminders(void)
if (ShouldFork || 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);
-1
View File
@@ -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"
+1
View File
@@ -56,6 +56,7 @@ Token TokArray[] = {
{ "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 },
-1
View File
@@ -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"
+85 -9
View File
@@ -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,7 +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_EXPR_STACKS 64 #define DB_PARSE_EXPR 64
/* Enumeration of the tokens */ /* Enumeration of the tokens */
enum TokTypes enum TokTypes
@@ -184,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 */
@@ -244,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;
+147 -178
View File
@@ -26,38 +26,36 @@
#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;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -80,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);
} }
@@ -98,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;
@@ -113,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) {
@@ -139,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) == '$') {
@@ -168,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++) {
if (!StrinCmp(DBufValue(&buf), local_array[i].name, VAR_NAME_LEN)) {
DBufFree(&buf); DBufFree(&buf);
DestroyUserFunc(func); DestroyUserFunc(func);
return E_NO_MEM; return E_REPEATED_ARG;
} }
func->nargs++; }
v->v.type = ERR_TYPE; i = func->nargs;
StrnCpy(v->name, DBufValue(&buf), VAR_NAME_LEN); if (i >= MAX_FUNC_ARGS-1) {
DBufFree(&buf); DBufFree(&buf);
v->next = func->locals; DestroyUserFunc(func);
func->locals = v; 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++;
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);
@@ -191,21 +222,51 @@ 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++;
}
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); DestroyUserFunc(func);
return E_NO_MEM; return E_NO_MEM;
} }
}
}
/* If an old definition of this function exists, destroy it */ /* If an old definition of this function exists, destroy it */
FUnset(func->name); FUnset(func->name);
@@ -228,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);
} }
@@ -262,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;
} }
@@ -285,134 +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 */
@@ -423,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;
}
}
+49 -15
View File
@@ -130,6 +130,14 @@ int DateOK(int y, int m, int d)
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 */
int _private_mul_overflow(int a, int b) int _private_mul_overflow(int a, int b)
@@ -187,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 (freecs) {
entry = freecs;
freecs = freecs->next;
} else {
entry = NEW(cs);
if (!entry) { if (!entry) {
return E_NO_MEM; return E_NO_MEM;
} }
entry->next = NULL;
entry->filename = StrDup(filename);
entry->func = StrDup(func);
entry->lineno = lineno;
if (!entry->filename || !entry->func) {
destroy_cs(entry);
return E_NO_MEM;
} }
entry->next = NULL;
entry->filename = filename;
entry->func = func;
entry->lineno = lineno;
entry->next = callstack; entry->next = callstack;
callstack = entry; callstack = entry;
return OK; return OK;
@@ -233,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
+73 -30
View File
@@ -22,7 +22,6 @@
#include <errno.h> #include <errno.h>
#include <locale.h> #include <locale.h>
#include "types.h" #include "types.h"
#include "expr.h"
#include "globals.h" #include "globals.h"
#include "protos.h" #include "protos.h"
#include "err.h" #include "err.h"
@@ -39,8 +38,6 @@ static int IntMax = INT_MAX;
static Var *VHashTbl[VAR_HASH_SIZE]; static Var *VHashTbl[VAR_HASH_SIZE];
typedef int (*SysVarFunc)(int, Value *);
static double static double
strtod_in_c_locale(char const *str, char **endptr) strtod_in_c_locale(char const *str, char **endptr)
{ {
@@ -167,6 +164,40 @@ static int latitude_func(int do_set, Value *val)
return latitude_longitude_func(do_set, val, &Latitude, -90.0, 90.0); return latitude_longitude_func(do_set, val, &Latitude, -90.0, 90.0);
} }
static int oncefile_func(int do_set, Value *val)
{
if (do_set) {
if (val->type != STR_TYPE) return E_BAD_TYPE;
if (! (*val->v.str) && (!OnceFile || !*OnceFile)) {
/* Trying to set already-empty string to empty string */
return OK;
}
if (OnceFile && !strcmp(OnceFile, val->v.str)) {
/* Trying to set to the exact same value */
return OK;
}
if (ProcessedOnce) {
Wprint("Not setting $OnceFile: Already processed a reminder with a ONCE clause");
return OK;
}
if (OnceFile) {
free( (void *) OnceFile);
}
OnceFile = StrDup(val->v.str);
if (!OnceFile) return E_NO_MEM;
return OK;
}
if (!OnceFile) {
val->v.str = StrDup("");
} else {
val->v.str = StrDup(OnceFile);
}
if (!val->v.str) return E_NO_MEM;
val->type = STR_TYPE;
return OK;
}
static int terminal_bg_func(int do_set, Value *val) static int terminal_bg_func(int do_set, Value *val)
{ {
UNUSED(do_set); UNUSED(do_set);
@@ -322,6 +353,29 @@ static int datetime_sep_func(int do_set, Value *val)
return OK; return OK;
} }
static int expr_time_limit_func(int do_set, Value *val)
{
if (!do_set) {
val->type = INT_TYPE;
val->v.val = ExpressionEvaluationTimeLimit;
return OK;
}
if (val->type != INT_TYPE) return E_BAD_TYPE;
if (val->v.val < 0) return E_2LOW;
if (!TopLevel()) {
/* Ignore attempts to set from non-toplevel unless it's
lower than current value */
if (val->v.val == 0 ||
val->v.val >= ExpressionEvaluationTimeLimit) {
return OK;
}
}
ExpressionEvaluationTimeLimit = val->v.val;
return OK;
}
static int default_color_func(int do_set, Value *val) static int default_color_func(int do_set, Value *val)
{ {
int col_r, col_g, col_b; int col_r, col_g, col_b;
@@ -490,7 +544,7 @@ int DeleteVar(char const *str)
/* */ /* */
/* SetVar */ /* SetVar */
/* */ /* */
/* Set the indicate variable to the specified value. */ /* Set the indicated variable to the specified value. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int SetVar(char const *str, Value const *val) int SetVar(char const *str, Value const *val)
@@ -511,24 +565,14 @@ int SetVar(char const *str, Value const *val)
/* Get a copy of the value of the variable. */ /* Get a copy of the value of the variable. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int GetVarValue(char const *str, Value *val, Var *locals, ParsePtr p) int GetVarValue(char const *str, Value *val)
{ {
Var *v; Var *v;
/* Try searching local variables first */
v = locals;
while (v) {
if (! StrinCmp(str, v->name, VAR_NAME_LEN))
return CopyValue(val, &v->v);
v = v->next;
}
/* Global variable... mark expression as non-constant */
if (p) p->nonconst_expr = 1;
v=FindVar(str, 0); v=FindVar(str, 0);
if (!v) { if (!v) {
Eprint("%s: %s", ErrMsg[E_NOSUCH_VAR], str); Eprint("%s: `%s'", ErrMsg[E_NOSUCH_VAR], str);
return E_NOSUCH_VAR; return E_NOSUCH_VAR;
} }
return CopyValue(val, &v->v); return CopyValue(val, &v->v);
@@ -543,7 +587,7 @@ int DoSet (Parser *p)
{ {
Value v; Value v;
int r; int r;
int ch;
DynamicBuffer buf; DynamicBuffer buf;
DynamicBuffer buf2; DynamicBuffer buf2;
DBufInit(&buf); DBufInit(&buf);
@@ -553,8 +597,16 @@ int DoSet (Parser *p)
if (r) return r; if (r) return r;
/* Allow optional equals-sign: SET var = value */ /* Allow optional equals-sign: SET var = value */
if (ParseNonSpaceChar(p, &r, 1) == '=') { ch = ParseNonSpaceChar(p, &r, 1);
if (r) return r;
if (ch == '=') {
ParseNonSpaceChar(p, &r, 0); ParseNonSpaceChar(p, &r, 0);
if (r) return r;
}
if (p->isnested) {
Eprint("%s", "Do not use [] around expression in SET command");
return E_CANTNEST_FDEF;
} }
r = EvaluateExpr(p, &v); r = EvaluateExpr(p, &v);
@@ -785,16 +837,6 @@ int DoPreserve (Parser *p)
/* */ /* */
/***************************************************************/ /***************************************************************/
/* 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;
/* Macro to access "min" but as a constval. Just to make source more /* Macro to access "min" but as a constval. Just to make source more
readable */ readable */
#define constval min #define constval min
@@ -831,6 +873,7 @@ static SysVar SysVarArr[] = {
{"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 }, {"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 },
{"EndSent", 1, STR_TYPE, &EndSent, 0, 0 }, {"EndSent", 1, STR_TYPE, &EndSent, 0, 0 },
{"EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 }, {"EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 },
{"ExpressionTimeLimit", 1, SPECIAL_TYPE, expr_time_limit_func, 0, 0 },
{"February", 1, STR_TYPE, &DynamicMonthName[1], 0, 0 }, {"February", 1, STR_TYPE, &DynamicMonthName[1], 0, 0 },
{"FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 }, {"FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 },
{"FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 }, {"FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 },
@@ -877,6 +920,7 @@ static SysVar SysVarArr[] = {
{"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 }, {"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 },
{"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0 }, {"October", 1, STR_TYPE, &DynamicMonthName[9], 0, 0 },
{"On", 1, STR_TYPE, &DynamicOn, 0, 0 }, {"On", 1, STR_TYPE, &DynamicOn, 0, 0 },
{"OnceFile", 1, SPECIAL_TYPE, oncefile_func, 0, 0 },
{"ParseUntriggered", 1, INT_TYPE, &ParseUntriggered, 0, 1 }, {"ParseUntriggered", 1, INT_TYPE, &ParseUntriggered, 0, 1 },
{"Pm", 1, STR_TYPE, &DynamicPm, 0, 0 }, {"Pm", 1, STR_TYPE, &DynamicPm, 0, 0 },
{"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 }, {"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 },
@@ -919,7 +963,6 @@ static SysVar SysVarArr[] = {
}; };
#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) ) #define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) )
static SysVar *FindSysVar (char const *name);
static void DumpSysVar (char const *name, const SysVar *v); static void DumpSysVar (char const *name, const SysVar *v);
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -1016,7 +1059,7 @@ int GetSysVar(char const *name, Value *val)
/* Find a system var with specified name. */ /* Find a system var with specified name. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static SysVar *FindSysVar(char const *name) SysVar *FindSysVar(char const *name)
{ {
int top=NUMSYSVARS-1, bottom=0; int top=NUMSYSVARS-1, bottom=0;
int mid=(top + bottom) / 2; int mid=(top + bottom) / 2;
+2 -2
View File
@@ -35,8 +35,8 @@ set a ansicolor(-1, 0, 0)
set a ansicolor(42, 42, 256) set a ansicolor(42, 42, 256)
set a ansicolor("foo") set a ansicolor("foo")
set a ansicolor("1 1") set a ansicolor("1 1")
set a ansicolor("-1 -1 0"); set a ansicolor("-1 -1 0")
set a ansicolor("256 1 1"); set a ansicolor("256 1 1")
set a ansicolor(128, 128, 128, 2) set a ansicolor(128, 128, 128, 2)
set a ansicolor(128, 128, 128, -1) set a ansicolor(128, 128, 128, -1)
set a ansicolor(128, 128, 128, 0, 2) set a ansicolor(128, 128, 128, 0, 2)
+55
View File
@@ -0,0 +1,55 @@
debug +sx
set a 1
set a 0&&0
set a 0&&1
set a 1&&0
set a 1&&1
set a 0||0
set a 0||1
set a 1||0
set a 1||1
set a 2, 3
set a iif(0, "foo", 0, "bar", 1, "blech", 0, "quux", 1, "borhy", "wacka")
set a max(2*3, 4+5, min(6*7+8, 7+6*8))
set a max(1,,1)
set a 5%0
set a 5/0
set a -$IntMin
set a $IntMin / -1
set a $IntMin % -1
set a (7+5)*(8+2)/(9-4)
set a "foo" * 5
set a "foo" / 5
set a "foo" * "five"
set a "foo" + "bar"
set a '2024-01-02' + 3
set a 3 + '2024-01-02'
set a 11:33 + 75
set a 75 + 11:33
set a '2024-01-01@11:33' + 1500
set a 1500 + '2024-01-01@11:33'
set a '2024-03-02' - '2024-01-01'
set a 15:00 - 14:44
set a (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
set a (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
set a isany(1)
set a isany(1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6)
set a isany("foo", 1 + 1, 2:00 + 1, '2021-01-01' + 1, '2021-01-01@14:00' + 1, "f" + "oo", "fo" + "o")
+4
View File
@@ -0,0 +1,4 @@
BANNER %
set $AddBlankLines 0
IF 1
INCLUDE [filedir()]/if2.rem
+5
View File
@@ -0,0 +1,5 @@
# Another unmatched IF
IF 0
ELSE
IF 1
+1 -1
View File
@@ -2,5 +2,5 @@ FSET msgprefix(x) "Priority: " + x + "; Filename: " + filename() + ": "
REM at 23:56 MSG foo REM at 23:56 MSG foo
REM PRIORITY 42 at 23:57 MSG bar REM PRIORITY 42 at 23:57 MSG bar
REM PRIORITY 999 at 23:58 MSQ quux REM PRIORITY 999 at 23:58 MSG quux
DO queue2.rem DO queue2.rem
+4 -4
View File
@@ -9,10 +9,10 @@ set $LatMin 24
set $LatSec 0 set $LatSec 0
IF $PSCAL IF $PSCAL
[trigger(moondate(0))] SPECIAL MOON 0 -1 -1 [moontime(0)] REM [trigger(moondate(0))] SPECIAL MOON 0 -1 -1 [moontime(0)]
[trigger(moondate(1))] SPECIAL MOON 1 -1 -1 [moontime(1)] REM [trigger(moondate(1))] SPECIAL MOON 1 -1 -1 [moontime(1)]
[trigger(moondate(2))] SPECIAL MOON 2 -1 -1 [moontime(2)] REM [trigger(moondate(2))] SPECIAL MOON 2 -1 -1 [moontime(2)]
[trigger(moondate(3))] SPECIAL MOON 3 -1 -1 [moontime(3)] REM [trigger(moondate(3))] SPECIAL MOON 3 -1 -1 [moontime(3)]
ENDIF ENDIF
REM 4 PS (First-Bit-Of-PS) REM 4 PS (First-Bit-Of-PS)
+7
View File
@@ -0,0 +1,7 @@
BANNER %
SET $OnceFile "../tests/once.timestamp"
REM ONCE MSG This should only be issued once per day.
SET $OnceFile "../tests/once.timestamp"
SET $OnceFile "../tests/once-again.timestamp"
+57 -3
View File
@@ -65,8 +65,6 @@ echo "Test 1" > ../tests/test.out
echo "" >> ../tests/test.out echo "" >> ../tests/test.out
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 >> ../tests/test.out 2>&1 ../src/remind -e -dxte ../tests/test.rem 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out echo "" >> ../tests/test.out
echo 'set a 1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+2*3))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))' | ../src/remind -ds - 16 feb 1991 12:13 >> ../tests/test.out 2>&1
echo "" >> ../tests/test.out
echo "Test 2" >> ../tests/test.out echo "Test 2" >> ../tests/test.out
echo "" >> ../tests/test.out echo "" >> ../tests/test.out
../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1 ../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out 2>&1
@@ -178,7 +176,7 @@ REM 1 Jan 2012 AT 10:00 MSG 10am: Should show up
MSG [$DontTrigAts] MSG [$DontTrigAts]
EOF EOF
# An OMITFUNC should indicate nonconst_expr # OMITFUNC should indicate nonconst_expr
../src/remind -pp - 1 jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1 ../src/remind -pp - 1 jan 2012 9:00 <<'EOF' >> ../tests/test.out 2>&1
REM Mon OMITFUNC foo MSG bar REM Mon OMITFUNC foo MSG bar
EOF EOF
@@ -541,8 +539,64 @@ EOF
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1 (echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1
../src/remind -ifoo - <<'EOF' >> ../tests/test.out 2>&1
BANNER %
DUMP
EOF
../src/remind '-i$AddBlankLines' - <<'EOF' >> ../tests/test.out 2>&1
BANNER %
DUMP
EOF
../src/remind ../tests/expr.rem >> ../tests/test.out 2>&1
../src/remind - <<'EOF' >> ../tests/test.out 2>&1
PUSH
POP
PUSH
PUSH
POP
POP
PUSH
PUSH
POP
PUSH
POP
PUSH
POP
EOF
../src/remind ../tests/if1.rem 2020-03-03 >> ../tests/test.out 2>&1
# Test ONCE with a timestamp file
rm -f ../tests/once.timestamp
../src/remind ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind ../tests/test-once.rem >> ../tests/test.out 2>&1
tail +2 ../tests/once.timestamp >> ../tests/test.out 2>&1
rm -f ../tests/once.timestamp
../src/remind - < ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind - < ../tests/test-once.rem >> ../tests/test.out 2>&1
../src/remind - < ../tests/test-once.rem >> ../tests/test.out 2>&1
tail +2 ../tests/once.timestamp >> ../tests/test.out 2>&1
rm -f ../tests/once.timestamp
# Newlines in calendar output
(echo 'REM 16 MSG foo%_bar%_baz wookie quux apple %_ %_ %_ blech'; echo "REM 16 MSG ANOTHER") | ../src/remind -c -w80 - 1 sep 1990 >> ../tests/test.out 2>&1
# Remove references to SysInclude, which is build-specific # Remove references to SysInclude, which is build-specific
grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out grep -F -v '$SysInclude' < ../tests/test.out > ../tests/test.out.1 && mv -f ../tests/test.out.1 ../tests/test.out
# If "man" accepts the --warnings flag, test all the man pages.
man man | grep -e --warnings > /dev/null 2>&1
if test $? = 0 ; then
for i in ../man/*.1 ; do
man --warnings=w $i 2>>../tests/test.out 1>/dev/null
done
fi
cmp -s ../tests/test.out ../tests/test.cmp cmp -s ../tests/test.out ../tests/test.cmp
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
echo "Remind: Acceptance test PASSED" echo "Remind: Acceptance test PASSED"
+1574 -340
View File
File diff suppressed because one or more lines are too long
+214 -37
View File
@@ -33,7 +33,7 @@ fset _h(x, y) trigger(hebdate(x,y))
# Test case from Remind mailing list # Test case from Remind mailing list
set mltest "a b" set mltest "a b"
INCLUDECMD printf 'REM %s\n' [mltest] INCLUDECMD printf 'REM MSG %s\n' [mltest]
# Disabling RUN in an !includecmd # Disabling RUN in an !includecmd
INCLUDECMD !echo MSG foo INCLUDECMD !echo MSG foo
@@ -49,38 +49,38 @@ INCLUDECMD echo MSG foo
RUN ON RUN ON
INCLUDECMD echo MSG foo INCLUDECMD echo MSG foo
[_h(1, "Tishrey")] MSG Rosh Hashana 1 REM [_h(1, "Tishrey")] MSG Rosh Hashana 1
[_h(2, "Tishrey")] MSG Rosh Hashana 2 REM [_h(2, "Tishrey")] MSG Rosh Hashana 2
[_h(3, "Tishrey")] MSG Tzom Gedalia REM [_h(3, "Tishrey")] MSG Tzom Gedalia
[_h(10, "Tishrey")] MSG Yom Kippur REM [_h(10, "Tishrey")] MSG Yom Kippur
[_h(15, "Tishrey")] MSG Sukkot 1 REM [_h(15, "Tishrey")] MSG Sukkot 1
[_h(25, "Kislev")] MSG Channuka REM [_h(25, "Kislev")] MSG Channuka
[_h(10, "Tevet")] MSG Asara B'Tevet REM [_h(10, "Tevet")] MSG Asara B'Tevet
[_h(15, "Shvat")] MSG Tu B'Shvat REM [_h(15, "Shvat")] MSG Tu B'Shvat
[_h(15, "Adar A")] MSG Purim Katan REM [_h(15, "Adar A")] MSG Purim Katan
[_h(14, "Adar")] MSG Purim REM [_h(14, "Adar")] MSG Purim
[_h(15, "Nisan")] MSG Pesach REM [_h(15, "Nisan")] MSG Pesach
[_h(27, "Nisan")] MSG Yom HaShoah REM [_h(27, "Nisan")] MSG Yom HaShoah
[_h(4, "Iyar")] MSG Yom HaZikaron REM [_h(4, "Iyar")] MSG Yom HaZikaron
[_h(5, "Iyar")] MSG Yom Ha'atzmaut REM [_h(5, "Iyar")] MSG Yom Ha'atzmaut
[_h(28, "Iyar")] MSG Yom Yerushalayim REM [_h(28, "Iyar")] MSG Yom Yerushalayim
[_h(6, "Sivan")] MSG Shavuot REM [_h(6, "Sivan")] MSG Shavuot
[_h(9, "Av")] MSG Tish'a B'Av REM [_h(9, "Av")] MSG Tish'a B'Av
# Test some jahrzeit cases # Test some jahrzeit cases
fset _i(x,y,z,a) trigger(hebdate(x,y,z,a)) fset _i(x,y,z,a) trigger(hebdate(x,y,z,a))
[_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete REM [_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete
[_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective REM [_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective
[_i(30, "Heshvan", today(), 5761)] MSG Illegal REM [_i(30, "Heshvan", today(), 5761)] MSG Illegal
[_i(30, "Kislev", today(), 5759)] MSG Complete-Complete REM [_i(30, "Kislev", today(), 5759)] MSG Complete-Complete
[_i(30, "Kislev", today(), 5760)] MSG Complete-Defective REM [_i(30, "Kislev", today(), 5760)] MSG Complete-Defective
[_i(30, "Kislev", today(), 5761)] MSG Illegal REM [_i(30, "Kislev", today(), 5761)] MSG Illegal
[_i(30, "Adar A", today(), 5755)] MSG Leap REM [_i(30, "Adar A", today(), 5755)] MSG Leap
[_i(30, "Adar A", today(), 5756)] MSG Illegal REM [_i(30, "Adar A", today(), 5756)] MSG Illegal
[_i(29, "Adar A", today(), 5755)] MSG Leap REM [_i(29, "Adar A", today(), 5755)] MSG Leap
[_i(29, "Adar A", today(), 5756)] MSG Illegal REM [_i(29, "Adar A", today(), 5756)] MSG Illegal
# This causes a parse error on version 03.01.01 # This causes a parse error on version 03.01.01
REM 1990-01-01 SATISFY 1 REM 1990-01-01 SATISFY 1
@@ -238,8 +238,8 @@ CLEAR-OMIT-CONTEXT
REM tag ill,egal MSG bad tag REM tag ill,egal MSG bad tag
REM MSG The tags are: [trigtags()] REM MSG The tags are: [trigtags()]
REM TAG foo The tags are: [trigtags()] REM TAG foo MSG The tags are: [trigtags()]
REM TAG foo TAG bar TAG quux TAG znort TAG cabbage The tags are: [trigtags()] REM TAG foo TAG bar TAG quux TAG znort TAG cabbage MSG The tags are: [trigtags()]
REM MSG The tags are: [trigtags()] REM MSG The tags are: [trigtags()]
# Test ADDOMIT # Test ADDOMIT
@@ -559,11 +559,11 @@ REM 1992-01-01 *1 UNTIL 1991-12-31 MSG Diagnosed
set x '1992-01-01' set x '1992-01-01'
REM [x] *1 UNTIL 1991-12-31 MSG Not diagnosed - nonconst expression REM [x] *1 UNTIL 1991-12-31 MSG Not diagnosed - nonconst expression
REM MON FROM 1992-01-01 UNTIL 1991-12-31 Diagnosed REM MON FROM 1992-01-01 UNTIL 1991-12-31 MSG Diagnosed
REM MON SCANFROM 1992-01-01 UNTIL 1991-12-31 Diagnosed REM MON SCANFROM 1992-01-01 UNTIL 1991-12-31 MSG Diagnosed
REM MON FROM [x] UNTIL 1991-12-31 Not diagnosed REM MON FROM [x] UNTIL 1991-12-31 MSG Not diagnosed
REM MON SCANFROM [x] UNTIL 1991-12-31 Not diagnosed REM MON SCANFROM [x] UNTIL 1991-12-31 MSG Not diagnosed
REM 1992-01-01 UNTIL 1992-02-02 MSG Diagnosed REM 1992-01-01 UNTIL 1992-02-02 MSG Diagnosed
REM [x] UNTIL 1992-02-02 MSG Diagnosed REM [x] UNTIL 1992-02-02 MSG Diagnosed
@@ -575,7 +575,7 @@ OMIT December 25 MSG X
OMIT 26 Dec 2010 THROUGH 27 Dec 2010 MSG This is not legal OMIT 26 Dec 2010 THROUGH 27 Dec 2010 MSG This is not legal
OMIT DUMP OMIT DUMP
# Regression test for bugfix in Hebrew calendar Adar jahrzeit # Regression test for bugfix in Hebrew calendar Adar jahrzeit
[_i(14, "Adar", today(), 5761)] MSG Purim REM [_i(14, "Adar", today(), 5761)] MSG Purim
# Regression test for bug found by Larry Hynes # Regression test for bug found by Larry Hynes
REM SATISFY [day(trigdate()-25) == 14] MSG Foo REM SATISFY [day(trigdate()-25) == 14] MSG Foo
@@ -708,6 +708,56 @@ set x ampm(21:12) + ""
set x ampm(22:12) + "" set x ampm(22:12) + ""
set x ampm(23:12) + "" set x ampm(23:12) + ""
set x ampm(0:12,"AM", "PM", 1) + ""
set x ampm(1:12,"AM", "PM", 1) + ""
set x ampm(2:12,"AM", "PM", 1) + ""
set x ampm(3:12,"AM", "PM", 1) + ""
set x ampm(4:12,"AM", "PM", 1) + ""
set x ampm(5:12,"AM", "PM", 1) + ""
set x ampm(6:12,"AM", "PM", 1) + ""
set x ampm(7:12,"AM", "PM", 1) + ""
set x ampm(8:12,"AM", "PM", 1) + ""
set x ampm(9:12,"AM", "PM", 1) + ""
set x ampm(10:12,"AM", "PM", 1) + ""
set x ampm(11:12,"AM", "PM", 1) + ""
set x ampm(12:12,"AM", "PM", 1) + ""
set x ampm(13:12,"AM", "PM", 1) + ""
set x ampm(14:12,"AM", "PM", 1) + ""
set x ampm(15:12,"AM", "PM", 1) + ""
set x ampm(16:12,"AM", "PM", 1) + ""
set x ampm(17:12,"AM", "PM", 1) + ""
set x ampm(18:12,"AM", "PM", 1) + ""
set x ampm(19:12,"AM", "PM", 1) + ""
set x ampm(20:12,"AM", "PM", 1) + ""
set x ampm(21:12,"AM", "PM", 1) + ""
set x ampm(22:12,"AM", "PM", 1) + ""
set x ampm(23:12,"AM", "PM", 1) + ""
set x ampm(0:02,"AM", "PM", 0) + ""
set x ampm(0:02,"AM", "PM", 0) + ""
set x ampm(2:02,"AM", "PM", 0) + ""
set x ampm(3:02,"AM", "PM", 0) + ""
set x ampm(4:02,"AM", "PM", 0) + ""
set x ampm(5:02,"AM", "PM", 0) + ""
set x ampm(6:02,"AM", "PM", 0) + ""
set x ampm(7:02,"AM", "PM", 0) + ""
set x ampm(8:02,"AM", "PM", 0) + ""
set x ampm(9:02,"AM", "PM", 0) + ""
set x ampm(00:02,"AM", "PM", 0) + ""
set x ampm(00:02,"AM", "PM", 0) + ""
set x ampm(02:02,"AM", "PM", 0) + ""
set x ampm(03:02,"AM", "PM", 0) + ""
set x ampm(04:02,"AM", "PM", 0) + ""
set x ampm(05:02,"AM", "PM", 0) + ""
set x ampm(06:02,"AM", "PM", 0) + ""
set x ampm(07:02,"AM", "PM", 0) + ""
set x ampm(08:02,"AM", "PM", 0) + ""
set x ampm(09:02,"AM", "PM", 0) + ""
set x ampm(20:02,"AM", "PM", 0) + ""
set x ampm(20:02,"AM", "PM", 0) + ""
set x ampm(22:02,"AM", "PM", 0) + ""
set x ampm(23:02,"AM", "PM", 0) + ""
# Coerce with am/pm # Coerce with am/pm
set x coerce("TIME", "12:45am") set x coerce("TIME", "12:45am")
set x coerce("TIME", "12:45") set x coerce("TIME", "12:45")
@@ -777,12 +827,15 @@ ENDIF
# Trig with a good warnfunc # Trig with a good warnfunc
FSET w(x) choose(x, 5, 3, 1, 0) FSET w(x) choose(x, 5, 3, 1, 0)
# Ugh. This is where short-circuit logical operators # Short-circuit operators
# would really come in handy.
IF trig("sun warn w") || trig("thu warn w") IF trig("sun warn w") || trig("thu warn w")
REM [trig()] +5 MSG Foo %b REM [trig()] +5 MSG Foo %b
ENDIF ENDIF
IF trig("thu warn w") || trig("sun warn w")
REM [trig()] +5 MSG Foo %b
ENDIF
REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo REM [trig("Mon", "Tue", "Wed", "Sat")] MSG foo
REM [trig("Mon", "Tue", "Wed")] MSG bar REM [trig("Mon", "Tue", "Wed")] MSG bar
@@ -869,6 +922,16 @@ REM MSG Here: %{custom}
REM MSG There: %*{custom} REM MSG There: %*{custom}
REM MSG Bad: %{custom REM MSG Bad: %{custom
REM MSG Undefined: %{nopity_nope_nope}
# Bad substitution functions
FSET subst_bad() "foo"
REM MSG %{bad}
FSET subst_ampm(a, b, c, d, e, f, g) "wookie"
REM AT 11:00 MSG %2
# Test FUNSET # Test FUNSET
FSET square(x) x*x FSET square(x) x*x
SET a square(5) SET a square(5)
@@ -942,6 +1005,120 @@ REM Tue AT 10:00 DURATION [trigtime()] MSG blort
# Make sure shellescape does not mangle UTF-8 characters # Make sure shellescape does not mangle UTF-8 characters
msg [shellescape("😆")] msg [shellescape("😆")]
This should be diagnosed as implicitly being REM
REM This should be diganosed as implicitly being MSG-type
# Check that user-defined functions with too many arguments are
# correctly diagnosed.
# This should be OK
FSET f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63) 3
# This should give an error
FSET f(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, a62, a63, a64) 3
# Check that SATISFY expressions that don't reference trigdate are diagnosed
# These should all NOT be diagnosed
set x 3
REM SATISFY 1
REM SATISFY trigdate() > '1990-01-01'
REM AT 23:59 SATISFY trigdatetime() > '1990-01-01@12:00'
REM SATISFY $T > '1990-01-01'
REM SATISFY $Ty > 1990
REM SATISFY $Tm > 0
REM SATISFY $Td > 0
REM SATISFY $Tw > -1
REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * max(5, $Td)]
FSET references_t(x) $T != x
REM SATISFY references_t($U)
FSET recursive_t(x) iif(x==0, recursive_t(1), references_t($U))
REM SATISFY recursive_t(0)
REM SATISFY recursive_t(2)
# These should be diagnosed
REM SATISFY 0
REM SATSIFY ""
REM SATISFY [version() > "01.00.00"]
REM SATISFY [max(x, max(x, 1, 2, 3), 4, 5, 6) * 5]
FSET gg(x) 0
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
FSET gg(x,y,z) 0
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
FSET gg() 0
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
FSET gg(x) x-x
REM WARN gg MSG Wookie
REM AT 11:00 SCHED gg MSG blork
REM OMITFUNC gg MSG hehe
REM WARN not_defined MSG Wookie
REM AT 11:00 SCHED not_defined MSG blork
REM OMITFUNC not_defined MSG hehe
### Strings in logical operators
SET logstr "" && 7
SET logstr "foo" && 7
SET logstr "" && ""
SET logstr "foo" && ""
SET logstr "" && "bar"
SET logstr "foo" && "bar"
SET logstr "" && '2023-02-01'
SET logstr "foo" && '2023-02-01'
SET logstr "" || 7
SET logstr "foo" || 7
SET logstr "" || ""
SET logstr "foo" || ""
SET logstr "" || "bar"
SET logstr "foo" || "bar"
SET logstr "" || '2023-02-01'
SET logstr "foo" || '2023-02-01'
set xyz ! 0
set xyz ! 1
set xyz ! 2
set xyz ! date(baseyr(), 1, 1)
set xyz ! date(baseyr(), 1, 2)
set xyz ! '2024-01-01'
set xyz ! datetime(baseyr(), 1, 1, 00:00)
set xyz ! datetime(baseyr(), 1, 1, 00:01)
set xyz ! datetime(baseyr(), 1, 2, 12:30)
set xyz ! '2024-01-01@11:47'
set xyz ! 00:00
set xyz ! 00:01
set xyz ! 23:59
set xyz ! ""
set xyz ! "foo"
set xyz ! "0"
# Test error messages for function calls with too many / too few args
set zxk version(1)
set zxk max()
fset dooby(x) 1
set zxk dooby()
set zxk dooby(1, 2)
set zxk dooby(1)
# Don't want Remind to queue reminders # Don't want Remind to queue reminders
EXIT EXIT
+3 -3
View File
@@ -1,7 +1,7 @@
MSG ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский REM MSG ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский ру́сский
MSG עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית REM MSG עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית עִבְרִית
Wed MSG With tabs and spaces REM Wed MSG With tabs and spaces
REM [moondate(0)] MSG 🌑 REM [moondate(0)] MSG 🌑
REM [moondate(1)] MSG 🌓 woo REM [moondate(1)] MSG 🌓 woo
+4 -4
View File
@@ -1,5 +1,5 @@
[moondate(0)] SPECIAL MOON 0 REM [moondate(0)] SPECIAL MOON 0
[moondate(1)] SPECIAL MOON 1 REM [moondate(1)] SPECIAL MOON 1
[moondate(2)] SPECIAL MOON 2 REM [moondate(2)] SPECIAL MOON 2
[moondate(3)] SPECIAL MOON 3 REM [moondate(3)] SPECIAL MOON 3
REM Monday SPECIAL WEEK (W[weekno()]) REM Monday SPECIAL WEEK (W[weekno()])
+36 -36
View File
@@ -9,8 +9,8 @@ IF !$PSCAL
FSET msgsuffix(x) "<P>" FSET msgsuffix(x) "<P>"
MSG The Hebrew date for today, %d %m, %y, is [_hstr(today())]. % REM MSG The Hebrew date for today, %d %m, %y, is [_hstr(today())]. %
MSG And the Hebrew date for tomorrow is [_hstr(today()+1)]. % REM MSG And the Hebrew date for tomorrow is [_hstr(today()+1)]. %
fset msgprefix(x) iif($NumTrig==OldTrig, "<H2>Upcoming Holidays</H2>"+char(13,10),"") fset msgprefix(x) iif($NumTrig==OldTrig, "<H2>Upcoming Holidays</H2>"+char(13,10),"")
set oldtrig $numtrig set oldtrig $numtrig
@@ -51,40 +51,40 @@ FSET _PastSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, \
SET InIsrael VALUE("InIsrael", 0) SET InIsrael VALUE("InIsrael", 0)
SET Reform VALUE("Reform", 0) SET Reform VALUE("Reform", 0)
[_h(1, "Tishrey")] ++12 MSG %"Rosh Hashana 1%" is %b. REM [_h(1, "Tishrey")] ++12 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")] ++12 MSG %"Rosh Hashana 2%" is %b. REM [_h(2, "Tishrey")] ++12 MSG %"Rosh Hashana 2%" is %b.
[_PastSat(3, "Tishrey")] ++12 MSG %"Tzom Gedalia%" is %b. REM [_PastSat(3, "Tishrey")] ++12 MSG %"Tzom Gedalia%" is %b.
ENDIF ENDIF
[_h(10, "Tishrey")] ++12 MSG %"Yom Kippur%" is %b. REM [_h(10, "Tishrey")] ++12 MSG %"Yom Kippur%" is %b.
[_h(15, "Tishrey")] ++12 MSG %"Sukkot 1%" is %b. REM [_h(15, "Tishrey")] ++12 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")] ++12 MSG %"Hoshana Rabba%" is %b. REM [_h(21, "Tishrey")] ++12 MSG %"Hoshana Rabba%" is %b.
[_h(22, "Tishrey")] ++12 MSG %"Shemini Atzeret%" is %b. REM [_h(22, "Tishrey")] ++12 MSG %"Shemini Atzeret%" is %b.
IF InIsrael IF InIsrael
[_h(22, "Tishrey")] ++12 MSG %"Simchat Torah%" is %b. REM [_h(22, "Tishrey")] ++12 MSG %"Simchat Torah%" is %b.
ELSE ELSE
[_h(23, "Tishrey")] ++12 MSG %"Simchat Torah%" is %b. REM [_h(23, "Tishrey")] ++12 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) TRIGGER(HEBDATE(24, "Kislev", today()-9)+x) FSET _chan(x) TRIGGER(HEBDATE(24, "Kislev", today()-9)+x)
[_chan(1)] ++12 MSG %"Chanukah 1%" is %b. REM [_chan(1)] ++12 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
@@ -92,8 +92,8 @@ IF !Reform
REM [_PastSat(10, "Tevet")] MSG %"Tzom Tevet%" is %b. REM [_PastSat(10, "Tevet")] MSG %"Tzom Tevet%" is %b.
ENDIF ENDIF
[_h(15, "Shvat")] ++12 MSG %"Tu B'Shvat%" is %b. REM [_h(15, "Shvat")] ++12 MSG %"Tu B'Shvat%" is %b.
[_h(15, "Adar A")] ++12 MSG %"Purim Katan%" is %b. REM [_h(15, "Adar A")] ++12 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
@@ -101,41 +101,41 @@ IF WKDAYNUM(_h2(13, "Adar")) != 6
ELSE ELSE
REM [TRIGGER(_h2(11, "Adar"))] ++12 MSG %"Fast of Esther%" is %b. REM [TRIGGER(_h2(11, "Adar"))] ++12 MSG %"Fast of Esther%" is %b.
ENDIF ENDIF
[_h(14, "Adar")] ++12 MSG %"Purim%" is %b. REM [_h(14, "Adar")] ++12 MSG %"Purim%" is %b.
[_h(15, "Nisan")] ++12 MSG %"Pesach%" is %b. REM [_h(15, "Nisan")] ++12 MSG %"Pesach%" is %b.
IF !InIsrael IF !InIsrael
[_h(16, "Nisan")] MSG %"Pesach 2%" is %b. REM [_h(16, "Nisan")] MSG %"Pesach 2%" is %b.
ENDIF ENDIF
[_h(21, "Nisan")] MSG %"Pesach 7%" is %b. REM [_h(21, "Nisan")] MSG %"Pesach 7%" is %b.
IF !InIsrael && !Reform IF !InIsrael && !Reform
[_h(22, "Nisan")] MSG %"Pesach 8%" is %b. REM [_h(22, "Nisan")] MSG %"Pesach 8%" is %b.
ENDIF ENDIF
[_h(27, "Nisan")] ++12 MSG %"Yom HaShoah%" is %b. REM [_h(27, "Nisan")] ++12 MSG %"Yom HaShoah%" is %b.
[_h(4, "Iyar")] ++12 MSG %"Yom HaZikaron%" is %b. REM [_h(4, "Iyar")] ++12 MSG %"Yom HaZikaron%" is %b.
[_h(5, "Iyar")] ++12 MSG %"Yom Ha'atzmaut%" is %b. REM [_h(5, "Iyar")] ++12 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")] ++12 MSG %"Lag B'Omer%" is %b. REM [_h(18, "Iyar")] ++12 MSG %"Lag B'Omer%" is %b.
ENDIF ENDIF
[_h(28, "Iyar")] ++12 MSG %"Yom Yerushalayim%" is %b. REM [_h(28, "Iyar")] ++12 MSG %"Yom Yerushalayim%" is %b.
[_h(6, "Sivan")] ++12 MSG %"Shavuot%" is %b. REM [_h(6, "Sivan")] ++12 MSG %"Shavuot%" is %b.
IF !InIsrael && !Reform IF !InIsrael && !Reform
[_h(7, "Sivan")] MSG %"Shavuot 2%" is %b. REM [_h(7, "Sivan")] MSG %"Shavuot 2%" is %b.
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")] ++12 MSG %"Tzom Tammuz%" is %b. REM [_PastSat(17, "Tamuz")] ++12 MSG %"Tzom Tammuz%" is %b.
[_PastSat(9, "Av")] ++12 MSG %"Tish'a B'Av%" is %b. REM [_PastSat(9, "Av")] ++12 MSG %"Tish'a B'Av%" is %b.
ENDIF ENDIF
fset msgprefix(x) "" fset msgprefix(x) ""