Compare commits

..

247 Commits

Author SHA1 Message Date
Dianne Skoll
649481cf01 Bump version to 05.00.02 2024-07-26 07:56:50 -04:00
Dianne Skoll
c253bdfcbe Update release date. 2024-07-26 07:54:21 -04:00
Dianne Skoll
1910808fd7 Update WHATSNEW 2024-07-10 14:04:06 -04:00
Dianne Skoll
1d8cb9749e Add a test to ensure ONCE works even when taking input from STDIN if $OnceFile is set. 2024-07-10 13:55:41 -04:00
Dianne Skoll
e3f9380fcd Allow ONCE to be used if input is stdin, as long as $OnceFile is set.
Clarify documentation.
2024-07-10 09:32:05 -04:00
Dianne Skoll
326c3f59b0 Make tests pass on any day, not just 2024-07-09. :) 2024-07-10 09:23:21 -04:00
Dianne Skoll
03f1c5a047 Fix typo 2024-07-09 09:36:52 -04:00
Dianne Skoll
02122491c3 Add test to ensure we warn if we try to set $OnceFile to something else after a ONCE has been seen. 2024-07-09 09:33:58 -04:00
Dianne Skoll
735f6f5686 Ignore attempts to set $OnceFile to the value it already has. 2024-07-09 09:33:02 -04:00
Dianne Skoll
ac033d75c0 Add the $OnceFile special variable.
This lets us specify a timestamp file that Remind
uses to track the last run for the purpose of handling
the ONCE keyword, rather than using the last access date
of the reminder file.

Keeping a timestamp file is a more reliable way to track
when Remind was last run.
2024-07-09 09:23:15 -04:00
Dianne Skoll
e2185e773a Add warning if someone attempts to create a multi-page SVG calendar. 2024-07-05 16:57:18 -04:00
Dianne Skoll
d9ae417e01 Fix typo 2024-07-05 14:22:55 -04:00
Dianne Skoll
e1d0948538 Update doc. 2024-07-05 14:22:16 -04:00
Dianne Skoll
357ddf285a Add --svg option to rem2pdf to produce SVG instead of PDF output. 2024-07-05 14:18:29 -04:00
Dianne Skoll
41859fc484 Remove some unused definitions; use symbolic value for sort initializer. 2024-07-04 16:58:41 -04:00
Dianne Skoll
07275e71b0 Remove obsolete definitions. 2024-07-04 13:13:27 -04:00
Dianne Skoll
f68521cb95 Fix spacing. 2024-07-04 13:09:21 -04:00
Dianne Skoll
526610bdd2 Stop parsing user func early if possible. 2024-07-02 09:16:47 -04:00
Dianne Skoll
973e3448ae Fix typo in comment. 2024-07-02 07:58:24 -04:00
Dianne Skoll
9a3f28f6fc Optimize storage of short string constants in expr_node objects. 2024-06-12 10:19:27 -04:00
Dianne Skoll
a3e32d2dc4 Update changelog. 2024-06-08 10:57:38 -04:00
Dianne Skoll
a8b78eff00 Add a few more tests. 2024-06-08 09:20:48 -04:00
Dianne Skoll
460db83298 Bump version to 05.00.01 2024-06-08 09:18:42 -04:00
Dianne Skoll
4560712778 Fix a couple of potential memory leaks. 2024-06-07 21:27:24 -04:00
Dianne Skoll
ce8803dde9 The zero-argument form of weekno() is not constant. 2024-06-07 21:22:32 -04:00
Dianne Skoll
60ca5d45e3 Convert "isany" to short-circuit evaluation. 2024-06-07 15:55:12 -04:00
Dianne Skoll
4454613d00 Fix typo 2024-06-06 12:51:02 -04:00
Dianne Skoll
0704808500 Don't rely on newer C feature that allows anonymous parameters 2024-06-06 12:48:44 -04:00
Dianne Skoll
166b1ac499 Only reset alarm if expression time limit was set 2024-06-06 11:47:39 -04:00
Dianne Skoll
e33bf4e80a Explicitly set sa_flags in sigaction. 2024-06-06 10:42:04 -04:00
Dianne Skoll
b3af44d212 Update docs/WHATSNEW for 05.00.00 release. 2024-06-06 10:28:38 -04:00
Dianne Skoll
1e753d5209 Preserve variables needed across iterations. 2024-06-05 15:08:12 -04:00
Dianne Skoll
4bf31005ea Better error location in a couple more cases. 2024-06-05 09:15:10 -04:00
Dianne Skoll
7c86bc910a Better diagnostics of unmatched IFs 2024-06-05 09:08:04 -04:00
Dianne Skoll
4f146a99a9 Simplify Eprint logic. 2024-06-04 21:10:17 -04:00
Dianne Skoll
a6a638e0e6 Add test for UNMATCHED PUSH 2024-06-04 16:27:23 -04:00
Dianne Skoll
325814f5e1 Print the location of unmatched PUSH 2024-06-04 16:25:14 -04:00
Dianne Skoll
5c4ea7d09e Allow "configure" to work, haha. 2024-06-04 12:02:54 -04:00
Dianne Skoll
2bf73987ac Don't need to flush unless we're about to print. 2024-06-04 11:56:05 -04:00
Dianne Skoll
3e9eeea8dc Update docs; kill time-limiter when we queue. 2024-06-04 11:51:27 -04:00
Dianne Skoll
d164d72c1c Update test file 2024-06-04 09:02:50 -04:00
Dianne Skoll
632cee62d9 Don't permit unary-minus overflow. 2024-06-04 09:02:26 -04:00
Dianne Skoll
7a40260f0d Add some expression evaluation tests. 2024-06-04 08:37:19 -04:00
Dianne Skoll
09f043b3de Don't print bytes allocated --- makes test output differ on different architectures. 2024-06-04 07:32:53 -04:00
Dianne Skoll
c0341c8ba3 Fix typo. 2024-06-03 21:54:12 -04:00
Dianne Skoll
850c717803 Handle unary '+' properly. 2024-06-03 20:20:54 -04:00
Dianne Skoll
f13f9e18bd Somewhat better error reporting. 2024-06-03 20:14:11 -04:00
Dianne Skoll
8bdca0d684 More code refactoring. 2024-06-03 19:49:36 -04:00
Dianne Skoll
129bf5612e Refactor some code. 2024-06-03 19:43:28 -04:00
Dianne Skoll
829962fae1 We don't need to check for excessive parse depth if we're not at the high water mark. 2024-06-03 19:35:42 -04:00
Dianne Skoll
c5f9ed8541 Fix logic error 2024-06-03 18:04:10 -04:00
Dianne Skoll
4a7cef4644 Better error diagnostics. 2024-06-03 18:02:19 -04:00
Dianne Skoll
0e010b56ec Fix some error messages. 2024-06-03 17:59:11 -04:00
Dianne Skoll
ee179ee2f5 Suppress duplicate call frames. 2024-06-03 17:47:38 -04:00
Dianne Skoll
e28712cef3 Add "s" to list of debug flags. 2024-06-03 16:16:44 -04:00
Dianne Skoll
8f0a2a7e79 Limit parse high-water to 2000 2024-06-03 15:15:58 -04:00
Dianne Skoll
ef23bba77f Track parse level high-water mark. 2024-06-03 15:13:32 -04:00
Dianne Skoll
602086ae2d Make "-ds" show built-in functions with ucfirst and user-defined ones all lower-case. 2024-06-03 14:56:32 -04:00
Dianne Skoll
f5a170acbd Update man page. 2024-06-03 11:58:37 -04:00
Dianne Skoll
8125b96f0b Add --max-excution-time cmdline option. 2024-06-03 10:40:58 -04:00
Dianne Skoll
0bb7d89bb9 Update man page. 2024-06-03 10:14:36 -04:00
Dianne Skoll
eb109bbbc0 Finish commenting expr.c 2024-06-03 10:01:38 -04:00
Dianne Skoll
1a0809fd31 Start updating man page. 2024-06-02 23:23:06 -04:00
Dianne Skoll
09625b9d68 More docs 2024-06-02 23:07:49 -04:00
Dianne Skoll
4e164c4268 More comments. 2024-06-02 21:08:04 -04:00
Dianne Skoll
691185f22c Update test file so tests pass. 2024-06-02 20:50:34 -04:00
Dianne Skoll
a8bfb41a9e Add $ExpressionTimeLimit system variable. 2024-06-02 14:01:17 -04:00
Dianne Skoll
fafb30db05 Add --max-execution-time option 2024-06-02 12:56:46 -04:00
Dianne Skoll
243e816523 Add translated error messages 2024-06-02 12:43:32 -04:00
Dianne Skoll
b49c0f52bd Implement EXPR ON and EXPR OFF 2024-06-02 12:39:00 -04:00
Dianne Skoll
07fca94a7f Properly handle purging of SCANFROM reminders. 2024-06-02 12:07:12 -04:00
Dianne Skoll
73917ee537 Only set nonconst_expr in purge mode in a couple of cases. 2024-06-02 12:02:29 -04:00
Dianne Skoll
76f9edecf6 Don't purge reminders with a SCANFROM clause. 2024-06-02 10:36:21 -04:00
Dianne Skoll
d77d9854d2 Remove src/expr.h - everything in it is now in types.h or protos.h 2024-06-02 10:26:41 -04:00
Dianne Skoll
c2b53f95a4 Remove dead code 2024-06-02 10:23:24 -04:00
Dianne Skoll
caef8b80d6 Make it easier to match error messages to error constants. 2024-06-02 10:00:04 -04:00
Dianne Skoll
5e016768af More comments. 2024-06-02 09:46:53 -04:00
Dianne Skoll
ee08ce98d7 More comments. 2024-06-01 23:33:33 -04:00
Dianne Skoll
581bd95838 Allow placing a literal [ in a reminder by using [[ 2024-06-01 18:46:17 -04:00
Dianne Skoll
bb92dab1ab More docs. 2024-06-01 16:44:37 -04:00
Dianne Skoll
83b5c52c76 Make parse_expr_token much any following whitespace to replicate prior behavior.
There may be other parts of the code that rely on this.
2024-06-01 16:35:20 -04:00
Dianne Skoll
93eca25141 Fix spurious "Missing ']'" error reported by Jochen Sprickerhof 2024-06-01 16:30:48 -04:00
Dianne Skoll
34421cb10e Don't funset the Jewish holiday functions. 2024-06-01 14:08:48 -04:00
Dianne Skoll
29b87898aa Add some ASCII art 2024-06-01 14:00:39 -04:00
Dianne Skoll
e9e4db94bd More comments 2024-06-01 13:44:21 -04:00
Dianne Skoll
c95ad0261a Start adding comments; do not distinguish between N_BINARY_OPERATOR and N_UNARY_OPERATOR 2024-06-01 13:37:58 -04:00
Dianne Skoll
7fef456483 Don't allocate - nodes if we don't need to 2024-06-01 13:23:52 -04:00
Dianne Skoll
386131e74d Show bytes used for expression notes. 2024-06-01 09:45:37 -04:00
Dianne Skoll
824d3c88f1 Store at most 5 function args on stack. Any more and we malloc. 2024-06-01 09:41:26 -04:00
Dianne Skoll
18a206abd2 Store args on stack if possible. 2024-06-01 09:30:42 -04:00
Dianne Skoll
8dbae776c9 Fix SEGV. 2024-06-01 09:27:27 -04:00
Dianne Skoll
b78702cc53 Assume all function names are lower-case. 2024-06-01 08:46:27 -04:00
Dianne Skoll
d2b43605ad Completely revamp expression engine. 2024-06-01 08:19:12 -04:00
Dianne Skoll
7728e09337 Don't allow duplicate arg names in function definitions. 2024-06-01 08:19:09 -04:00
Dianne Skoll
2666353ce6 Put an include guard around auto.rem 2024-06-01 08:19:08 -04:00
Dianne Skoll
0b8a306483 Disable CI for "wip" branches. 2024-06-01 08:19:08 -04:00
Dianne Skoll
b51a0b2d08 Document that -ivar is the same as -ivar=0 2024-05-25 10:26:00 -04:00
Dianne Skoll
959355b19c Stricter parsing of '-i' option. 2024-05-25 09:36:33 -04:00
Dianne Skoll
9c3f0f1994 Allow "-ifoo" on the command-line, which is the same as "-ifoo=0" 2024-05-25 09:22:41 -04:00
Dianne Skoll
dac337a65b Convert all leading spaces to tabs. Per Tim Chase. 2024-05-01 09:48:59 -04:00
Dianne Skoll
98739dfdbc Replace spaces with tab (per Emanuele Torre)
Because different invisible things are different.
2024-05-01 09:11:19 -04:00
Dianne Skoll
17b7a1ea84 Fix typo 2024-04-29 17:13:52 -04:00
Dianne Skoll
4d45925758 Fix typo 2024-04-29 16:18:49 -04:00
Dianne Skoll
8cadb23f48 Update release notes. 2024-04-29 16:16:42 -04:00
Dianne Skoll
63211b65c2 Bump version to 04.03.07 2024-04-22 14:55:47 -04:00
Dianne Skoll
1be84525b1 Don't rely on behavior of "%" with negative args. 2024-04-22 09:57:32 -04:00
Dianne Skoll
67ae95a464 Make sure shellescape() does not mangle UTF-8 characters. 2024-04-22 09:50:17 -04:00
Dianne Skoll
c03a95ad94 Use built-in versions of strdup, strcasecmp and strncasecmp instead of writing our own. 2024-04-21 14:44:24 -04:00
Dianne Skoll
51aa7aecb9 Make $Tt a synonym for trigtime() 2024-04-20 11:50:39 -04:00
Dianne Skoll
592cfe5a20 Use "uint32_t" if we have <stdint.h> for MD5 code. 2024-04-20 10:50:44 -04:00
Dianne Skoll
b4cf15e73e Remove some unused autoconf cruft. 2024-04-20 10:45:22 -04:00
Dianne Skoll
862e143372 Ugh, forgot to regen ./configure. 2024-04-20 10:40:06 -04:00
Dianne Skoll
1f10ca49ad Pass proper args to AC_INIT; include Remind home page in usage output. 2024-04-20 10:39:12 -04:00
Dianne Skoll
4a0c4ffdca Add a test to ensure we don't save trigger time while parsing. 2024-04-18 23:48:30 -04:00
Dianne Skoll
27c8737f3a Only save trigger date when computing it, not while parsing. 2024-04-18 23:41:47 -04:00
Dianne Skoll
ecf45fc453 Add tests for commit 0a1178cfd7: Don't clear out last trigger time unnecessarily. 2024-04-18 18:32:38 -04:00
Dianne Skoll
0a1178cfd7 Don't clear out last trigger time unnecessarily. 2024-04-18 17:56:49 -04:00
Dianne Skoll
20a35dc627 Put the tabbed notebook blurb after the blurb about obtaining default settings. 2024-04-10 09:44:40 -04:00
Dianne Skoll
79887c06f0 Tweak wording. 2024-04-10 09:43:13 -04:00
Dianne Skoll
f7ff424904 Remove debugging line. 2024-04-04 13:43:44 -04:00
Dianne Skoll
6678721fe3 Make build.tk add a little note if it obtained default settings from an existing Remind installation. 2024-04-04 13:42:31 -04:00
Dianne Skoll
496302097b Add a missing release note... sigh. 2024-04-02 09:13:59 -04:00
Dianne Skoll
fe3e2b9a20 Install include files with proper permissions. 2024-04-02 09:05:25 -04:00
Dianne Skoll
400a6b066f Add Portuguese holidays, courtesy of Joop Kiefte. 2024-04-02 09:01:45 -04:00
Dianne Skoll
76d181e7fc Update docs 2024-04-02 08:56:27 -04:00
Dianne Skoll
77373eed2d Fix tests on FreeBSD. We need to copy the results of getenv or when we change it, it will be overwritten. 2024-04-02 08:54:40 -04:00
Dianne Skoll
6b52be388f Update docs. 2024-04-02 08:23:21 -04:00
Dianne Skoll
0518a12a91 Don't fail make install if we can't jigger desktop icons. 2024-04-01 14:42:17 -04:00
Dianne Skoll
362a02c4b8 Don't include inotify-related code if we don't have inotify.
Fixes bug that broke compilation on FreeBSD.
2024-04-01 14:24:00 -04:00
Dianne Skoll
3e3a0cde47 Add "all" option to compare-language-mods.pl 2024-04-01 12:03:34 -04:00
Dianne Skoll
c16f93effd Update WHATSNEW. 2024-04-01 09:14:25 -04:00
Dianne Skoll
61b27c02b5 Be a little more aggressive. 2024-04-01 09:07:13 -04:00
Dianne Skoll
ba28eaad53 Fix typo. 2024-04-01 08:42:48 -04:00
Dianne Skoll
92c2d0cc9f Remove obsolete #ifdef tests 2024-04-01 08:41:29 -04:00
Dianne Skoll
21d5e8a095 Tweak daemon mode documentation. 2024-04-01 08:39:24 -04:00
Dianne Skoll
60417d68a7 Bump version to 04.03.05 2024-04-01 08:31:59 -04:00
Dianne Skoll
7d25387403 Remove the MICROSOFT-AND-APPLE file. I think I've made my point. 2024-03-31 12:07:11 -04:00
Dianne Skoll
b454cf5b20 Refactor set_cloexec to make it more convenient to call. 2024-03-31 09:49:32 -04:00
Dianne Skoll
9ea6385b72 Tweak whitespace. 2024-03-31 09:42:13 -04:00
Dianne Skoll
55b7908444 Add International Transgender Day of Visibility to man page
Done to annoy the anti-LGBT crowd.
2024-03-31 09:40:58 -04:00
Dianne Skoll
e9ff66478b Document that we use inotify, where possible, to detect changes to reminder scripts / directories. 2024-03-30 13:25:57 -04:00
Dianne Skoll
d3240d711d Rename DaemonWait to ServerWait. 2024-03-29 19:57:47 -04:00
Dianne Skoll
a8d63a4199 Use inotfy even in normal daemon mode (-zn where n>0) to detect file changes. 2024-03-29 08:28:41 -04:00
Dianne Skoll
a4807a21c3 Save the value of $DefaultColor that was in effect at the time a reminder was queued. Restore this value when triggering the reminder. 2024-03-26 11:44:42 -04:00
Dianne Skoll
a394ef53a0 Print the "passthru" field for PASSTHRU_TYPE reminders when listing the queue. 2024-03-26 11:29:37 -04:00
Dianne Skoll
eb8243743a Prevent mishandling of SPECIAL COLOR reminders when pulling them from the queue.
Bug reported by Damien Tardy-Panis.
2024-03-26 11:21:17 -04:00
Dianne Skoll
8444bb15c5 Fix typo. 2024-03-26 09:54:31 -04:00
Dianne Skoll
b86245c4c6 Fix wording. 2024-03-25 10:24:39 -04:00
Dianne Skoll
499fcfad41 Fix typo 2024-03-25 10:24:20 -04:00
Dianne Skoll
bddbbf061b Update changelog. 2024-03-25 10:21:05 -04:00
Dianne Skoll
05136f4cf0 Add a basic CSS file for the demo WWW stuff. 2024-03-25 10:12:46 -04:00
Dianne Skoll
e62e3d5d03 More multitrig examples. 2024-03-24 11:05:15 -04:00
Dianne Skoll
595fdaa4a4 Increment version to 04.03.04. 2024-03-24 10:54:44 -04:00
Dianne Skoll
133febc2c1 Document that you can't have an AT clause in a multitrig trigger. 2024-03-24 10:50:21 -04:00
Dianne Skoll
f8c6d7ff6c Document multitrig. 2024-03-24 10:46:53 -04:00
Dianne Skoll
db3341db91 Fix bug in code that stripes the Queue... output grey and white. 2024-03-23 10:57:56 -04:00
Dianne Skoll
8cbcd3ec01 Show Hebrew dates on PDF version of Jewish holiday calendar. 2024-03-22 17:38:30 -04:00
Dianne Skoll
4c6512e9b6 Update the HTML remind demo. 2024-03-22 17:26:23 -04:00
Dianne Skoll
4769789a8f More common HTML path. 2024-03-22 17:14:13 -04:00
Dianne Skoll
b4a8cb085c Add "blank PDF calendar" to www choices. 2024-03-22 17:13:21 -04:00
Dianne Skoll
4f816d52fd Another test for multitrig / scanfrom interaction. 2024-03-22 12:40:50 -04:00
Dianne Skoll
94b3a0248d Add tests for multitrig 2024-03-22 12:39:58 -04:00
Dianne Skoll
5fbf1d82e4 Remove duplicated line. 2024-03-22 11:23:04 -04:00
Dianne Skoll
55eb3ebe95 Add multitrig built-in function which is how trig() should originally have been done. 2024-03-22 11:21:26 -04:00
Dianne Skoll
2afa3c5558 Raise windows after errors so they remain visible. 2024-03-21 16:08:18 -04:00
Dianne Skoll
30d9a42db2 Remove debugging statement. 2024-03-21 12:39:48 -04:00
Dianne Skoll
74d357b84d Use placeholders that are less likely to be used as filenames. :) 2024-03-21 12:22:43 -04:00
Dianne Skoll
09c98a93ec Fix month navigation. 2024-03-21 12:19:39 -04:00
Dianne Skoll
ed89ab7c04 Remove unnecessary code and variable. 2024-03-21 12:13:32 -04:00
Dianne Skoll
27c74be02b Don't need to monitor reminder file for changes; reap zombies. 2024-03-21 12:11:39 -04:00
Dianne Skoll
8f22ab39dc Apply POSIX-escaping to filenames. 2024-03-21 11:46:29 -04:00
Dianne Skoll
795c53f4ce Format queue items better; make them clickable to open an editor. 2024-03-21 11:02:07 -04:00
Dianne Skoll
3fd18a9cc0 Document clickable errors. 2024-03-21 10:45:32 -04:00
Dianne Skoll
a5afb4a87e In the "show errors" text box, fire up the editor when you click on an error if we can figure out the filename and line number. 2024-03-20 20:53:19 -04:00
Dianne Skoll
379fc4542d Make errors stand out more obviously. 2024-03-20 20:33:46 -04:00
Dianne Skoll
538ef6c8ce Check for both $CalMode and $PsCal 2024-03-20 16:32:02 -04:00
Dianne Skoll
95c7e8856c Add standard "moonphases.rem" file with moon phases. 2024-03-20 16:28:50 -04:00
Dianne Skoll
95523c8f10 Convert cosd() function into a macro. 2024-03-19 16:26:07 -04:00
Dianne Skoll
97b53d5e40 Fix typo 2024-03-18 11:18:46 -04:00
Dianne Skoll
fb688850b1 Update release notes. 2024-03-18 11:11:16 -04:00
Dianne Skoll
5b1bad2650 Document DEL server command and qid value. 2024-03-16 15:50:58 -04:00
Dianne Skoll
e5ff132c5e Remove all traces of tk_dialog 2024-03-16 15:45:29 -04:00
Dianne Skoll
a0830ad23c Bump version to 04.03.03 2024-03-16 15:27:34 -04:00
Dianne Skoll
9290f53466 Fix typo 2024-03-16 15:24:11 -04:00
Dianne Skoll
e5711032bd Keep reading inotify events until 0.2 seconds elapses between successive events. 2024-03-16 15:22:48 -04:00
Dianne Skoll
08e3c1d5a2 Use tk_messageBox instead of tk_dialog. 2024-03-16 15:22:36 -04:00
Dianne Skoll
29c579a301 Refactor some code. 2024-03-16 14:01:21 -04:00
Dianne Skoll
61f55bceee Fix "Delete this reminder completely" functionality. 2024-03-16 13:56:28 -04:00
Dianne Skoll
6586fae3eb Fix logic bug that would sometimes fail to send a queue update message to controlling process. 2024-03-16 13:36:24 -04:00
Dianne Skoll
d5a86f3e4f Actually de-queue and free reminders that expire out of the queue. 2024-03-16 13:29:59 -04:00
Dianne Skoll
96551ccaa4 Make TkRemind use the "DEL qid" facility to ignore reminders. Fix potential use of freed memory in queue.c 2024-03-16 13:25:47 -04:00
Dianne Skoll
c83ee86d10 Implement DEL command to removed a queued reminder from the queue in -zj mode. 2024-03-16 12:54:30 -04:00
Dianne Skoll
c913306cbd Add "qid" member to JSONQUEUE. 2024-03-16 11:17:28 -04:00
Dianne Skoll
03d385df97 Add a test for the "-ds" debugging flag. 2024-03-13 12:17:49 -04:00
Dianne Skoll
61fcc1b275 Add "s" debugging flag to see expression-parsing stack high-water marks.
Reduce default value stack size from 1000 to 100.
2024-03-13 12:06:55 -04:00
Dianne Skoll
26977a4ac0 Fix a bunch of cppcheck complaints and also update the cppcheck Makefile target. 2024-03-11 12:10:03 -04:00
Dianne Skoll
28acd05215 Better icon. 2024-03-08 14:28:19 -05:00
Dianne Skoll
be4eed8b20 Update TkRemind logo to include a white outline. 2024-03-07 15:36:31 -05:00
Dianne Skoll
cdb0850373 Add a comment to the troff source so it doesn't confuse Emacs syntax highlighting. 2024-03-03 14:55:40 -05:00
Dianne Skoll
0d55e04284 Make a local OMIT that doesn't specify a weekday name into a syntax error. 2024-03-03 14:51:53 -05:00
Dianne Skoll
f4cce54b70 Warn if a local OMIT doesn't actually omit any weekdays. 2024-03-03 11:00:35 -05:00
Dianne Skoll
2dc6ca44f1 Fix typo 2024-03-01 09:10:26 -05:00
Dianne Skoll
d1d833f0f3 Document fix in commit 1d44577ce9 2024-03-01 09:04:09 -05:00
Dianne Skoll
1d44577ce9 Exit rather than return if we forked in System(). 2024-03-01 09:00:36 -05:00
Dianne Skoll
1be7c2d6d7 Bump version to 04.03.02 and document fix to install: target. 2024-03-01 08:50:00 -05:00
Dianne Skoll
b1f418ee42 Install desktop and icon file in correct paths: $prefix/share/applications and $prefix/share/pixmaps 2024-03-01 08:45:43 -05:00
Dianne Skoll
72b0bf96fe Update release notes. 2024-02-29 20:28:09 -05:00
Dianne Skoll
3388849fa5 Fix test bug. 2024-02-29 20:27:19 -05:00
Dianne Skoll
dc9650d5fa Fix test bug. SIGH. 2024-02-29 20:25:13 -05:00
Dianne Skoll
8eb40ae748 Note the bug fix. 2024-02-29 15:53:31 -05:00
Dianne Skoll
89184f1d0f Update release notes. 2024-02-29 15:52:43 -05:00
Dianne Skoll
e899c790b9 Add Catalan translation file, courtesy of Eloi Torrents 2024-02-29 15:29:24 -05:00
Dianne Skoll
bd6d695020 Add some more test cases. 2024-02-29 15:27:55 -05:00
Dianne Skoll
20d4626a71 Bump version to 04.03.00 2024-02-29 13:48:15 -05:00
Dianne Skoll
8ff94c5031 Install the .desktop and icon files; add to menu. 2024-02-29 13:19:00 -05:00
Dianne Skoll
ee185a0eeb Desktop file should be executable. 2024-02-29 13:08:49 -05:00
Dianne Skoll
06f8932efd Add .desktop file and icon for TkRemind, courtesy of Eloi Torrents 2024-02-29 13:04:19 -05:00
Dianne Skoll
1dc627148c Fix tests so they don't depend on current date; add more tests for Feb 29 edge cases. 2024-02-29 12:44:59 -05:00
Dianne Skoll
3cdde5351f Issue "NOTE newdate" in legacy mode in response to an inotify event. 2024-02-29 11:28:05 -05:00
Dianne Skoll
6e93b8a73d Update TkRemind man page to properly reflect inotify support. 2024-02-29 11:26:07 -05:00
Dianne Skoll
267e8533cf Fix stupid bug. 2024-02-29 11:14:05 -05:00
Dianne Skoll
d3bfb0a28f Let Remind handle the inotify stuff. 2024-02-29 11:07:32 -05:00
Dianne Skoll
5a31bc7058 Integrate inotify support directly into Remind for server mode. 2024-02-29 11:03:28 -05:00
Dianne Skoll
746bde71bd Check for inotify_init1 2024-02-29 10:41:49 -05:00
Dianne Skoll
b274ac635c Clarify comment. 2024-02-29 09:46:56 -05:00
Dianne Skoll
9e0a74e583 Don't spit anything out to client for RUN-type reminders in server mode. 2024-02-29 09:31:37 -05:00
Dianne Skoll
0f782f7697 Set CLOEXEC flag on files we open.
When running programs in server mode, connect stdin and stdout to /dev/null
2024-02-29 09:22:15 -05:00
Dianne Skoll
8efde3e9af Fix typo 2024-02-28 10:59:51 -05:00
Dianne Skoll
3bf3137dc4 Check for existence of tags key. 2024-02-28 10:58:46 -05:00
Dianne Skoll
63ec32d28d Add missing $Sunday to man page. 2024-02-27 18:40:35 -05:00
Dianne Skoll
d2f4177cdb Update man pages. 2024-02-27 11:11:21 -05:00
Dianne Skoll
1d958fb7a8 Use JSON server mode from TkRemind. 2024-02-27 10:55:26 -05:00
Dianne Skoll
fcd580d42e Add a test for -zj 2024-02-27 10:28:08 -05:00
Dianne Skoll
34dab68805 Finish implementing "-zj" mode - Daemon mode with JSON responses. 2024-02-27 10:18:18 -05:00
Dianne Skoll
216dd03922 Start adding support for JSON-formatted daemon responses. 2024-02-27 09:54:35 -05:00
Dianne Skoll
5eef9ac621 Add test for zero-arg forms of easterdate() and orthodoxeaster() 2024-02-26 17:21:12 -05:00
Dianne Skoll
6b798d5f7c Allow arg to easterdate() and orthodoxeaster() to be omitted, defaulting it to today(). 2024-02-26 17:19:22 -05:00
Dianne Skoll
22ccce0934 Lay groundwork for having TriggerReminder put the results in a DynamicBuffer rather than sending to stdout
Eventually should allow us to make a JSON-based daemon mode.
2024-02-25 09:17:54 -05:00
Dianne Skoll
fe2af14952 Remove obsolete file 2024-02-24 09:46:56 -05:00
Dianne Skoll
8e99ed27e7 Take is_queued into account when deciding to issue banner. 2024-02-24 09:33:59 -05:00
Dianne Skoll
bb12362cc8 Make "Go To Date..." dialog non-modal. 2024-02-14 11:16:24 -05:00
Dianne Skoll
1bfc630a64 Uneascape JSON properly. 2024-02-07 10:27:38 -05:00
Dianne Skoll
987983f8ae Add empty line between queue items. 2024-02-05 13:49:11 -05:00
Dianne Skoll
657a6118aa Set -selectbackground 2024-02-05 13:46:28 -05:00
Dianne Skoll
43e7e6ec7f Alternate queue item background colors. 2024-02-05 10:10:52 -05:00
Dianne Skoll
b8b3c19fbf *sigh* A JSON key was changed. :( 2024-02-05 09:56:47 -05:00
Dianne Skoll
69298c96a5 Make the version of rem2html track the version of Remind. 2024-02-04 21:23:57 -05:00
79 changed files with 7984 additions and 4160 deletions

2
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

@@ -15,6 +15,8 @@
# the next line restarts using wish \ # the next line restarts using wish \
exec wish "$0" "$@" exec wish "$0" "$@"
global RemindExecutable
#*********************************************************************** #***********************************************************************
# %PROCEDURE: SetConfigDefaults # %PROCEDURE: SetConfigDefaults
# %ARGUMENTS: # %ARGUMENTS:
@@ -40,7 +42,7 @@ proc SetConfigDefaults {} {
set Config(WESTERN_HEMISPHERE) 1 set Config(WESTERN_HEMISPHERE) 1
set Config(LANGUAGE) "English" set Config(LANGUAGE) "English"
set Config(INST_DIR) "/usr/local/bin" set Config(INST_DIR) "/usr/local/bin"
set Config(MAN_DIR) "/usr/local/man" set Config(MAN_DIR) "/usr/local/share/man"
} }
#*********************************************************************** #***********************************************************************
@@ -53,7 +55,7 @@ proc SetConfigDefaults {} {
# Pops up an error dialog; then calls exit. # Pops up an error dialog; then calls exit.
#*********************************************************************** #***********************************************************************
proc Bail { msg } { proc Bail { msg } {
tk_dialog .err "Remind Configuration Error" $msg error 0 "Bummer" tk_messageBox -message "Remind Build Error" -detail $msg -icon error -type ok
exit 1 exit 1
} }
@@ -124,7 +126,7 @@ proc CreateMainDialog {} {
# Creates the "installation directories" dialog. # Creates the "installation directories" dialog.
#*********************************************************************** #***********************************************************************
proc CreateInstallDirDialog { w } { proc CreateInstallDirDialog { w } {
global Config global Config RemindExecutable
label $w.binlabel -text "Location for programs: " label $w.binlabel -text "Location for programs: "
entry $w.bin -width 30 entry $w.bin -width 30
$w.bin insert end $Config(INST_DIR) $w.bin insert end $Config(INST_DIR)
@@ -133,16 +135,19 @@ proc CreateInstallDirDialog { w } {
entry $w.man -width 30 entry $w.man -width 30
$w.man insert end $Config(MAN_DIR) $w.man insert end $Config(MAN_DIR)
text $w.blurb -width 1 -height 5 -wrap word -relief flat -takefocus 0 text $w.blurb -width 1 -height 20 -wrap word -relief flat -takefocus 0
$w.blurb insert end "\n(Tabbed-notebook Tcl code taken from \"Effective Tcl/Tk Programming\" by Mark Harrison and Michael McLennan, Addison-Wesley Professional Computing Series.)" if { "$RemindExecutable" != "" } {
$w.blurb configure -state disabled $w.blurb insert end "Note: Default settings were obtained by querying the existing installed version of Remind found at: $RemindExecutable\n"
# Disable all text-window behaviour }
bindtags $w.blurb {NoSuchTag} $w.blurb insert end "\n(Tabbed-notebook Tcl code taken from \"Effective Tcl/Tk Programming\" by Mark Harrison and Michael McLennan, Addison-Wesley Professional Computing Series.)\n"
grid $w.binlabel -row 0 -column 0 -sticky e grid $w.binlabel -row 0 -column 0 -sticky e
grid $w.bin -row 0 -column 1 -sticky nsew grid $w.bin -row 0 -column 1 -sticky nsew
grid $w.manlabel -row 1 -column 0 -sticky e grid $w.manlabel -row 1 -column 0 -sticky e
grid $w.man -row 1 -column 1 -sticky nsew grid $w.man -row 1 -column 1 -sticky nsew
grid $w.blurb - -sticky nsew grid $w.blurb - -sticky nsew
# Disable all text-window behaviour
bindtags $w.blurb {NoSuchTag}
$w.blurb configure -state disabled
} }
#*********************************************************************** #***********************************************************************
@@ -727,7 +732,7 @@ proc notebook_fix_size {win} {
#*********************************************************************** #***********************************************************************
proc FindRemind {} { proc FindRemind {} {
global env global env
set path [concat [split $env(PATH) ":"] "/bin" "/usr/bin" "/usr/local/bin"] set path [concat [split $env(PATH) ":"] "/usr/local/bin" "/bin" "/usr/bin" ]
foreach thing $path { foreach thing $path {
if [file executable [file join $thing "remind"]] { if [file executable [file join $thing "remind"]] {
return [file join $thing "remind"] return [file join $thing "remind"]
@@ -745,16 +750,17 @@ proc FindRemind {} {
# sensible defaults. # sensible defaults.
#*********************************************************************** #***********************************************************************
proc SetConfigFromRemind {} { proc SetConfigFromRemind {} {
global Config global Config RemindExecutable
SetConfigDefaults SetConfigDefaults
set rem [FindRemind] set rem [FindRemind]
set RemindExecutable $rem
if {"$rem" == ""} { if {"$rem" == ""} {
return return
} }
set dir [file dirname $rem] set dir [file dirname $rem]
set Config(INST_DIR) $dir set Config(INST_DIR) $dir
if {"$dir" == "/usr/local/bin"} { if {"$dir" == "/usr/local/bin"} {
set Config(MAN_DIR) "/usr/local/man" set Config(MAN_DIR) "/usr/local/share/man"
} elseif {$dir == "/usr/bin"} { } elseif {$dir == "/usr/bin"} {
set Config(MAN_DIR) "/usr/share/man" set Config(MAN_DIR) "/usr/share/man"
} }
@@ -764,6 +770,8 @@ proc SetConfigFromRemind {} {
set Config(MAN_DIR) "/usr/share/man" set Config(MAN_DIR) "/usr/share/man"
} elseif {[file readable "/usr/man/man1/remind.1"]} { } elseif {[file readable "/usr/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/man" set Config(MAN_DIR) "/usr/man"
} elseif {[file readable "/usr/local/share/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/local/share/man"
} elseif {[file readable "/usr/local/man/man1/remind.1"]} { } elseif {[file readable "/usr/local/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/local/man" set Config(MAN_DIR) "/usr/local/man"
} }

130
configure vendored
View File

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

View File

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

View File

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

View File

@@ -1,5 +1,282 @@
CHANGES TO REMIND CHANGES TO REMIND
* VERSION 5.0 Patch 2 - 2024-07-26
* IMPROVEMENT: Remind: Revamp how ONCE works. You can now set a
special variable $OnceFile to be the path to a timestamp file. The
ONCE directive uses this timestamp file to track when it was last
run rather than the access date of the main reminder script. This
is more reliable because it doesn't rely on the atime of a file
(which might not be maintained accurately) and is not affected if
you edit your reminder script.
* CHANGE: Taking input from stdin no longer implies the "-o" option; ONCE can
work if you set $OnceFile
* CHANGE: Any of the -c, -n, -p and -s options implicitly enable the
-o option. As before, specifying a repeat factor *N or a date that
is not today on the command-line also implies -o.
* MINOR NEW FEATURE: Rem2PDF: Add the "--svg" command-line option to produce
SVG output rather than PDF.
* MINOR IMPROVEMENT: Remind: Improve the storage efficiency of short
string constants in compiled expression trees.
* MINOR IMPROVEMENT: Remind: Remove some obsolete macro definitions
* VERSION 5.0 Patch 1 - 2024-06-08
* MINOR IMPROVEMENT: Add short-circuit evaluation to the isany() function,
which now only evaluates those arguments absolutely necessary to determine
the result.
* BUG FIX: Mark weekno() as a non-constant function (the zero-argument form
depends on external conditions.)
* BUG FIX: Fix a couple of memory leaks.
* BUG FIX: Don't rely on support for unnamed function parameters; this caused
compilation failures with older versions of gcc.
* VERSION 5.0 Patch 0 - 2024-06-06
* MAJOR CHANGE: The expression evaluation engine has been completely replaced
with a new one that splits parsing and evaluating into two separate steps.
It also features short-circuit evaluation of &&, ||, iif() and choose().
This should speed up expression-heavy reminder files.
NOTE INCOMPATIBILITY: In expressions with side-effects, the short-circuit
evaluation might change the result you get. For example, consider
running the following file through: remind file.rem 2024-06-04
SET a trig("Mon +7") || trig("Thu +7")
MSG trig = [trig()]
Older Remind versions will output:
trig = 2024-06-06
whereas this version outputs:
trig = 2024-06-10
because the second part of the "||" expression is not evaluated. The vast
majority of Remind expressions do not have side-effects and should yield
the same results as before.
The newer expression engine also permits recursive functions, but
these are not recommended. Still, if you want to, you can do:
fset factorial(n) iif(n <= 1, 1, n*factorial(n-1))
and it will work for values of n that don't cause integer overflow.
* IMPROVEMENT: If there's an unmatched PUSH-OMIT-CONTEXT, print the
filename and line number containing it.
* IMPROVEMENT: If there's an IF with a missing ENDIF, print the filename
and line number of the IF statement.
* NEW FEATURE: Add EXPR OFF command to completely disable expression
evaluation. Useful if you INCLUDE files that you don't expect to
contain expressions and may come from slightly untrustworthy sources.
* NEW FEATURE: Add $ExpressionTimeLimit system variable to enforce a
maximum limit on how long evaluating an expression is allowed to take.
* NEW FEATURE: Add --max-execution-time=n command-line option to terminate
Remind if it runs for more than n seconds.
* CHANGE: Make the command-line option "-ifoo" equivalent to "-ifoo=0"
* CHANGE: Permit a literal [ in a reminder by using the sequence [[
The old ["["] still works.
* BUG FIX: In "purge" mode, Remind would sometimes purge reminders with
a relative "SCANFROM" which haven't actually expired. This has been fixed.
* BUG FIX: Disallow something like: FSET func(x, x) expr
which shouldn't have been allowed in the first place.
* BUG FIX: Replace leading spaces with tabs in Makefiles (per Emanuele Torre
and Tim Chase)
* VERSION 4.3 Patch 7 - 2024-04-29
* IMPROVEMENT: build.tk: Add a note if build.tk obtains default settings
from an existing Remind installation.
* IMPROVEMENT: configure: Pass all args to AC_INIT including the Remind
home page. Remove some unused autoconf cruft.
* IMPROVEMENT: Use standard C library versions of strdup, strcasecmp and
strncasecmp where available, rather than using our own versions.
* MINOR FEATURE: remind: Make $Tt a synonym for trigtime().
* BUG FIX: remind: Make sure shellescape() doesn't mangle UTF-8 characters
with high-bits set.
* BUG FIX: remind: Don't rely on undefined behavior of "%" operator in
the ord() built-in function.
* BUG FIX: remind: Do not clear out trigtime() unnecessarily. Before,
you could not write things like the following; now you can:
REM Tue AT 11:30 DURATION 0:30 MSG Thing 1
REM Tue AT [trigtime()+trigduration()] DURATION 1:00 MSG Thing 2
REM Tue AT [trigtime()+trigduration()] DURATION 0:45 MSG Thing 3
for successive reminders that should be moved as a block if the time of
the first one changes.
* BUG FIX: Don't update trigdate() or trigtime() while parsing a REM
statement... only when actually computing the trigger.
* VERSION 4.3 Patch 6 - 2024-04-02
* NEW FILE: Add [$SysInclude]/holidays/pt.rem - Portuguese holidays, courtesy
of Joop Kiefte.
* BUG FIX: remind: Fix compile error on systems that don't support inotify(7).
* BUG FIX: remind: Fix test failures on FreeBSD. On FreeBSD, you have to copy
the result of getenv() or else a subsequent setenv() can change the stored
value.
* VERSION 4.3 Patch 5 - 2024-04-01
* IMPROVEMENT: remind: Use inotify to detect reminder file changes
even in regular daemon mode (-zn where n>0).
* INTERNAL IMPROVEMENTS: Rearrange and refactor some code.
* FIXES: Various fixes and improvements to man pages.
* BUG FIX: remind: Handle queued "SPECIAL COLOR" reminders correctly.
* BUG FIX: remind: Preserve the value of $DefaultColor that was in effect
when a reminder was queued; restore it before issuing the queued reminder.
* VERSION 4.3 Patch 4 - 2024-03-25
* NEW FEATURE: remind: Add the new "multitrig" function, which is how "trig"
should have worked in the first place. See man page for details.
* IMPROVEMENT: tkremind: Make errors in your reminders file stand out more
prominently.
* IMPROVEMENT: tkremind: If you click on an error in the "Errors..." popup,
tkremind will open a text editor on the offending file and line.
* IMPROVEMENT: tkremind: Format the "Queue..." output better and make queue
items clickable; clicking on a queue item opens a text editor on the
corresponding file and line.
* IMPROVEMENT: Add a standard [$SysInclude]/moonphases.rem file to display
moon phases on your calendar.
* IMPROVEMENT: Clean up the demo code in www/ and add PDF output.
* BUG FIX: tkremind: Correctly handle filenames containing spaces and other
characters that tend to confuse the shell.
* BUG FIX: tkremind: Raise dialog boxes after errors so that they
remain visible.
* CLEANUP: tkremind: Remove some dead code.
* VERSION 4.3 Patch 3 - 2024-03-18
* IMPROVEMENT: tkremind: Update icon to include a white border so it shows
up better on dark backgrounds.
* IMPROVEMENT: C code: Fix a number of cppcheck static-analysis warnings.
* IMPROVEMENT: remind: Update the "-zj" protocol to include a queue-id for
each queued reminder and add the DEL client command to delete a specific
item from the queue. Used by tkremind to implement "don't remind me about
this again today."
* MINOR NEW FEATURE: Add a "-ds" debugging flag to print out expression-parsing
stack high-water marks on exit. This esoteric feature is of no use to
anyone but the Remind author.
* IMPROVEMENT: tkremind: Stop using the deprecated tk_dialog command in favor
of the newer tk_messageBox command.
* IMPROVEMENT: remind: In server mode, try to minimize redraws by
consuming inotify events until at least 0.2s elapses without an
event appearing.
* BUG FIX: tkremind: The "Don't remind me about this again today" feature
was unreliable and only worked for reminders created with TkRemind itself.
It has been made more reliable and works with any reminder.
* BUG FIX: remind: Make it a syntax error if a local OMIT in a REM statement
is not followed by at least one weekday name.
* VERSION 4.3 Patch 2 - 2024-03-01
- BUG FIX: remind: Fix a logic error when implementing the RUN command in
server mode. As it turns out, the error is harmless, but it's best to do
things correctly.
- BUG FIX: The Makefile would install the tkremind.png and tkremind.desktop
files in the wrong location. This has been fixed.
* VERSION 4.3 Patch 1 - 2024-02-29
- BUG FIX: tests: "make test" could fail because of a bad test. This
has been fixed. There are no actual code changes to any of the programs
in Remind compared to 04.03.00.
* VERSION 4.3 Patch 0 - 2024-02-29
- IMPROVEMENT: remind: If Remind is compiled on a system that supports
inotify, then in server mode (-z0 or -zj) it monitors the reminders file
and restarts itself if it detects a change, and also notifies the client.
Moving inotify support directly into Remind means that tkremind no longer
has to invoke a separate inotifywait process.
- IMPROVEMENT: remind: Set the CLOEXEC flag on files we open so we
don't leak file descriptors to programs that we run. While I don't
think there's a security issue here (any program you run can do
anything as your userid anyway) it's best to be clean and tidy.
- IMPROVEMENT: remind: Add localization for the Catalan language, courtesy
of Eloi Torrents.
- IMPROVEMENT: tkremind: Add a .desktop file and icon so TkRemind can be
integrated into the desktop menu system, courtesy of Eloi Torrents.
- CHANGE: Add a new server mode with the "-zj" flag. This is just
like "-z0" except it uses JSON messages to communicate with the
client rather than an ad-hoc protocol. The "-z0" mode is still
supported, but is deprecated.
- CHANGE: In server mode (-z0 or -zj) any RUN-type reminders, or message
commands of the "-kcommand" type are run with standard input and standard
output connected to /dev/null. NOTE INCOMPATIBILITY: If you previously
relied on RUN-type reminders to pop up reminders in TkRemind, they no
longer do. If you want this, you'll have to get the command that you
run to pop up its own window with "xmessage" or something similar.
- IMPROVEMENT: tkremind: Make the "Go to date..." dialog non-modal.
- CHANGE: remind: Allow the argument to easterdate() and
orthodoxeaster() to be omitted, in which case it defaults to
today().
- BUG FIX: Miscellaneous man page fixes.
- BUG FIX: Fix a leap-year edge-case. The reminder: REM 29 MSG whatever
was not triggered on Feb 29 of leap years.
- BUG FIX: rem2html: Make the version of rem2html track the version of
Remind. Noted by Ian! D. Allen.
* VERSION 4.2 Patch 9 - 2024-02-04 * VERSION 4.2 Patch 9 - 2024-02-04
- CHANGE: remind: Do not attempt to guess terminal background color on - CHANGE: remind: Do not attempt to guess terminal background color on
@@ -18,7 +295,7 @@ CHANGES TO REMIND
- MINOR NEW FEATURE: remind: The expression STRING * INT or INT * STRING - MINOR NEW FEATURE: remind: The expression STRING * INT or INT * STRING
is now accepted and yields a string that is INT concatenations of the is now accepted and yields a string that is INT concatenations of the
origina STRING. In this case, INT must be non-negative and the total original STRING. In this case, INT must be non-negative and the total
string length can't exceed $MaxStringLen. string length can't exceed $MaxStringLen.
- DOCUMENTATION: Add "Astronomical Algorithms" by Jean Meeus to bibliography. - DOCUMENTATION: Add "Astronomical Algorithms" by Jean Meeus to bibliography.

View File

@@ -106,7 +106,3 @@ IF !Reform
[_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b. [_PastSat(17, "Tamuz")] ++4 MSG %"Tzom Tammuz%" is %b.
[_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b. [_PastSat(9, "Av")] ++4 MSG %"Tish'a B'Av%" is %b.
ENDIF ENDIF
# Clean up
FUNSET _h _h2 _PastSat _BackTwoFri _BackTwoSat _chan

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

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

View File

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

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

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

View File

@@ -87,3 +87,5 @@ SET daylightST_starts_str "Beginn Sommerzeit"
# Daylight saving time ends # Daylight saving time ends
SET daylightST_ends_str "Ende Sommerzeit" SET daylightST_ends_str "Ende Sommerzeit"
PRESERVE earthseasons_Perihelion_str earthseasons_EquinoxMar_str earthseasons_SolsticeJun_str earthseasons_Aphelion_str earthseasons_EquinoxSep_str earthseasons_SolsticeDec_str daylightST_starts_str daylightST_ends_str

View File

@@ -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

14
include/moonphases.rem Normal file
View File

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

View File

@@ -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
@@ -66,7 +63,7 @@ causes \fBRemind\fR to display reminders on the calendar on the
day they actually occur \fIas well as\fR on any preceding days day they actually occur \fIas well as\fR on any preceding days
specified by the reminder's \fIdelta\fR. This \fIalso\fR causes specified by the reminder's \fIdelta\fR. This \fIalso\fR causes
\fBRemind\fR to include text outside %"...%" sequences that would \fBRemind\fR to include text outside %"...%" sequences that would
otherwise be removed (though the actual %" markers themselves are removed.) otherwise be removed (though the actual %" markers themselves are removed.) \"" Add comment to avoid Emacs highlighting problems
.TP .TP
.B 'l' .B 'l'
causes \fBRemind\fR to use VT100 line-drawing characters to draw causes \fBRemind\fR to use VT100 line-drawing characters to draw
@@ -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,
@@ -300,6 +304,11 @@ Echo lines when displaying error messages
.TP .TP
.B f .B f
Trace the reading of reminder files Trace the reading of reminder files
.TP
.B s
Trace expression parsing and display the internal expression node
tree. This is unlikely to be useful unless you are working on
\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]]]]
@@ -361,16 +370,24 @@ You use both \fB\-k\fR\fIcmd1\fR and \fB\-k:\fR\fIcmd2\fR to use different
commands for queued versus non-queued reminders. commands for queued versus non-queued reminders.
.RE .RE
.TP .TP
\fB\-z\fR[\fIn\fR] Runs \fBRemind\fR in the daemon mode. If \fIn\fR \fB\-z\fR[\fIn\fR] Runs \fBRemind\fR in "daemon mode". If \fIn\fR
is supplied, it specifies how often (in minutes) \fBRemind\fR should is supplied, it specifies how often (in minutes) \fBRemind\fR should
wake up to check if the reminder script has been changed. \fIN\fR wake up to check if the reminder script has been changed. \fIN\fR
defaults to 1, and can range from 1 to 60. Note that the use of the defaults to 1, and can range from 1 to 60. Note that the use of the
\fB\-z\fR option also enables the \fB\-f\fR option. \fB\-z\fR option also enables the \fB\-f\fR option.
.PP .PP
.RS .RS
If you supply the option \fB\-z0\fR, \fBRemind\fR runs in a If \fBRemind\fR is compiled on a system that supports
\fBinotify\fR(7), then if the reminder script supplied on the
command-line is actually a directory, \fBRemind\fR additionally checks
if all files within that directory have been modified since startup.
.PP
If you supply the option \fB\-zj\fR, \fBRemind\fR runs in a
special mode called \fBserver mode\fR. This is documented special mode called \fBserver mode\fR. This is documented
in the tkremind man page; see tkremind(1). in the tkremind man page; see tkremind(1). The older server mode
option \fB\-z0\fR still works, but is deprecated; it uses an ad-hoc
method to communicate with the client rather than using JSON to communicate
with the client.
.RE .RE
.TP .TP
\fB\-u\fR\fIname\fR \fB\-u\fR\fIname\fR
@@ -407,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.
@@ -438,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:
.RP
.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
@@ -446,14 +489,14 @@ text editor capable of creating plain-text files to create a
very simple and almost immediately understandable: very simple and almost immediately understandable:
.PP .PP
.nf .nf
REM 6 Jan MSG Dianne's birthday REM Mar 31 MSG International Transgender Day of Visibility
.fi .fi
.PP .PP
to the baroque and obscure: to the baroque and obscure:
.PP .PP
.nf .nf
REM [date(thisyear, 1, 1) + 180] ++5 OMIT \\ REM [date(thisyear, 1, 1) + 180] ++5 OMIT \\
sat sun BEFORE MSG [ord(thisyear-1980)] payment due %b! sat sun BEFORE MSG [ord(thisyear-1980)] payment due %b!
.fi .fi
.PP .PP
A reminder file consists of commands, with one command per line. Several A reminder file consists of commands, with one command per line. Several
@@ -984,26 +1027,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 \B$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
@@ -1867,24 +1919,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
@@ -1896,6 +1930,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
@@ -2148,13 +2206,13 @@ than those on higher lines. The operators approximately correspond to
C operators. 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
@@ -2266,16 +2324,15 @@ 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. Neither of its operands can be
same type and must not be \fBSTRING\fR type. Returns the second \fBSTRING\fR type. Returns the second operand if both operands are
operand if both operands are non-zero. Otherwise, returns a zero non-zero. Otherwise, returns whichever operand is zero.
of the same type as the operands.
.TP .TP
.B || .B ||
This is the logical OR operator. Both of its operands must be of This is the logical OR operator. Neither of its operands can be
the same type and must not be of \fBSTRING\fR type. It returns \fBSTRING\fR type. It returns the first operand that is non-zero; if
the first operand that is non-zero; if both operands are zero, then both operands are zero, then returns the second operand.
returns a zero of the same type as the operands.
.PP .PP
.B NOTES .B NOTES
.PP .PP
@@ -2296,15 +2353,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
@@ -2386,9 +2438,9 @@ Universal Time Coordinated in the \fB$MinsFromUTC\fR system variable.
If non-zero, then the \fB\-c\fR option was supplied on the command line. If non-zero, then the \fB\-c\fR option was supplied on the command line.
.TP .TP
.B $Daemon (read-only) .B $Daemon (read-only)
If the daemon mode \fB\-z\fR was invoked, contains the number of If "daemon mode" \fB\-z\fR was invoked, contains the number of
minutes between wakeups. If not running in daemon mode, contains minutes between wakeups. If not running in daemon mode, contains
0. 0. In server mode (either \fB-z0\fR or \fB-zj\fR), contains -1.
.TP .TP
.B $DateSep .B $DateSep
This variable can be set only to "/" or "-". It holds the character This variable can be set only to "/" or "-". It holds the character
@@ -2461,6 +2513,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.
@@ -2489,9 +2549,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,
@@ -2648,6 +2708,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
@@ -2734,6 +2808,9 @@ Equivalent to \fBwkdaynum(trigdate())\fR.
.B $Ty (read-only) .B $Ty (read-only)
Equivalent to \fByear(trigdate())\fR. Equivalent to \fByear(trigdate())\fR.
.TP .TP
.B $Tt (read-only, TIME type)
Equivalent to \fBtrigtime()\fR.
.TP
.B $TimeSep .B $TimeSep
This variable can be set only to ":" or ".". It holds the character This variable can be set only to ":" or ".". It holds the character
used to separate portions of a time when \fBRemind\fR prints a TIME or used to separate portions of a time when \fBRemind\fR prints a TIME or
@@ -3080,11 +3157,12 @@ will produce undefined results.
Returns the time of "civil twilight" on the specified \fIdate\fR. If Returns the time of "civil twilight" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR. \fIdate\fR is omitted, defaults to \fBtoday()\fR.
.TP .TP
.B easterdate(dqi_arg) .B easterdate([dqi_arg])
If \fIarg\fR is an \fBINT\fR, then returns the date of Easter Sunday If \fIarg\fR is an \fBINT\fR, then returns the date of Easter Sunday
for the specified year. If \fIarg\fR is a \fBDATE\fR or for the specified year. If \fIarg\fR is a \fBDATE\fR or
\fBDATETIME\fR, then returns the date of the next Easter Sunday on or \fBDATETIME\fR, then returns the date of the next Easter Sunday on or
after \fIarg\fR. (The time component of a datetime is ignored.) after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS .RS
.P .P
Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox Note that \fBeasterdate\fR computes the Western Easter. For the Orthodox
@@ -3346,6 +3424,62 @@ which default to \fBtoday()\fR and midnight, respectively. The returned
value is an integer from 0 to 359, representing the phase of the moon value is an integer from 0 to 359, representing the phase of the moon
in degrees. 0 is a new moon, 180 is a full moon, 90 is first-quarter, etc. in degrees. 0 is a new moon, 180 is a full moon, 90 is first-quarter, etc.
.TP .TP
.B multitrig(s_trig1 [,s_trig2, [... s_trigN]])
\fBmultitrig\fR evaluates each string as a trigger, similar to \fBevaltrig\fR,
and returns the \fIearliest\fR trigger date that is on or after \fBtoday()\fR.
\fBmultitrig\fR is similar to \fBtrig\fR but has the following difference:
.RS
.PP
\fBtrig\fR returns the \fIfirst\fR trigger date that would have triggered today,
whereas \fBmultitrig\fR returns the \fIearliest\fR trigger date later than
today, regardless of whether it would have triggered today.
.PP
If no trigger can be computed that is later than \fBtoday()\fR, then
\fBmultitrig\fR returns 1990-01-01.
.PP
Consider the following examples, assuming that today is Sunday, 24 March 2024:
.PP
.nf
# Returns 1990-01-01 because neither would trigger today
SET a trig("Mon", "Wed")
# Returns 2024-03-25 because it's the earlier trigger date
SET a multitrig("Mon", "Wed")
# Returns 2024-03-27 because it's the first that would trigger today
SET a trig("Wed +3", "Mon +3")
# Returns 2024-03-25 because it's the earlier trigger date
SET a multitrig("Wed +3", "Mon +3")
# Returns 1990-01-01 because all triggers have expired
SET a multitrig("2000", "2022", "1998", "2023")
.fi
.PP
In general, \fBmultitrig\fR works better with the Remind algorithm than
\fBtrig\fR and should be used most of the time. As an example, this
reminder is issued at the end of each quarter:
.PP
.nf
REM [multitrig("Mar 31", "Jun 30", "Sep 30", "Dec 31")] +7 MSG \\
%"End of [ord($Tm/3)] quarter%" is %b.
.fi
.PP
If you want the last working day of each quarter, you could use:
.PP
.nf
PUSH-OMIT-CONTEXT
OMIT Sat Sun
REM [multitrig("Mar ~1", "Jun ~1", "Sep ~1", "Dec ~1")] +7 MSG \\
%"Last working day of [ord($Tm/3)] quarter%" is %b.
POP-OMIT-CONTEXT
.fi
.PP
Note that unlike \fBevaltrig\fR, \fBmultitrig\fR always returns a \fBDATE\fR
and never a \fBDATETIME\fR. Including an \fBAT\fR clause in a trigger
supplied to \fBmultitrig\fR will result in an error.
.RE
.TP
.B ndawn([dq_date]) .B ndawn([dq_date])
Returns the time of "nautical dawn" on the specified \fIdate\fR. If Returns the time of "nautical dawn" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR. If a \fIdatetime\fR object \fIdate\fR is omitted, defaults to \fBtoday()\fR. If a \fIdatetime\fR object
@@ -3439,11 +3573,12 @@ the actual time, or a time supplied on the command line.
Returns a string that is the ordinal number \fInum\fR. For example, Returns a string that is the ordinal number \fInum\fR. For example,
\fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th". \fBord(2)\fR returns "2nd", and \fBord(213)\fR returns "213th".
.TP .TP
.B orthodoxeaster(dqi_arg) .B orthodoxeaster([dqi_arg])
If \fIarg\fR is an \fBINT\fR, then returns the date of Orthodox Easter Sunday If \fIarg\fR is an \fBINT\fR, then returns the date of Orthodox Easter Sunday
for the specified year. If \fIarg\fR is a \fBDATE\fR or for the specified year. If \fIarg\fR is a \fBDATE\fR or
\fBDATETIME\fR, then returns the date of the next Orthodox Easter Sunday on or \fBDATETIME\fR, then returns the date of the next Orthodox Easter Sunday on or
after \fIarg\fR. (The time component of a datetime is ignored.) after \fIarg\fR. (The time component of a datetime is ignored.) If \fIarg\fR
is omitted, then it defaults to \fBtoday()\fR.
.RS .RS
.P .P
Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western Note that \fBorthodoxeaster\fR computes the Orthodox Easter. For the Western
@@ -3764,7 +3899,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
@@ -3772,58 +3907,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.
@@ -4163,6 +4249,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
@@ -4710,7 +4804,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.
@@ -5014,21 +5108,25 @@ structuring comments in your PostScript code.
.PP .PP
.SH DAEMON MODE .SH DAEMON MODE
.PP .PP
If you use the \fB\-z\fR command-line option, \fBRemind\fR runs in the If you use the \fB\-z\fR command-line option, \fBRemind\fR runs in
"daemon" mode. In this mode, no "normal" reminders are issued. "daemon mode". In this mode, no "normal" reminders are issued.
Instead, only timed reminders are collected and queued, and are then Instead, only timed reminders are collected and queued, and are then
issued whenever they reach their trigger time. issued whenever they reach their trigger time.
.PP .PP
In addition, \fBRemind\fR wakes up every few minutes to check the modification In addition, \fBRemind\fR wakes up every few minutes to check the
date on the reminder script (the filename supplied on the command line.) modification date on the reminder script (the filename supplied on the
If \fBRemind\fR detects that the script has changed, it re-executes itself command line.) If \fBRemind\fR detects that the script has changed,
in daemon mode, and interprets the changed script. it re-executes itself in daemon mode, and interprets the changed
script. If \fBRemind\fR was compiled with support for
\fBinotify\fR(7), then if the command-line reminder script is really a
directory, \fBRemind\fR also re-executes itself if any of the files in
the directory is changed.
.PP .PP
In daemon mode, \fBRemind\fR also re-reads the remind script when it In daemon mode, \fBRemind\fR also re-reads the remind script when it
detects that the system date has changed. detects that the system date has changed.
.PP .PP
In daemon mode, \fBRemind\fR acts as if the \fB\-f\fR option had been used, In daemon mode, \fBRemind\fR acts as if the \fB\-f\fR option had been used,
so to run in the daemon mode in the background, use: so to run in daemon mode in the background, use:
.PP .PP
.nf .nf
remind \-z .reminders & remind \-z .reminders &
@@ -5255,7 +5353,7 @@ A number of system variables let you translate various phrases
to other languages. These system variables are: to other languages. These system variables are:
.PP .PP
.TP .TP
.B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday .B $Monday, $Tuesday, $Wednesday, $Thursday, $Friday, $Saturday, $Sunday
Set each of these system variables to a string representing the corresponding Set each of these system variables to a string representing the corresponding
day's name in your language. Strings must be valid UTF-8 strings. day's name in your language. Strings must be valid UTF-8 strings.
.TP .TP
@@ -5358,7 +5456,7 @@ as:
.PP .PP
You can define your own substitution sequences in addition to the built-in You can define your own substitution sequences in addition to the built-in
ones as follows: If you define a function named \fBsubst_\fIname\fB(alt, date, time)\fR, then the sequence \fB%{name}\fR calls the function with \fBalt\fR ones as follows: If you define a function named \fBsubst_\fIname\fB(alt, date, time)\fR, then the sequence \fB%{name}\fR calls the function with \fBalt\fR
set to 0 and \fBdate\fR and \fRtime\fR to the trigger date and time, set to 0 and \fBdate\fR and \fBtime\fR to the trigger date and time,
respectively. The \fB%{name}\fR sequence is replaced with whatever the respectively. The \fB%{name}\fR sequence is replaced with whatever the
function returns. The sequence \fB%*{name}\fR is similar, but calls function returns. The sequence \fB%*{name}\fR is similar, but calls
the function with \fBalt\fR set to 1. the function with \fBalt\fR set to 1.

View File

@@ -184,7 +184,9 @@ on the reminder.
If there are any errors in your reminder file, the "Queue..." button If there are any errors in your reminder file, the "Queue..." button
changes to "Errors...". Click on "Errors..." to see the Remind error changes to "Errors...". Click on "Errors..." to see the Remind error
output. Click "OK" to close the error window; this makes the button output. Click "OK" to close the error window; this makes the button
in the main TkRemind window to revert to "Queue..." in the main TkRemind window to revert to "Queue..." You can click on
any error message to open an editor on the file and line number that
caused the error.
.SH BACKGROUND REMINDERS .SH BACKGROUND REMINDERS
@@ -301,11 +303,11 @@ Today
.SH IMMEDIATE UPDATES .SH IMMEDIATE UPDATES
If you are running \fBTkRemind\fR on Linux and have the If you are running \fBTkRemind\fR on Linux and \fBRemind\fR has been
\fBinotifywait\fR program installed (part of the \fBinotify-tools\fR compiled with \fBinotify\fR(7) support, then \fBTkRemind\fR redraws
or similar package), then \fBTkRemind\fR redraws the calendar window the calendar window \fIimmediately\fR if \fB$HOME/.reminders\fR
\fIimmediately\fR if \fB$HOME/.reminders\fR changes (or, if it is a changes (or, if it is a directory, any files in that directory
directory, any files in that directory change.) change.)
.PP .PP
This lets \fBTkRemind\fR react immediately to hand-edited reminders or This lets \fBTkRemind\fR react immediately to hand-edited reminders or
to reminder files that are imported from another calendar system (for example, to reminder files that are imported from another calendar system (for example,
@@ -366,75 +368,99 @@ your hand-edited files in a separate \fB*.rem\fR file than \fBTkRemind\fR's
\fBRemind\fR has a special mode for interacting with programs like \fBRemind\fR has a special mode for interacting with programs like
\fBTkRemind\fR. This mode is called \fIserver mode\fR and is \fBTkRemind\fR. This mode is called \fIserver mode\fR and is
selected by supplying the \fB\-z0\fR option to \fBRemind\fR. selected by supplying the \fB\-zj\fR option to \fBRemind\fR.
In server mode, \fBRemind\fR operates similar to daemon mode, except In server mode, \fBRemind\fR operates similar to daemon mode, except
it reads commands (one per line) it reads commands (one per line) from standard input and writes status
from standard input and writes status lines to standard output. lines to standard output. Each status line is a JSON object.
The commands accepted in server mode are: The commands accepted in server mode are:
.TP .TP
EXIT EXIT
Terminate the \fBRemind\fR process. EOF on standard input does the Terminate the \fBRemind\fR process. EOF on standard input does the
same thing. same thing. \fBRemind\fR exits immediately without printing
a JSON status line.
.TP .TP
STATUS STATUS
Return the number of queued reminders. Return the number of queued reminders. The JSON object looks
something like this:
.nf
{"response":"queued","nqueued":n,"command":"STATUS"}
.fi
where \fIn\fR is the number of reminders queued.
.TP .TP
QUEUE QUEUE or JSONQUEUE
Returns the contents of the queue, printed between "NOTE queue" and Returns the contents of the queue. The JSON object looks something
"NOTE endqueue" lines. like this:
.nf
{"response":"queue","queue":[ ... ],"command":"QUEUE"}
.fi
The value of the \fBqueue\fR key is an array of JSON objects, each
representing a queued reminder.
.TP .TP
JSONQUEUE DEL \fIqid\fR
Returns the contents of the queue in JSON format, printed between Delete the reminder with queue-id \fIqid\fR from the queue.
"NOTE JSONQUEUE" and "NOTE ENDJSONQUEUE" lines.
.TP .TP
REREAD REREAD
Re-read the reminder file Re-read the reminder file. Returns the following status line:
.nf
{"response":"reread","command":"REREAD"}
.fi
.PP .PP
The status lines written are as follows: Additional status lines written are as follows:
.TP .TP
NOTE reminder \fItime\fR \fItag\fR .nf
Signifies the beginning of a timed reminder whose trigger time is
\fItime\fR with tag \fItag\fR. If the reminder has no tag, an
asterisk is supplied for \fItag\fR. All lines following this line
are the body of the reminder, until the line \fBNOTE endreminder\fR
is transmitted.
{"response":"reminder","ttime":tt,"now":now,"tags":tags,"qid":qid,"body":body}
.fi
In this line, \fItt\fR is the trigger time of the reminder (expressed
as a string), \fInow\fR is the current time, \fItags\fR (if present)
is the tag or tags associated with the reminder, and \fIbody\fR is the
body of the reminder. This response causes \fBTkRemind\fR to pop up a
reminder notification. \fIqid\fR is a unique identifier for this
reminder. You may delete it from the queue by sending a \fBDEL\fR
\fIqid\fR command to the server. Note that \fIqid\fRs are not stable
across re-reads; if \fBRemind\fR restarts itself to re-read the reminder
file, then the \fIqid\fR values are likely to change, and any reminder
deleted with a \fBDEL\fR \fIqid\fR command is likely to be re-queued.
.TP .TP
NOTE newdate .nf
{"response":"newdate"}
.fi
This line is emitted whenever \fBRemind\fR has detected a rollover of This line is emitted whenever \fBRemind\fR has detected a rollover of
the system date. The front-end program should redraw its calendar the system date. The front-end program should redraw its calendar
or take whatever other action is needed. or take whatever other action is needed.
.TP .TP
NOTE reread .nf
This line is emitted whenever the number of reminders in \fBRemind\fR's
queue changes because of a date rollover or a \fBREREAD\fR command.
The front-end should issue a \fBSTATUS\fR command in response to this
message.
.TP {"response":"reread","command":"inotify"}
NOTE queued \fIn\fR
This line is emitted in response to a \fBSTATUS\fR command. The number
\fIn\fR is the number of reminders in the queue.
.TP .fi
NOTE queue
Indicates that queue contents are about to follow. The end of the
queue is indicated by a NOTE endqueue line.
.TP If \fBRemind\fR was compiled with support for \fBinotify\fR(7), then
NOTE JSONQUEUE if it detects a change to the top-level reminder file or directory,
Indicates that queue contents in JSON format are about to follow. The it issues the above response. The front-end should redraw its
end of the queue is indicated by a NOTE ENDJSONQUEUE line. calendar since this response indicates that a change has been made
to the reminder file or directory.
.PP .PP
Please note that \fBRemind\fR can write a status message \fIat any time\fR Please note that \fBRemind\fR can write a status message \fIat any time\fR

View File

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

View File

@@ -10,7 +10,7 @@ use Encode;
my %Options; my %Options;
my $rem2html_version = '2.1'; my $rem2html_version = '@VERSION@';
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks, my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
@Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen); @Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);

View File

@@ -60,7 +60,7 @@ my $settings = {
margin_bottom => 36, margin_bottom => 36,
margin_left => 36, margin_left => 36,
margin_right => 36, margin_right => 36,
svg => 0,
verbose => 0, verbose => 0,
}; };
@@ -80,6 +80,7 @@ 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
-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 +113,7 @@ 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},
'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},
@@ -187,8 +189,14 @@ my $done_one = 0;
my $errored_out = 0; my $errored_out = 0;
my $surface = Cairo::PdfSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef, my $surface;
$settings->{width}, $settings->{height}); if ($settings->{svg}) {
$surface = Cairo::SvgSurface->create_for_stream(sub { print $_[1] unless $errored_out; }, undef,
$settings->{width}, $settings->{height});
} 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'); };
@@ -199,6 +207,7 @@ eval { $surface->set_metadata('subject', 'Calendar'); };
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});
my $warned = 0;
while(1) { while(1) {
my ($obj, $err) = Remind::PDF->create_from_stream(*STDIN, my ($obj, $err) = Remind::PDF->create_from_stream(*STDIN,
{color => 1, {color => 1,
@@ -215,8 +224,15 @@ while(1) {
} }
last; last;
} }
$done_one = 1; if ($settings->{svg} && $done_one) {
if (!$warned) {
print STDERR "WARNING: --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 +289,18 @@ __END__
=head1 NAME =head1 NAME
rem2pdf - draw a PDF calendar from Remind output rem2pdf - draw a PDF or SVG 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
=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 or SVG code that draws a calendar to standard output.
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 +315,12 @@ output at all.
=over =over
=item --svg
Output SVG instead of PDF. In this case, you should feed C<rem2pdf>
only one month's worth of calendar data, because it cannot create
a multi-page SVG file.
=item --landscape, -l =item --landscape, -l
Print the calendar in landscape orientation. Essentially, this swaps Print the calendar in landscape orientation. Essentially, this swaps
@@ -486,7 +509,7 @@ output for the invalid reminder.
=head1 ABSOLUTELY-POSITIONED TEXT =head1 ABSOLUTELY-POSITIONED TEXT
If your B<PANGO> special reminder starts with C<@I<x>,I<y>> where I<x> If your B<PANGO> special reminder starts with C<@I<x>,I<y>> where I<x>
and I<y> are floating-point numbers, then the Pango marked-up test is and I<y> are floating-point numbers, then the Pango marked-up text is
positioned absolutely with respect to the day's box (and is not positioned absolutely with respect to the day's box (and is not
counted when calculating the box's height.) counted when calculating the box's height.)

View File

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

15
resources/tkremind.desktop Executable file
View File

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

BIN
resources/tkremind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -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"
@@ -475,7 +474,7 @@ void PrintJSONKeyPairTime(char const *name, int t)
} }
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
void PutWideChar(wchar_t const wc) void PutWideChar(wchar_t const wc, DynamicBuffer *output)
{ {
char buf[MB_CUR_MAX+1]; char buf[MB_CUR_MAX+1];
int len; int len;
@@ -483,7 +482,11 @@ void PutWideChar(wchar_t const wc)
len = wctomb(buf, wc); len = wctomb(buf, wc);
if (len > 0) { if (len > 0) {
buf[len] = 0; buf[len] = 0;
fputs(buf, stdout); if (output) {
DBufPuts(output, buf);
} else {
fputs(buf, stdout);
}
} }
} }
#endif #endif
@@ -1037,7 +1040,7 @@ static void DoCalendarOneMonth(void)
printf("\"entries\":[\n"); printf("\"entries\":[\n");
} }
} }
while (WriteCalendarRow()) continue; while (WriteCalendarRow()) /* continue */;
if (PsCal == PSCAL_LEVEL1) { if (PsCal == PSCAL_LEVEL1) {
printf("%s\n", PSEND); printf("%s\n", PSEND);
@@ -1227,7 +1230,7 @@ static void PrintLeft(char const *s, int width, char pad)
ws = buf; ws = buf;
for (i=0; i<width;) { for (i=0; i<width;) {
if (*ws) { if (*ws) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
i+= wcwidth(*ws); i+= wcwidth(*ws);
} else { } else {
break; break;
@@ -1235,7 +1238,7 @@ static void PrintLeft(char const *s, int width, char pad)
} }
/* Mop up any potential combining characters */ /* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) { while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
} }
/* Possibly send lrm control sequence */ /* Possibly send lrm control sequence */
@@ -1308,7 +1311,7 @@ static void PrintCentered(char const *s, int width, char *pad)
for (i=0; i<d; i++) fputs(pad, stdout); for (i=0; i<d; i++) fputs(pad, stdout);
for (i=0; i<width; i++) { for (i=0; i<width; i++) {
if (*ws) { if (*ws) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
if (wcwidth(*ws) == 0) { if (wcwidth(*ws) == 0) {
/* Don't count this character... it's zero-width */ /* Don't count this character... it's zero-width */
i--; i--;
@@ -1319,7 +1322,7 @@ static void PrintCentered(char const *s, int width, char *pad)
} }
/* Mop up any potential combining characters */ /* Mop up any potential combining characters */
while (*ws && wcwidth(*ws) == 0) { while (*ws && wcwidth(*ws) == 0) {
PutWideChar(*ws++); PutWideChar(*ws++, NULL);
} }
/* Possibly send lrm control sequence */ /* Possibly send lrm control sequence */
send_lrm(); send_lrm();
@@ -1448,7 +1451,7 @@ static int WriteOneColLine(int col)
} }
numwritten += wcwidth(*ws); numwritten += wcwidth(*ws);
} }
PutWideChar(*ws); PutWideChar(*ws, NULL);
} }
} }
e->wc_pos = ws; e->wc_pos = ws;
@@ -1463,7 +1466,7 @@ static int WriteOneColLine(int col)
if (wcwidth(*ws) > 0) { if (wcwidth(*ws) > 0) {
numwritten += wcwidth(*ws); numwritten += wcwidth(*ws);
} }
PutWideChar(*ws); PutWideChar(*ws, NULL);
} }
} }
} }
@@ -1666,6 +1669,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;
@@ -1766,7 +1770,7 @@ static int DoCalRem(ParsePtr p, int col)
DBufInit(&raw_buf); DBufInit(&raw_buf);
/* Parse the trigger date and time */ /* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim, 1)) ) { if ( (r=ParseRem(p, &trig, &tim)) ) {
FreeTrig(&trig); FreeTrig(&trig);
return r; return r;
} }
@@ -2306,15 +2310,9 @@ void WriteJSONTrigger(Trigger const *t, int include_tags, int today)
if (t->noqueue) { if (t->noqueue) {
PrintJSONKeyPairInt("noqueue", 1); PrintJSONKeyPairInt("noqueue", 1);
} }
if (*t->sched) { PrintJSONKeyPairString("sched", t->sched);
PrintJSONKeyPairString("sched", t->sched); PrintJSONKeyPairString("warn", t->warn);
} PrintJSONKeyPairString("omitfunc", t->omitfunc);
if (*t->warn) {
PrintJSONKeyPairString("warn", t->warn);
}
if (*t->omitfunc) {
PrintJSONKeyPairString("omitfunc", t->omitfunc);
}
if (t->addomit) { if (t->addomit) {
PrintJSONKeyPairInt("addomit", 1); PrintJSONKeyPairInt("addomit", 1);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,9 +23,8 @@
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
static int ParseTimeTrig (ParsePtr s, TimeTrig *tim, int save_in_globals); static int ParseTimeTrig (ParsePtr s, TimeTrig *tim);
static int ParseLocalOmit (ParsePtr s, Trigger *t); static int ParseLocalOmit (ParsePtr s, Trigger *t);
static int ParseScanFrom (ParsePtr s, Trigger *t, int type); static int ParseScanFrom (ParsePtr s, Trigger *t, int type);
static int ParsePriority (ParsePtr s, Trigger *t); static int ParsePriority (ParsePtr s, Trigger *t);
@@ -63,7 +62,7 @@ int DoRem(ParsePtr p)
DBufInit(&buf); DBufInit(&buf);
/* Parse the trigger date and time */ /* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim, 1)) ) { if ( (r=ParseRem(p, &trig, &tim)) ) {
FreeTrig(&trig); FreeTrig(&trig);
return r; return r;
} }
@@ -163,6 +162,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 +182,7 @@ int DoRem(ParsePtr p)
if (dse == DSEToday && if (dse == DSEToday &&
!(!IgnoreOnce && !(!IgnoreOnce &&
trig.once != NO_ONCE && trig.once != NO_ONCE &&
FileAccessDate == DSEToday)) GetOnceDate() == DSEToday))
QueueReminder(p, &trig, &tim, trig.sched); QueueReminder(p, &trig, &tim, trig.sched);
/* If we're in daemon mode, do nothing over here */ /* If we're in daemon mode, do nothing over here */
if (Daemon) { if (Daemon) {
@@ -192,7 +192,7 @@ int DoRem(ParsePtr p)
r = OK; r = OK;
if (ShouldTriggerReminder(&trig, &tim, dse, &err)) { if (ShouldTriggerReminder(&trig, &tim, dse, &err)) {
if ( (r=TriggerReminder(p, &trig, &tim, dse, 0)) ) { if ( (r=TriggerReminder(p, &trig, &tim, dse, 0, NULL)) ) {
FreeTrig(&trig); FreeTrig(&trig);
return r; return r;
} }
@@ -220,7 +220,7 @@ int DoRem(ParsePtr p)
/* trigger structure. */ /* trigger structure. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals) int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
{ {
register int r; register int r;
DynamicBuffer buf; DynamicBuffer buf;
@@ -262,10 +262,6 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
trig->need_wkday = 0; trig->need_wkday = 0;
trig->adj_for_last = 0; trig->adj_for_last = 0;
if (save_in_globals) {
LastTriggerTime = NO_TIME;
}
int parsing = 1; int parsing = 1;
while(parsing) { while(parsing) {
/* Read space-delimited string */ /* Read space-delimited string */
@@ -315,10 +311,6 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
trig->m = m; trig->m = m;
trig->d = d; trig->d = d;
tim->ttime = (tok.val % MINUTES_PER_DAY); tim->ttime = (tok.val % MINUTES_PER_DAY);
if (save_in_globals) {
LastTriggerTime = tim->ttime;
SaveLastTimeTrig(tim);
}
break; break;
case T_WkDay: case T_WkDay:
@@ -355,14 +347,14 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
DBufFree(&buf); DBufFree(&buf);
if (tim->ttime != NO_TIME) return E_TIME_TWICE; if (tim->ttime != NO_TIME) return E_TIME_TWICE;
tim->ttime = tok.val; tim->ttime = tok.val;
r = ParseTimeTrig(s, tim, save_in_globals); r = ParseTimeTrig(s, tim);
if (r) return r; if (r) return r;
trig->duration_days = ComputeTrigDuration(tim); trig->duration_days = ComputeTrigDuration(tim);
break; break;
case T_At: case T_At:
DBufFree(&buf); DBufFree(&buf);
r=ParseTimeTrig(s, tim, save_in_globals); r=ParseTimeTrig(s, tim);
if (r) return r; if (r) return r;
trig->duration_days = ComputeTrigDuration(tim); trig->duration_days = ComputeTrigDuration(tim);
break; break;
@@ -481,10 +473,10 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
r=ParseToken(s, &buf); r=ParseToken(s, &buf);
if (r) return r; if (r) return r;
StrnCpy(trig->omitfunc, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(trig->omitfunc, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->omitfunc);
/* An OMITFUNC counts as a nonconst_expr! */ /* An OMITFUNC counts as a nonconst_expr! */
s->expr_happened = 1; s->expr_happened = 1;
s->nonconst_expr = 1; s->nonconst_expr = 1;
DBufFree(&buf); DBufFree(&buf);
break; break;
@@ -492,6 +484,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
r=ParseToken(s, &buf); r=ParseToken(s, &buf);
if(r) return r; if(r) return r;
StrnCpy(trig->warn, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(trig->warn, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->warn);
DBufFree(&buf); DBufFree(&buf);
break; break;
@@ -522,9 +515,6 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
} else { } else {
tim->duration = NO_TIME; tim->duration = NO_TIME;
} }
if (save_in_globals) {
SaveLastTimeTrig(tim);
}
trig->duration_days = ComputeTrigDuration(tim); trig->duration_days = ComputeTrigDuration(tim);
break; break;
default: default:
@@ -536,6 +526,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
r=ParseToken(s, &buf); r=ParseToken(s, &buf);
if(r) return r; if(r) return r;
StrnCpy(trig->sched, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(trig->sched, DBufValue(&buf), VAR_NAME_LEN);
strtolower(trig->sched);
DBufFree(&buf); DBufFree(&buf);
break; break;
@@ -608,7 +599,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
/* ParseTimeTrig - parse the AT part of a timed reminder */ /* ParseTimeTrig - parse the AT part of a timed reminder */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int ParseTimeTrig(ParsePtr s, TimeTrig *tim, int save_in_globals) static int ParseTimeTrig(ParsePtr s, TimeTrig *tim)
{ {
Token tok; Token tok;
int r; int r;
@@ -643,11 +634,6 @@ static int ParseTimeTrig(ParsePtr s, TimeTrig *tim, int save_in_globals)
default: default:
if (tim->ttime == NO_TIME) return E_EXPECT_TIME; if (tim->ttime == NO_TIME) return E_EXPECT_TIME;
/* Save trigger time in global variable */
if (save_in_globals) {
LastTriggerTime = tim->ttime;
SaveLastTimeTrig(tim);
}
PushToken(DBufValue(&buf), s); PushToken(DBufValue(&buf), s);
DBufFree(&buf); DBufFree(&buf);
return OK; return OK;
@@ -679,6 +665,9 @@ static int ParseLocalOmit(ParsePtr s, Trigger *t)
break; break;
default: default:
if (t->localomit == NO_WD) {
return E_EXPECTING_WEEKDAY;
}
PushToken(DBufValue(&buf), s); PushToken(DBufValue(&buf), s);
DBufFree(&buf); DBufFree(&buf);
return OK; return OK;
@@ -873,6 +862,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:
@@ -910,7 +902,7 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
/* Trigger the reminder if it's a RUN or MSG type. */ /* Trigger the reminder if it's a RUN or MSG type. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued) int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output)
{ {
int r, y, m, d; int r, y, m, d;
char PrioExpr[VAR_NAME_LEN+25]; char PrioExpr[VAR_NAME_LEN+25];
@@ -977,7 +969,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
} }
/* If it's a MSG-type reminder, and no -k option was used, issue the banner. */ /* If it's a MSG-type reminder, and no -k option was used, issue the banner. */
if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE) if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE)
&& !DidMsgReminder && !NextMode && !msg_command) { && !DidMsgReminder && !NextMode && !msg_command && !is_queued) {
DidMsgReminder = 1; DidMsgReminder = 1;
if (!DoSubstFromString(DBufValue(&Banner), &buf, if (!DoSubstFromString(DBufValue(&Banner), &buf,
DSEToday, NO_TIME) && DSEToday, NO_TIME) &&
@@ -1044,11 +1036,18 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
return E_NO_MEM; return E_NO_MEM;
} }
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf)); r = OK;
if (output) {
if (DBufPuts(output, DBufValue(&calRow)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&pre_buf)) != OK) r = E_NO_MEM;
if (DBufPuts(output, DBufValue(&buf)) != OK) r = E_NO_MEM;
} else {
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf));
}
DBufFree(&buf); DBufFree(&buf);
DBufFree(&pre_buf); DBufFree(&pre_buf);
DBufFree(&calRow); DBufFree(&calRow);
return OK; return r;
} }
/* Correct colors */ /* Correct colors */
@@ -1144,23 +1143,27 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queue
case MSG_TYPE: case MSG_TYPE:
case PASSTHRU_TYPE: case PASSTHRU_TYPE:
if (msg_command) { if (msg_command) {
DoMsgCommand(msg_command, DBufValue(&buf)); DoMsgCommand(msg_command, DBufValue(&buf), is_queued);
} else { } else {
/* Add a space before "NOTE endreminder" */ if (output) {
if (IsServerMode() && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) { DBufPuts(output, DBufValue(&buf));
printf(" %s", DBufValue(&buf));
} else { } else {
printf("%s", DBufValue(&buf)); /* Add a space before "NOTE endreminder" */
if (IsServerMode() && !strncmp(DBufValue(&buf), "NOTE endreminder", 16)) {
printf(" %s", DBufValue(&buf));
} else {
printf("%s", DBufValue(&buf));
}
} }
} }
break; break;
case MSF_TYPE: case MSF_TYPE:
FillParagraph(DBufValue(&buf)); FillParagraph(DBufValue(&buf), output);
break; break;
case RUN_TYPE: case RUN_TYPE:
System(DBufValue(&buf)); System(DBufValue(&buf), is_queued);
break; break;
default: /* Unknown/illegal type? */ default: /* Unknown/illegal type? */
@@ -1188,7 +1191,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int dse, int *err)
*err = 0; *err = 0;
/* Handle the ONCE modifier in the reminder. */ /* Handle the ONCE modifier in the reminder. */
if (!IgnoreOnce && t->once !=NO_ONCE && FileAccessDate == DSEToday) if (!IgnoreOnce && t->once !=NO_ONCE && GetOnceDate() == DSEToday)
return 0; return 0;
if (dse < DSEToday) return 0; if (dse < DSEToday) return 0;
@@ -1270,20 +1273,28 @@ 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;
t = p->pos; sat_node = ParseExpr(p, &r);
if (r != OK) {
return r;
}
if (!sat_node) {
return E_SWERR;
}
iter = 0; iter = 0;
start = trig->scanfrom; start = trig->scanfrom;
while (iter++ < MaxSatIter) { while (iter++ < MaxSatIter) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0); dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0);
if (r) { if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r; if (r == E_CANT_TRIG) return OK; else return r;
} }
if (dse != start && trig->duration_days) { if (dse != start && trig->duration_days) {
dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days); dse = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days);
if (r) { if (r) {
free_expr_tree(sat_node);
if (r == E_CANT_TRIG) return OK; else return r; if (r == E_CANT_TRIG) return OK; else return r;
} }
} else if (dse == start) { } else if (dse == start) {
@@ -1296,13 +1307,18 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
SaveAllTriggerInfo(trig, tt, dse, tt->ttime, 1); SaveAllTriggerInfo(trig, tt, dse, tt->ttime, 1);
} }
if (dse == -1) { if (dse == -1) {
free_expr_tree(sat_node);
return E_EXPIRED; return E_EXPIRED;
} }
s = p->pos; r = evaluate_expression(sat_node, NULL, &v, &nonconst);
r = EvaluateExpr(p, &v); if (r) {
t = p->pos; free_expr_tree(sat_node);
if (r) return r; return r;
if (v.type != INT_TYPE && v.type != STR_TYPE) return E_BAD_TYPE; }
if (v.type != INT_TYPE && v.type != STR_TYPE) {
free_expr_tree(sat_node);
return E_BAD_TYPE;
}
if ((v.type == INT_TYPE && v.v.val) || if ((v.type == INT_TYPE && v.v.val) ||
(v.type == STR_TYPE && *v.v.str)) { (v.type == STR_TYPE && *v.v.str)) {
AdjustTriggerForDuration(trig->scanfrom, dse, trig, tt, 1); AdjustTriggerForDuration(trig->scanfrom, dse, trig, tt, 1);
@@ -1327,17 +1343,17 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
} }
fprintf(ErrFp, "\n"); fprintf(ErrFp, "\n");
} }
free_expr_tree(sat_node);
return OK; return OK;
} }
p->pos = s;
if (dse+trig->duration_days < start) { if (dse+trig->duration_days < start) {
start++; start++;
} else { } else {
start = dse+trig->duration_days+1; start = dse+trig->duration_days+1;
} }
} }
p->pos = t;
LastTrigValid = 0; LastTrigValid = 0;
free_expr_tree(sat_node);
return E_CANT_TRIG; return E_CANT_TRIG;
} }
@@ -1387,7 +1403,7 @@ static int ParsePriority(ParsePtr s, Trigger *t)
/* Execute the '-k' command, escaping shell chars in message. */ /* Execute the '-k' command, escaping shell chars in message. */
/* */ /* */
/***************************************************************/ /***************************************************************/
int DoMsgCommand(char const *cmd, char const *msg) int DoMsgCommand(char const *cmd, char const *msg, int is_queued)
{ {
int r; int r;
int i, l; int i, l;
@@ -1424,7 +1440,7 @@ int DoMsgCommand(char const *cmd, char const *msg)
} }
r = OK; r = OK;
System(DBufValue(&execBuffer)); System(DBufValue(&execBuffer), is_queued);
finished: finished:
DBufFree(&buf); DBufFree(&buf);

View File

@@ -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"
@@ -50,7 +48,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
int err, done; int err, done;
int c; int c;
int d, m, y; int d, m, y;
int tim = tt->ttime; int tim = NO_TIME;
int h, min, hh, ch, cmin, chh; int h, min, hh, ch, cmin, chh;
int i; int i;
char const *pm, *cpm; char const *pm, *cpm;
@@ -72,6 +70,9 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
FromDSE(dse, &y, &m, &d); FromDSE(dse, &y, &m, &d);
if (tt) {
tim = tt->ttime;
}
if (tim == NO_TIME) tim = curtime; if (tim == NO_TIME) tim = curtime;
tdiff = tim - curtime; tdiff = tim - curtime;
adiff = ABS(tdiff); adiff = ABS(tdiff);
@@ -139,6 +140,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]);
} }
@@ -163,6 +165,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse,
} else { } else {
r = -1; r = -1;
} }
DestroyValue(v);
} else { } else {
Eprint("%s", ErrMsg[r]); Eprint("%s", ErrMsg[r]);
} }
@@ -239,7 +242,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++;
} }
@@ -266,10 +269,10 @@ 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) { if (UserFuncExists(uf) == 3) {
snprintf(s, sizeof(s), "subst_%c(%d,'%04d-%02d-%02d',%02d:%02d)", snprintf(s, sizeof(s), "subst_%c(%d,'%04d-%02d-%02d',%02d:%02d)",
c, altmode ? 1 : 0, y, m+1, d, h, min); tolower(c), altmode ? 1 : 0, y, m+1, d, h, min);
expr = (char const *) s; expr = (char const *) s;
r = EvalExpr(&expr, &v, NULL); r = EvalExpr(&expr, &v, NULL);
if (r == OK) { if (r == OK) {
@@ -341,10 +344,10 @@ 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) { if (UserFuncExists(uf) == 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) {

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

217
src/err.h
View File

@@ -121,6 +121,11 @@
#define E_STRING_TOO_LONG 101 #define E_STRING_TOO_LONG 101
#define E_TIME_TWICE 102 #define E_TIME_TWICE 102
#define E_DURATION_NO_AT 103 #define E_DURATION_NO_AT 103
#define E_EXPECTING_WEEKDAY 104
#define E_REPEATED_ARG 105
#define E_EXPR_DISABLED 106
#define E_TIME_EXCEEDED 107
#ifdef MK_GLOBALS #ifdef MK_GLOBALS
#undef EXTERN #undef EXTERN
#define EXTERN #define EXTERN
@@ -138,110 +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 needs odd number of arguments",
"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 */ "Bad function in WARN clause",
"Can't convert between time zones", /* E_CANT_CONVERT_TZ */ "Can't convert between time zones",
"No files matching *.rem", /* E_NO_MATCHING_REMS */ "No files matching *.rem",
"String too long", /* E_STRING_TOO_LONG */ "String too long",
"Time specified twice", /* E_TIME_TWICE */ "Time specified twice",
"Cannot specify DURATION without specifying AT" /* E_DURATION_NO_AT */ "Cannot specify DURATION without specifying AT",
/* E_EXPECTING_WEEKDAY */ "Expecting weekday name",
/* E_REPEATED_ARG */ "Duplicate argument name",
/* E_EXPR_DISABLED */ "Expression evaluation is disabled",
/* E_TIME_EXCEEDED */ "Time limit for expression evaluation exceeded",
} }
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
; ;

3697
src/expr.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +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) \
if (OpStackPtr >= OP_STACK_SIZE) \
return E_OP_STK_OVER; \
else \
OpStack[OpStackPtr++] = (op)
#define PopOpStack(op) \
if (OpStackPtr <= 0) \
return E_OP_STK_UNDER; \
else \
(op) = OpStack[--OpStackPtr]
#define PushValStack(val) \
if (ValStackPtr >= VAL_STACK_SIZE) \
return E_VA_STK_OVER; \
else \
ValStack[ValStackPtr++] = (val)
#define PopValStack(val) \
if (ValStackPtr <= 0) \
return E_VA_STK_UNDER; \
else \
(val) = ValStack[--ValStackPtr]
/* These functions are in utils.c and are used to detect overflow
in various arithmetic operators. They have to be in separate
functions with extern linkage to defeat compiler optimizations
that would otherwise break the overflow checks. */
extern int _private_mul_overflow(int a, int b);
extern int _private_add_overflow(int a, int b);
extern int _private_sub_overflow(int a, int b);

View File

@@ -15,7 +15,7 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <fcntl.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
@@ -42,8 +42,8 @@
/* Convenient macros for closing files */ /* Convenient macros for closing files */
#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL)) #define FCLOSE(fp) ((((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
#define PCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL)) #define PCLOSE(fp) ((((fp)!=stdin)) ? (pclose(fp),(fp)=NULL) : ((fp)=NULL))
/* Define the structures needed by the file caching system */ /* Define the structures needed by the file caching system */
typedef struct cache { typedef struct cache {
@@ -79,6 +79,7 @@ typedef struct {
int LineNo; int LineNo;
unsigned int IfFlags; unsigned int IfFlags;
int NumIfs; int NumIfs;
int IfLinenos[IF_NEST];
long offset; long offset;
CachedLine *CLine; CachedLine *CLine;
int ownedByMe; int ownedByMe;
@@ -100,6 +101,22 @@ static int CheckSafety (void);
static int CheckSafetyAux (struct stat *statbuf); static int CheckSafetyAux (struct stat *statbuf);
static int PopFile (void); static int PopFile (void);
static int IncludeCmd(char const *); static int IncludeCmd(char const *);
void set_cloexec(FILE *fp)
{
int flags;
int fd;
if (fp) {
fd = fileno(fp);
flags = fcntl(fd, F_GETFD);
if (flags >= 0) {
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
}
}
}
static void OpenPurgeFile(char const *fname, char const *mode) static void OpenPurgeFile(char const *fname, char const *mode)
{ {
DynamicBuffer fname_buf; DynamicBuffer fname_buf;
@@ -123,6 +140,7 @@ static void OpenPurgeFile(char const *fname, char const *mode)
if (!PurgeFP) { if (!PurgeFP) {
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno)); fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
} }
set_cloexec(PurgeFP);
DBufFree(&fname_buf); DBufFree(&fname_buf);
} }
@@ -327,6 +345,7 @@ int OpenFile(char const *fname)
} }
} else { } else {
fp = fopen(fname, "r"); fp = fopen(fname, "r");
set_cloexec(fp);
if (DebugFlag & DB_TRACE_FILES) { if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname); fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
} }
@@ -346,6 +365,7 @@ int OpenFile(char const *fname)
if (strcmp(fname, "-")) { if (strcmp(fname, "-")) {
fp = fopen(fname, "r"); fp = fopen(fname, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN; if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fp);
if (PurgeMode) OpenPurgeFile(fname, "w"); if (PurgeMode) OpenPurgeFile(fname, "w");
} else { } else {
fp = stdin; fp = stdin;
@@ -507,8 +527,14 @@ static int NextChainedFile(IncludeStruct *i)
static int PopFile(void) static int PopFile(void)
{ {
IncludeStruct *i; IncludeStruct *i;
int j;
if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]); if (!Hush && NumIfs) {
Eprint("%s", ErrMsg[E_MISS_ENDIF]);
for (j=NumIfs-1; j >=0; j--) {
fprintf(ErrFp, "%s(%d): IF without ENDIF\n", FileName, IfLinenos[j]);
}
}
if (!IStackPtr) return E_EOF; if (!IStackPtr) return E_EOF;
i = &IStack[IStackPtr-1]; i = &IStack[IStackPtr-1];
@@ -528,6 +554,7 @@ static int PopFile(void)
LineNo = i->LineNo; LineNo = i->LineNo;
IfFlags = i->IfFlags; IfFlags = i->IfFlags;
memcpy(IfLinenos, i->IfLinenos, IF_NEST);
NumIfs = i->NumIfs; NumIfs = i->NumIfs;
CLine = i->CLine; CLine = i->CLine;
fp = NULL; fp = NULL;
@@ -542,6 +569,7 @@ static int PopFile(void)
if (strcmp(i->filename, "-")) { if (strcmp(i->filename, "-")) {
fp = fopen(i->filename, "r"); fp = fopen(i->filename, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN; if (!fp || !CheckSafety()) return E_CANT_OPEN;
set_cloexec(fp);
if (PurgeMode) OpenPurgeFile(i->filename, "a"); if (PurgeMode) OpenPurgeFile(i->filename, "a");
} else { } else {
fp = stdin; fp = stdin;
@@ -851,6 +879,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;
@@ -953,6 +982,7 @@ int IncludeFile(char const *fname)
i->LineNo = LineNo; i->LineNo = LineNo;
i->NumIfs = NumIfs; i->NumIfs = NumIfs;
i->IfFlags = IfFlags; i->IfFlags = IfFlags;
memcpy(i->IfLinenos, IfLinenos, IF_NEST);
i->CLine = CLine; i->CLine = CLine;
i->offset = -1L; i->offset = -1L;
i->chain = NULL; i->chain = NULL;

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@
#define INIT(var, val) var #define INIT(var, val) var
#endif #endif
#include <signal.h>
#ifdef HAVE_SYS_TYPES_H #ifdef HAVE_SYS_TYPES_H
#include <sys/types.h> #include <sys/types.h>
#endif #endif
@@ -39,7 +40,7 @@ EXTERN FILE *ErrFp;
#define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 )) #define IsLeapYear(y) (((y) % 4) ? 0 : ((!((y) % 100) && ((y) % 400)) ? 0 : 1 ))
#define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y)) #define DaysInMonth(m, y) ((m) != 1 ? MonthDays[m] : 28 + IsLeapYear(y))
#define DestroyValue(x) (void) (((x).type == STR_TYPE && (x).v.str) ? (free((x).v.str),(x).v.str = NULL,(x).type = ERR_TYPE) : 0) #define DestroyValue(x) do { if ((x).type == STR_TYPE && (x).v.str) { free((x).v.str); (x).v.str = NULL; } (x).type = ERR_TYPE; } while (0)
EXTERN int DSEToday; EXTERN int DSEToday;
EXTERN int RealToday; EXTERN int RealToday;
@@ -73,10 +74,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);
@@ -92,6 +99,7 @@ EXTERN INIT( int DontQueue, 0);
EXTERN INIT( int NumQueued, 0); EXTERN INIT( int NumQueued, 0);
EXTERN INIT( int DontIssueAts, 0); EXTERN INIT( int DontIssueAts, 0);
EXTERN INIT( int Daemon, 0); EXTERN INIT( int Daemon, 0);
EXTERN INIT( int DaemonJSON, 0);
EXTERN INIT( char DateSep, DATESEP); EXTERN INIT( char DateSep, DATESEP);
EXTERN INIT( char TimeSep, TIMESEP); EXTERN INIT( char TimeSep, TIMESEP);
EXTERN INIT( char DateTimeSep, DATETIMESEP); EXTERN INIT( char DateTimeSep, DATETIMESEP);
@@ -109,11 +117,12 @@ EXTERN INIT( int PurgeIncludeDepth, 0);
EXTERN INIT( FILE *PurgeFP, NULL); EXTERN INIT( FILE *PurgeFP, NULL);
EXTERN INIT( int NumIfs, 0); EXTERN INIT( int NumIfs, 0);
EXTERN INIT( unsigned int IfFlags, 0); EXTERN INIT( unsigned int IfFlags, 0);
EXTERN INIT( int IfLinenos[IF_NEST], {0});
EXTERN INIT( int LastTrigValid, 0); EXTERN INIT( int LastTrigValid, 0);
EXTERN Trigger LastTrigger; EXTERN Trigger LastTrigger;
EXTERN TimeTrig LastTimeTrig; EXTERN TimeTrig LastTimeTrig;
EXTERN INIT( int LastTriggerDate, 0); EXTERN INIT( int LastTriggerDate, 0);
EXTERN INIT( int LastTriggerTime, 0); EXTERN INIT( int LastTriggerTime, NO_TIME);
EXTERN INIT( int ShouldCache, 0); EXTERN INIT( int ShouldCache, 0);
EXTERN char const *CurLine; EXTERN char const *CurLine;
EXTERN INIT( int NumTriggered, 0); EXTERN INIT( int NumTriggered, 0);
@@ -158,6 +167,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 */

View File

@@ -20,6 +20,12 @@
#include "protos.h" #include "protos.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#define HOUR 1080L #define HOUR 1080L
#define DAY (24L*HOUR) #define DAY (24L*HOUR)
#define WEEK (7L*DAY) #define WEEK (7L*DAY)

View File

@@ -19,7 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <time.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -38,7 +38,6 @@
#include "types.h" #include "types.h"
#include "globals.h" #include "globals.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#include "err.h" #include "err.h"
static int should_guess_terminal_background = 1; static int should_guess_terminal_background = 1;
@@ -78,6 +77,7 @@ static void ProcessLongOption(char const *arg);
* t = Display trigger dates * t = Display trigger dates
* v = Dump variables at end * v = Dump variables at end
* l = Display entire line in error messages * l = Display entire line in error messages
* s = Display expression-parsing stack usage before exit
* -e = Send messages normally sent to stderr to stdout instead * -e = Send messages normally sent to stderr to stdout instead
* -z[n] = Daemon mode waking up every n (def 1) minutes. * -z[n] = Daemon mode waking up every n (def 1) minutes.
* -bn = Time format for cal (0, 1, or 2) * -bn = Time format for cal (0, 1, or 2)
@@ -241,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;
} }
@@ -324,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':
@@ -425,7 +425,11 @@ void InitRemind(int argc, char const *argv[])
case 'z': case 'z':
case 'Z': case 'Z':
DontFork = 1; DontFork = 1;
if (*arg == '0') { if (*arg == 'j' || *arg == 'J') {
while (*arg) arg++;
Daemon = -1;
DaemonJSON = 1;
} else if (*arg == '0') {
PARSENUM(Daemon, arg); PARSENUM(Daemon, arg);
if (Daemon == 0) Daemon = -1; if (Daemon == 0) Daemon = -1;
else if (Daemon < 1) Daemon = 1; else if (Daemon < 1) Daemon = 1;
@@ -453,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 */
@@ -497,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') {
@@ -523,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' ||
@@ -600,6 +607,7 @@ void InitRemind(int argc, char const *argv[])
case 'D': case 'D':
while (*arg) { while (*arg) {
switch(*arg++) { switch(*arg++) {
case 's': case 'S': DebugFlag |= DB_PARSE_EXPR; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break; case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break; case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break; case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
@@ -715,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;
} }
@@ -800,6 +809,7 @@ void Usage(void)
fprintf(ErrFp, " -m Start calendar with Monday rather than Sunday\n"); fprintf(ErrFp, " -m Start calendar with Monday rather than Sunday\n");
fprintf(ErrFp, " -y Synthesize tags for tagless reminders\n"); fprintf(ErrFp, " -y Synthesize tags for tagless reminders\n");
fprintf(ErrFp, " -j[n] Run in 'purge' mode. [n = INCLUDE depth]\n"); fprintf(ErrFp, " -j[n] Run in 'purge' mode. [n = INCLUDE depth]\n");
fprintf(ErrFp, "\nRemind home page: %s\n", PACKAGE_URL);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#endif /* L_USAGE_OVERRIDE */ #endif /* L_USAGE_OVERRIDE */
@@ -919,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) {
varname[r++] = *str; if (isalpha(*str) || *str == '_' || (r > 0 && *str == '(') || (r == 0 && *str == '$') || (r > 0 && isdigit(*str))) {
varname[r++] = *str;
} else {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_ILLEGAL_CHAR]);
return;
}
} }
if (*str == '(') { if (*str == '(') {
/* Do a function definition if we see a paren */ /* Do a function definition if we see a paren */
@@ -929,10 +944,28 @@ static void InitializeVar(char const *str)
str++; str++;
} }
varname[r] = 0; varname[r] = 0;
if (!*str) { if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]); fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return; return;
} }
if (!*str) {
/* Setting a system var does require =expr on the commandline */
if (*varname == '$') {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]);
return;
}
val.type = INT_TYPE;
val.v.val = 0;
r = SetVar(varname, &val);
if (!r) {
r = PreserveVar(varname);
}
if (r) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]);
}
return;
}
if (!*varname) { if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]); fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return; return;
@@ -985,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);
} }
@@ -1000,7 +1085,7 @@ guess_terminal_background(int *r, int *g, int *b)
{ {
int ttyfd; int ttyfd;
struct pollfd p; struct pollfd p;
int rr, gg, bb; unsigned int rr, gg, bb;
char buf[128]; char buf[128];
int n; int n;
@@ -1066,9 +1151,9 @@ guess_terminal_background(int *r, int *g, int *b)
/* Couldn't scan color codes */ /* Couldn't scan color codes */
return; return;
} }
*r = (rr >> 8) & 255; *r = (int) ((rr >> 8) & 255);
*g = (gg >> 8) & 255; *g = (int) ((gg >> 8) & 255);
*b = (bb >> 8) & 255; *b = (int) ((bb >> 8) & 255);
} }
static struct termios orig_termios; static struct termios orig_termios;

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",
@@ -245,7 +245,11 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Odotettu viikonpäivän nimi",
"Päällekkäinen argumentin nimi",
"Lausekkeiden arviointi on poistettu käytöstä",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -271,12 +275,10 @@ void Usage(void)
fprintf(ErrFp, " -o Älä noudata ONCE-lauseita\n"); fprintf(ErrFp, " -o Älä noudata ONCE-lauseita\n");
fprintf(ErrFp, " -t Laukaise kaikki viestit deltan arvosta välittämättä\n"); fprintf(ErrFp, " -t Laukaise kaikki viestit deltan arvosta välittämättä\n");
fprintf(ErrFp, " -h Suppeat tulostukset\n"); fprintf(ErrFp, " -h Suppeat tulostukset\n");
#ifdef HAVE_QUEUED
fprintf(ErrFp, " -a Älä laukaise viestejä heti - lisää ne jonoon\n"); fprintf(ErrFp, " -a Älä laukaise viestejä heti - lisää ne jonoon\n");
fprintf(ErrFp, " -q Älä lisää viestejä jonoon\n"); fprintf(ErrFp, " -q Älä lisää viestejä jonoon\n");
fprintf(ErrFp, " -f Laukaise viestit, pysy etualalla\n"); fprintf(ErrFp, " -f Laukaise viestit, pysy etualalla\n");
fprintf(ErrFp, " -z[n] Käynnisty demonina, herätys n:n (5:n) minuutin välein\n"); fprintf(ErrFp, " -z[n] Käynnisty demonina, herätys n:n (5:n) minuutin välein\n");
#endif
fprintf(ErrFp, " -d... Virheenetsintä: e=echo x=expr-eval t=trig v=dumpvars l=showline\n"); fprintf(ErrFp, " -d... Virheenetsintä: e=echo x=expr-eval t=trig v=dumpvars l=showline\n");
fprintf(ErrFp, " -e Ohjaa virhetulostus stdout-vuohon\n"); fprintf(ErrFp, " -e Ohjaa virhetulostus stdout-vuohon\n");
fprintf(ErrFp, " -b[n] Ajan ilmaisu: 0=ap/ip, 1=24 tuntia, 2=ei aikoja\n"); fprintf(ErrFp, " -b[n] Ajan ilmaisu: 0=ap/ip, 1=24 tuntia, 2=ei aikoja\n");

View File

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

View File

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

View File

@@ -144,7 +144,7 @@ EXTERN char *ErrMsg[] =
"Ok", "Ok",
"Falta um ']'", "Falta um ']'",
"Falta uma aspa", "Falta uma aspa",
"Expressao muito complexa - muitos operadores", "Expressao muito complexa",
"Expressao muito complexa - muitos operandos", "Expressao muito complexa - muitos operandos",
"Falta um ')'", "Falta um ')'",
"Funcao nao definida", "Funcao nao definida",
@@ -244,7 +244,10 @@ EXTERN char *ErrMsg[] =
"No files matching *.rem", "No files matching *.rem",
"String too long", "String too long",
"Time specified twice", "Time specified twice",
"Cannot specify DURATION without specifying AT" "Cannot specify DURATION without specifying AT",
"Esperando nome do dia da semana",
"Nome de argumento duplicado",
"A avaliação da expressão está desabilitada",
}; };
#endif /* MK_GLOBALS */ #endif /* MK_GLOBALS */
@@ -270,12 +273,10 @@ void Usage(void)
fprintf(ErrFp, " -o Ignora diretivas ONCE\n"); fprintf(ErrFp, " -o Ignora diretivas ONCE\n");
fprintf(ErrFp, " -t Aciona todos os compromissos futuros, sem considerar o delta\n"); fprintf(ErrFp, " -t Aciona todos os compromissos futuros, sem considerar o delta\n");
fprintf(ErrFp, " -h Modo `Hush' - quieto\n"); fprintf(ErrFp, " -h Modo `Hush' - quieto\n");
#ifdef HAVE_QUEUED
fprintf(ErrFp, " -a Nao aciona compromissos com hora imediatamente - apenas coloca na fila\n"); fprintf(ErrFp, " -a Nao aciona compromissos com hora imediatamente - apenas coloca na fila\n");
fprintf(ErrFp, " -q Nao coloca compromissos com hora na fila\n"); fprintf(ErrFp, " -q Nao coloca compromissos com hora na fila\n");
fprintf(ErrFp, " -f Aciona compromissos com hora em modo foreground\n"); fprintf(ErrFp, " -f Aciona compromissos com hora em modo foreground\n");
fprintf(ErrFp, " -z[n] Modo `daemon', acordando a cada n (5) minutos.\n"); fprintf(ErrFp, " -z[n] Modo `daemon', acordando a cada n (5) minutos.\n");
#endif
fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trigger v=dumpvars l=showline\n"); fprintf(ErrFp, " -d... Debug: e=echo x=expr-eval t=trigger v=dumpvars l=showline\n");
fprintf(ErrFp, " -e Desvia mensagens normalmente enviadas a stderr para stdout\n"); fprintf(ErrFp, " -e Desvia mensagens normalmente enviadas a stderr para stdout\n");
fprintf(ErrFp, " -b[n] Formato da hora para o cal: 0=am/pm, 1=24hr, 2=nenhum\n"); fprintf(ErrFp, " -b[n] Formato da hora para o cal: 0=am/pm, 1=24hr, 2=nenhum\n");

View File

@@ -14,12 +14,19 @@
#define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE 600
#include "config.h" #include "config.h"
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <stdarg.h> #include <stdarg.h>
#ifdef HAVE_LOCALE_H #ifdef HAVE_LOCALE_H
#include <locale.h> #include <locale.h>
@@ -39,12 +46,46 @@
#include "types.h" #include "types.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
static void DoReminders(void); static void DoReminders(void);
/* Macro for simplifying common block so as not to litter code */
#define OUTPUT(c) do { if (output) { DBufPutc(output, c); } else { putchar(c); } } while(0)
void
exitfunc(void)
{
/* Kill any execution-time-limiter process */
unlimit_execution_time();
if (DebugFlag & DB_PARSE_EXPR) {
fflush(stdout);
fflush(stderr);
UnsetAllUserFuncs();
print_expr_nodes_stats();
}
}
static void sigalrm(int sig)
{
UNUSED(sig);
if (ExpressionEvaluationTimeLimit) {
ExpressionTimeLimitExceeded = 1;
}
}
static void sigxcpu(int sig)
{
UNUSED(sig);
int r = write(STDERR_FILENO, "\n\nmax-execution-time exceeded.\n\n", 32);
/* Pretend to use r to avoid compiler warning */
_exit(1 + (r-r));
}
/***************************************************************/ /***************************************************************/
/***************************************************************/ /***************************************************************/
/** **/ /** **/
@@ -56,6 +97,8 @@ int main(int argc, char *argv[])
{ {
int pid; int pid;
struct sigaction act;
#ifdef HAVE_SETLOCALE #ifdef HAVE_SETLOCALE
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
#endif #endif
@@ -68,9 +111,30 @@ int main(int argc, char *argv[])
ArgV = (char const **) argv; ArgV = (char const **) argv;
InitRemind(argc, (char const **) argv); InitRemind(argc, (char const **) argv);
act.sa_handler = sigalrm;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
if (sigaction(SIGALRM, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno));
exit(1);
}
act.sa_handler = sigxcpu;
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
if (sigaction(SIGXCPU, &act, NULL) < 0) {
fprintf(stderr, "%s: sigaction() failed: %s\n",
argv[0], strerror(errno));
exit(1);
}
DBufInit(&(LastTrigger.tags)); DBufInit(&(LastTrigger.tags));
ClearLastTriggers(); ClearLastTriggers();
atexit(exitfunc);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) { if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar(); ProduceCalendar();
return 0; return 0;
@@ -94,7 +158,7 @@ int main(int argc, char *argv[])
} }
if (!Hush) { if (!Hush) {
if (DestroyOmitContexts()) if (DestroyOmitContexts(1))
Eprint("%s", ErrMsg[E_PUSH_NOPOP]); Eprint("%s", ErrMsg[E_PUSH_NOPOP]);
if (!Daemon && !NextMode && !NumTriggered && !NumQueued) { if (!Daemon && !NextMode && !NumTriggered && !NumQueued) {
printf("%s\n", ErrMsg[E_NOREMINDERS]); printf("%s\n", ErrMsg[E_NOREMINDERS]);
@@ -147,7 +211,7 @@ void
PerIterationInit(void) PerIterationInit(void)
{ {
ClearGlobalOmits(); ClearGlobalOmits();
DestroyOmitContexts(); DestroyOmitContexts(1);
DestroyVars(0); DestroyVars(0);
DefaultColorR = -1; DefaultColorR = -1;
DefaultColorG = -1; DefaultColorG = -1;
@@ -176,7 +240,8 @@ static void DoReminders(void)
if (!UseStdin) { if (!UseStdin) {
FileAccessDate = GetAccessDate(InitialFile); FileAccessDate = GetAccessDate(InitialFile);
} else { } else {
FileAccessDate = DSEToday; FileAccessDate = DSEToday - 1;
if (FileAccessDate < 0) FileAccessDate = 0;
} }
if (FileAccessDate < 0) { if (FileAccessDate < 0) {
@@ -274,9 +339,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;
@@ -285,10 +352,8 @@ static void DoReminders(void)
/* If we don't recognize the command, do a REM by default */ /* If we don't recognize the command, do a REM by default */
/* 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: DestroyParser(&p); CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
} }
if (r && (!Hush || r != E_RUN_DISABLED)) { if (r && (!Hush || r != E_RUN_DISABLED)) {
@@ -446,6 +511,16 @@ int ParseChar(ParsePtr p, int *err, int peek)
return *(p->pos++); return *(p->pos++);
} }
} }
/* Convert [[ to just a literal [ */
if (*p->pos == BEG_OF_EXPR && *(p->pos+1) == BEG_OF_EXPR) {
if (peek) {
return *(p->pos+1);
} else {
p->pos++;
return *(p->pos++);
}
}
p->expr_happened = 1; p->expr_happened = 1;
p->pos++; p->pos++;
r = EvalExpr(&(p->pos), &val, p); r = EvalExpr(&(p->pos), &val, p);
@@ -454,8 +529,15 @@ int ParseChar(ParsePtr p, int *err, int peek)
DestroyParser(p); DestroyParser(p);
return 0; return 0;
} }
while(*p->pos && (isempty(*p->pos))) {
p->pos++;
}
if (*p->pos != END_OF_EXPR) { if (*p->pos != END_OF_EXPR) {
*err = E_MISS_END; if (*p->pos) {
*err = E_PARSE_ERR;
} else {
*err = E_MISS_END;
}
DestroyParser(p); DestroyParser(p);
DestroyValue(val); DestroyValue(val);
return 0; return 0;
@@ -570,6 +652,57 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
} }
} }
/***************************************************************/
/* */
/* ParseExpr */
/* */
/* We are expecting an expression here. Parse it and return */
/* the value node tree. */
/* */
/***************************************************************/
expr_node * ParseExpr(ParsePtr p, int *r)
{
int bracketed = 0;
expr_node *node;
if (p->isnested) {
*r = E_PARSE_ERR; /* Can't nest expressions */
return NULL;
}
if (!p->pos) {
*r = E_PARSE_ERR; /* Missing expression */
return NULL;
}
while (isempty(*p->pos)) (p->pos)++;
if (!*(p->pos)) {
*r = E_EOLN;
return NULL;
}
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
}
node = parse_expression(&(p->pos), r, NULL);
if (*r) {
return free_expr_tree(node);
}
if (bracketed) {
if (*p->pos != END_OF_EXPR) {
if (*p->pos) {
*r = E_PARSE_ERR;
} else {
*r = E_MISS_END;
}
return free_expr_tree(node);
}
(p->pos)++;
}
return node;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* EvaluateExpr */ /* EvaluateExpr */
@@ -581,21 +714,22 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
int EvaluateExpr(ParsePtr p, Value *v) int EvaluateExpr(ParsePtr p, Value *v)
{ {
int bracketed = 0;
int r; int r;
int nonconst = 0;
expr_node *node = ParseExpr(p, &r);
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */ if (r != OK) {
while (isempty(*p->pos)) (p->pos)++; return r;
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
} }
r = EvalExpr(&(p->pos), v, p); if (!node) {
return E_SWERR;
}
r = evaluate_expression(node, NULL, v, &nonconst);
free_expr_tree(node);
if (r) return r; if (r) return r;
if (bracketed) { if (nonconst) {
if (*p->pos != END_OF_EXPR) return E_MISS_END; p->nonconst_expr = 1;
(p->pos)++;
} }
return OK; return OK;
} }
@@ -631,36 +765,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;
} }
/***************************************************************/ /***************************************************************/
@@ -829,6 +963,7 @@ int DoIf(ParsePtr p)
} }
} }
IfLinenos[NumIfs] = LineNo;
NumIfs++; NumIfs++;
IfFlags &= ~(IF_MASK << (2*NumIfs - 2)); IfFlags &= ~(IF_MASK << (2*NumIfs - 2));
IfFlags |= syndrome << (2 * NumIfs - 2); IfFlags |= syndrome << (2 * NumIfs - 2);
@@ -893,7 +1028,7 @@ int DoIfTrig(ParsePtr p)
if ((size_t) NumIfs >= IF_NEST) return E_NESTED_IF; if ((size_t) NumIfs >= IF_NEST) return E_NESTED_IF;
if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE; if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE;
else { else {
if ( (r=ParseRem(p, &trig, &tim, 1)) ) return r; if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if (trig.typ != NO_TYPE) return E_PARSE_ERR; if (trig.typ != NO_TYPE) return E_PARSE_ERR;
dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1); dse = ComputeTrigger(trig.scanfrom, &trig, &tim, &r, 1);
if (r) { if (r) {
@@ -1011,6 +1146,12 @@ int DoDebug(ParsePtr p)
else DebugFlag &= ~DB_ECHO_LINE; else DebugFlag &= ~DB_ECHO_LINE;
break; break;
case 's':
case 'S':
if (val) DebugFlag |= DB_PARSE_EXPR;
else DebugFlag &= ~DB_PARSE_EXPR;
break;
case 'x': case 'x':
case 'X': case 'X':
if (val) DebugFlag |= DB_PRTEXPR; if (val) DebugFlag |= DB_PRTEXPR;
@@ -1076,7 +1217,7 @@ int DoBanner(ParsePtr p)
} }
} }
DBufFree(&Banner); DBufFree(&Banner);
err = DBufPuts(&Banner, DBufValue(&buf)); err = DBufPuts(&Banner, DBufValue(&buf));
DBufFree(&buf); DBufFree(&buf);
return err; return err;
@@ -1088,7 +1229,6 @@ int DoBanner(ParsePtr p)
/* */ /* */
/* Enable or disable the RUN command under program control */ /* Enable or disable the RUN command under program control */
/* */ /* */
/* */
/***************************************************************/ /***************************************************************/
int DoRun(ParsePtr p) int DoRun(ParsePtr p)
{ {
@@ -1115,6 +1255,38 @@ int DoRun(ParsePtr p)
return VerifyEoln(p); return VerifyEoln(p);
} }
/***************************************************************/
/* */
/* DoExpr */
/* */
/* Enable or disable expression evaluation */
/* */
/***************************************************************/
int DoExpr(ParsePtr p)
{
int r;
DynamicBuffer buf;
DBufInit(&buf);
if ( (r=ParseToken(p, &buf)) ) return r;
/* Only allow EXPR ON in top-level script */
if (! StrCmpi(DBufValue(&buf), "ON")) {
if (TopLevel()) ExpressionEvaluationDisabled = 0;
}
/* But allow EXPR OFF anywhere */
else if (! StrCmpi(DBufValue(&buf), "OFF"))
ExpressionEvaluationDisabled = 1;
else {
DBufFree(&buf);
return E_PARSE_ERR;
}
DBufFree(&buf);
return VerifyEoln(p);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DoFlush */ /* DoFlush */
@@ -1251,19 +1423,19 @@ int CalcMinsFromUTC(int dse, int tim, int *mins, int *isdst)
return 0; return 0;
} }
static char const *OutputEscapeSequences(char const *s, int print) static char const *OutputEscapeSequences(char const *s, int print, DynamicBuffer *output)
{ {
while (*s == 0x1B && *(s+1) == '[') { while (*s == 0x1B && *(s+1) == '[') {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
while (*s && (*s < 0x40 || *s > 0x7E)) { while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
} }
if (*s) { if (*s) {
if (print) putchar(*s); if (print) OUTPUT(*s);
s++; s++;
} }
} }
@@ -1272,19 +1444,19 @@ static char const *OutputEscapeSequences(char const *s, int print)
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
#define ISWBLANK(c) (iswspace(c) && (c) != '\n') #define ISWBLANK(c) (iswspace(c) && (c) != '\n')
static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print) static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print, DynamicBuffer *output)
{ {
while (*s == 0x1B && *(s+1) == '[') { while (*s == 0x1B && *(s+1) == '[') {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
while (*s && (*s < 0x40 || *s > 0x7E)) { while (*s && (*s < 0x40 || *s > 0x7E)) {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
} }
if (*s) { if (*s) {
if (print) PutWideChar(*s); if (print) PutWideChar(*s, output);
s++; s++;
} }
} }
@@ -1293,7 +1465,7 @@ static wchar_t const *OutputEscapeSequencesWS(wchar_t const *s, int print)
static void static void
FillParagraphWCAux(wchar_t const *s) FillParagraphWCAux(wchar_t const *s, DynamicBuffer *output)
{ {
int line = 0; int line = 0;
int i, j; int i, j;
@@ -1308,7 +1480,7 @@ FillParagraphWCAux(wchar_t const *s)
/* If it's a carriage return, output it and start new paragraph */ /* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') { if (*s == '\n') {
putchar('\n'); OUTPUT('\n');
s++; s++;
line = 0; line = 0;
while(ISWBLANK(*s)) s++; while(ISWBLANK(*s)) s++;
@@ -1321,7 +1493,7 @@ FillParagraphWCAux(wchar_t const *s)
number of spaces */ number of spaces */
j = line ? SubsIndent : FirstIndent; j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) { for (i=0; i<j; i++) {
putchar(' '); OUTPUT(' ');
} }
/* Calculate the amount of room left on this line */ /* Calculate the amount of room left on this line */
@@ -1334,7 +1506,7 @@ FillParagraphWCAux(wchar_t const *s)
if (*s == '\n') break; if (*s == '\n') break;
while(1) { while(1) {
t = s; t = s;
s = OutputEscapeSequencesWS(s, 1); s = OutputEscapeSequencesWS(s, 1, output);
if (s == t) break; if (s == t) break;
while(ISWBLANK(*s)) s++; while(ISWBLANK(*s)) s++;
} }
@@ -1342,7 +1514,7 @@ FillParagraphWCAux(wchar_t const *s)
len = 0; len = 0;
while(*s && !iswspace(*s)) { while(*s && !iswspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') { if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequencesWS(s, 0); s = OutputEscapeSequencesWS(s, 0, output);
continue; continue;
} }
len += wcwidth(*s); len += wcwidth(*s);
@@ -1353,17 +1525,17 @@ FillParagraphWCAux(wchar_t const *s)
} }
if (!pendspace || len+pendspace <= roomleft) { if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) { for (i=0; i<pendspace; i++) {
putchar(' '); OUTPUT(' ');
} }
while(t < s) { while(t < s) {
PutWideChar(*t); PutWideChar(*t, output);
if (strchr(EndSent, *t)) doublespace = 2; if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1; else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++; t++;
} }
} else { } else {
s = t; s = t;
putchar('\n'); OUTPUT('\n');
line++; line++;
break; break;
} }
@@ -1374,7 +1546,7 @@ FillParagraphWCAux(wchar_t const *s)
} }
static int static int
FillParagraphWC(char const *s) FillParagraphWC(char const *s, DynamicBuffer *output)
{ {
size_t len; size_t len;
wchar_t *buf; wchar_t *buf;
@@ -1384,7 +1556,7 @@ FillParagraphWC(char const *s)
buf = calloc(len+1, sizeof(wchar_t)); buf = calloc(len+1, sizeof(wchar_t));
if (!buf) return E_NO_MEM; if (!buf) return E_NO_MEM;
(void) mbstowcs(buf, s, len+1); (void) mbstowcs(buf, s, len+1);
FillParagraphWCAux(buf); FillParagraphWCAux(buf, output);
free(buf); free(buf);
return OK; return OK;
} }
@@ -1404,7 +1576,7 @@ FillParagraphWC(char const *s)
/* A macro safe ONLY if used with arg with no side effects! */ /* A macro safe ONLY if used with arg with no side effects! */
#define ISBLANK(c) (isspace(c) && (c) != '\n') #define ISBLANK(c) (isspace(c) && (c) != '\n')
void FillParagraph(char const *s) void FillParagraph(char const *s, DynamicBuffer *output)
{ {
int line = 0; int line = 0;
@@ -1422,7 +1594,7 @@ void FillParagraph(char const *s)
if (!*s) return; if (!*s) return;
#ifdef REM_USE_WCHAR #ifdef REM_USE_WCHAR
if (FillParagraphWC(s) == OK) { if (FillParagraphWC(s, output) == OK) {
return; return;
} }
#endif #endif
@@ -1432,7 +1604,7 @@ void FillParagraph(char const *s)
/* If it's a carriage return, output it and start new paragraph */ /* If it's a carriage return, output it and start new paragraph */
if (*s == '\n') { if (*s == '\n') {
putchar('\n'); OUTPUT('\n');
s++; s++;
line = 0; line = 0;
while(ISBLANK(*s)) s++; while(ISBLANK(*s)) s++;
@@ -1445,7 +1617,7 @@ void FillParagraph(char const *s)
number of spaces */ number of spaces */
j = line ? SubsIndent : FirstIndent; j = line ? SubsIndent : FirstIndent;
for (i=0; i<j; i++) { for (i=0; i<j; i++) {
putchar(' '); OUTPUT(' ');
} }
/* Calculate the amount of room left on this line */ /* Calculate the amount of room left on this line */
@@ -1458,7 +1630,7 @@ void FillParagraph(char const *s)
if (*s == '\n') break; if (*s == '\n') break;
while(1) { while(1) {
t = s; t = s;
s = OutputEscapeSequences(s, 1); s = OutputEscapeSequences(s, 1, output);
if (s == t) break; if (s == t) break;
while(ISBLANK(*s)) s++; while(ISBLANK(*s)) s++;
} }
@@ -1466,7 +1638,7 @@ void FillParagraph(char const *s)
len = 0; len = 0;
while(*s && !isspace(*s)) { while(*s && !isspace(*s)) {
if (*s == 0x1B && *(s+1) == '[') { if (*s == 0x1B && *(s+1) == '[') {
s = OutputEscapeSequences(s, 0); s = OutputEscapeSequences(s, 0, output);
continue; continue;
} }
s++; s++;
@@ -1477,17 +1649,17 @@ void FillParagraph(char const *s)
} }
if (!pendspace || len+pendspace <= roomleft) { if (!pendspace || len+pendspace <= roomleft) {
for (i=0; i<pendspace; i++) { for (i=0; i<pendspace; i++) {
putchar(' '); OUTPUT(' ');
} }
while(t < s) { while(t < s) {
putchar(*t); OUTPUT(*t);
if (strchr(EndSent, *t)) doublespace = 2; if (strchr(EndSent, *t)) doublespace = 2;
else if (!strchr(EndSentIg, *t)) doublespace = 1; else if (!strchr(EndSentIg, *t)) doublespace = 1;
t++; t++;
} }
} else { } else {
s = t; s = t;
putchar('\n'); OUTPUT('\n');
line++; line++;
break; break;
} }
@@ -1651,12 +1823,51 @@ SaveLastTimeTrig(TimeTrig const *t)
memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig)); memcpy(&LastTimeTrig, t, sizeof(LastTimeTrig));
} }
/* Wrapper to ignore warnings about ignoring return value of system() */ /* Wrapper to ignore warnings about ignoring return value of system()
Also redirects stdin and stdout to /dev/null for queued reminders */
void void
System(char const *cmd) System(char const *cmd, int is_queued)
{ {
int r; int r;
pid_t kid;
int fd;
int status;
int do_exit = 0;
if (is_queued && IsServerMode()) {
do_exit = 1;
/* Server mode... redirect stdin and stdout to /dev/null */
kid = fork();
if (kid == (pid_t) -1) {
/* Fork failed... nothing we can do */
return;
} else if (kid == 0) {
/* In the child */
(void) close(STDIN_FILENO);
(void) close(STDOUT_FILENO);
fd = open("/dev/null", O_RDONLY);
if (fd >= 0 && fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(STDIN_FILENO);
}
fd = open("/dev/null", O_WRONLY);
if (fd >= 0 && fd != STDOUT_FILENO) {
dup2(fd, STDOUT_FILENO);
close(STDOUT_FILENO);
}
} else {
/* In the parent */
while (waitpid(kid, &status, 0) != kid) /* continue */ ;
return;
}
}
/* This is the child process or original if we never forked */
r = system(cmd); r = system(cmd);
if (do_exit) {
/* In the child process, so exit! */
exit(0);
}
if (r == 0) { if (r == 0) {
return; return;
} }
@@ -1681,3 +1892,40 @@ get_month_name(int mon)
if (DynamicMonthName[mon]) return DynamicMonthName[mon]; if (DynamicMonthName[mon]) return DynamicMonthName[mon];
return MonthName[mon]; return MonthName[mon];
} }
static int GetOnceDateFromFile(void)
{
FILE *fp;
int once_date = 0;
fp = fopen(OnceFile, "r");
if (fp) {
if (fscanf(fp, "%d", &once_date) != 1) {
once_date = 0;
}
fclose(fp);
}
/* Save today to file */
fp = fopen(OnceFile, "w");
if (!fp) {
Wprint("Warning: Unable to save ONCE timestamp to %s: %s",
OnceFile, strerror(errno));
return once_date;
}
fprintf(fp, "%d\n# This is a timestamp file used by Remind to track ONCE reminders.\n# Do not edit or delete it.\n", DSEToday);
fclose(fp);
return once_date;
}
int GetOnceDate(void)
{
ProcessedOnce = 1;
if (IgnoreOnce || !OnceFile || !*OnceFile) {
return FileAccessDate;
}
if (OnceDate < 0) {
OnceDate = GetOnceDateFromFile();
}
return OnceDate;
}

View File

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

View File

@@ -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

View File

@@ -14,15 +14,14 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "types.h" #include "types.h"
#include "protos.h" #include "protos.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "expr.h"
static int BexistsIntArray (int array[], int num, int key); static int BexistsIntArray (int const array[], int num, int key);
static void InsertIntoSortedArray (int *array, int num, int key); static void InsertIntoSortedArray (int *array, int num, int key);
/* Arrays for the global omits */ /* Arrays for the global omits */
@@ -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);
@@ -251,7 +271,7 @@ int IsOmitted(int dse, int localomit, char const *omitfunc, int *omit)
/* element is found, 0 otherwise. */ /* element is found, 0 otherwise. */
/* */ /* */
/***************************************************************/ /***************************************************************/
static int BexistsIntArray(int array[], int num, int key) static int BexistsIntArray(int const array[], int num, int key)
{ {
int top=num-1, bot=0, mid; int top=num-1, bot=0, mid;

View File

@@ -13,6 +13,18 @@
/* Suppress unused variable warnings */ /* Suppress unused variable warnings */
#define UNUSED(x) (void) x #define UNUSED(x) (void) x
#ifdef HAVE_STRDUP
#define StrDup strdup
#endif
#ifdef HAVE_STRNCASECMP
#define StrinCmp strncasecmp
#endif
#ifdef HAVE_STRCASECMP
#define StrCmpi strcasecmp
#endif
/* Define a string assignment macro - be careful!!! */ /* Define a string assignment macro - be careful!!! */
#define STRSET(x, str) { if (x) free(x); (x) = StrDup(str); } #define STRSET(x, str) { if (x) free(x); (x) = StrDup(str); }
@@ -31,22 +43,30 @@
int CallUserFunc (char const *name, int nargs, ParsePtr p); int CallUserFunc (char const *name, int nargs, ParsePtr p);
int DoFset (ParsePtr p); int DoFset (ParsePtr p);
int DoFunset (ParsePtr p); int DoFunset (ParsePtr p);
void UnsetAllUserFuncs(void);
void ProduceCalendar (void); void ProduceCalendar (void);
char const *SimpleTime (int tim); char const *SimpleTime (int tim);
char const *CalendarTime (int tim, int duration); char const *CalendarTime (int tim, int duration);
int DoRem (ParsePtr p); int DoRem (ParsePtr p);
int DoFlush (ParsePtr p); int DoFlush (ParsePtr p);
void DoExit (ParsePtr p); void DoExit (ParsePtr p);
int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals); int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim);
int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued); int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int dse, int is_queued, DynamicBuffer *output);
int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err); int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int dse, int *err);
int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode); int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int dse, int mode);
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim); int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int dse, int tim);
int ParseLiteralDate (char const **s, int *dse, int *tim); int 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);
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);
@@ -65,8 +85,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, ...);
@@ -86,10 +107,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);
@@ -103,13 +125,26 @@ int ComputeTrigger (int today, Trigger *trig, TimeTrig *tim, int *err, int save_
int ComputeTriggerNoAdjustDuration (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals, int duration_days); int ComputeTriggerNoAdjustDuration (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals, int duration_days);
int AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int save_in_globals); int AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int save_in_globals);
char *StrnCpy (char *dest, char const *source, int n); char *StrnCpy (char *dest, char const *source, int n);
#ifndef HAVE_STRNCASECMP
int StrinCmp (char const *s1, char const *s2, int n); int StrinCmp (char const *s1, char const *s2, int n);
#endif
#ifndef HAVE_STRDUP
char *StrDup (char const *s); char *StrDup (char const *s);
#endif
#ifndef HAVE_STRCASECMP
int StrCmpi (char const *s1, char const *s2); int StrCmpi (char const *s1, char const *s2);
#endif
void strtolower(char *s);
Var *FindVar (char const *str, int create); Var *FindVar (char const *str, int create);
SysVar *FindSysVar (char const *name);
int DeleteVar (char const *str); int DeleteVar (char const *str);
int SetVar (char const *str, Value *val); int SetVar (char const *str, Value const *val);
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p); int GetVarValue (char const *str, Value *val);
int DoSet (Parser *p); int DoSet (Parser *p);
int DoUnset (Parser *p); int DoUnset (Parser *p);
int DoDump (ParsePtr p); int DoDump (ParsePtr p);
@@ -118,14 +153,14 @@ void DestroyVars (int all);
int PreserveVar (char const *name); int PreserveVar (char const *name);
int DoPreserve (Parser *p); int DoPreserve (Parser *p);
int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p); int DoSatRemind (Trigger *trig, TimeTrig *tt, ParsePtr p);
int DoMsgCommand (char const *cmd, char const *msg); int DoMsgCommand (char const *cmd, char const *msg, int is_queued);
int ParseNonSpaceChar (ParsePtr p, int *err, int peek); int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
unsigned int HashVal (char const *str); unsigned int HashVal (char const *str);
int DateOK (int y, int m, int d); int DateOK (int y, int m, int d);
Operator *FindOperator (char const *name, Operator where[], int num); BuiltinFunc *FindBuiltinFunc (char const *name);
BuiltinFunc *FindFunc (char const *name, BuiltinFunc where[], int num);
int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio); int InsertIntoSortBuffer (int dse, int tim, char const *body, int typ, int prio);
void IssueSortedReminders (void); void IssueSortedReminders (void);
UserFunc *FindUserFunc(char const *name);
int UserFuncExists (char const *fn); int UserFuncExists (char const *fn);
void DSEToHeb (int dse, int *hy, int *hm, int *hd); void DSEToHeb (int dse, int *hy, int *hm, int *hd);
int HebNameToNum (char const *mname); int HebNameToNum (char const *mname);
@@ -142,7 +177,7 @@ int GetSysVar (char const *name, Value *val);
int SetSysVar (char const *name, Value *val); int SetSysVar (char const *name, Value *val);
void DumpSysVarByName (char const *name); void DumpSysVarByName (char const *name);
int CalcMinsFromUTC (int dse, int tim, int *mins, int *isdst); int CalcMinsFromUTC (int dse, int tim, int *mins, int *isdst);
void FillParagraph (char const *s); void FillParagraph (char const *s, DynamicBuffer *output);
void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime); void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime);
void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime); void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime);
int MoonPhase (int date, int time); int MoonPhase (int date, int time);
@@ -168,27 +203,40 @@ void PrintJSONKeyPairString(char const *name, char const *val);
void PrintJSONKeyPairDate(char const *name, int dse); void PrintJSONKeyPairDate(char const *name, int dse);
void PrintJSONKeyPairDateTime(char const *name, int dt); void PrintJSONKeyPairDateTime(char const *name, int dt);
void PrintJSONKeyPairTime(char const *name, int t); void PrintJSONKeyPairTime(char const *name, int t);
void System(char const *cmd); void System(char const *cmd, int queued);
int ShellEscape(char const *in, DynamicBuffer *out); int ShellEscape(char const *in, DynamicBuffer *out);
int AddGlobalOmit(int dse); int AddGlobalOmit(int dse);
void set_lat_and_long_from_components(void); void set_lat_and_long_from_components(void);
void set_components_from_lat_and_long(void); void set_components_from_lat_and_long(void);
void DebugExitFunc(void);
int GetTerminalBackground(void); int GetTerminalBackground(void);
char const *get_day_name(int wkday); char const *get_day_name(int wkday);
char const *get_month_name(int mon); char const *get_month_name(int mon);
void set_cloexec(FILE *fp);
int push_call(char const *filename, char const *func, int lineno); int push_call(char const *filename, char const *func, int lineno);
void clear_callstack(void); void clear_callstack(void);
int print_callstack(FILE *fp); int print_callstack(FILE *fp);
int have_callstack(void);
void pop_call(void); void pop_call(void);
void FixSpecialType(Trigger *trig); void FixSpecialType(Trigger *trig);
void WriteJSONTrigger(Trigger const *t, int include_tags, int today); void 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); 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);

View File

@@ -24,16 +24,26 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h>
#include <sys/select.h> #include <sys/select.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h>
#include "types.h" #include "types.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#undef USE_INOTIFY
#if defined(HAVE_SYS_INOTIFY_H) && defined(HAVE_INOTIFY_INIT1)
#define USE_INOTIFY 1
#include <sys/inotify.h>
int watch_fd = -1;
static void consume_inotify_events(int fd);
static int setup_inotify_watch(void);
#endif
/* A list of filenames associated with queued reminders */ /* A list of filenames associated with queued reminders */
typedef struct queuedfname { typedef struct queuedfname {
@@ -55,6 +65,7 @@ typedef struct queuedrem {
char sched[VAR_NAME_LEN+1]; char sched[VAR_NAME_LEN+1];
Trigger t; Trigger t;
TimeTrig tt; TimeTrig tt;
int red, green, blue;
} QueuedRem; } QueuedRem;
/* Global variables */ /* Global variables */
@@ -68,11 +79,38 @@ static void CheckInitialFile (void);
static int CalculateNextTime (QueuedRem *q); static int CalculateNextTime (QueuedRem *q);
static QueuedRem *FindNextReminder (void); static QueuedRem *FindNextReminder (void);
static int CalculateNextTimeUsingSched (QueuedRem *q); static int CalculateNextTimeUsingSched (QueuedRem *q);
static void DaemonWait (struct timeval *sleep_tv); static void ServerWait (struct timeval *sleep_tv);
static void reread (void); static void reread (void);
static void PrintQueue(void); static void PrintQueue(void);
static char const *QueueFilename(char const *fname); static char const *QueueFilename(char const *fname);
static void chomp(DynamicBuffer *buf)
{
char *s = DBufValue(buf);
int l = DBufLen(buf);
while (l) {
if (s[l-1] == '\n') {
s[l-1] = 0;
DBufLen(buf)--;
l--;
} else {
break;
}
}
}
char const *SimpleTimeNoSpace(int tim)
{
char *s = (char *) SimpleTime(tim);
if (s && *s) {
size_t l = strlen(s);
if (l > 0 && s[l-1] == ' ') {
s[l-1] = 0;
}
}
return s;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
/* QueueFilename */ /* QueueFilename */
@@ -115,6 +153,34 @@ static char const *QueueFilename(char const *fname)
return elem->fname; return elem->fname;
} }
static void del_reminder(QueuedRem *qid)
{
QueuedRem *q = QueueHead;
QueuedRem *next;
if (!q) {
return;
}
if (q == qid) {
QueueHead = q->next;
if (q->text) free((void *) q->text);
free(q);
return;
}
while(q->next) {
next = q->next;
if (q->next == qid) {
q->next = q->next->next;
if (next->text) free((void *) next->text);
free(next);
return;
}
q = q->next;
}
}
static void del_reminder_ul(unsigned long qid) {
del_reminder((QueuedRem *) qid);
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -140,6 +206,9 @@ int QueueReminder(ParsePtr p, Trigger *trig,
if (!qelem) { if (!qelem) {
return E_NO_MEM; return E_NO_MEM;
} }
qelem->red = DefaultColorR;
qelem->green = DefaultColorG;
qelem->blue = DefaultColorB;
qelem->text = StrDup(p->pos); /* Guaranteed that parser is not nested. */ qelem->text = StrDup(p->pos); /* Guaranteed that parser is not nested. */
if (!qelem->text) { if (!qelem->text) {
free(qelem); free(qelem);
@@ -212,7 +281,14 @@ print_num_queued(void)
} }
q = q->next; q = q->next;
} }
printf("NOTE queued %d\n", nqueued); if (DaemonJSON) {
printf("{");
PrintJSONKeyPairString("response", "queued");
PrintJSONKeyPairInt("nqueued", nqueued);
printf("\"command\":\"STATUS\"}\n");
} else {
printf("NOTE queued %d\n", nqueued);
}
fflush(stdout); fflush(stdout);
} }
@@ -232,9 +308,13 @@ void HandleQueuedReminders(void)
struct timeval tv; struct timeval tv;
struct timeval sleep_tv; struct timeval sleep_tv;
struct sigaction sa; struct sigaction sa;
char qid[64];
/* Suppress the BANNER from being issued */ /* Disable any potential pending SIGALRMs */
DidMsgReminder = 1; alarm(0);
/* Un-limit execution time */
unlimit_execution_time();
/* Turn off sorting -- otherwise, TriggerReminder has no effect! */ /* Turn off sorting -- otherwise, TriggerReminder has no effect! */
SortByDate = 0; SortByDate = 0;
@@ -279,12 +359,16 @@ 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);
} }
#ifdef USE_INOTIFY
watch_fd = setup_inotify_watch();
#endif
/* Sit in a loop, issuing reminders when necessary */ /* Sit in a loop, issuing reminders when necessary */
while(1) { while(1) {
q = FindNextReminder(); q = FindNextReminder();
@@ -310,8 +394,6 @@ void HandleQueuedReminders(void)
SleepTime = 60*Daemon; SleepTime = 60*Daemon;
} }
/* Wake up once a minute to recalibrate sleep time in
case of laptop hibernation */
if (IsServerMode()) { if (IsServerMode()) {
/* Wake up on the next exact minute */ /* Wake up on the next exact minute */
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
@@ -322,7 +404,9 @@ void HandleQueuedReminders(void)
} else { } else {
sleep_tv.tv_usec = 0; sleep_tv.tv_usec = 0;
} }
DaemonWait(&sleep_tv); ServerWait(&sleep_tv);
/* A DEL command might have deleted our queued reminder! */
q = FindNextReminder();
} else { } else {
sleep(SleepTime); sleep(SleepTime);
} }
@@ -340,7 +424,9 @@ void HandleQueuedReminders(void)
} }
} }
if (Daemon > 0 && SleepTime) CheckInitialFile(); if (Daemon > 0 && SleepTime) {
CheckInitialFile();
}
if (Daemon && !q) { if (Daemon && !q) {
if (IsServerMode()) { if (IsServerMode()) {
@@ -367,24 +453,51 @@ void HandleQueuedReminders(void)
/* Trigger the reminder */ /* Trigger the reminder */
CreateParser(q->text, &p); CreateParser(q->text, &p);
RunDisabled = q->RunDisabled; RunDisabled = q->RunDisabled;
if (IsServerMode()) { if (IsServerMode() && q->typ != RUN_TYPE) {
printf("NOTE reminder %s", if (DaemonJSON) {
SimpleTime(q->tt.ttime)); printf("{\"response\":\"reminder\",");
printf("%s", SimpleTime(MinutesPastMidnight(1))); snprintf(qid, sizeof(qid), "%lx", (unsigned long) q);
if (!*DBufValue(&q->t.tags)) { PrintJSONKeyPairString("qid", qid);
printf("*\n"); PrintJSONKeyPairString("ttime", SimpleTimeNoSpace(q->tt.ttime));
} else { PrintJSONKeyPairString("now", SimpleTimeNoSpace(MinutesPastMidnight(1)));
printf("%s\n", DBufValue(&(q->t.tags))); PrintJSONKeyPairString("tags", DBufValue(&q->t.tags));
} } else {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(MinutesPastMidnight(1)));
if (!*DBufValue(&q->t.tags)) {
printf("*\n");
} else {
printf("%s\n", DBufValue(&(q->t.tags)));
}
}
} }
/* Set up global variables so some functions like trigdate() /* Set up global variables so some functions like trigdate()
and trigtime() work correctly */ and trigtime() work correctly */
SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1); SaveAllTriggerInfo(&(q->t), &(q->tt), DSEToday, q->tt.ttime, 1);
FileName = (char *) q->fname; FileName = (char *) q->fname;
(void) TriggerReminder(&p, &q->t, &q->tt, DSEToday, 1); DefaultColorR = q->red;
DefaultColorG = q->green;
DefaultColorB = q->blue;
/* Make a COPY of q->t because TriggerReminder can change q->t.typ */
Trigger tcopy = q->t;
if (DaemonJSON) {
DynamicBuffer out;
DBufInit(&out);
(void) TriggerReminder(&p, &tcopy, &q->tt, DSEToday, 1, &out);
if (q->typ != RUN_TYPE) {
printf("\"body\":\"");
chomp(&out);
PrintJSONString(DBufValue(&out));
printf("\"}\n");
}
DBufFree(&out);
} else {
(void) TriggerReminder(&p, &tcopy, &q->tt, DSEToday, 1, NULL);
}
FileName = NULL; FileName = NULL;
if (IsServerMode()) { if (IsServerMode() && !DaemonJSON && q->typ != RUN_TYPE) {
printf("NOTE endreminder\n"); printf("NOTE endreminder\n");
} }
fflush(stdout); fflush(stdout);
@@ -394,7 +507,6 @@ void HandleQueuedReminders(void)
/* Calculate the next trigger time */ /* Calculate the next trigger time */
q->tt.nexttime = CalculateNextTime(q); q->tt.nexttime = CalculateNextTime(q);
/* If it's dequeued, update num_queued */
if (q->tt.nexttime != NO_TIME) { if (q->tt.nexttime != NO_TIME) {
/* If trigger time is way in the past because computer has been /* If trigger time is way in the past because computer has been
suspended or hibernated, remove from queue */ suspended or hibernated, remove from queue */
@@ -404,9 +516,13 @@ void HandleQueuedReminders(void)
} }
} }
/* If we have dequeued a reminder, update controlling process */ /* If queued reminder has expired, actually remove it from queue
if (q->tt.nexttime == NO_TIME && IsServerMode()) { and update status */
print_num_queued(); if (q->tt.nexttime == NO_TIME) {
del_reminder(q);
if (IsServerMode()) {
print_num_queued();
}
} }
} }
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
@@ -527,7 +643,25 @@ static void CheckInitialFile(void)
/* If date has rolled around, or file has changed, spawn a new version. */ /* If date has rolled around, or file has changed, spawn a new version. */
time_t tim = FileModTime; time_t tim = FileModTime;
int y, m, d; int y, m, d;
#ifdef USE_INOTIFY
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
int n;
#endif
#ifdef USE_INOTIFY
/* If there are any inotify events, reread */
if (watch_fd >= 0) {
while(1) {
n = read(watch_fd, buf, sizeof(buf));
if (n < 0 && errno == EINTR) continue;
if (n > 0) {
close(watch_fd);
reread();
}
break;
}
}
#endif
if (stat(InitialFile, &StatBuf) == 0) tim = StatBuf.st_mtime; if (stat(InitialFile, &StatBuf) == 0) tim = StatBuf.st_mtime;
if (tim != FileModTime || if (tim != FileModTime ||
RealToday != SystemDate(&y, &m, &d)) { RealToday != SystemDate(&y, &m, &d)) {
@@ -605,7 +739,11 @@ static void
json_queue(QueuedRem const *q) json_queue(QueuedRem const *q)
{ {
int done = 0; int done = 0;
if (DaemonJSON) {
printf("{\"response\":\"queue\",\"queue\":");
}
printf("["); printf("[");
char idbuf[64];
while(q) { while(q) {
if (q->tt.nexttime == NO_TIME) { if (q->tt.nexttime == NO_TIME) {
q = q->next; q = q->next;
@@ -618,6 +756,8 @@ json_queue(QueuedRem const *q)
printf("{"); printf("{");
WriteJSONTrigger(&(q->t), 1, DSEToday); WriteJSONTrigger(&(q->t), 1, DSEToday);
WriteJSONTimeTrigger(&(q->tt)); WriteJSONTimeTrigger(&(q->tt));
snprintf(idbuf, sizeof(idbuf), "%lx", (unsigned long) q);
PrintJSONKeyPairString("qid", idbuf);
PrintJSONKeyPairInt("rundisabled", q->RunDisabled); PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig); PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairString("filename", q->fname); PrintJSONKeyPairString("filename", q->fname);
@@ -631,7 +771,10 @@ json_queue(QueuedRem const *q)
case PS_TYPE: PrintJSONKeyPairString("type", "PS_TYPE"); break; case PS_TYPE: PrintJSONKeyPairString("type", "PS_TYPE"); break;
case PSF_TYPE: PrintJSONKeyPairString("type", "PSF_TYPE"); break; case PSF_TYPE: PrintJSONKeyPairString("type", "PSF_TYPE"); break;
case MSF_TYPE: PrintJSONKeyPairString("type", "MSF_TYPE"); break; case MSF_TYPE: PrintJSONKeyPairString("type", "MSF_TYPE"); break;
case PASSTHRU_TYPE: PrintJSONKeyPairString("type", "PASSTHRU_TYPE"); break; case PASSTHRU_TYPE:
PrintJSONKeyPairString("type", "PASSTHRU_TYPE");
PrintJSONKeyPairString("passthru", q->passthru);
break;
default: PrintJSONKeyPairString("type", "?"); break; default: PrintJSONKeyPairString("type", "?"); break;
} }
@@ -647,30 +790,48 @@ json_queue(QueuedRem const *q)
printf("\"}"); printf("\"}");
q = q->next; q = q->next;
} }
printf("]\n"); printf("]");
if (DaemonJSON) {
printf(",\"command\":\"QUEUE\"}\n");
} else {
printf("\n");
}
} }
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DaemonWait */ /* ServerWait */
/* */ /* */
/* Sleep or read command from stdin in "daemon -1" mode */ /* Sleep or read command from stdin in server mode */
/* */ /* */
/***************************************************************/ /***************************************************************/
static void DaemonWait(struct timeval *sleep_tv) static void ServerWait(struct timeval *sleep_tv)
{ {
fd_set readSet; fd_set readSet;
int retval; int retval;
int y, m, d; int y, m, d;
int max = 1;
char cmdLine[256]; char cmdLine[256];
FD_ZERO(&readSet); FD_ZERO(&readSet);
FD_SET(0, &readSet); FD_SET(0, &readSet);
retval = select(1, &readSet, NULL, NULL, sleep_tv);
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
FD_SET(watch_fd, &readSet);
if (watch_fd > max-1)
max = watch_fd+1;
}
#endif
retval = select(max, &readSet, NULL, NULL, sleep_tv);
/* If date has rolled around, restart */ /* If date has rolled around, restart */
if (RealToday != SystemDate(&y, &m, &d)) { if (RealToday != SystemDate(&y, &m, &d)) {
printf("NOTE newdate\nNOTE reread\n"); if (DaemonJSON) {
printf("{\"response\":\"newdate\"}\n{\"response\":\"reread\",\"command\":\"newdate\"}\n");
} else {
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout); fflush(stdout);
reread(); reread();
} }
@@ -678,6 +839,24 @@ static void DaemonWait(struct timeval *sleep_tv)
/* If nothing readable or interrupted system call, return */ /* If nothing readable or interrupted system call, return */
if (retval <= 0) return; if (retval <= 0) return;
/* If inotify watch descriptor is readable, handle it */
#ifdef USE_INOTIFY
if (watch_fd >= 0) {
if (FD_ISSET(watch_fd, &readSet)) {
consume_inotify_events(watch_fd);
if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"inotify\"}\n");
} else {
/* In deprecated server mode, we need to spit out
a NOTE newdate to force the front-end to redraw
the calendar */
printf("NOTE newdate\nNOTE reread\n");
}
fflush(stdout);
reread();
}
}
#endif
/* If stdin not readable, return */ /* If stdin not readable, return */
if (!FD_ISSET(0, &readSet)) return; if (!FD_ISSET(0, &readSet)) return;
@@ -696,43 +875,72 @@ static void DaemonWait(struct timeval *sleep_tv)
} else if (!strcmp(cmdLine, "STATUS\n")) { } else if (!strcmp(cmdLine, "STATUS\n")) {
print_num_queued(); print_num_queued();
} else if (!strcmp(cmdLine, "QUEUE\n")) { } else if (!strcmp(cmdLine, "QUEUE\n")) {
printf("NOTE queue\n"); if (DaemonJSON) {
QueuedRem *q = QueueHead; json_queue(QueueHead);
while (q) { } else {
if (q->tt.nexttime != NO_TIME) { printf("NOTE queue\n");
switch (q->typ) { QueuedRem *q = QueueHead;
case NO_TYPE: printf("NO_TYPE "); break; while (q) {
case MSG_TYPE: printf("MSG_TYPE "); break; if (q->tt.nexttime != NO_TIME) {
case RUN_TYPE: printf("RUN_TYPE "); break; switch (q->typ) {
case CAL_TYPE: printf("CAL_TYPE "); break; case NO_TYPE: printf("NO_TYPE"); break;
case SAT_TYPE: printf("SAT_TYPE "); break; case MSG_TYPE: printf("MSG_TYPE"); break;
case PS_TYPE: printf("PS_TYPE "); break; case RUN_TYPE: printf("RUN_TYPE"); break;
case PSF_TYPE: printf("PSF_TYPE "); break; case CAL_TYPE: printf("CAL_TYPE"); break;
case MSF_TYPE: printf("MSF_TYPE "); break; case SAT_TYPE: printf("SAT_TYPE"); break;
case PASSTHRU_TYPE: printf("PASSTHRU_TYPE "); break; case PS_TYPE: printf("PS_TYPE"); break;
default: printf("? "); break; case PSF_TYPE: printf("PSF_TYPE"); break;
} case MSF_TYPE: printf("MSF_TYPE"); break;
printf("RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1)); case PASSTHRU_TYPE: printf("PASSTHRU_TYPE"); break;
printf("%s %s %s\n", default: printf("?"); break;
(q->passthru[0] ? q->passthru : "*"), }
(q->sched[0] ? q->sched : "*"), printf(" RunDisabled=%d ntrig=%d ttime=%02d:%02d nexttime=%02d:%02d delta=%d rep=%d duration=%d ", q->RunDisabled, q->ntrig, q->tt.ttime/60, q->tt.ttime % 60, q->tt.nexttime / 60, q->tt.nexttime % 60, q->tt.delta, (q->tt.rep != NO_TIME ? q->tt.rep : -1), (q->tt.duration != NO_TIME ? q->tt.duration : -1));
q->text ? q->text : "NULL"); printf("%s %s %s\n",
(q->passthru[0] ? q->passthru : "*"),
(q->sched[0] ? q->sched : "*"),
q->text ? q->text : "NULL");
}
q = q->next;
} }
q = q->next; printf("NOTE endqueue\n");
} }
printf("NOTE endqueue\n");
fflush(stdout); fflush(stdout);
} else if (!strcmp(cmdLine, "JSONQUEUE\n")) { } else if (!strcmp(cmdLine, "JSONQUEUE\n")) {
printf("NOTE JSONQUEUE\n"); if (!DaemonJSON) {
printf("NOTE JSONQUEUE\n");
}
json_queue(QueueHead); json_queue(QueueHead);
printf("NOTE ENDJSONQUEUE\n"); if (!DaemonJSON) {
printf("NOTE ENDJSONQUEUE\n");
}
fflush(stdout); fflush(stdout);
} else if (!strcmp(cmdLine, "REREAD\n")) { } else if (!strcmp(cmdLine, "REREAD\n")) {
printf("NOTE reread\n"); if (DaemonJSON) {
printf("{\"response\":\"reread\",\"command\":\"REREAD\"}\n");
} else {
printf("NOTE reread\n");
}
fflush(stdout); fflush(stdout);
reread(); reread();
} else if (!strncmp(cmdLine, "DEL ", 4)) {
unsigned long qid;
if (sscanf(cmdLine, "DEL %lx", &qid) == 1) {
del_reminder_ul(qid);
}
print_num_queued();
fflush(stdout);
} else { } else {
printf("ERR Invalid daemon command: %s", cmdLine); if (DaemonJSON) {
size_t l = strlen(cmdLine);
if (l && cmdLine[l-1] == '\n') {
cmdLine[l-1] = 0;
}
printf("{\"response\":\"error\",\"error\":\"Unknown command\",\"command\":\"");
PrintJSONString(cmdLine);
printf("\"}\n");
} else {
printf("ERR Invalid daemon command: %s", cmdLine);
}
fflush(stdout); fflush(stdout);
} }
} }
@@ -749,3 +957,56 @@ static void reread(void)
execvp(ArgV[0], (char **) ArgV); execvp(ArgV[0], (char **) ArgV);
} }
#ifdef USE_INOTIFY
static void consume_inotify_events(int fd)
{
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
int n;
struct timespec sleeptime;
int slept = 0;
/* Consume all the inotify events */
while(1) {
n = read(fd, buf, sizeof(buf));
if (n > 0) {
/* Something new since we slept */
slept = 0;
}
if (n < 0) {
if (errno == EINTR) continue;
if (slept) {
/* Nothing new since we slept */
return;
}
slept = 1;
/* HACK: sleep for 0.2 seconds to let multiple events queue up so we
only do a single reread */
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = 200000000;
nanosleep(&sleeptime, NULL);
}
}
}
static int setup_inotify_watch(void)
{
int fd;
/* Don't inotify_watch stdin */
if (!strcmp(InitialFile, "-")) {
return -1;
}
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (fd < 0) {
return fd;
}
if (inotify_add_watch(fd, InitialFile, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO) < 0) {
close(fd);
return -1;
}
return fd;
}
#endif

View File

@@ -18,7 +18,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "types.h" #include "types.h"
#include "protos.h" #include "protos.h"
#include "expr.h"
#include "globals.h" #include "globals.h"
#include "err.h" #include "err.h"
@@ -136,7 +135,7 @@ void IssueSortedReminders(void)
switch(cur->typ) { switch(cur->typ) {
case MSG_TYPE: case MSG_TYPE:
if (MsgCommand && *MsgCommand) { if (MsgCommand && *MsgCommand) {
DoMsgCommand(MsgCommand, cur->text); DoMsgCommand(MsgCommand, cur->text, 0);
} else { } else {
if (cur->trigdate != olddate) { if (cur->trigdate != olddate) {
IssueSortBanner(cur->trigdate); IssueSortBanner(cur->trigdate);
@@ -151,11 +150,11 @@ void IssueSortedReminders(void)
IssueSortBanner(cur->trigdate); IssueSortBanner(cur->trigdate);
olddate = cur->trigdate; olddate = cur->trigdate;
} }
FillParagraph(cur->text); FillParagraph(cur->text, NULL);
break; break;
case RUN_TYPE: case RUN_TYPE:
System(cur->text); System(cur->text, 0);
break; break;
} }

View File

@@ -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 },

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"
@@ -72,7 +71,10 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
m++; m++;
if (m == 12) { m = 0; y++; } if (m == 12) { m = 0; y++; }
} }
while (trig->d > DaysInMonth(m, trig->y)) m++; while (trig->d > DaysInMonth(m, y)) {
m++;
if (m == 12) { m = 0; y++; }
}
j = DSE(y, m, trig->d); j = DSE(y, m, trig->d);
return j; return j;
@@ -241,6 +243,7 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
case GOT_WD+GOT_MON+GOT_YR: case GOT_WD+GOT_MON+GOT_YR:
if (y > trig->y || (y == trig->y && m > trig->m)) return -1; if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
/* cppcheck-suppress knownConditionTrueFalse */
if (trig->y > y || (trig->y == y && trig->m > m)) { if (trig->y > y || (trig->y == y && trig->m > m)) {
j = DSE(trig->y, trig->m, 1); j = DSE(trig->y, trig->m, 1);
ADVANCE_TO_WD(j, trig->wd); ADVANCE_TO_WD(j, trig->wd);

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,6 +201,7 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
#define DB_DUMP_VARS 8 #define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16 #define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32 #define DB_TRACE_FILES 32
#define DB_PARSE_EXPR 64
/* Enumeration of the tokens */ /* Enumeration of the tokens */
enum TokTypes enum TokTypes
@@ -183,7 +235,8 @@ enum TokTypes
T_MaybeUncomputable, T_MaybeUncomputable,
T_Ordinal, T_Ordinal,
T_In, T_In,
T_LastBack T_LastBack,
T_Expr
}; };
/* The structure of a token */ /* The structure of a token */
@@ -243,3 +296,26 @@ 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;
} UserFunc;

View File

@@ -16,43 +16,46 @@
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "types.h" #include "types.h"
#include "globals.h" #include "globals.h"
#include "protos.h" #include "protos.h"
#include "err.h" #include "err.h"
#include "expr.h"
#define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */ #define FUNC_HASH_SIZE 32 /* Size of User-defined function hash table */
/* Define the data structure used to hold a user-defined function */
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
char const *text;
Var *locals;
char IsActive;
int nargs;
char const *filename;
int lineno;
} UserFunc;
/* The hash table */ /* The hash table */
static UserFunc *FuncHash[FUNC_HASH_SIZE]; static UserFunc *FuncHash[FUNC_HASH_SIZE];
/* Access to built-in functions */
extern int NumFuncs;
extern BuiltinFunc Func[];
/* We need access to the expression evaluation stack */
extern Value ValStack[];
extern int ValStackPtr;
static void DestroyUserFunc (UserFunc *f); static void DestroyUserFunc (UserFunc *f);
static void FUnset (char const *name); static void FUnset (char const *name);
static void FSet (UserFunc *f); static void FSet (UserFunc *f);
static int SetUpLocalVars (UserFunc *f);
static void DestroyLocalVals (UserFunc *f); /***************************************************************/
/* */
/* HashVal */
/* Given a string, compute the hash value. */
/* */
/***************************************************************/
unsigned int HashVal_nocase(char const *str)
{
register unsigned int i=0;
register unsigned int j=1;
register unsigned int len=0;
while(*str && len < VAR_NAME_LEN) {
i += j * (*str);
str++;
len++;
j = 3-j;
}
return i;
}
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -75,6 +78,7 @@ int DoFunset(ParsePtr p)
break; break;
} }
seen_one = 1; seen_one = 1;
strtolower(DBufValue(&buf));
FUnset(DBufValue(&buf)); FUnset(DBufValue(&buf));
DBufFree(&buf); DBufFree(&buf);
} }
@@ -93,8 +97,11 @@ int DoFset(ParsePtr p)
{ {
int r; int r;
int c; int c;
int i;
UserFunc *func; UserFunc *func;
Var *v; UserFunc *existing;
Var *locals = NULL;
Var local_array[MAX_FUNC_ARGS];
int orig_namelen; int orig_namelen;
DynamicBuffer buf; DynamicBuffer buf;
@@ -108,6 +115,22 @@ 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;
}
}
/* Should be followed by '(' */ /* Should be followed by '(' */
c = ParseNonSpaceChar(p, &r, 0); c = ParseNonSpaceChar(p, &r, 0);
if (r) { if (r) {
@@ -137,25 +160,22 @@ int DoFset(ParsePtr p)
StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN); StrnCpy(func->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf); DBufFree(&buf);
if (!Hush) { if (!Hush) {
if (FindFunc(func->name, Func, NumFuncs)) { if (FindBuiltinFunc(func->name)) {
Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name); Eprint("%s: `%s'", ErrMsg[E_REDEF_FUNC], func->name);
} }
} }
func->locals = NULL; func->node = NULL;
func->text = NULL;
func->IsActive = 0;
func->nargs = 0; func->nargs = 0;
func->args = NULL;
/* Get the local variables - we insert the local variables in REVERSE /* Get the local variables */
order, but that's OK, because we pop them off the stack in reverse
order, too, so everything works out just fine. */
c=ParseNonSpaceChar(p, &r, 1); c=ParseNonSpaceChar(p, &r, 1);
if (r) return r; if (r) return r;
if (c == ')') { if (c == ')') {
(void) ParseNonSpaceChar(p, &r, 0); (void) ParseNonSpaceChar(p, &r, 0);
} } else {
else { locals = local_array;
while(1) { while(1) {
if ( (r=ParseIdentifier(p, &buf)) ) return r; if ( (r=ParseIdentifier(p, &buf)) ) return r;
if (*DBufValue(&buf) == '$') { if (*DBufValue(&buf) == '$') {
@@ -163,18 +183,25 @@ int DoFset(ParsePtr p)
DestroyUserFunc(func); DestroyUserFunc(func);
return E_BAD_ID; return E_BAD_ID;
} }
v = NEW(Var); /* If we've already seen this local variable, error */
if (!v) { for (i=0; i<func->nargs; i++) {
DBufFree(&buf); if (!StrinCmp(DBufValue(&buf), local_array[i].name, VAR_NAME_LEN)) {
DestroyUserFunc(func); DBufFree(&buf);
return E_NO_MEM; DestroyUserFunc(func);
} return E_REPEATED_ARG;
}
}
i = func->nargs;
if (i >= MAX_FUNC_ARGS-1) {
DBufFree(&buf);
DestroyUserFunc(func);
return E_2MANY_ARGS;
}
local_array[i].v.type = ERR_TYPE;
StrnCpy(local_array[i].name, DBufValue(&buf), VAR_NAME_LEN);
local_array[i].next = &(local_array[i+1]);
local_array[i+1].next = NULL;
func->nargs++; func->nargs++;
v->v.type = ERR_TYPE;
StrnCpy(v->name, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
v->next = func->locals;
func->locals = v;
c = ParseNonSpaceChar(p, &r, 0); c = ParseNonSpaceChar(p, &r, 0);
if (c == ')') break; if (c == ')') break;
else if (c != ',') { else if (c != ',') {
@@ -189,17 +216,42 @@ int DoFset(ParsePtr p)
if (c == '=') { if (c == '=') {
(void) ParseNonSpaceChar(p, &r, 0); (void) ParseNonSpaceChar(p, &r, 0);
} }
/* Copy the text over */
if (p->isnested) { if (p->isnested) {
Eprint("%s", ErrMsg[E_CANTNEST_FDEF]); Eprint("%s", ErrMsg[E_CANTNEST_FDEF]);
DestroyUserFunc(func); DestroyUserFunc(func);
return E_PARSE_ERR; return E_PARSE_ERR;
} }
func->text = StrDup(p->pos); while(*(p->pos) && isspace(*(p->pos))) {
if (!func->text) { p->pos++;
DestroyUserFunc(func); }
return E_NO_MEM; if (!*(p->pos)) {
DestroyUserFunc(func);
return E_EOLN;
}
/* Parse the expression */
func->node = parse_expression(&(p->pos), &r, locals);
if (!func->node) {
DestroyUserFunc(func);
return r;
}
c = ParseNonSpaceChar(p, &r, 1);
if (c != 0) {
DestroyUserFunc(func);
return E_EXPECTING_EOL;
}
/* Save the argument names */
if (func->nargs) {
func->args = calloc(sizeof(char *), func->nargs);
for (i=0; i<func->nargs; i++) {
func->args[i] = StrDup(local_array[i].name);
if (!func->args[i]) {
DestroyUserFunc(func);
return E_NO_MEM;
}
}
} }
/* If an old definition of this function exists, destroy it */ /* If an old definition of this function exists, destroy it */
@@ -223,23 +275,22 @@ int DoFset(ParsePtr p)
/***************************************************************/ /***************************************************************/
static void DestroyUserFunc(UserFunc *f) static void DestroyUserFunc(UserFunc *f)
{ {
Var *v, *prev; int i;
/* Free the local variables first */
v = f->locals;
while(v) {
DestroyValue(v->v);
prev = v;
v = v->next;
free(prev);
}
/* Free the function definition */ /* Free the function definition */
if (f->text) free( (char *) f->text); if (f->node) free_expr_tree(f->node);
/* Free the filename */ /* Free the filename */
if (f->filename) free( (char *) f->filename); if (f->filename) free( (char *) f->filename);
/* Free arg names */
if (f->args) {
for (i=0; i<f->nargs; i++) {
if (f->args[i]) free(f->args[i]);
}
free(f->args);
}
/* Free the data structure itself */ /* Free the data structure itself */
free(f); free(f);
} }
@@ -257,12 +308,12 @@ static void FUnset(char const *name)
UserFunc *cur, *prev; UserFunc *cur, *prev;
int h; int h;
h = HashVal(name) % FUNC_HASH_SIZE; h = HashVal_nocase(name) % FUNC_HASH_SIZE;
cur = FuncHash[h]; cur = FuncHash[h];
prev = NULL; prev = NULL;
while(cur) { while(cur) {
if (! StrinCmp(name, cur->name, VAR_NAME_LEN)) break; if (! strncmp(name, cur->name, VAR_NAME_LEN)) break;
prev = cur; prev = cur;
cur = cur->next; cur = cur->next;
} }
@@ -280,134 +331,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 */
@@ -418,12 +357,33 @@ static void DestroyLocalVals(UserFunc *f)
/***************************************************************/ /***************************************************************/
int UserFuncExists(char const *fn) int UserFuncExists(char const *fn)
{ {
UserFunc *f; UserFunc *f = FindUserFunc(fn);
int h = HashVal(fn) % FUNC_HASH_SIZE;
f = FuncHash[h];
while (f && StrinCmp(fn, f->name, VAR_NAME_LEN)) f = f->next;
if (!f) return -1; if (!f) return -1;
else return f->nargs; else return f->nargs;
} }
/***************************************************************/
/* */
/* UnsetAllUserFuncs */
/* */
/* Call FUNSET on all user funcs. Used with -ds flag to */
/* ensure no expr_node memory leaks. */
/* */
/***************************************************************/
void
UnsetAllUserFuncs(void)
{
UserFunc *f;
UserFunc *next;
int i;
for (i=0; i<FUNC_HASH_SIZE; i++) {
f = FuncHash[i];
while(f) {
next = f->next;
DestroyUserFunc(f);
f = next;
}
FuncHash[i] = NULL;
}
}

View File

@@ -17,6 +17,11 @@ static char const DontEscapeMe[] =
#include "err.h" #include "err.h"
#include <string.h> #include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
@@ -46,6 +51,7 @@ char *StrnCpy(char *dest, char const *source, int n)
return odest; return odest;
} }
#ifndef HAVE_STRNCASECMP
/***************************************************************/ /***************************************************************/
/* */ /* */
/* StrinCmp - compare strings, case-insensitive */ /* StrinCmp - compare strings, case-insensitive */
@@ -64,6 +70,9 @@ int StrinCmp(char const *s1, char const *s2, int n)
if (n) return (toupper(*s1) - toupper(*s2)); else return 0; if (n) return (toupper(*s1) - toupper(*s2)); else return 0;
} }
#endif
#ifndef HAVE_STRDUP
/***************************************************************/ /***************************************************************/
/* */ /* */
/* StrDup */ /* StrDup */
@@ -79,6 +88,9 @@ char *StrDup(char const *s)
return ret; return ret;
} }
#endif
#ifndef HAVE_STRCASECMP
/***************************************************************/ /***************************************************************/
/* */ /* */
/* StrCmpi */ /* StrCmpi */
@@ -98,6 +110,8 @@ int StrCmpi(char const *s1, char const *s2)
return toupper(*s1) - toupper(*s2); return toupper(*s1) - toupper(*s2);
} }
#endif
/***************************************************************/ /***************************************************************/
/* */ /* */
/* DateOK */ /* DateOK */
@@ -116,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)
@@ -151,11 +173,15 @@ int _private_sub_overflow(int a, int b)
int int
ShellEscape(char const *in, DynamicBuffer *out) ShellEscape(char const *in, DynamicBuffer *out)
{ {
while(*in) { unsigned char const *i = (unsigned char const *) in;
if (!strchr(DontEscapeMe, *in)) { while(*i) {
if (DBufPutc(out, '\\') != OK) return E_NO_MEM; /* Don't escape chars with high bit set. That will mangle UTF-8 */
if (! (*i & 0x80) ) {
if (!strchr(DontEscapeMe, *i)) {
if (DBufPutc(out, '\\') != OK) return E_NO_MEM;
}
} }
if (DBufPutc(out, *in++) != OK) return E_NO_MEM; if (DBufPutc(out, *i++) != OK) return E_NO_MEM;
} }
return OK; return OK;
} }
@@ -169,31 +195,33 @@ typedef struct cs_s {
} cs; } cs;
static cs *callstack = NULL; static cs *callstack = NULL;
static cs *freecs = NULL;
static void static void
destroy_cs(cs *entry) destroy_cs(cs *entry)
{ {
if (entry->filename) free( (void *) entry->filename); entry->next = freecs;
if (entry->func) free( (void *) entry->func); freecs = entry;
free( (void *) entry);
} }
int int
push_call(char const *filename, char const *func, int lineno) push_call(char const *filename, char const *func, int lineno)
{ {
cs *entry = NEW(cs); cs *entry;
if (!entry) { if (freecs) {
return E_NO_MEM; entry = freecs;
freecs = freecs->next;
} else {
entry = NEW(cs);
if (!entry) {
return E_NO_MEM;
}
} }
entry->next = NULL; entry->next = NULL;
entry->filename = StrDup(filename); entry->filename = filename;
entry->func = StrDup(func); entry->func = func;
entry->lineno = lineno; entry->lineno = lineno;
if (!entry->filename || !entry->func) {
destroy_cs(entry);
return E_NO_MEM;
}
entry->next = callstack; entry->next = callstack;
callstack = entry; callstack = entry;
return OK; return OK;
@@ -215,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

110
src/var.c
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);
@@ -175,6 +206,19 @@ static int terminal_bg_func(int do_set, Value *val)
return OK; return OK;
} }
static int trig_time_func(int do_set, Value *val)
{
UNUSED(do_set);
if (LastTriggerTime != NO_TIME) {
val->type = TIME_TYPE;
val->v.val = LastTriggerTime;
} else {
val->type = INT_TYPE;
val->v.val = 0;
}
return OK;
}
static int trig_date_func(int do_set, Value *val) static int trig_date_func(int do_set, Value *val)
{ {
UNUSED(do_set); UNUSED(do_set);
@@ -309,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;
@@ -477,10 +544,10 @@ 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 *val) int SetVar(char const *str, Value const *val)
{ {
Var *v = FindVar(str, 1); Var *v = FindVar(str, 1);
@@ -498,20 +565,10 @@ int SetVar(char const *str, Value *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) {
@@ -544,6 +601,11 @@ int DoSet (Parser *p)
ParseNonSpaceChar(p, &r, 0); ParseNonSpaceChar(p, &r, 0);
} }
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);
if (r) { if (r) {
DBufFree(&buf); DBufFree(&buf);
@@ -772,16 +834,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
@@ -818,6 +870,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 },
@@ -864,6 +917,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 },
@@ -887,6 +941,7 @@ static SysVar SysVarArr[] = {
{"Tm", 0, SPECIAL_TYPE, trig_mon_func, 0, 0 }, {"Tm", 0, SPECIAL_TYPE, trig_mon_func, 0, 0 },
{"Today", 1, STR_TYPE, &DynamicToday, 0, 0 }, {"Today", 1, STR_TYPE, &DynamicToday, 0, 0 },
{"Tomorrow", 1, STR_TYPE, &DynamicTomorrow, 0, 0 }, {"Tomorrow", 1, STR_TYPE, &DynamicTomorrow, 0, 0 },
{"Tt", 0, SPECIAL_TYPE, trig_time_func, 0, 0 },
{"Tuesday", 1, STR_TYPE, &DynamicDayName[1], 0, 0 }, {"Tuesday", 1, STR_TYPE, &DynamicDayName[1], 0, 0 },
{"Tw", 0, SPECIAL_TYPE, trig_wday_func, 0, 0 }, {"Tw", 0, SPECIAL_TYPE, trig_wday_func, 0, 0 },
{"Ty", 0, SPECIAL_TYPE, trig_year_func, 0, 0 }, {"Ty", 0, SPECIAL_TYPE, trig_year_func, 0, 0 },
@@ -905,7 +960,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);
/***************************************************************/ /***************************************************************/
/* */ /* */
@@ -1002,7 +1056,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;

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
tests/expr.rem Normal file
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
tests/if1.rem Normal file
View File

@@ -0,0 +1,4 @@
BANNER %
set $AddBlankLines 0
IF 1
INCLUDE [filedir()]/if2.rem

5
tests/if2.rem Normal file
View File

@@ -0,0 +1,5 @@
# Another unmatched IF
IF 0
ELSE
IF 1

7
tests/test-once.rem Normal file
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"

View File

@@ -176,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
@@ -452,7 +452,136 @@ rm -rf include_dir/ww
# Test queueing. Because eventstart depends on the actual system # Test queueing. Because eventstart depends on the actual system
# date, we have to convert it to some constant (in this case, # date, we have to convert it to some constant (in this case,
# VOLATILE) so that tests are not dependent on the system date. # VOLATILE) so that tests are not dependent on the system date.
echo JSONQUEUE | ../src/remind -z0 ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' >> ../tests/test.out 2>&1 echo JSONQUEUE | ../src/remind -z0 ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
echo QUEUE | ../src/remind -zj ../tests/queue1.rem 2>&1 | sed -e 's/"eventstart":"................"/"eventstart":"VOLATILE"/g' | sed -e 's/"qid":"[0-9a-f]*",//g' >> ../tests/test.out 2>&1
# Test for leap year bug that was fixed
../src/remind -dte - 28 Feb 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 1 Mar 2024 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2024 MSG three
REM 29 Feb 2024 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2024 MSG three
REM Thursday 29 Feb 2024 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2024 MSG three
REM Wednesday 29 Feb 2024 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2024 MSG three
REM Friday 29 Feb 2024 MSG four
EOF
../src/remind -dte - 28 Feb 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
../src/remind -dte - 1 Mar 2025 <<'EOF' >> ../tests/test.out 2>&1
BANNER %
REM 29 MSG One
REM 29 Feb MSG two
REM 29 2025 MSG three
REM 29 Feb 2025 MSG four
REM Thursday 29 MSG One
REM Thursday 29 Feb MSG two
REM Thursday 29 2025 MSG three
REM Thursday 29 Feb 2025 MSG four
REM Wednesday 29 MSG One
REM Wednesday 29 Feb MSG two
REM Wednesday 29 2025 MSG three
REM Wednesday 29 Feb 2025 MSG four
REM Friday 29 MSG One
REM Friday 29 Feb MSG two
REM Friday 29 2025 MSG three
REM Friday 29 Feb 2025 MSG four
EOF
(echo 'BANNER %'; echo 'REM 29 MSG No bug') | ../src/remind -dt - 29 Feb 2024 >> ../tests/test.out 2>&1
../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
# 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

File diff suppressed because one or more lines are too long

View File

@@ -329,6 +329,7 @@ set a052 time(1+2, 3+4)
rem 10 jan 1992 AT 11:22 CAL rem 10 jan 1992 AT 11:22 CAL
set a053 trigdate() set a053 trigdate()
set a054 trigtime() set a054 trigtime()
set a054b $Tt
set a055 trigvalid() set a055 trigvalid()
set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh")
set a057 value("a05"+"6") set a057 value("a05"+"6")
@@ -381,9 +382,11 @@ msg [a076]%
set a077 dosubst("%*Y %*Z", '1992/5/5') set a077 dosubst("%*Y %*Z", '1992/5/5')
msg [a077]% msg [a077]%
set a078 easterdate(today()) set a078 easterdate(today())
set a078 easterdate()
set a079 easterdate(1992) set a079 easterdate(1992)
set a080 easterdate(1995) set a080 easterdate(1995)
set a078 orthodoxeaster(today()) set a078 orthodoxeaster(today())
set a078 orthodoxeaster()
set a079 orthodoxeaster(1992) set a079 orthodoxeaster(1992)
set a080 orthodoxeaster(1995) set a080 orthodoxeaster(1995)
set a080 orthodoxeaster(2023) set a080 orthodoxeaster(2023)
@@ -455,6 +458,7 @@ set a129 23:30 + '2019-02-02@16:44'
REM 13 AT 16:00 DURATION 72:00 MSG 72-hour event REM 13 AT 16:00 DURATION 72:00 MSG 72-hour event
set a130 trigdate() set a130 trigdate()
set a131 trigtime() set a131 trigtime()
set a131b $Tt
set a132 trigdatetime() set a132 trigdatetime()
set a133 trigduration() set a133 trigduration()
set a134 trigeventstart() set a134 trigeventstart()
@@ -773,15 +777,27 @@ 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
# Multitrig
REM [multitrig("10", "17")] MSG multitrig-1
REM [multitrig("Feb 15", "Mar 20")] MSG multitrig-2
REM [multitrig("Oct 7 1992", "1991")] MSG multitrig-3
REM [multitrig("16 Feb AFTER OMIT Sat Sun", "29 March")] MSG multitrig-4
REM [multitrig("2", "3", "5", "7")] MSG multitrig-5
REM [multitrig("15 SCANFROM -7", "14 SCANFROM -7")] MSG multitrig-6
REM [multitrig("15 SCANFROM -7", "14 SCANFROM -7")] SCANFROM -7 MSG multitrig-7
# The new syntactic sugar # The new syntactic sugar
REM First Monday January MSG x REM First Monday January MSG x
REM Second Tuesday in April MSG x REM Second Tuesday in April MSG x
@@ -908,6 +924,27 @@ set a 7 * "Cabbage! "
# Should result in errors # Should result in errors
set pqxya 1+2) set pqxya 1+2)
# Should result in an error
REM Tue OMIT 2024-01-01 MSG Wookie
# No error
REM Tue OMIT Wed 2024-01-01 MSG Blort
# Make sure trigtime() is not reset between invocations
REM Tue AT 16:00 DURATION 30 MSG Thing One
REM [$T] AT [trigtime()+trigduration()] DURATION 15 MSG Thing Two
REM [$T] AT [$Tt+trigduration()] DURATION 30 MSG Thing Three
REM [$T] AT [trigtime()+trigduration()] DURATION 10 MSG Last Thing
# Make sure trigtime is not reset during parsing
REM Tue AT 16:00 MSG blort
REM Tue AT 10:00 DURATION [$Tt] MSG blort
REM Tue AT 16:00 MSG blort
REM Tue AT 10:00 DURATION [trigtime()] MSG blort
# Make sure shellescape does not mangle UTF-8 characters
msg [shellescape("😆")]
# Don't want Remind to queue reminders # Don't want Remind to queue reminders
EXIT EXIT

View File

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

View File

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

41
www/calendar.css Normal file
View File

@@ -0,0 +1,41 @@
html, body {
padding: 0px;
margin-left: 10px;
margin-right: 10px;
font-family: "Open Sans", Arial, sans-serif
background: #FFFFFF;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 5px;
margin-bottom: 5px;
padding-top: 0px;
padding-bottom: 0px;
font-family: inherit;
font-weight: 600;
}
h1, .h1 {
font-size: 1.75rem; }
h2, .h2 {
font-size: 1.5rem; }
h3, .h3 {
font-size: 1.25rem; }
a {
text-decoration: none;
color: #1a0dab;
}
a:visited {
text-decoration: none;
color: #1a0dab;
}
a:hover {
text-decoration: none;
color: #ff0000;
}

View File

@@ -1,7 +1,8 @@
<HTML> <HTML>
<!-- Sample HTML file with links to the calendar stuff --> <!-- Sample HTML file with links to the calendar stuff -->
<HEAD> <HEAD>
<TITLE>Remind Calendar Server</TITLE> <TITLE>Remind Calendar Server</TITLE>
<LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">
</HEAD> </HEAD>
<BODY> <BODY>
@@ -15,10 +16,14 @@ Sunset Information</a><P>
Moon Phase Information</a><P> Moon Phase Information</a><P>
<a HREF="%CGIDIR%/cal_dispatch?calps"> <a HREF="%CGIDIR%/cal_dispatch?calps">
Blank PostScript Calendar</a> (Approximately 20kB)<P> Blank PostScript Calendar</a> (Approximately 20kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?calpdf">
Blank PDF Calendar</a> (Approximately 15kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?hebdate"> <a HREF="%CGIDIR%/cal_dispatch?hebdate">
Today's Hebrew Date</a><P> Today's Hebrew Date</a><P>
<a HREF="%CGIDIR%/cal_dispatch?hebps"> <a HREF="%CGIDIR%/cal_dispatch?hebps">
PostScript Calendar with Jewish Holidays</a> (Approximately 35 kB)<P> PostScript Calendar with Jewish Holidays</a> (Approximately 35 kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?hebpdf">
PDF Calendar with Jewish Holidays</a> (Approximately 20 kB)<P>
<a HREF="%CGIDIR%/cal_dispatch?hebhtml"> <a HREF="%CGIDIR%/cal_dispatch?hebhtml">
HTML Calendar with Jewish Holidays</a> HTML Calendar with Jewish Holidays</a>
<HR> <HR>

11
www/calpdf Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# PostScript calendar shell script
#
# This file is part of REMIND.
# Copyright (C) 1992-2018 by Dianne Skoll
echo "Content-type: application/pdf"
echo
$REMIND -p $DIR/blank.rem | $REM2PDF -e -c3 -l
exit 0

View File

@@ -11,6 +11,7 @@ cat <<EOM
<HTML> <HTML>
<HEAD> <HEAD>
<TITLE>Hebrew date</TITLE> <TITLE>Hebrew date</TITLE>
<LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">
</HEAD> </HEAD>
<BODY> <BODY>

View File

@@ -165,13 +165,15 @@ ENDIF
IF !$PSCAL IF !$PSCAL
REM 20 ++40 msg Also available: <a HREF="%CGIDIR%/cal_dispatch?hebps">a PostScript calendar</a> (about 35KB) for %m %y, complete with Hebrew dates, Jewish holidays, and moon phases for [$Location]. REM 20 ++40 msg Also available: <a HREF="%CGIDIR%/cal_dispatch?hebps">a PostScript calendar</a> (about 35KB) for %m %y, complete with Hebrew dates, Jewish holidays, and moon phases for [$Location].
REM 20 ++40 msg And: <a HREF="%CGIDIR%/cal_dispatch?hebhtml">an HTML version</a> of the above. REM 20 ++40 msg And: <a HREF="%CGIDIR%/cal_dispatch?hebhtml">an HTML version</a> of the above.
REM 20 ++40 msg And: <a HREF="%CGIDIR%/cal_dispatch?hebpdf">a PDF version</a> of the above.
ELSE ELSE
[trigger(moondate(0))] SPECIAL MOON 0 REM [moondate(0)] SPECIAL MOON 0
[trigger(moondate(1))] SPECIAL MOON 1 REM [moondate(1)] SPECIAL MOON 1
[trigger(moondate(2))] SPECIAL MOON 2 REM [moondate(2)] SPECIAL MOON 2
[trigger(moondate(3))] SPECIAL MOON 3 REM [moondate(3)] SPECIAL MOON 3
REM PS Border Border moveto /DayFont findfont 10 scalefont setfont ([hebday(today())] [hebmon(today())]) show REM PS Border Border moveto /DayFont findfont 10 scalefont setfont ([hebday($U)] [hebmon($U)]) show
REM SPECIAL HTML <P>[hebday(today())] [hebmon(today())]</P> REM SPECIAL HTML <P>[hebday($U)] [hebmon($U)]</P>
REM SPECIAL PANGO @4,-1 <span size="6400"><i>[hebday($U)] [hebmon($U)]</i></span>
ENDIF ENDIF

17
www/hebpdf Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
# Hebrew PostScript calendar shell script
#
# This file is part of REMIND.
# Copyright (C) 1992-2018 by Dianne Skoll
# Figure out the month: If day <= 20, use this month; otherwise, use
# next month.
echo "Content-type: application/pdf"
echo ""
$REMIND - <<EOR
BANNER %
REM 20 ++40 RUN $REMIND -iHTML=0 -p $DIR/hebdate.rem %m %y | $REM2PDF -e -c3 -l
EOR
exit 0

View File

@@ -9,6 +9,7 @@ banner %
MSG <HEAD>% MSG <HEAD>%
MSG <TITLE>Moon over [$Location]</TITLE>% MSG <TITLE>Moon over [$Location]</TITLE>%
MSG <LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">%
MSG </HEAD>% MSG </HEAD>%
MSG <BODY> MSG <BODY>

View File

@@ -1,341 +0,0 @@
<?php
class Remind
{
# For validating commands we send to popen
function is_valid_day($d) {
return (preg_match('/^\d+$/', $d)) &&
$d >= 1 && $d <= 31;
}
function is_valid_month($m) {
return
($m == 'January') ||
($m == 'February') ||
($m == 'March') ||
($m == 'April') ||
($m == 'May') ||
($m == 'June') ||
($m == 'July') ||
($m == 'August') ||
($m == 'September') ||
($m == 'October') ||
($m == 'November') ||
($m == 'December');
}
function is_valid_year($y) {
return preg_match('/^\d\d\d\d$/', $y) &&
$y >= 1900;
}
function get_el(&$array, $i)
{
if (!array_key_exists($i, $array)) return null;
return $array[$i];
}
function get_elem($array, $indexes)
{
foreach ($indexes as $i) {
if (!is_array($array)) return null;
if (!array_key_exists($i, $array)) return null;
$array = $array[$i];
}
return $array;
}
function munge_entry($day, &$results, &$specials, &$options, $str, &$e) {
return htmlspecialchars($str);
}
function format_entry($day, &$results, &$specials, &$options, &$e) {
$special = $this->get_el($e, 'special');
$body = $this->get_el($e, 'body');
if ($body === null) $body = '';
if ($special === null || $special == '*') {
return $this->munge_entry($day, $results, $specials, $options, $body, $e);
}
if ($special == 'COLOR' || $special == 'COLOUR') {
if (preg_match('/^(\d+)\s+(\d+)\s+(\d+)\s+(.*)/', $body, $matches)) {
return sprintf('<span style="color: #%02x%02x%02x">%s</span>',
$matches[1] % 255,
$matches[2] % 255,
$matches[3] % 255,
$this->munge_entry($day, $results, $specials, $options, $matches[4], $e));
}
return 'Bad COLOR spec: ' . htmlspecialchars($body);
}
# HTML is passed through un-munged.
if ($special == 'HTML') return $body;
# Ignore unknown specials
return '';
}
function format_entries($day, &$results, &$specials, &$options, &$entries) {
$html = '';
foreach ($entries as $e) {
$html .= '<div class="rem-entry">' . $this->format_entry($day, $results, $specials, $options, $e) . '</div>';
}
return $html;
}
function do_one_day($day, &$results, &$specials, &$options) {
$class = $this->get_elem($specials, array('HTMLCLASS', $day, 0, 'body'));
$shade = $this->get_elem($specials, array('SHADE', $day, 0, 'body'));
$moon = $this->get_elem($specials, array('MOON', $day, 0, 'body'));
if ($class === null) $class = 'rem-cell';
$bg = '';
if ($shade !== null) {
if (preg_match('/(\d+)\s+(\d+)\s+(\d+)/', $shade, $matches)) {
if ($matches[1] <= 255 && $matches[2] <= 255 && $matches[3] <= 255) {
$bg = sprintf(' style="background: #%02x%02x%02x"',
$matches[1], $matches[2], $matches[3]);
}
}
}
$html = "<td class=\"$class\"$bg>";
$week = $this->get_elem($specials, array('WEEK', $day, 0, 'body'));
if ($week === null) {
$week = '';
} else {
$week = ' ' . $week;
}
$moon_html = '';
if ($moon !== null) {
$phase = -1;
if (preg_match('/(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/', $moon, $matches)) {
$phase = $matches[1];
$moonsize = $matches[2];
$fontsize = $matches[3];
$msg = $matches[4];
} elseif (preg_match('/(\d+)/', $moon, $matches)) {
$phase = $matches[1];
$msg = '';
}
if ($phase >= 0) {
if ($phase == 0) {
$img = 'newmoon.png';
$title = 'New Moon';
$alt = 'new';
} elseif ($phase == 1) {
$img = 'firstquarter.png';
$title = 'First Quarter';
$alt = '1st';
} elseif ($phase == 2) {
$img = 'fullmoon.png';
$alt = 'full';
$title = 'Full Moon';
} else {
$img = 'lastquarter.png';
$alt = 'last';
$title = 'Last Quarter';
}
$base = rtrim($this->get_el($options, 'imgbase'), '/');
if ($base !== null) {
$img = $base . '/' . $img;
}
$moon_html = '<div class="rem-moon">' . "<img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">" . htmlspecialchars($msg) . '</div>';
}
}
# Day number
$html .= $moon_html . '<div class="rem-daynumber">' . $day . $week . '</div>';
# And the entries
$entries = $this->get_elem($results, array('entries', $day));
if (is_array($entries) && count($entries) > 0) {
$html .= '<div class="rem-entries">';
$html .= $this->format_entries($day, $results, $specials, $options, $entries);
$html .= '</div>';
}
$html .= "</td>\n";
return $html;
}
function small_calendar($results, $month, $monlen, $first_col, $which, &$options)
{
$monday_first = $results['monday_flag'];
if ($monday_first) {
$first_col--;
if ($first_col < 0) {
$first_col = 6;
}
}
$html = "<td class=\"rem-small-calendar\">\n<table class=\"rem-sc-table\">\n<caption class=\"rem-sc-caption\">";
# TODO: URL for small calendar
$html .= $month;
$html .= "</caption>\n";
}
function generate_html(&$results, &$specials, &$options)
{
$monday_first = $results['monday_flag'];
$first_col = $results['first_day'];
if ($monday_first) {
$first_col--;
if ($first_col < 0) $first_col = 6;
}
$last_col = ($first_col + $results['days_in_mon'] -1) % 7;
$html = '<table class="rem-cal"><caption class="rem-cal-caption">' .
htmlspecialchars($results['month']) . ' ' . htmlspecialchars($results['year']) .
"</caption>\n";
$html .= '<tr class="rem-cal-hdr-row">';
if (!$monday_first) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][0]) . '</th>';
for ($i=1; $i<7; $i++) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][$i]) . '</th>';
if ($monday_first) $html .= '<th class="rem-cal-hdr">' . htmlspecialchars($results['day_names'][0]) . '</th>';
$html .= "</tr>\n";
# Do the leading empty columns
for ($col=0; $col < $first_col; $col++) {
if ($col == 0) $html .= '<tr class="rem-cal-body-row">';
$html .= '<td class="rem-empty">&nbsp;</td>';
}
for ($day=1; $day <= $results['days_in_mon']; $day++) {
if ($col == 0) $html .= '<tr class="rem-cal-body-row">';
$col++;
$html .= $this->do_one_day($day, $results, $specials, $options);
if ($col == 7) {
$html .= "</tr>\n";
$col = 0;
}
}
if ($col) {
while ($col++ < 7) {
$html .= '<td class="rem-empty">&nbsp;</td>';
}
}
$html .= "</tr>\n";
$html .= "</table>\n";
return $html;
}
function parse_remind_output ($fp)
{
while(1) {
$line = fgets($fp);
if ($line === false) break;
$line = trim($line);
if ($line == '# rem2ps begin') break;
}
if ($line === false) {
return array('success' => 0,
'error' => 'Could not find any Rem2PS data');
}
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($month, $year, $days_in_mon, $first_day, $monday_flag) = explode(' ', $line);
$retval = array('month' => $month,
'year' => $year,
'days_in_mon' => $days_in_mon,
'first_day' => $first_day,
'monday_flag' => $monday_flag);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
$retval['day_names'] = explode(' ', $line);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($m, $n) = explode(' ', $line);
$retval['prev'] = array('month' => $m, 'days' => $n);
$line = fgets($fp);
if ($line === false) {
return array('success' => 0,
'error' => 'Unexpected end-of-file');
}
$line = trim($line);
list($m, $n) = explode(' ', $line);
$retval['next'] = array('month' => $m, 'days' => $n);
$line_info = 0;
$entries = array();
$specials = array();
while (1) {
$line = fgets($fp);
if ($line === false) break;
$line = trim($line);
if ($line == '# rem2ps end') break;
if (strpos($line, '# fileinfo ') === 0) {
list($lno, $fname) = explode(' ', substr($line, 11), 2);
$lineinfo = array('file' => $fname, 'line' => $lno);
continue;
}
list($date, $special, $tags, $duration, $time, $body) = explode(' ', $line, 6);
list($y, $m, $d) = explode('/', $date);
$d = preg_replace('/^0(.)/', '$1', $d);
$m = preg_replace('/^0(.)/', '$1', $m);
$entry = array('day' => $d,
'month' => $m,
'year' => $y,
'special' => $special,
'tags' => $tags,
'duration' => $duration,
'time' => $time,
'body' => $body);
if (is_array($lineinfo)) {
$entry['line'] = $lineinfo['line'];
$entry['file'] = $lineinfo['file'];
$lineinfo = 0;
}
if ($special != '*' && $special != 'COLOR' && $special != 'COLOUR' && $special != 'HTML') {
if (!array_key_exists($special, $specials)) {
$specials[$special] = array();
}
if (!array_key_exists($d, $specials[$special])) {
$specials[$special][$d] = array();
}
$specials[$special][$d][] = $entry;
} else {
if (!array_key_exists($d, $entries)) {
$entries[$d] = array();
}
$entries[$d][] = $entry;
}
}
$retval['entries'] = $entries;
return array('success' => 1, 'results' => $retval, 'specials' => $specials);
}
}
$fp = popen('rem -p -l', 'r');
$r = new Remind;
$ans = $r->parse_remind_output($fp);
pclose($fp);
print_r($ans);
$options = array();
#print $r->generate_html($ans['results'], $ans['specials'], $options);
?>

View File

@@ -8,6 +8,7 @@ set now now()
banner % banner %
MSG <HEAD>% MSG <HEAD>%
MSG <TITLE>Sunrise in [$Location]</TITLE>% MSG <TITLE>Sunrise in [$Location]</TITLE>%
MSG <LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">%
MSG </HEAD>% MSG </HEAD>%
MSG <BODY> MSG <BODY>

View File

@@ -10,6 +10,7 @@ banner %
MSG <HEAD>% MSG <HEAD>%
MSG <TITLE>Sunset in [$Location]</TITLE>% MSG <TITLE>Sunset in [$Location]</TITLE>%
MSG <LINK rel="stylesheet" href="%IMAGEBASE%/calendar.css">%
MSG </HEAD>% MSG </HEAD>%
MSG <BODY> MSG <BODY>