Compare commits

...

237 Commits

Author SHA1 Message Date
David F. Skoll
fdb4b302ac Update WHATSNEW with release date. 2010-06-20 16:38:44 -04:00
David F. Skoll
760d5d4a09 Fix trap to reset signal disposition to default. 2010-06-02 09:05:11 -04:00
David F. Skoll
d561f98c42 Fix syntax error. 2010-05-18 20:45:36 -04:00
David F. Skoll
ea74f276dc Don't rkrphgvba more than once. 2010-05-18 12:46:34 -04:00
David F. Skoll
42cf82a5d5 More Apple / Cygwin detection. 2010-05-17 09:32:50 -04:00
David F. Skoll
825a6700da Make "trap" command follow POSIX standard. 2010-05-17 06:59:41 -04:00
David F. Skoll
2f27645dac Handle SPECIAL COLOR correctly with -n commandline option. 2010-05-01 20:52:25 -04:00
David F. Skoll
1e5b44d063 Make handling of continued lines same as in 3.1.8. 2010-04-25 10:07:12 -04:00
David F. Skoll
099ad9945d Fix huge bug in handling of continued lines. 2010-04-25 10:03:13 -04:00
David F. Skoll
540ce3d946 Allow "timepart" to take a TIME or DATETIME. Allow "datepart" to take a
DATE or DATETIME.  Update docs.
2010-04-25 09:50:39 -04:00
David F. Skoll
ac2efcc993 Document timepart() and datepart(). 2010-04-25 09:40:01 -04:00
David F. Skoll
b4bb1d9e37 Update copyright date. 2010-04-25 09:34:49 -04:00
David F. Skoll
9ec8b30934 Update copyright date. 2010-04-22 11:45:19 -04:00
David F. Skoll
fb38e5b35b Update WHATSNEW. 2010-04-21 16:01:04 -04:00
David F. Skoll
5fa10e9d84 Document Purge Mode. 2010-04-21 14:55:28 -04:00
David F. Skoll
3bc1b5bd46 Clean up tests; add test for non-parseable line in purge mode. 2010-04-21 13:30:10 -04:00
David F. Skoll
b266b399ba Test for fix of parse error.
Try REALLY hard to detect constant expressions...
2010-04-21 13:23:26 -04:00
David F. Skoll
0e827a457d Fix YomHashoah date and bug in DoSatReminder. 2010-04-21 09:49:33 -04:00
David F. Skoll
d05d85b243 More tests. 2010-04-21 09:04:48 -04:00
David F. Skoll
a1faa8d804 Finish up Purge Mode:
o Make it recognize constant expressions (yay!)
o Make it not add a blank line to end of *.purged files.
o Make it nuke #!P comments in the source files.
2010-04-21 09:02:25 -04:00
David F. Skoll
70adbf90c2 Add tests for purge mode. 2010-04-20 15:20:49 -04:00
David F. Skoll
501c04f4b6 More work on purge mode. 2010-04-20 14:01:54 -04:00
David F. Skoll
d294e62be1 More work on purge mode. 2010-04-20 11:34:07 -04:00
David F. Skoll
31f90b6c53 Add purge mode. 2010-04-20 10:59:57 -04:00
David F. Skoll
184d29c592 More work on -j ("Purge Mode") option. 2010-04-19 15:57:35 -04:00
David F. Skoll
2d798bc4ec Add support for DURATION in TkRemind. Patch from Marek Marczykowski. 2010-04-19 14:13:48 -04:00
David F. Skoll
bd9a3761a6 Sort PSFILE and PS reminders with same priority (Ticket #17581) 2010-04-19 13:17:00 -04:00
David F. Skoll
b1063b0a9e More purge-mode work. 2010-04-09 11:01:45 -04:00
David F. Skoll
cb2fffab92 Don't expire lines containing expressions. 2010-04-07 15:32:20 -04:00
David F. Skoll
55cca49428 Start implementing a "-j" mode to purge old reminders. 2010-04-07 15:30:29 -04:00
David F. Skoll
5a0ab9ed8a Fix shade.rem to be independent of location and time zone of test machine. 2010-03-10 11:14:36 -05:00
David F. Skoll
1f2cfdfd84 Bump version to 03.01.09. 2010-03-09 10:05:56 -05:00
David F. Skoll
dfd688caa6 Update WHATSNEW date. 2010-03-09 09:55:00 -05:00
David F. Skoll
830e3e339c Make it only mildly annoying to compile on Windows and Mac OS X. 2010-03-06 17:39:39 -05:00
David F. Skoll
bc9f560950 Update WHATSNEW file. 2010-03-03 11:18:35 -05:00
David F. Skoll
f91b748f50 I don't like Microsoft or Apple. So the ./configure script will bail out on those platforms. 2010-03-02 19:15:40 -05:00
David F. Skoll
3adbd7468c Update "WINDOWS" file; rename it to MICROSOFT-AND-APPLE. 2010-03-02 19:06:06 -05:00
David F. Skoll
b66039ee24 Ignore msgprefix()/msgsuffix() on RUN-type reminders. 2010-02-26 09:48:18 -05:00
David F. Skoll
27371f6475 Document new $T, $U system variables. 2009-12-29 21:26:11 -05:00
David F. Skoll
19e7c34b47 Bump version to 03.01.08. 2009-12-29 10:52:06 -05:00
David F. Skoll
3375aff3a7 Add $U, $Um, $Ud, $Uy and $Uw special variables. Just like the $T.. versions,
but using today() instead of trigdate().
2009-12-29 08:47:07 -05:00
David F. Skoll
41e9cb5ca3 Clean up handling of SPECIAL_TYPE variales. Make
$T return 1990-01-01 if last trigger date was not valid.
2009-12-26 13:06:50 -05:00
David F. Skoll
6348860adf Update test for new debugging output. 2009-12-26 00:28:16 -05:00
David F. Skoll
4e9d401002 Add $T, $Td, $Tm, $Tw and $Ty system variables as shorthands for
trigdate(), day(tridate()), month(trigdate()), wkdayno(trigdate()) and
year(trigdate())
2009-12-26 00:26:14 -05:00
David F. Skoll
3db2fc7d0f Make "$t" a synonym for "trigdate()" 2009-12-22 18:25:01 -05:00
David F. Skoll
0101d76113 Better -df output. 2009-11-16 00:09:40 -05:00
David F. Skoll
6fabab6d4b Add remind-conf-mode 2009-11-03 13:32:10 -05:00
David F. Skoll
905f1b0aaf Add some scripts in contrib/ 2009-11-03 13:25:17 -05:00
David F. Skoll
6a5ab8e70c Update man page. 2009-11-01 09:54:28 -05:00
David F. Skoll
943b037589 Better iconphoto. 2009-10-30 22:08:26 -04:00
David F. Skoll
5624cd44de Set a nice iconphoto. 2009-10-30 13:52:19 -04:00
David F. Skoll
8529f8b843 Fix bugs in Makefile.in 2009-10-15 15:19:56 -04:00
David F. Skoll
08be7c43cf Don't make SHADE specials obliterate MOON specials. Fixes ticket 15068. 2009-10-05 14:33:51 -04:00
David F. Skoll
aae76207ec Fix bug in SCHED function calculation (RT #15644) 2009-09-06 18:36:34 -04:00
David F. Skoll
5434f8330e Sort specials. 2009-09-06 18:36:27 -04:00
David F. Skoll
e18b4ed119 Update copyright. 2009-05-31 13:06:05 -04:00
David F. Skoll
51f6ffc093 Document changes. 2009-05-31 13:05:05 -04:00
David F. Skoll
72de7c6b14 Make CallFunc and built-in functions re-entrant. 2009-05-26 22:35:57 -04:00
David F. Skoll
bd4785d631 Remove spurious trigger() function calls from examples.
Bump version to 3.1.7
2009-05-16 10:56:12 -04:00
David F. Skoll
cb08f12470 Add another test. 2009-05-16 10:47:54 -04:00
David F. Skoll
04146db69b More man page updates. 2009-05-16 10:47:10 -04:00
David F. Skoll
d3fe045a39 Make slide function diagnose "Can't OMIT every weekday" 2009-05-16 10:42:43 -04:00
David F. Skoll
2be1e16087 Add some tests. 2009-05-16 10:30:25 -04:00
David F. Skoll
051e44ae3e Update man page. 2009-05-16 10:22:43 -04:00
David F. Skoll
6d5ae7a258 Allow short-hand date specs after UNTIL and SCANFROM.
Start updating man page.
2009-05-16 10:07:57 -04:00
David F. Skoll
2e69e140eb Accept a T_Date or T_DateTime on cmdline to set now() and today(). 2009-05-12 22:46:01 -04:00
David F. Skoll
516c4e2c39 Allow literal dates and datetimes in REM and OMIT statements.
This lets us avoid cluttering up files with [trigger(expr)]; we can
use [expr] directly.
2009-05-09 16:41:15 -04:00
David F. Skoll
b58ed62000 Simplify. 2009-05-09 00:17:11 -04:00
David F. Skoll
c685818783 Use macro to refer to RetVal.v.val 2009-05-09 00:10:45 -04:00
David F. Skoll
3e20ce56c9 Add the slide(date, amt [,localomits]) built-in function. 2009-05-09 00:07:59 -04:00
David F. Skoll
dd8d67f659 Update man page. 2009-02-03 23:11:50 -05:00
David F. Skoll
5ef4061819 Let Remind accept a date of the form YYYY-MM-DD on cmdline. 2009-02-03 23:08:27 -05:00
David F. Skoll
a146a0c819 Update WHATSNEW; fix typo in man page. 2008-11-16 11:01:01 -05:00
David F. Skoll
0f5a93cdf1 Fix up man page. 2008-11-16 10:51:10 -05:00
David F. Skoll
7db51e913e Clarify WEEK example. 2008-11-15 09:24:09 -05:00
David F. Skoll
93ac1aebf7 Document WEEK special. Clean up rem2ps handling of WEEK. 2008-11-15 09:20:54 -05:00
David F. Skoll
56ebedb9de Proper implementation of WEEK special. 2008-11-14 17:02:51 -05:00
David F. Skoll
e87d33de50 Handle the WEEK special. 2008-11-14 16:56:08 -05:00
David F. Skoll
a5774fd793 Document weekno() function. 2008-11-14 16:16:13 -05:00
David F. Skoll
adb60f3400 Fix example in man page. 2008-11-14 16:07:25 -05:00
David F. Skoll
47a0f63caa Fix tests. 2008-10-05 14:29:31 -04:00
David F. Skoll
30a385a7bb Fix bug in IsTopLevel. 2008-10-05 14:29:03 -04:00
David F. Skoll
9f2ae1773c Document evaltrig. 2008-10-05 14:11:58 -04:00
David F. Skoll
f470297958 Make max. string length controllable with $MaxStringLen system variable. 2008-10-05 13:49:45 -04:00
David F. Skoll
93dfc7995c Fix man page. 2008-09-23 22:39:12 -04:00
David F. Skoll
c96869817f Rejigger infrastructure to detect recursive OMITFUNC calls in evaltrig() 2008-09-23 22:21:39 -04:00
David F. Skoll
1bffa0e00b Allow any of the args to date() function to be a date,
in which case we just extract the appropriate part.
2008-09-20 11:07:33 -04:00
David F. Skoll
36035dda01 Issue a warning if we detect a SCANFROM used in two-arg evaltrig(). 2008-09-20 00:14:13 -04:00
David F. Skoll
50a15cf089 Make evaltrig return a DATETIME if there's an AT clause. 2008-09-20 00:03:38 -04:00
David F. Skoll
b59d4ed4a4 Add "evaltrig" function. 2008-09-19 23:49:11 -04:00
David F. Skoll
4c53710279 Set maximum string length to 65535 to minimize chances of self-DOS. 2008-09-19 23:28:19 -04:00
David F. Skoll
2274e88485 Remove extraneous semicolon 2008-09-19 23:10:57 -04:00
David F. Skoll
984a4677f2 Clean up funcs.c; add weekno function for ISO week number. 2008-09-19 23:08:15 -04:00
David F. Skoll
5e3dc54725 Document sorting of timed vs. untimed reminders.
Add an $UntimedFirst system variable.
2008-09-18 17:30:31 -04:00
David F. Skoll
53a7bf9773 Make tests pass. 2008-09-18 17:20:09 -04:00
David F. Skoll
1dcc3897d6 Allow sorting of untimed before timed. 2008-09-18 17:17:38 -04:00
David F. Skoll
fbc0375739 Fix bug if MON DAY WKDAY crosses year boundary. 2008-09-16 22:10:09 -04:00
David F. Skoll
4a74c03fa4 Small man-page fix. 2008-08-31 15:51:25 -04:00
David F. Skoll
d8833e6ce2 Updated man page. 2008-08-31 15:44:57 -04:00
David F. Skoll
2b8b9a56aa Fix typo. 2008-08-31 10:32:25 -04:00
David F. Skoll
85a8ebb357 Slight efficiency improvement in OMIT testing. 2008-08-31 10:30:59 -04:00
David F. Skoll
a67d9e2524 Change behaviour: OMITFUNC ignores local/global OMIT context. Only sane
way to solve garbage-day problem; also allows for more flexibility.
2008-08-31 10:29:28 -04:00
David F. Skoll
6845e72fd5 Communicate errors back for bad OMITFUNCs. 2008-08-30 22:10:06 -04:00
David F. Skoll
0ab977fa05 Whitespace cleanup 2008-08-30 21:43:38 -04:00
David F. Skoll
ad64a12b2a Update man page. 2008-08-30 16:35:48 -04:00
David F. Skoll
cfde4b50bb Updated man page. 2008-08-29 21:28:25 -04:00
David F. Skoll
f302c8eb95 Document OMITFUNC and fix huge bug. :-) 2008-08-29 21:22:48 -04:00
David F. Skoll
7ecc355358 Add test for OMITFUNC. 2008-08-29 21:03:01 -04:00
David F. Skoll
a52216d7e7 Update test.cmp for version. 2008-08-29 20:56:22 -04:00
David F. Skoll
8d8d4b667d Implement OMITFUNC clause in REM command. 2008-08-29 20:48:58 -04:00
David F. Skoll
670369121e Remove -a flag from "ShowTodaysReminders" 2008-08-28 12:29:38 -04:00
David F. Skoll
4365e580ee If main reminder file is a directory and AppendFile does not exist,
create AppendFile.
2008-04-16 21:54:05 -04:00
David F. Skoll
a8a09a905e Update test.cmp for new version. 2008-04-16 21:45:58 -04:00
David F. Skoll
dbb7381eb1 Update version number in configure. 2008-04-16 21:44:44 -04:00
David F. Skoll
aa3531a6bf Merge branch 'master' of ssh://source.roaringpenguin.com/home/dfs/personal-git-repos/Remind 2008-04-16 13:35:36 -04:00
David F. Skoll
2848203d75 Note new behaviour: set var [expr] no longer works. 2008-04-16 13:34:38 -04:00
David F. Skoll
f0aef0703c Update WHATSNEW file. 2008-04-15 18:26:25 -04:00
David F. Skoll
7356a511e0 Update release notes and man page. 2008-04-14 21:30:35 -04:00
David F. Skoll
5be26a3cff Add "c" flag to -c option that causes Remind to approximate
SPECIAL COLOR reminders with vt100 escape sequences.
2008-04-14 21:03:45 -04:00
David F. Skoll
36a80acb0c Make tkremind handle the case when ~/.reminders is a directory better.
It uses ~/.reminders/100-tkremind.rem as the append file.
2008-03-31 14:00:09 -04:00
David F. Skoll
37ab03b49d Make tkremind work better on small-screen devices like eeepc. 2008-03-30 12:11:07 -04:00
David F. Skoll
75209cb40e Update tkremind copyright date. 2008-03-27 22:13:24 -04:00
David F. Skoll
4b2d274748 Add "l" flag to -c to draw calendar
using VT100 line-drawing characters.
2008-03-27 22:04:06 -04:00
David F. Skoll
1423495b88 Document -tn option
Implement "-ca" which is analagous to "-pa" and "-sa".
2008-03-26 21:51:30 -04:00
David F. Skoll
42e4ec8890 Make DEBUG command handle "f" flag. 2008-03-25 17:21:07 -04:00
David F. Skoll
58fbd51ef4 Call globfree to free memory used by glob. 2008-03-24 23:12:27 -04:00
David F. Skoll
7552dbb812 Update copyright. Put -df in online usage blurb. 2008-03-24 21:36:29 -04:00
David F. Skoll
31e0ecb71d Fix tests for version change. 2008-03-24 20:42:19 -04:00
David F. Skoll
f6743a4f87 Bump version to 03.01.05. 2008-03-24 20:41:12 -04:00
David F. Skoll
5f574949e6 Nicer -df output. Document -df. 2008-03-24 20:39:47 -04:00
David F. Skoll
724bce4fb9 Add "-df" debugging flag to track file opens. 2008-03-24 20:22:00 -04:00
David F. Skoll
8d59f025af Patch from Ian! Allen. 2008-03-24 14:10:01 -04:00
David F. Skoll
aad44f4716 Spit out errors if we can't open a non-readable file in a dir/*.rem. 2008-03-24 14:02:39 -04:00
David F. Skoll
0a38d7d213 Output fileinfo lines in tests. 2008-03-24 13:56:51 -04:00
David F. Skoll
4fcd60fd95 Increase INCLUDE_DEPTH to account for (wasted) top-of-stack entry.
Document that filename argument for INCLUDE and command-line can be a directory.
2008-03-24 13:54:21 -04:00
David F. Skoll
38beeb8cb0 Add some tests for INCLUDE functionality. 2008-03-24 13:29:31 -04:00
David F. Skoll
26f0acab35 Opening of directories works properly. 2008-03-24 13:16:28 -04:00
David F. Skoll
3897973bc1 Improve error messages. 2008-03-24 13:11:21 -04:00
David F. Skoll
7b3da27461 Fix parse error bug in calendar mode. 2008-03-24 13:02:05 -04:00
David F. Skoll
a85ee5f31c INCLUDE /directory works. Still need more futzing to fix parse error. 2008-03-24 12:58:23 -04:00
David F. Skoll
b272863aa0 Prep for push. 2008-03-24 10:00:34 -04:00
David F. Skoll
8249964380 Start coding feature allowing "INCLUDE /directory" which basically
includes "/directory/*.rem" files in glob-order.
2008-03-22 23:28:51 -04:00
David F. Skoll
af81bbb299 Preserve "\n" in continued lines.
Set a flag if parser encounters [expr].
2008-03-15 14:06:12 -04:00
David F. Skoll
d818dd9c80 Fix bug in AM/PM option setting for existing reminders. 2008-03-09 11:23:27 -04:00
David F. Skoll
fe8e34dece Better error message if nonsensical -i option given. 2008-02-06 23:42:12 -05:00
David F. Skoll
ba928989f7 Make expression parser accept backslashes in strings so we can do:
SET a "Some \"embedded\" quotes"
2008-02-06 23:35:46 -05:00
David F. Skoll
742f973028 Allow command-line '-i' option to define a function as well as a var.
Example:  remind ... '-isquare(x)=x*x' ...

Also, allow the syntax:

      SET var = expr
      FSET func(args) = defn

Old syntax still works, of course:

      SET var expr
      FSET func(args) defn
2008-02-06 23:12:51 -05:00
David F. Skoll
043932fcbe Add a $DeltaOffset read-only system variable that is set by "-tNNN" option. 2008-02-06 22:24:45 -05:00
David F. Skoll
ad5e096caa Make '-t' take a numeric argument that sets the "DeltaOffset" to that
value.  We trigger any reminder that will trigger between today and
today+DeltaOffset days.  A bare '-t' sets InfiniteDelta as before.
2008-02-06 22:16:47 -05:00
David F. Skoll
06c8fc8f78 Oops... forgot a release note. 2008-02-03 17:14:31 -05:00
David F. Skoll
487338d10c Updated for 3.1.4 release. 2008-02-03 17:12:16 -05:00
David F. Skoll
3d1c0e5667 Fix awful bug in rem2html for a 28-day February that starts on Sunday... 2008-01-31 23:31:45 -05:00
David F. Skoll
ce3bb191c4 More code cleanups and const-correctness. 2008-01-30 21:37:19 -05:00
David F. Skoll
03e9dbf364 More const qualification. 2008-01-28 11:39:15 -05:00
David F. Skoll
518aab3d02 Don't trash argv[] so reread works properly. 2008-01-28 11:28:17 -05:00
David F. Skoll
f055664f77 More const-correctness. 2008-01-27 11:02:03 -05:00
David F. Skoll
ba3e01f2ce Add a bunch of const qualifiers. 2008-01-26 21:55:58 -05:00
David F. Skoll
b6883c8a45 Keep version number in ONE place (configure.in) instead of two. 2008-01-26 15:34:32 -05:00
David F. Skoll
9e49ee3acb Make tkremind obey -b1 option (24-hour mode.) 2008-01-26 12:01:43 -05:00
David F. Skoll
7481f68bb2 Make tkremind work better with Tk8.5. 2007-12-24 11:40:22 -05:00
David F. Skoll
99792eec03 Add a test. 2007-11-05 11:52:50 -05:00
David F. Skoll
91458c7845 Better patch for parsing bug. 2007-11-05 11:44:48 -05:00
David F. Skoll
cad5849dc0 Merge branch 'master' of ssh://magnesium.roaringpenguin.com/home/dfs/personal-git-repos/Remind 2007-11-05 11:38:17 -05:00
David F. Skoll
a38e120e99 Fix weird parsing bug if you have a SKIP on an omitted day with a SATISFY. 2007-11-05 11:37:48 -05:00
David F. Skoll
0b31a78cf2 Patch to clean up HTML output (Jorey Bump) 2007-10-16 11:46:58 -04:00
David F. Skoll
861858c1a6 Bump version to 03.01.04. 2007-10-15 14:17:09 -04:00
David F. Skoll
d43a2277da Updated WHATSNEW. 2007-10-15 14:00:09 -04:00
David F. Skoll
f678dc96b8 Write man page for rem2html 2007-10-07 22:24:35 -04:00
David F. Skoll
c2e371904e Update distclean target. 2007-10-07 22:10:25 -04:00
David F. Skoll
2a1cbff22e Add www/Makfile to gitignore. 2007-10-07 22:09:47 -04:00
David F. Skoll
a2d9f277c4 Minor tweaks to Makefile.in 2007-10-07 22:09:37 -04:00
David F. Skoll
d939059417 Improve www/Makefile stuff. 2007-10-07 22:05:35 -04:00
David F. Skoll
740cd4cfed Update README. 2007-10-06 11:41:40 -04:00
David F. Skoll
91a5306a67 Fix typo. 2007-10-06 11:38:23 -04:00
David F. Skoll
b78ffcbffd Tidy up option processing. 2007-10-06 11:35:57 -04:00
David F. Skoll
4f72f557b7 Minor fix to --nostyle output. 2007-10-05 11:52:16 -04:00
David F. Skoll
e3b21d6d0d More tweaks. 2007-10-04 22:02:27 -04:00
David F. Skoll
af28c49d8d Fixed up the www stuff. 2007-10-04 22:01:09 -04:00
David F. Skoll
67f4635573 Fix DST rules. 2007-10-04 13:41:42 -04:00
David F. Skoll
5f8d10908d Minor cleanups. 2007-10-03 12:32:41 -04:00
David F. Skoll
7307421812 Add --nostyle option to rem2html so it can produce basic HTML that does
not rely on an external stylesheet.
2007-10-02 18:47:38 -04:00
David F. Skoll
5cd3b628c5 Minor tweaks. 2007-10-02 14:03:19 -04:00
David F. Skoll
29e483ab64 Updated Makefile and rem2html to use IMGBASE for stylesheet also. 2007-10-02 11:31:06 -04:00
David F. Skoll
66ddba45af Add support for HTMLCLASS special. Add CSS code for rem-today class to
highlight "today".
2007-10-02 11:21:40 -04:00
David F. Skoll
9325b3f035 Minor tweaks to CSS. 2007-10-01 18:03:21 -04:00
David F. Skoll
522c4c94e1 Add --imgbase option. 2007-10-01 17:59:33 -04:00
David F. Skoll
730505cff3 Less verbose. 2007-10-01 17:27:58 -04:00
David F. Skoll
019783e1fa Redo rem2html. Uses CSS instead of hard-coded stuff. 2007-10-01 17:21:59 -04:00
David F. Skoll
8612bb6480 Do not evaluate SATISFY expression if reminder has expired. 2007-09-26 08:41:34 -04:00
David F. Skoll
7f1e6d21d9 Update version number and copyright year. 2007-09-24 10:32:06 -04:00
David F. Skoll
e6c0371be1 Make calendar times show duration. 2007-09-16 17:44:33 -04:00
David F. Skoll
58feaa7e0c Update version in test.cmp 2007-09-12 10:08:04 -04:00
David F. Skoll
a828378466 Update version. 2007-09-12 09:55:40 -04:00
David F. Skoll
53316e8b06 Update WHATSNEW for 3.1.2 release. 2007-09-11 21:41:00 -04:00
David F. Skoll
6127e390a1 Make tkremind notify when firing up editor. 2007-09-10 16:56:10 -04:00
David F. Skoll
4caa77f6b1 Make PgUp and PgDn work like Left and Right, respectively. 2007-09-06 14:25:52 -04:00
David F. Skoll
70419e8757 Found dates for almost all releases. 2007-09-05 17:25:50 -04:00
David F. Skoll
21d8bd1406 Add dates (where known) to releases. 2007-09-05 17:07:48 -04:00
David F. Skoll
25abee3259 Ensure that tkremind draws moons before anything else. 2007-09-05 16:20:33 -04:00
David F. Skoll
2d490c2b7a Clarify WHATSNEW 2007-09-05 15:52:57 -04:00
David F. Skoll
902b7895b7 Merge branch 'master' of ssh://magnesium.roaringpenguin.com/home/dfs/personal-git-repos/Remind 2007-09-05 07:04:36 -04:00
David F. Skoll
1c80bb649f Trigger queued reminders at least once even if we hibernate past trigger time. 2007-09-05 07:04:11 -04:00
David F. Skoll
4508215617 Merge branch 'master' of ssh://magnesium.roaringpenguin.com/home/dfs/personal-git-repos/Remind 2007-09-04 21:58:37 -04:00
David F. Skoll
739b68347a Update WHATSNEW 2007-09-04 21:53:33 -04:00
David F. Skoll
d834760cda Merge branch 'master' of ssh://magnesium.roaringpenguin.com/home/dfs/personal-git-repos/Remind 2007-09-04 21:46:16 -04:00
David F. Skoll
1bc78650bf Exit in queue mode if date rolls over. 2007-09-04 21:44:59 -04:00
David F. Skoll
ec4e203099 Fix typo that caused SEGV. 2007-09-04 14:38:46 -04:00
David F. Skoll
d23528da82 Don't trigger reminders if they are more than 60 seconds in the past.
We want to avoid a huge flurry of triggering up emerging from suspend
or hibernation on a laptop.
2007-09-04 14:24:32 -04:00
David F. Skoll
7401a0ba23 In queue mode, wake up once a minute to recalibrate sleep time. Required
for laptops that hibernate or suspend.
2007-09-04 14:16:12 -04:00
David F. Skoll
b14b4f08b1 build.tk tries to reuse existing settings if an installed version of Remind
is found.
2007-09-03 21:37:58 -04:00
David F. Skoll
70ece81340 Flash buttons before invoking. 2007-09-03 10:23:15 -04:00
David F. Skoll
19ac350527 Silence gcc warning on Ubuntu Feisty. 2007-09-03 10:09:07 -04:00
David F. Skoll
a9b08d7db8 Updated docs. 2007-09-03 07:06:53 -04:00
David F. Skoll
df8fa05632 Note that psmoon and psshade are deprecated. 2007-09-03 06:48:38 -04:00
David F. Skoll
6556137aad Warn about deprecated psmoon and psshade functions. 2007-09-03 06:46:58 -04:00
David F. Skoll
aa1ab2bbb4 Don't sort SPECIALs ahead of normal reminders. Allow all SPECIALs to include
times; back-ends can use or ignore them as they see fit.
2007-09-03 06:28:34 -04:00
David F. Skoll
be38d86521 Make non-PSFile and non-PostScript SPECIALs preserve time
in -p field.
2007-09-02 23:02:25 -04:00
David F. Skoll
3931afb6b1 Remove all mention of remind-all. 2007-08-31 23:37:19 -04:00
David F. Skoll
7d2df86b75 Remove mention of remind-all 2007-08-31 23:36:06 -04:00
David F. Skoll
5a3bd5839b Tweak "make distro" 2007-08-31 23:29:39 -04:00
David F. Skoll
b91ce9a3f7 Incorporate rem's functionality directly into Remind. 2007-08-31 23:10:40 -04:00
David F. Skoll
ac889dd329 Corrections to Jewish Holidays from Art Werschulz. 2007-08-31 11:37:40 -04:00
David F. Skoll
420df9b6ed Bump version to 03.01.02. 2007-08-31 11:06:19 -04:00
David F. Skoll
f862b6811d Add test for REM AT ... SPECIAL COLOR ... 2007-08-31 11:02:26 -04:00
David F. Skoll
c072698927 Handle queued SPECIAL COLOR reminders properly. 2007-08-31 11:00:49 -04:00
David F. Skoll
d22895831b Fix enter/leave events. 2007-08-31 10:32:01 -04:00
David F. Skoll
3c41f4ade4 add rem2ps to gitignore. 2007-08-30 23:31:42 -04:00
David F. Skoll
335d5fb984 Don't include .gitignore when making distro. 2007-08-30 23:30:51 -04:00
David F. Skoll
66bd2b8fac Add a .gitignore file. 2007-08-30 23:29:20 -04:00
David F. Skoll
fed3cad3b8 Fix regression that broke non-COLOR specials. 2007-08-30 23:23:45 -04:00
David F. Skoll
b20578f24f Add and document keyboard shortcuts to tkremind. 2007-08-30 22:31:11 -04:00
David F. Skoll
7a86e7f777 Use spacing rather than "....." to separate reminders in TkRemind. 2007-08-30 22:19:50 -04:00
David F. Skoll
16c93fcad6 Add rem.1 man page to install target. 2007-08-30 21:58:28 -04:00
David F. Skoll
edd66d8d89 Added man page for rem script.
Added more tests.
2007-08-30 21:57:30 -04:00
David F. Skoll
3ba39b6efe Add "rem" Perl script. 2007-08-30 17:33:26 -04:00
David F. Skoll
c656a1a169 Make SPECIAL COLOR always output color triplet in calendar mode even with %"%" escapes. 2007-08-30 15:40:25 -04:00
David F. Skoll
19f03e419a Fix test.cmp for new version. 2007-08-22 20:32:49 -04:00
93 changed files with 12741 additions and 6145 deletions

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
*.bak
autom4te.cache
config.log
config.status
src/Makefile
www/Makefile
*.o
src/config.h
src/remind
src/*.tar.gz*
tests/test.out
.gitignore
*~
src/rem2ps
src/version.h

View File

@@ -3,7 +3,7 @@ THE REMIND COPYRIGHT
1. REMIND refers to the entire set of files and documentation in the
REMIND package.
2. REMIND is Copyright 1999-2005 Roaring Penguin Software Inc.,
2. REMIND is Copyright 1999-2010 Roaring Penguin Software Inc.,
except where noted in individual files.
3. DISTRIBUTION AND USE

30
MICROSOFT-AND-APPLE Normal file
View File

@@ -0,0 +1,30 @@
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.
--
David F. Skoll

View File

@@ -24,9 +24,14 @@ clean:
find . -name '*~' -exec rm {} \;
cd src; $(MAKE) clean
test:
@cd src && $(MAKE) test
distclean: clean
rm -f config.cache config.log config.status src/Makefile src/config.h tests/test.out
rm -f config.cache config.log config.status src/Makefile src/config.h tests/test.out www/Makefile
src/Makefile: src/Makefile.in
./configure
# DO NOT DELETE

15
WINDOWS
View File

@@ -1,15 +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.
--
David F. Skoll.

120
build.tk
View File

@@ -41,6 +41,8 @@ proc SetConfigDefaults {} {
set Config(NORTHERN_HEMISPHERE) 1
set Config(WESTERN_HEMISPHERE) 1
set Config(LANGUAGE) "English"
set Config(INST_DIR) "/usr/local/bin"
set Config(MAN_DIR) "/usr/local/man"
}
#***********************************************************************
@@ -97,7 +99,7 @@ proc CreateMainDialog {} {
destroy .c
update idletasks
SetConfigDefaults
SetConfigFromRemind
tabnotebook_create .tn
set Instdir [tabnotebook_page .tn "Installation Directories"]
@@ -128,13 +130,14 @@ proc CreateMainDialog {} {
# Creates the "installation directories" dialog.
#***********************************************************************
proc CreateInstallDirDialog { w } {
global Config
label $w.binlabel -text "Location for programs: "
entry $w.bin -width 30
$w.bin insert end "/usr/local/bin"
$w.bin insert end $Config(INST_DIR)
label $w.manlabel -text "Location for man pages: "
entry $w.man -width 30
$w.man insert end "/usr/local/man"
$w.man insert end $Config(MAN_DIR)
text $w.blurb -width 1 -height 5 -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.)"
@@ -863,10 +866,121 @@ proc doLogo {} {
.c create text 4 28 -anchor nw -text "http://www.roaringpenguin.com" \
-fill red -font {-family courier -size -14 -weight bold}
set rem [FindRemind]
if {$rem != ""} {
.c create text 4 52 -anchor nw -text "Found existing Remind... reusing settings" -fill red -font {-family courier -size -14 -weight bold}
.c create text 4 66 -anchor nw -text "from $rem" -fill red -font {-family courier -size -14 -weight bold}
}
update idletasks
after 2500
}
#***********************************************************************
# %PROCEDURE: FindRemind
# %ARGUMENTS:
# None
# %RETURNS:
# Full path to an existing "remind" if one is found. Otherwise,
# empty string.
#***********************************************************************
proc FindRemind {} {
global env
set path [concat [split $env(PATH) ":"] "/bin" "/usr/bin" "/usr/local/bin"]
foreach thing $path {
if [file executable [file join $thing "remind"]] {
return [file join $thing "remind"]
}
}
return {}
}
#***********************************************************************
# %PROCEDURE: SetConfigFromRemind
# %ARGUMENTS:
# None
# %RETURNS:
# Sets config settings based on existing remind (if one found) or else
# sensible defaults.
#***********************************************************************
proc SetConfigFromRemind {} {
global Config
SetConfigDefaults
set rem [FindRemind]
if {"$rem" == ""} {
return
}
set dir [file dirname $rem]
set Config(INST_DIR) $dir
if {"$dir" == "/usr/local/bin"} {
set Config(MAN_DIR) "/usr/local/man"
} elseif {$dir == "/usr/bin"} {
set Config(MAN_DIR) "/usr/share/man"
}
# Check for existing man page
if {[file readable "/usr/share/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/share/man"
} elseif {[file readable "/usr/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/man"
} elseif {[file readable "/usr/local/man/man1/remind.1"]} {
set Config(MAN_DIR) "/usr/local/man"
}
# Query Remind for the rest
QueryRemind $rem LAT_DEG {$LatDeg}
QueryRemind $rem LAT_MIN {$LatMin}
QueryRemind $rem LON_DEG {$LongDeg}
QueryRemind $rem LON_MIN {$LongMin}
QueryRemind $rem LOCATION {$Location}
QueryRemind $rem DATESEP {$DateSep}
QueryRemind $rem TIMESEP {$TimeSep}
QueryRemind $rem LANGUAGE {language()}
set $Config(LAT_MIN) [expr abs($Config(LAT_MIN))]
if {$Config(LAT_DEG) >= 0} {
set Config(NORTHERN_HEMISPHERE) 1
} else {
set Config(NORTHERN_HEMISPHERE) 0
set Config(LAT_DEG) [expr abs($Config(LAT_DEG))]
}
set $Config(LON_MIN) [expr abs($Config(LON_MIN))]
if {$Config(LON_DEG) >= 0} {
set Config(WESTERN_HEMISPHERE) 1
} else {
set Config(WESTERN_HEMISPHERE) 0
set Config(LON_DEG) [expr abs($Config(LON_DEG))]
}
# Get default page from rem2ps
set rem2ps [file join $dir "rem2ps"]
catch {
exec $rem2ps -m help
} err
set errlist [split $err "\n"]
set err [lindex $errlist end]
if {[string match "Default media type is*" $err]} {
set Config(DEFAULT_PAGE) [lindex $err end]
}
}
proc QueryRemind { rem symbol rem_msg } {
global Config
catch {
set fp [open "| $rem -" "r+"]
puts $fp "banner %\nMSG \[$rem_msg\]%\nFLUSH\n"
flush $fp
gets $fp line
catch { close $fp }
}
if {"$line" == ""} {
return
}
set Config($symbol) $line
}
CheckSanity
CreateMainDialog

5337
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,36 @@ EOF
AC_CONFIG_HEADER(src/config.h)
if test "`uname -s`" = "Darwin" ; then
trap 'echo Be patient...' INT TERM
cat <<'EOF'
Please don't use Apple products. This script will continue in 30 seconds
if you insist on compiling Remind on Mac OS X.
EOF
for i in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ; do
sleep 1
done
trap - INT
trap - TERM
fi
if uname -s | grep -i -q 'cygwin' ; then
trap 'echo Be patient...' INT TERM
cat <<'EOF'
Please don't use Microsoft products. This script will continue in 30
seconds if you insist on compiling Remind on Cygwin.
EOF
for i in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ; do
sleep 1
done
trap - INT
trap - TERM
fi
dnl Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL
@@ -31,7 +61,7 @@ AC_CHECK_SIZEOF(unsigned int)
AC_CHECK_SIZEOF(unsigned long)
dnl Checks for header files.
AC_CHECK_HEADERS(sys/file.h)
AC_CHECK_HEADERS(sys/file.h glob.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_STRUCT_TM
@@ -44,5 +74,7 @@ if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wstrict-prototypes"
fi
AC_CHECK_FUNCS(setenv unsetenv)
AC_OUTPUT(src/Makefile)
AC_CHECK_FUNCS(setenv unsetenv glob)
VERSION=03.01.09
AC_SUBST(VERSION)
AC_OUTPUT(src/Makefile www/Makefile src/version.h)

10
contrib/README Normal file
View File

@@ -0,0 +1,10 @@
This directory contains contributed scripts. They are provided
"as-is" with no warranty. Please do not contact David Skoll
or Roaring Penguin Software Inc. for help with these scripts;
instead, contact the script authors.
You should check the upstream sources; there may be newer versions
of these scripts available.
--
David F. Skoll

279
contrib/ical2rem.pl Executable file
View File

@@ -0,0 +1,279 @@
#!/usr/bin/perl -w
#
# ical2rem.pl -
# Reads iCal files and outputs remind-compatible files. Tested ONLY with
# calendar files created by Mozilla Calendar/Sunbird. Use at your own risk.
# Copyright (c) 2005, 2007, Justin B. Alcorn
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# version 0.5.2 2007-03-23
# - BUG: leadtime for recurring events had a max of 4 instead of DEFAULT_LEAD_TIME
# - remove project-lead-time, since Category was a non-standard attribute
# - NOTE: There is a bug in iCal::Parser v1.14 that causes multiple calendars to
# fail if a calendar with recurring events is followed by a calendar with no
# recurring events. This has been reported to the iCal::Parser author.
# version 0.5.1 2007-03-21
# - BUG: Handle multiple calendars on STDIN
# - add --heading option for priority on section headers
# version 0.5 2007-03-21
# - Add more help options
# - --project-lead-time option
# - Supress printing of heading if there are no todos to print
# version 0.4
# - Version 0.4 changes all written or inspired by, and thanks to Mark Stosberg
# - Change to GetOptions
# - Change to pipe
# - Add --label, --help options
# - Add Help Text
# - Change to subroutines
# - Efficiency and Cleanup
# version 0.3
# - Convert to GPL (Thanks to Mark Stosberg)
# - Add usage
# version 0.2
# - add command line switches
# - add debug code
# - add SCHED _sfun keyword
# - fix typos
# version 0.1 - ALPHA CODE.
=head1 SYNOPSIS
cat /path/to/file*.ics | ical2rem.pl > ~/.ical2rem
All options have reasonable defaults:
--label Calendar name (Default: Calendar)
--lead-time Advance days to start reminders (Default: 3)
--todos, --no-todos Process Todos? (Default: Yes)
--heading Define a priority for static entries
--help Usage
--man Complete man page
Expects an ICAL stream on STDIN. Converts it to the format
used by the C<remind> script and prints it to STDOUT.
=head2 --label
ical2rem.pl --label "Bob's Calendar"
The syntax generated includes a label for the calendar parsed.
By default this is "Calendar". You can customize this with
the "--label" option.
=head2 --lead-time
ical2rem.pl --lead-time 3
How may days in advance to start getting reminders about the events. Defaults to 3.
=head2 --no-todos
ical2rem.pl --no-todos
If you don't care about the ToDos the calendar, this will surpress
printing of the ToDo heading, as well as skipping ToDo processing.
=head2 --heading
ical2rem.pl --heading "PRIORITY 9999"
Set an option on static messages output. Using priorities can made the static messages look different from
the calendar entries. See the file defs.rem from the remind distribution for more information.
=cut
use strict;
use iCal::Parser;
use DateTime;
use Getopt::Long 2.24 qw':config auto_help';
use Pod::Usage;
use Data::Dumper;
use vars '$VERSION';
$VERSION = "0.5.2";
# Declare how many days in advance to remind
my $DEFAULT_LEAD_TIME = 3;
my $PROCESS_TODOS = 1;
my $HEADING = "";
my $help;
my $man;
my $label = 'Calendar';
GetOptions (
"label=s" => \$label,
"lead-time=i" => \$DEFAULT_LEAD_TIME,
"todos!" => \$PROCESS_TODOS,
"heading=s" => \$HEADING,
"help|?" => \$help,
"man" => \$man
);
pod2usage(1) if $help;
pod2usage(-verbose => 2) if $man;
my $month = ['None','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
my @calendars;
my $in;
while (<>) {
$in .= $_;
if (/END:VCALENDAR/) {
push(@calendars,$in);
$in = "";
}
}
my $parser = iCal::Parser->new();
my $hash = $parser->parse_strings(@calendars);
##############################################################
#
# Subroutines
#
#############################################################
#
# _process_todos()
# expects 'todos' hashref from iCal::Parser is input
# returns String to output
sub _process_todos {
my $todos = shift;
my ($todo, @newtodos, $leadtime);
my $output = "";
$output .= 'REM '.$HEADING.' MSG '.$label.' ToDos:%"%"%'."\n";
# For sorting, make sure everything's got something
# To sort on.
my $now = DateTime->now;
for $todo (@{$todos}) {
# remove completed items
if ($todo->{'STATUS'} && $todo->{'STATUS'} eq 'COMPLETED') {
next;
} elsif ($todo->{'DUE'}) {
# All we need is a due date, everything else is sugar
$todo->{'SORT'} = $todo->{'DUE'}->clone;
} elsif ($todo->{'DTSTART'}) {
# for sorting, sort on start date if there's no due date
$todo->{'SORT'} = $todo->{'DTSTART'}->clone;
} else {
# if there's no due or start date, just make it now.
$todo->{'SORT'} = $now;
}
push(@newtodos,$todo);
}
if (! (scalar @newtodos)) {
return "";
}
# Now sort on the new Due dates and print them out.
for $todo (sort { DateTime->compare($a->{'SORT'}, $b->{'SORT'}) } @newtodos) {
my $due = $todo->{'SORT'}->clone();
my $priority = "";
if (defined($todo->{'PRIORITY'})) {
if ($todo->{'PRIORITY'} == 1) {
$priority = "PRIORITY 1000";
} elsif ($todo->{'PRIORITY'} == 3) {
$priority = "PRIORITY 7500";
}
}
if (defined($todo->{'DTSTART'}) && defined($todo->{'DUE'})) {
# Lead time is duration of task + lead time
my $diff = ($todo->{'DUE'}->delta_days($todo->{'DTSTART'})->days())+$DEFAULT_LEAD_TIME;
$leadtime = "+".$diff;
} else {
$leadtime = "+".$DEFAULT_LEAD_TIME;
}
$output .= "REM ".$due->month_abbr." ".$due->day." ".$due->year." $leadtime $priority MSG \%a $todo->{'SUMMARY'}\%\"\%\"\%\n";
}
$output .= 'REM '.$HEADING.' MSG %"%"%'."\n";
return $output;
}
#######################################################################
#
# Main Program
#
######################################################################
print _process_todos($hash->{'todos'}) if $PROCESS_TODOS;
my ($leadtime, $yearkey, $monkey, $daykey,$uid,%eventsbyuid);
print 'REM '.$HEADING.' MSG '.$label.' Events:%"%"%'."\n";
my $events = $hash->{'events'};
foreach $yearkey (sort keys %{$events} ) {
my $yearevents = $events->{$yearkey};
foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){
my $monevents = $yearevents->{$monkey};
foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) {
my $dayevents = $monevents->{$daykey};
foreach $uid (sort {
DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'})
} keys %{$dayevents}) {
my $event = $dayevents->{$uid};
if ($eventsbyuid{$uid}) {
my $curreventday = $event->{'DTSTART'}->clone;
$curreventday->truncate( to => 'day' );
$eventsbyuid{$uid}{$curreventday->epoch()} =1;
for (my $i = 0;$i < $DEFAULT_LEAD_TIME && !defined($event->{'LEADTIME'});$i++) {
if ($eventsbyuid{$uid}{$curreventday->subtract( days => $i+1 )->epoch() }) {
$event->{'LEADTIME'} = $i;
}
}
} else {
$eventsbyuid{$uid} = $event;
my $curreventday = $event->{'DTSTART'}->clone;
$curreventday->truncate( to => 'day' );
$eventsbyuid{$uid}{$curreventday->epoch()} =1;
}
}
}
}
}
foreach $yearkey (sort keys %{$events} ) {
my $yearevents = $events->{$yearkey};
foreach $monkey (sort {$a <=> $b} keys %{$yearevents}){
my $monevents = $yearevents->{$monkey};
foreach $daykey (sort {$a <=> $b} keys %{$monevents} ) {
my $dayevents = $monevents->{$daykey};
foreach $uid (sort {
DateTime->compare($dayevents->{$a}->{'DTSTART'}, $dayevents->{$b}->{'DTSTART'})
} keys %{$dayevents}) {
my $event = $dayevents->{$uid};
if (exists($event->{'LEADTIME'})) {
$leadtime = "+".$event->{'LEADTIME'};
} else {
$leadtime = "+".$DEFAULT_LEAD_TIME;
}
my $start = $event->{'DTSTART'};
print "REM ".$start->month_abbr." ".$start->day." ".$start->year." $leadtime ";
if ($start->hour > 0) {
print " AT ";
print $start->strftime("%H:%M");
print " SCHED _sfun MSG %a %2 ";
} else {
print " MSG %a ";
}
print "%\"$event->{'SUMMARY'}";
print " at $event->{'LOCATION'}" if $event->{'LOCATION'};
print "\%\"%\n";
}
}
}
}
exit 0;
#:vim set ft=perl ts=4 sts=4 expandtab :

View File

@@ -0,0 +1,13 @@
DESTDIR?=
PREFIX?=/usr
BINDIR?=$(PREFIX)/bin
MANDIR?=$(PREFIX)/share/man
default: rem2ics.1
rem2ics.1:
pod2man -c "" rem2ics > rem2ics.1
install: rem2ics.1
install -p -D rem2ics $(DESTDIR)$(BINDIR)/rem2ics
install -p -D -m 0644 rem2ics.1 $(DESTDIR)$(MANDIR)/man1/rem2ics.1

847
contrib/rem2ics-0.93/rem2ics Executable file
View File

@@ -0,0 +1,847 @@
#!/usr/bin/perl -w
# rem2ics -- convert the output of "remind -s" into RFC2445 iCalendar format.
# Copyright 2007,2008,2009
# Mark Atwood <me@mark.atwood.name>
# Paul Hinze <paul dot t dot hinze at gmail dot com>
# Michael Schultz <mjschultz at gmail dot com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.
# 51 Franklin Street, Fifth Floor
# Boston MA 02110-1301 USA
use strict;
use warnings;
=head1 NAME
rem2ics - convert the output of "remind -s" into RFC2445 iCalendar format.
=head1 SYNOPSIS
TZ=I<timezone> B<rem2ics> [B<-man>] [B<-do>] [B<-norecur>] [B<-usetag>] E<lt>input E<gt>output
=head1 OPTIONS AND ARGUMENTS
=over 8
=item B<-man>
Print the manual page and exit.
=item B<-do>
Actually do the conversion. Otherwise just print a brief help message
and usage example, and then exit.
=item B<-norecur>
Do not attempt to detect and fold together recurring events.
=item B<-usetag>
Generate UIDs using remind TAG clauses.
=back
Input is from standard input
Output is to standard output.
=head1 USAGE
remind -s360 -irem2ics=1 ~/.reminders 1 Jan 1991 | TZ=PST8PDT rem2ics -do >reminders.ics
This tells B<remind> to use ~/.reminders, and to process the entire
range of dates it can handle (from Jan 1 1991 to Dec 31 2020), and to
define a variable named C<rem2ics>, which can be used in
C<IF !defined("rem2ics")> / C<ENDIF> pairs.
B<rem2ics> will use a timezone of PST8PDT, and will fold events that
have the same name and duration into a single iCalendar VEVENT object.
=head1 NOTES
=head2 Timezones and the TZ environment variable.
B<rem2ics> uses the TZ environment variable to determine the value of
the RFC2445 TZID property. If you are running on a Linux or other GNU
libc based system, you probably don't (and probably shouldn't)
normally have TZ set. You can confirm this by running C<printenv TZ>
at a shell prompt. If your remind data is in your "local time", and
it probably is, you should probably set TZ to a good name for your
local timezone just for the run of this script. You probably should
NOT set TZ in your login scripts.
You can use TZ like this:
remind -s ~/.reminders | TZ=PST8PDT rem2ics -do >reminders.ics
or
remind -s ~/.reminders | TZ=US/Pacific rem2ics -do >reminders.ics
If, for some reason, your remind files are all in GMT instead of
localtime (you smart person you!), you can do this:
remind -s ~/.reminders | TZ=GMT rem2ics -do >reminders.ics
or
remind -s ~/.reminders | TZ=0 rem2ics -do >reminders.ics
and B<rem2ics> will use the ISO8601 "Z" notation for GMT time in the ics
file. (Other synonyms for GMT are the empty string (not the same as
the TZ not set), "I<0>", "I<Z>", "I<Zulu>", "I<Greenwitch>", "I<GMT>",
"I<GMT+0>, and "I<GMT-0>".)
If you leave TZ undefined and unset, B<rem2ics> will use the ISO8601
"T" notation date strings with no TZID property, which RFC2445 calls a
"floating time". Who knows, it might work for you!
The TZ string value is literally incorporated into the iCalendar
stream, so whatever your iCalendar-using application needs is what you
should use. You may have to experiment a bit.
You can look around in C</usr/share/zoneinfo> to get the names of
every timezone anywhere. This is the "Olson database" that RFC2445
refers to. Read the man page for L<tzfile(5)> for more than you ever
wanted to know about the format of these files, and about GNU libc's
handling of timezones. As complex as it is, it's certainly better
than what POSIX defines or what most legacy UNIX systems do, and most
certainly better than Microsoft, who in their "cutting edge" "state of
the art" "server" OS, still hasn't figured out that daylight time
rules might be different in different years.
If you just ran B<rem2ics> without reading all this stuff, or if you
don't want to worry about it at all, and somehow your iCalendar
application manager is able to guess the proper timezone for you, just
leave TZ undefined, and B<rem2ics> will use the ISO8601 "T" notation
date strings with no TZID property, which RFC2445 calls a "floating
time". Who knows, it might work for you!
=head2 Detecting recurring events
B<rem2ics> tries to detect recurring events. If any multiple events
appear with exactly the text and exactly the same duration (including
"no duration"), instead of multiple VEVENT objects, there will be just
one VEVENT object, that will have a RFC2445 C<RDATE> property.
B<rem2ics> is not yet smart enough to derive an C<RRULE> based
recurrance. If you really want that feature, either implement it and
send in a patch, or contact the author and convince him to do it.
=head2 Other iCalendar Properties
B<rem2ics> does not generate C<DESCRIPTION> or C<LOCATION>. One
would have to heuristically parse them out of the text, and everyone
uses a idiosyncratic way of putting things in B<remind>.
If the B<-usetag> option is not used or no C<TAG> is set for a
reminder, B<rem2ics> will synthesize C<UID> properties for each
VEVENT, but the UIDs will be different (and unique) for each run of
B<rem2ics>. If you run rem2ics twice and import the two resulting ICS
streams into your new scheduling program, your appointments will
appear twice. If, however, you have set C<TAG> clauses in your
reminders and activated B<-usetag>, these will be used. The same
applies for tags synthesized by B<remind>'s B<-y> option. Hence, it
is more useful to use the B<-y> option than to let B<rem2ics>
synthesize UIDs, because the UIDs will stay the same across multiple
runs.
=head2 Other iCalendar Perl objects
Why does't B<rem2ics> use any of the iCalendar Perl stuff in CPAN?
Because I don't trust them, and they are too big for this app. One
links to a binary library. Another hasn't been maintained since 1991,
and is full of notes as to how buggy and incomplete it is. And so
forth. I am not at this moment interested in groveling around in
L<Net::iCal>, L<DateTime::Format::iCal>, L<Tie::iCal>,
L<iCal::Parser>, or C<libical>.
=head2 Previous implementation
There is a working quick & dirty rem2ics written in awk
by Anthony J. Chivetta E<lt>achivetta@gmail.comE<gt>.
But it has the following problems: it doesn't escape the text, it
doesn't handle events that cross over midnight, it doesn't do
timezones, it doesn't enforce the correct EOL sequence, and it doesn't
fold long lines. This is a replacement for that script.
=head1 TODO
If TZ not set, grab out of system config somewhere
Detect recurring events.
If I'm REALLY smart, derive RRULE
Handle characters not in US-ASCII. Latin1? UTF8?
=head1 VERSION HISTORY
=over 8
=item version 0.1 2007-02-08
First cut.
=item version 0.2 2007-02-09
Reorg into multipass over a data structure.
=item version 0.3 2007-02-10
Collapse repeating events. Fold output lines.
=item version 0.9 2007-02-11
POD. Command line options. First public release.
=item version 0.91 2007-02-14
Bug fix, error message for non-recurring events
=item version 0.92 2008-01-28
Bug fix,
rem2ics 0.91 chokes on timed reminders with
duration using `remind -s` as it functions in remind-03.01.03.
Remind 3.01 changed how the -s data is formated for events that have a duration
Patch by Paul Hinze E<lt>paul dot t dot hinze at gmail dot comE<gt>
and Michael Schultz E<lt>mjschultz at gmail dot comE<gt>
=item version 0.93 2009-06-25
Add B<-usetag> option to allow for UIDs to stay the same across multiple runs
by using the remind TAG clause.
Patch by Tim Weber E<lt>scy at scytale dot nameE<gt>
=back
=head1 SEE ALSO
L<http://mark.atwood.name/code/rem2ics>
L<http://en.wikipedia.org/wiki/ICalendar>
L<http://tools.ietf.org/html/rfc2445>
L<http://www.roaringpenguin.com/en/penguin/openSourceProducts/remind>
L<remind(1)>
=head1 AUTHOR
Copyright 2007,2008,2009 by Mark Atwood E<lt>me+rem2ics@mark.atwood.nameE<gt>. L<http://mark.atwood.name/>.
Please report bugs (with patches, if possible).
Inspired by Anthony J. Chivetta E<lt>achivetta@gmail.comE<gt>'s
rem2ics in awk.
Thank you to David Skoll E<lt>dfs@roaringpengiun.com<gt> for Remind,
and to the IETF calsch wg for the iCalendar specification.
=cut
use Getopt::Long;
use Pod::Usage;
my $app_name = "rem2ics";
my $app_version = "0.93";
# process the command line
my %options;
GetOptions(\%options, qw(man do norecurr usetag)) || pod2usage(2);
pod2usage(-verbose => 2) if ($options{man});
unless ($options{do}) {
print STDERR "Run \"$0 -man\" for information and usage examples.\n"
. "Pay special attention to information about timezone.\n";
exit(99);
}
# grab the hostname
# this is used as part of the UID property of each VEVENT
my $ical_uid_hostname = $ENV{'HOSTNAME'};
unless ($ical_uid_hostname) {
print STDERR "Warning! "
. "The environment variable HOSTNAME was not properly set.\n"
. "Will use \"localhost\" in the RFC2445 UID property\n";
$ical_uid_hostname = "localhost";
}
# look for the TZ
my $ical_tzid = undef;
if (exists $ENV{'TZ'}) {
$ical_tzid = $ENV{'TZ'};
my %synonyms_for_gmt = (
'' => 1, '0' => 1, 'z' => 1, 'zulu' => 1, 'greenwitch' => 1,
'gmt' => 1, 'gmt+0' => 1, 'gmt-0' => 1, );
if (exists $synonyms_for_gmt{lc($ical_tzid)}) {
$ical_tzid = ''; # empty means GMT, below
}
} else {
# leave it undefined, that has a meaning below
}
# RFC2445 DTSTAMP property will be the time we started running ($^T)
my ($ical_dtstamp);
{
my @gt = gmtime($^T);
$ical_dtstamp = sprintf("%04d%02d%02dZ%02d%02d%02dZ",
1900+$gt[5], $gt[4]+1, $gt[3],
$gt[2], $gt[1], $gt[0]);
}
my ($cnt, $v, @events);
$cnt = 0;
foreach (<STDIN>) {
$cnt++;
s/#.*//; # toss comments
next if /^\s*$/; # skip blank lines
chomp;
$v = undef;
# store the raw line
$v->{src} = $_;
$v->{cnt} = $cnt;
# split and parse the line
# if we don't like it, skip it and go around again
# sf[0] = date, in yyyy/mm/dd format
# sf[1] = special, usually "*"
# sf[2] = tag, usually "*"
# sf[3] = duration, in minutes
# sf[4] = time, since midnight, in minutes
# sf[5] = text
my @sf = split(' ', $_, 6);
next unless ($sf[1] eq '*'); # ignore SPECIAL lines
next unless (($sf[3] eq '*') or ($sf[3] =~ m/\d+/));
next unless (($sf[4] eq '*') or ($sf[4] =~ m/\d+/));
next unless (length($sf[5]) > 0);
my @dt = split('/', $sf[0], 3);
next unless ($dt[0] =~ m/^\d{4}$/); # year
next unless ($dt[1] =~ m/^\d{2}$/); # month
next unless ($dt[2] =~ m/^\d{2}$/); # day
if ($sf[4] ne "*") { # a time was given
# When an event has a time, remind -s "helpfully" also
# puts it as text at the start of the text portion.
# This takes the following form:
# ##:##[a|p]m <rest of MSG>
# or, if the event has a duration:
# ##:##[a|p]m-##:##[a|p]m <rest of MSG>
# Rather than a nasty regex, just splitting at the
# first space does the trick.
my($extra_time, $textmsg) = split(' ', $sf[5], 2);
$sf[5] = $textmsg;
}
$v->{sf} = \@sf;
$v->{dt} = \@dt;
push @events, $v;
}
# generate the "date time string" for each event
foreach $v (@events) {
if (${$v->{sf}}[4] eq "*") { # no time was given
$v->{dts} = sprintf("%04d%02d%02d", @{$v->{dt}});
} else { # a time was given
my ($t_hr, $t_mn) = &idiv(${$v->{sf}}[4], 60);
$v->{dts} = sprintf("%04d%02d%02dT%02d%02d00",
@{$v->{dt}}, $t_hr, $t_mn);
}
}
my(%grovel);
# if the user doesnt want recurrance detection
unless ($options{norecurr}) {
# then dont put events in the grovel hash
foreach $v (@events) {
# key is duration followed by text
# \036 is "ASCII RS Record Separator"
my $k = ${$v->{sf}}[3] . "\036" . ${$v->{sf}}[5];
push @{$grovel{$k}}, $v;
}
foreach my $k (keys %grovel) {
if ((scalar @{$grovel{$k}}) > 1) {
$v = ${$grovel{$k}}[0];
$v->{recurlist} = \@{$grovel{$k}};
foreach my $v0 (@{$grovel{$k}}) {
$v0->{is_recurrance} = $v;
}
}
}
}
# All of the individual events are in the @events array. All of the
# unique combinations of duration/event name are the keys in the
# %grovel hash, the elements of which are references to an arrays of
# references to the events, in the same ordering as they we read by
# us, which *ought* to be in datewise order. I don't know if "remind
# -s" actually sorts into true chronological order. If it doesn't, we
# might have a problem if a recurring event has two instances both on
# the first day.
# Every event that is recurring has a "is_recurrance" property.
# Additionally, (hopefully) the first/earliest event in a set of
# recurrances has a "recurlist" property. The "recurlist" is a
# reference to a list of references to each of the events. The first
# one on that list will be the same event that has the "recurlist"
# property. The "is_recurrance" property is a reference back to the
# event that has the "recurlist" property.
foreach my $k (keys %grovel) {
next if ((scalar @{$grovel{$k}}) <= 1);
my $recur_str = "";
foreach $v (@{$grovel{$k}}) {
if (${$v->{sf}}[4] eq "*") { # no time was given
$recur_str .= ($v->{dts} . ",");
} else {
if (defined($ical_tzid)) {
if ($ical_tzid eq '') { # tz is defined but empty, so in GMT
$recur_str .= $v->{dts} . "Z,";
} else { # tz is non-zero, so output the tz as well
$recur_str .= $v->{dts} . ",";
}
} else { # undefined tz, just floating time
$recur_str .= $v->{dts} . ",";
}
}
}
# the recur_str now has an extra comma at the end. chop it off
chop($recur_str);
${$grovel{$k}}[0]->{recur_str} = $recur_str;
}
foreach my $k (keys %grovel) {
next if ((scalar @{$grovel{$k}}) <= 1);
my $v = ${$grovel{$k}}[0]; # grab the head of each list
if (${$v->{sf}}[4] eq "*") { # no time was given
# the default value type for an RDATE is DATE-TIME,
# we much change the type to DATE
$v->{i_rdate} = sprintf("RDATE;VALUE=DATE:");
} else {
if (defined($ical_tzid)) {
if ($ical_tzid eq '') { # tz is defined but empty, so in GMT
$v->{i_rdate} = sprintf("RDATE:");
} else { # tz is non-zero, so output the tz as well
$v->{i_rdate} = sprintf("RDATE;TZID=%s:", $ical_tzid);
}
} else { # undefined tz, just floating time
$v->{i_rdate} = sprintf("RDATE:");
}
}
# now stick the recur_str onto the end
$v->{i_rdate} .= $v->{recur_str};
# if we ever get memory tight, we can probably undef($v->{recur_str})
}
foreach $v (@events) {
# for recurrant events, skip those that arnt the "head"
next if ($v->{is_recurrance} and (not $v->{recurlist}));
if (${$v->{sf}}[4] eq "*") { # no time was given
$v->{i_dtstart} = sprintf("DTSTART:%s", $v->{dts});
} else {
if (defined($ical_tzid)) {
if ($ical_tzid eq '') { # tz is defined but empty, so in GMT
$v->{i_dtstart} = sprintf("DTSTART:%sZ", $v->{dts});
} else { # tz is non-zero, so output the tz as well
$v->{i_dtstart} = sprintf("DTSTART;TZID=%s:%s",
$ical_tzid, $v->{dts});
}
} else { # undefined tz, just floating time
$v->{i_dtstart} = sprintf("DTSTART:%s", $v->{dts});
}
}
if (${$v->{sf}}[3] ne "*") { # a duration was given
# It's convienient that RFC2445 defines DURATION, thus we
# don't need to calculate DTEND, with awkward figuring out
# crossing hours, days, months, year, etc. Instead we
# will let the iCalendar consuming application worry about it.
$v->{i_duration} = sprintf("PT%dM", ${$v->{sf}}[3]);
}
}
# output header
print "BEGIN:VCALENDAR\015\012"
. "VERSION:2.0\015\012"
. "PRODID:http://mark.atwood.name/code/rem2ics"
. " $app_name $app_version\015\012";
# output each vevent
foreach $v (@events) {
# for recurrant events, only output the "head", skip the others
next if ($v->{is_recurrance} and (not $v->{recurlist}));
print "BEGIN:VEVENT\015\012";
my $tag = ${$v->{sf}}[2];
# if $tag is not set, fake up a UID from start time, process id & input line count
if ($tag eq "*" || !$options{usetag}) {
$tag = sprintf("%x.%x.%x", $^T, $$, $v->{cnt});
}
# add rem2ics and hostname to UID
print &lineify(sprintf("UID:rem2ics.%s@%s",
$tag, $ical_uid_hostname));
print &lineify("SUMMARY:" . &quotify(${$v->{sf}}[5]));
print &lineify($v->{i_dtstart});
print &lineify("DURATION:" . $v->{i_duration})
if ($v->{i_duration});
print &lineify($v->{i_rdate})
if ($v->{i_rdate});
print &lineify("DTSTAMP:" . $ical_dtstamp);
print &lineify("COMMENT: generated by $app_name $app_version\\n"
. " http://mark.atwood.name/code/rem2ics\\n"
. " data[" . $v->{cnt} . "]=|" . &quotify($v->{src}) . "|");
print "END:VEVENT\015\012";
}
# output trailer
print "END:VCALENDAR\015\012";
# integer division, return both quotient and remainder
sub idiv {
my $n = shift; my $d = shift;
my $r = $n; my $q = 0;
while ($r >= $d) {
$r = $r - $d;
$q = $q + 1;
}
return ($q, $r);
}
# todo, perl5 version that defines ()*, need to specify a requires up top
sub lineify {
return join("\015\012 ", unpack('(A72)*', shift)) . "\015\012";
}
sub quotify {
my $s = shift;
return $s if $s =~ m/^(\w| )*$/;
$s =~ s/\\/\\\\/gso;
$s =~ s/\n/\\n/gso;
$s =~ s/\s/ /gso;
$s =~ s/\"/\\"/gso;
$s =~ s/\,/\\,/gso;
$s =~ s/\:/\\:/gso;
$s =~ s/\;/\\;/gso;
return $s;
}
__END__
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
EOF

View File

@@ -0,0 +1,50 @@
Name: rem2ics
Version: 0.93
Release: 1%{?dist}
Summary: Converts the output of "remind -s" into RFC2445 iCalendar format
Group: Applications/Productivity
License: GPLv2+
URL: http://mark.atwood.name/code/rem2ics/
Source0: http://mark.atwood.name/code/rem2ics/rem2ics-%{version}.tar.gz
Source1: rem2ics-Makefile
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildArch: noarch
BuildRequires: perl
%description
rem2ics converts the output of "remind -s" into RFC2445 iCalendar format.
You may want to install remind if you install this package.
%prep
%setup -q -c
cp -a %SOURCE1 Makefile
%build
make %{?_smp_mflags}
%install
rm -rf $RPM_BUILD_ROOT
mkdir $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
%doc
%{_bindir}/rem2ics
%{_mandir}/man1/rem2ics.1*
%changelog
* Tue Mar 25 2008 Till Maas <opensource till name> - 0.92-1
- initial spec for Fedora

View File

@@ -0,0 +1,56 @@
;;; setup for remind autocompletion (totally optional)
;; put something like
;; (add-hook 'remind-conf-mode '(load-library "ac-remind")) in your .emacs file.
(require 'auto-complete)
(define-key ac-complete-mode-map "\r" nil)
(defvar ac-remind-keywords
'((candidates
. (lambda ()
(all-completions ac-target remind-keywords ))))
"Source for remind-conf completion keywords.")
(defvar ac-remind-time-words
'((candidates
. (lambda ()
(all-completions ac-target remind-time-words))))
"Source for remind-conf time words completions.")
(defvar ac-remind-builtin-variables
'((candidates
. (lambda ()
(all-completions ac-target remind-builtin-variables))))
"Source for remind-conf builtin variables.")
(defvar ac-remind-type-keywords
'((candidates
. (lambda ()
(all-completions ac-target remind-type-keywords))))
"Source for remind-conf type keywords.")
(defvar ac-remind-builtin-functions
'((candidates
. (lambda ()
(all-completions ac-target remind-builtin-functions))))
"Source for remind-conf completion builtin functions.")
(add-hook 'remind-conf-mode-hook
(lambda () "Makes auto-completion work in remind-conf-mode"
(make-local-variable 'ac-sources)
(setq ac-sources '(ac-remind-keywords
ac-remind-builtin-variables
ac-remind-builtin-functions
ac-remind-type-keywords
ac-remind-time-words
ac-source-abbrev))
(auto-complete-mode 1)))
(provide 'ac-remind)
;; (define-skeleton ac-look
;; ""
;; (skeleton-read "well? ")
;; "(when (looking-at (regexp-opt remind-" str " 'words))" \n
;; >"(setq ac-sources '(ac-remind-" str ")))"
;; )

View File

@@ -0,0 +1,444 @@
;;; remind-conf-mode.el --- A mode to help configure remind.
;; Copyright (C) 2008 Shelagh Manton <shelagh.manton@gmail.com>
;; Author: Shelagh Manton <shelagh.manton@gmail.com> with help from
;; David F. Skoll
;; Keywords: remind configure mode
;; Version: .04
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 2
;; of the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.
;;; Commentary:
;; Use this mode to help with the configuration of remind configuration files.
;; Put (require 'remind-conf-mode) in your .emacs file
;; or (autoload 'remind-conf-mode "remind-conf-mode" "Mode to help with remind files" t)
;; also put (add-to-list 'auto-mode-alist '("\\.rem\\'" . remind-conf-mode)) and
;; (setq auto-mode-alist
;; (cons '(".reminders$" . remind-conf-mode) auto-mode-alist))
;; if you want to have the mode work automatically when you open a remind configuration file.
;; If you want to use the auto-complete stuff, you will need to download and install the
;; auto-complete library from http://www.cx4a.org/pub/auto-complete.el and put
;; (require 'auto-complete) in your emacs with
;; (add-hook 'remind-conf-mode-hook
;; (lambda ()
;; (make-local-variable 'ac-sources)
;; (setq ac-sources '(ac-remind-conf ac-remind-builtin-variables ac-remind-builtin-functions))
;; (auto-complete t)))
;; in your .emacs file
;; PS. you could add ac-source-abbrev ac-source-words-in-buffer to have abbrevs and
;; other words in buffer auto-complete too
;;; History:
;; Thu, Feb 14, 2008
;; Based mode on wpld-mode tutorial and sample-mode on emacs wiki.
;; Ideas from mupad.el for font-lock styles.
;; Mon, Jan 26, 2008
;; Added rem-setup-colors to make it easy for colourised remind output.
;; Added a demo skeleton for people to copy for easy entry of coloured remind entries.
;; tried to hook in the auto-complete library so that all known functions and keywords can be easily entered.
;; EXPERIMENTAL, but seems to work well here (emacs cvs).
;; Seems to work without case folding which is nice. wonder why it didn't yesterday?
;;; Code:
(require 'font-lock); this goes in the define-derived-mode part.
(when (featurep 'xemacs)
(require 'overlay)) ;I wonder if this will help with font-lock and xemacs?
(defgroup remind-conf nil
"Options for remind-conf-mode."
:group 'remind-conf
:prefix "remind-conf-")
(defvar remind-conf-mode-hook nil
"Hook to run in `remind-conf-mode'.")
;; keymap
(defvar remind-conf-mode-map
(let ((remind-conf-mode-map (make-sparse-keymap)))
remind-conf-mode-map)
"Keymap for `remind-conf-mode'.")
(define-key remind-conf-mode-map "\C-cr" 'rem-skel)
(define-key remind-conf-mode-map "\C-ct" 'rem-today)
(define-key remind-conf-mode-map "\C-cd" 'rem-today-skel)
(define-key remind-conf-mode-map "\C-cw" 'rem-week-away)
(define-key remind-conf-mode-map "\C-cx" 'rem-tomorrow)
(define-key remind-conf-mode-map "\C-ca" 'rem-days-away)
(define-key remind-conf-mode-map "\M-j" 'remind-indent-line)
(define-key remind-conf-mode-map (kbd "RET") 'remind-indent-line)
(define-key remind-conf-mode-map "\C-c\C-c" 'rem-save-file)
;; syntax-table
(defvar remind-conf-syntax-table
(let ((remind-conf-syntax-table (make-syntax-table text-mode-syntax-table)))
(modify-syntax-entry ?\; ". 1b" remind-conf-syntax-table)
(modify-syntax-entry ?\# ". 1b" remind-conf-syntax-table)
(modify-syntax-entry ?\n "> b" remind-conf-syntax-table)
;Names with _ are still one word.
(modify-syntax-entry ?_ "w" remind-conf-syntax-table)
(modify-syntax-entry ?. "w" remind-conf-syntax-table)
remind-conf-syntax-table)
"Syntax table for `remind-conf-mode'.")
;;; keyword sets
(defconst remind-keywords
(sort
(list "RUN" "REM" "ONCE" "SATISFY" "BEFORE" "UNSET" "OMIT"
"OMIT" "DATE" "SKIP" "ONCE" "AFTER" "WARN" "PRIORITY" "AT" "SCHED" "IF" "ELSE" "ENDIF"
"WARN" "UNTIL" "SCANFROM" "DURATION" "TAG" "MSG" "MSF" "CAL" "SPECIAL" "IFTRIG"
"PS" "PSFILE" "BANNER" "INCLUDE" "PUSH-OMIT-CONTEXT" "DEBUG" "DUMPVARS"
"CLEAR-OMIT-CONTEXT" "POP-OMIT-CONTEXT" "SET" "ERRMSG" "FSET"
"EXIT" "FLUSH" "PRESERVE" "MOON" "COLOR")
#'(lambda (a b) (> (length a) (length b)))))
(defconst remind-type-keywords
(sort
(list "INT" "STRING" "TIME" "DATE" "SHADE")
#'(lambda (a b) (> (length a) (length b)))))
(defconst remind-builtin-variables
(sort
(list "$CalcUTC" "$CalMode" "$DefaultPrio" "$EndSent" "$EndSentIg" "$NumTrig"
"$FirstIndent" "$FoldYear" "$FormWidth" "$MinsFromUTC" "$LatDeg" "$LatMin" "$LatSec"
"$Location" "$LongDeg" "$LongMin" "$LongSec" "$MaxSatIter" "$SubsIndent")
#'(lambda (a b) (> (length a) (length b)))))
(defconst remind-time-words
(sort
(list "Jan" "January" "Feb" "Mar" "Apr" "Jun" "Jul" "Aug" "Sept" "Sep" "Oct" "Nov" "Dec" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December" "Mon" "Monday" "Tue" "Tues" "Tuesday" "Wed" "Wednesday" "Thu" "Thursday" "Fri" "Friday" "Saturday" "Sat" "Sun" "Sunday")
#'(lambda (a b) (> (length a) (length b)))))
(defconst remind-builtin-functions
(sort
(list "abs" "access" "shell" "args" "asc" "baseyr" "char" "choose" "coerce" "date"
"dawn" "today" "day" "daysinmon" "defined" "dosubst" "dusk" "easterdate" "easter"
"filedir" "filename" "getenv" "hour" "iif" "trigger" "index" "isdst" "isleap"
"isomitted" "hebdate" "hebday" "hebmon" "hebyear" "language" "ord" "thisyear"
"sunrise" "sunset" "lower" "max" "min" "minute" "mon" "moondate" "moontime"
"moonphase" "now" "ostype" "plural" "realnow" "realtoday" "sgn" "strlen" "psshade"
"substr" "trigdate" "trigger" "trigtime" "trigvalid" "typeof" "upper" "psmoon"
"value" "version" "wkday" "wkdaynum" "msgprefix" "msgsuffix" "year")
#'(lambda (a b) (> (length a) (length b)))))
;;; faces
;;example of setting up special faces for a mode.
(defvar remind-conf-command-face 'remind-conf-command-face
"Remind commands.")
(defface remind-conf-command-face
'((t :foreground "SeaGreen4" :bold t))
"Font Lock mode face used to highlight commands."
:group 'remind-conf)
(defvar remind-conf-keyword-face 'remind-conf-keyword-face
"Remind keywords.")
(defface remind-conf-keyword-face
'((t :foreground "blue violet"))
"Font Lock mode face used to highlight keywords."
:group 'remind-conf)
(defvar remind-conf-substitutes-face 'remind-conf-substitutes-face
"Remind substitutes.")
(defface remind-conf-substitutes-face
'((t :foreground "blue2"))
"Font Lock mode face used to highlight substitutes."
:group 'remind-conf)
(defvar remind-conf-endline-face 'remind-conf-endline-face
"Remind endline.")
(defface remind-conf-endline-face
'((t :foreground "goldenrod2" :bold t))
"Font Lock mode face used to highlight commands."
:group 'remind-conf)
(defvar remind-conf-variable-face 'remind-conf-variable-face
"Remind variable.")
(defface remind-conf-variable-face
'((t :foreground "DeepPink2" :bold t))
"Font Lock mode face used to highlight commands."
:group 'remind-conf)
(defvar remind-conf-color-face 'remind-conf-color-face
"Remind color variables.")
(defface remind-conf-color-face
'((t :foreground "gold" :bold t))
"Font Lock mode face used to highlight color changes."
:group 'remind-conf)
(defvar remind-conf-delta-face 'remind-conf-delta-face
"Remind deltas.")
(defface remind-conf-delta-face
'((t :foreground "sandy brown" :bold t))
"Font Lock mode face used to highlight deltas."
:group 'remind-conf)
(defvar remind-comment-face 'remind-comment-face
"Remind comments.")
(defface remind-comment-face
'((t :foreground "brown"))
"Font-lock face for highlighting comments."
:group 'remind-conf)
(defvar remind-string-face 'remind-string-face
"Remind strings.")
(defface remind-string-face
'((t :foreground "tomato"))
"Font lock mode face used to highlight strings."
:group 'remind-conf)
(defvar remind-time-face 'remind-time-face
"Remind time words.")
(defface remind-time-face
'((t :foreground "LightSeaGreen" :bold t))
"Font lock mode face to highlight time phrases."
:group 'remind-conf)
(defvar remind-conf-type-face 'remind-conf-type-face
"Remind type keywords.")
(defface remind-conf-type-face
'((t :foreground "orange" :bold t))
"Font lock mode face to highlight type keywords."
:group 'remind-conf)
(defcustom rem-post-save-function ""
"Name of shell function that can be run when you save and close a remind file.
If you put a & after the name of the function, it will run asyncronously. This might
be useful if the process takes a long time."
:type 'string
:group 'remind-conf
)
;; keywords
(defconst remind-conf-font-lock-keywords-1
(list
'("^[\;\#]\\s-+.*$" . remind-comment-face)
(cons (regexp-opt remind-keywords 'words) remind-conf-keyword-face)
'("%[\"_]" . font-lock-warning-face)
'("\\(%[a-mops-w]\\)" . remind-conf-substitutes-face)
'("\"[^\"]*\"" . remind-string-face))
"Minimal font-locking for `remind-conf-mode'.")
(defconst remind-conf-font-lock-keywords-2
(append remind-conf-font-lock-keywords-1
(list
(cons (regexp-opt remind-time-words 'words) remind-time-face)
(cons (regexp-opt remind-builtin-functions 'words) remind-conf-command-face)
'("%$" . remind-conf-endline-face)))
"Additional commands to highlight in `remind-conf-mode'.")
(defconst remind-conf-font-lock-keywords-3
(append remind-conf-font-lock-keywords-2
(list
(cons (regexp-opt remind-type-keywords 'words) remind-conf-type-face)
'("\[[a-zA-Z]\\{3,6\\}\]" . remind-conf-color-face)
'("\\s-+\\([12][0-9]\\|3[01]\\|0?[0-9]\\)\\s-+" . remind-conf-substitutes-face);better date regexp
'("\\s-+\\(\\(?:20\\|19\\)[0-9][0-9]\\)\\s-+" . remind-conf-substitutes-face);years
'("\\s-+\\(2[0-4]\\|[01]?[0-9][.:][0-5][0-9]\\)\\s-+" . remind-conf-substitutes-face);24hour clock, more precise
'("\\s-+\\([+-][+-]?[1-9][0-9]*\\)\\s-+" 1 remind-conf-delta-face prepend)
(cons (regexp-opt remind-builtin-variables 'words) remind-conf-variable-face)))
"The ultimate in highlighting experiences for `remind-conf-mode'.")
(defcustom remind-conf-font-lock-keywords 'remind-conf-font-lock-keywords-3
"Font-lock highlighting level for `remind-conf-mode'."
:group 'remind-conf
:type '(choice (const :tag "Barest minimum of highlighting." remind-conf-font-lock-keywords-1)
(const :tag "Medium highlighting." remind-conf-font-lock-keywords-2)
(const :tag "Highlighting deluxe." remind-conf-font-lock-keywords-3)))
;;; Indentation (I'm sure this could be made more simple. But at least it works.)
(defcustom remind-indent-level 4
"User definable indentation."
:group 'remind-conf
:type '(integer)
)
(defun remind-indent-line ()
"Indent current line for remind configuration files."
(interactive)
(forward-line 0) ;remember this happens on every line as it is done per line basis
(if (bobp)
(indent-line-to 0)
(let ((not-indented t) cur-indent)
(if (looking-at "^[ \t]*\\<\\(ENDIF\\|POP\\(?:-OMIT-CONTEXT\\)?\\)\\>")
(progn
(save-excursion
(forward-line -1)
(setq cur-indent (- (current-indentation) remind-indent-level))) ;note that not-indented is still t
(if (< cur-indent 0) (setq cur-indent 0)))
(save-excursion
(while not-indented
(forward-line -1)
(if (looking-at "^[ \t]*\\<\\(ENDIF\\|POP\\(?:-OMIT-CONTEXT\\)?\\)\\>")
(progn
(setq cur-indent 0)
(delete-horizontal-space) ;don't know why I need this when other similar indent functions don't.
(setq not-indented nil))
(if (looking-at "\\<\\(IF\\(?:TRIG\\)?\\|PUSH\\(?:-OMIT-CONTEXT\\)?\\)\\>")
(progn
(setq cur-indent remind-indent-level)
(setq not-indented nil))
(if (bobp)
(setq not-indented nil))))))
(if cur-indent
(indent-line-to cur-indent)
(indent-line-to 0))))))
;;; Convenience functions
(define-skeleton rem-skel
"Skeleton to insert a rem line in a remind configuration file."
nil
"REM "(skeleton-read "Date? " )
("Optional: How many days ahead? " " +" str )
resume:
("Optional: At what time? Format eg 13:00. " " AT " str)
resume:
("Optional: How many minutes ahead? " " +" str )
resume:
("Optional: At what priority? eg 0-9999" " PRIORITY " str )
resume:
" MSG %\"" (skeleton-read "Your message? " )"%b%\"%" \n
)
(define-skeleton rem-today-skel
"Skeleton to insert a line for today's date."
nil
"REM " (format-time-string "%d %b %Y")
("Optional: At what time? Format eg 13:20. " " AT " str)
resume:
("Optional: How many minutes ahead? " " +" str )
resume:
("Optional: At what priority? eg 0-9999" " PRIORITY " str )
resume:
" MSG " (skeleton-read "Your message? " )"%b.%" \n
)
(defun rem-today ()
"Insert the date for today in a remind friendly style."
(interactive)
(insert (format-time-string "%e %b %Y")))
(defun rem-tomorrow ()
"Insert tomorrow's date in a remind friendly style."
(interactive)
(insert (format-time-string "%e %b %Y" (time-add (current-time) (days-to-time 1)))))
(defun rem-days-away (arg)
"Insert a day N number of days in the future.
Takes a prefix argument, but defaults to 4."
(interactive "nHow many Days?: ")
(insert (format-time-string "%e %b %Y" (time-add (current-time) (days-to-time arg)))))
(defun rem-week-away ()
"Insert a day 7 days in the future."
(interactive)
(insert (format-time-string "%e %b %Y" (time-add (current-time) (days-to-time 7)))))
(setq skeleton-end-hook nil) ; so the skeletons will not automatically go to a new line.
;;; private function
;; could make it useful for others. Put somethin like the following in your .emacs
;(setq rem-post-save-function "~/bin/dailypic &")
(defun rem-save-file ()
"Save the file and start the shell function in one go.
This function will close the window after running. It needs the
variable `rem-post-save-function' to be set. It will be most
useful to people who have some sort of function they run to use
remind data ie procucing calendars."
(interactive)
(if (boundp 'rem-post-save-function)
(progn (save-buffer)
(shell-command rem-post-save-function)
(kill-buffer-and-window))
(error "`rem-post-save-function' variable is not set")))
(defun rem-setup-colors ()
"Insert set of variables for coloured output in remind messages."
(interactive)
(find-file (expand-file-name "~/.reminders"))
(goto-char 0) ;we do want it somewhere near the top of the file.
(save-excursion
(re-search-forward "\n\n"); squeeze it in where you have a free line.
(insert "\nSET Esc CHAR(27)
SET Nrm Esc + \"[0m\"
SET Blk Esc + \"[0;30m\"
SET Red Esc + \"[0;31m\"
SET Grn Esc + \"[0;32m\"
SET Ylw Esc + \"[0;33m\"
SET Blu Esc + \"[0;34m\"
SET Mag Esc + \"[0;35m\"
SET Cyn Esc + \"[0;36m\"
SET Wht Esc + \"[0;37m\"
SET Gry Esc + \"[30;1m\"
SET BrRed Esc + \"[31;1m\"
SET BrGrn Esc + \"[32;1m\"
SET BrYlw Esc + \"[33;1m\"
SET BrBlu Esc + \"[34;1m\"
SET BrMag Esc + \"[35;1m\"
SET BrCyn Esc + \"[36;1m\"
SET BrWht Esc + \"[37;1m\" \n \n")))
;; So now you can do things like:
(define-skeleton birthcol
"Make birthdays magenta.
Acts on the region or places point where it needs to be."
nil
"[Mag]" _ " [Nrm]")
;; finally the derived mode.
;;;###autoload
(define-derived-mode remind-conf-mode text-mode "REM"
"Major mode for editing remind calendar configuration files.
\\{remind-conf-mode-map}"
:syntax-table remind-conf-syntax-table
(set (make-local-variable 'font-lock-keywords-case-fold-search) t) ;this is not working atm 2009-04-13
(set (make-local-variable 'font-lock-defaults) '(remind-conf-font-lock-keywords))
(set (make-local-variable 'comment-start) ";")
(set (make-local-variable 'comment-start) "#")
(set (make-local-variable 'comment-end) "\n")
(set (make-local-variable 'fill-column) '100);cause I was having problems with autofill.
(set (make-local-variable 'indent-line-function) 'remind-indent-line)
(use-local-map remind-conf-mode-map)
)
(provide 'remind-conf-mode)
;;; remind-conf-mode.el ends here
;;; Indentation code
;;; work out how to make the syntax highlighting work only before the (MSG|MSF)
;;; keywords and not after.
;;; for my own use. keymap to save file and do dailypic C-c C-c in time honoured tradition?

View File

@@ -1,6 +1,204 @@
CHANGES TO REMIND
* Version 3.1 Patch 1 - 23 August 2007
* Version 3.1 Patch 9 - 2010-06-20
- MAJOR ENHANCEMENT: New "purge mode" to delete expired reminders. See
the PURGE MODE section of the remind man page.
- ENHANCEMENT: Support DURATION in TkRemind. Thanks to Marek Marczykowski.
- BUG FIX: Don't change the order of PS and PSFILE reminders. Bug found
by John McGowan.
- BUG FIX: "REM 1990-01-01 SATISFY 1" would yield a spurious parse error
in earlier versions of Remind.
- BUG FIX: Yom HaShoah is moved to Thursday if it would normally fall on
a Friday. Thanks to Jonathan Kamens for pointing this out.
* Version 3.1 Patch 8 - 2010-03-09
- ENHANCEMENT: Include some useful scripts in contrib/
- ENHANCEMENT: Add the $T, $Td, $Tm, $Tw, $Ty, $U, $Ud, $Um, $Uw, $Uy
special variables to make reminder files less wordy. See man page
for details.
- MINOR ENHANCEMENT: Set an icon photo window manager resource on TkRemind.
- POLICY CHANGE: Discourage use of Remind on MS Windows or Apple Mac OS X.
- BUG FIX: Ignore msgprefix() and msgsuffix() on RUN-type reminders.
- BUG FIX: Adjust Remind and Rem2PS so that SHADE specials don't obliterate
earlier MOON specials.
- BUG FIX: Fix bug in SCHED calculations if Remind is started in the middle
of a SCHED interval.
* Version 3.1 Patch 7 - 2009-05-31
- ENHANCEMENT: Wherever you could write "day Mon year", the parser now
accepts "YYYY-MM-DD". This applies on the command-line and to the
REM and OMIT keywords. You can avoid wrapping date calculations in
the trigger() function in many cases.
- ENHANCEMENT: New slide() built-in function eases some complicated reminders.
* Version 3.1 Patch 6 - 2008-11-16
- MAJOR ENHANCEMENT: A new OMITFUNC clause gives you additional
control and flexibility over "omitted days" calculations. This is
useful when holidays influence the timing of events several days
later. See "COMPUTED LOCAL OMITS" in the man page.
- ENHANCEMENT: The new evaltrig() built-in function lets you evaluate triggers
from within an expression.
- ENHANCEMENT: The new weekno() built-in function returns the ISO 8601
week number of a date.
- ENHANCEMENT: The "WEEK" special lets you annotate calendar output with the
week number. The TkRemind, rem2ps and rem2html back-ends support WEEK.
- MINOR ENHANCEMENT: You can control whether timed reminders come before or
after non-timed reminders with the "-g" flag.
- BUG FIX: TkRemind did not work correctly if ~/.reminders was a directory.
- BUG FIX: TkRemind incorrectly invoked Remind with the "-a" flag when
showing today's reminders.
- BUG FIX: In certain cases, a trigger containing a day, month and
weekday would fail if it needed to cross a year boundary. This has
been fixed.
* Version 3.1 Patch 5 - 2008-04-15
- MAJOR ENHANCEMENT: If you supply a directory name on the command line
or for an INCLUDE command, then Remind reads all *.rem file in that
directory (in the order returned by "glob")
- ENHANCEMENT: The plain-text calendar ("-c") can draw lines using VT-100
line-drawing characters if invoked as "-cl"
- ENHANCEMENT: The plain-text calendar can approximate SPECIAL COLOR reminders
using VT-100 color escape sequences if invoked as "-cc". (You can combine
the colors and line-drawing characters with -clc or -ccl.)
- ENHANCEMENT: The "-t" option can take a numeric argument n. In this case,
all reminders are assumed to have a delta of +n. (Without the argument,
an infinite delta is assumed, as before.) If a numeric argument is given,
the new system variable $DeltaOffset is set to the argument.
- MINOR ENHANCEMENT: The "-i" command-line option can be used to define
a function as well as set a variable.
- MINOR ENHANCEMENT: String constants can have embedded quotes "Like \"this"
- MINOR ENHANCEMENT: tkremind works better on small screens like that of
the Eee-PC.
- BUG FIX: Minor fix to HTML output courtesy of Ian! Allen.
- BUG FIX: Parse error in calendar mode was fixed.
* Version 3.1 Patch 4 - 2008-02-03
- ENHANCMENT: tkremind respects the "-b1" option and operates in 24-hour
clock mode if the option is supplied.
- ENHANCEMENT: tkremind has been tweaked to look better with Tcl/Tk 8.5.
- CLEANUP: Version is kept only in configure.in instead of two different
places.
- CLEANUP: Added "const" qualifier to many places in the code that previously
lacked it.
- BUG FIX: A rare parsing error involving interaction between SATISFY and
SKIP has been fixed.
- BUG FIX: rem2html would output a horribly-wrong calendar for a 28-day
February starting on Sunday (such as February 2009.) This has been fixed.
- BUG FIX: The "-ivar=value" command-line option failed if Remind re-execed
itself because we overwrote argv[]. This has been fixed.
* Version 3.1 Patch 3 - 2007-10-15
+ MINOR ENHANCEMENTS
- rem2html now uses CSS for a much better-looking calendar.
NOTE: rem2html was completely rewritten and some of the command-line
options have changed!
- If a reminder has a DURATION clause, then the starting and ending times
are output in calendar mode.
+ BUG FIXES
- DST rules in "defs.rem" were updated to reflect new US/Canadian DST rules.
- If a REM command cannot compute a trigger date, the SATISFY expression
is not evaluated. This helps avoid spurious error messages in some
reminders.
* Version 3.1 Patch 2 - 2007-09-12
+ MINOR ENHANCEMENTS
- build.tk tries to set defaults for location, paper size, etc from an
existing "remind" installation if it detects one.
- In queue mode, wake up once a minute and recalibrate sleep time.
This should make Remind work better on laptops that suspend or
hibernate. Note that "remind -q" does *not* handle date-rollover
well; it simply exits if it notices date rollover. "remind -z0"
(as used by tkremind) handles date rollover properly; it rereads the
reminder file and rebuilds the queue if it notices date rollover.
- tkremind: Added some key bindings to make navigation easier.
- tkremind: Made calendar boxes use space more efficiently.
- remind: The functionality of "rem" is now built into remind. If you
invoke remind as "rem", then it uses a default filename. The installer
sets up "rem" as a symbolic link to "remind".
+ CHANGE
- "remind -p" no longer sorts SPECIAL reminders before non-SPECIAL.
*** THIS MAY AFFECT BACKENDS ***
Backends supplied by Roaring Penguin (rem2ps, rem2html and tkremind)
are known to work properly.
- "remind -p" no longer suppresses any AT-time associated with SPECIAL
reminders.
*** THIS MAY AFFECT BACKENDS ***
Backends supplied by Roaring Penguin (rem2ps, rem2html and tkremind)
are known to work properly.
+ BUG FIXES
- examples/defs.rem: A few corrections to Jewish holidays courtesy of
Art Werschulz.
- src/Makefile.in: Added install-nostripped target.
- SPECIAL COLOR now works more like MSG, including proper support for AT and
for the %" %" escape sequence.
- SPECIAL COLOR is queued correctly if it has an AT clause.
- Using the psshade() or psmoon() functions emits a warning on stderr. You
should use SPECIAL SHADE or SPECIAL MOON instead.
* Version 3.1 Patch 1 - 2007-08-23
+ MAJOR ENHANCEMENTS
@@ -30,7 +228,7 @@ CHANGES TO REMIND
- Various man-page fixes.
* Version 3.1 Patch 0 - 14 August 2007
* Version 3.1 Patch 0 - 2007-07-14
+ MAJOR ENHANCEMENTS
@@ -93,7 +291,7 @@ CHANGES TO REMIND
- rem2ps would produce invalid PostScript in some rare cases
(eg, for February 2007). This has been fixed.
* Version 3.0 Patch 24
* Version 3.0 Patch 24 - 2005-11-19
+ MINOR ENHANCEMENTS
@@ -116,7 +314,7 @@ CHANGES TO REMIND
- Fixed a bug in the tokenizer that could make Remind segfault. Fix courtesy
of Stan Tobias.
* Version 3.0 Patch 23
* Version 3.0 Patch 23 - 2005-04-14
+ MINOR ENHANCEMENTS
@@ -140,7 +338,7 @@ CHANGES TO REMIND
- Fixed parser error for unterminated date constant: '2005/01/01
* Version 3.0 Patch 22
* Version 3.0 Patch 22 - 2000-06-16
+ MINOR ENHANCEMENTS
@@ -162,7 +360,7 @@ CHANGES TO REMIND
- Fixed serious bug in which background queued reminders were ignored and
Remind simply exited. Doh! Sorry about that.
* Version 3.0 Patch 21
* Version 3.0 Patch 21 - 2000-03-15
+ MINOR ENHANCEMENTS
@@ -193,7 +391,7 @@ CHANGES TO REMIND
- Fixed compilation problem on FreeBSD, IRIX, Tru64 and other UNIXes.
* Version 3.0 Patch 20
* Version 3.0 Patch 20 - 1999-04-12
+ NEWS
@@ -222,7 +420,7 @@ CHANGES TO REMIND
- Fixed a typo in danish.h, courtesy of Niels Kristian Bech Jensen.
* Version 3.0 Patch 19
* Version 3.0 Patch 19 - 1998-05-09
+ MAJOR ENHANCEMENTS
@@ -264,7 +462,7 @@ CHANGES TO REMIND
- Lots more silly little bugs squashed -- too many to go into in
detail.
* Version 3.0 Patch 18
* Version 3.0 Patch 18 - 1998-02-15
+ MAJOR ENHANCEMENTS
@@ -328,7 +526,7 @@ CHANGES TO REMIND
- Getting rid of fixed-sized buffers meant lots of changes to code.
No doubt, I missed a few regression tests.
* Version 3.0 Patch 17
* Version 3.0 Patch 17 - 1997-09-07
+ MINOR ENHANCEMENTS
@@ -369,7 +567,7 @@ CHANGES TO REMIND
library. All three of these fixes are courtesy of Christopher
J. Madsen <madsen@iglobal.net>. Thanks, Christopher.
* Version 3.0 Patch 16
* Version 3.0 Patch 16 - 1997-02-11
+ MINOR ENHANCEMENTS
@@ -397,7 +595,7 @@ CHANGES TO REMIND
- Fixed a problem under Solaris 2.5 whereby rem2ps was skipping some
latin1 characters which it interpreted as white space.
* Version 3.0 Patch 15
* Version 3.0 Patch 15 - 1996-10-27
+ IMPORTANT NOTES
@@ -427,7 +625,7 @@ CHANGES TO REMIND
- Fixed bug in TkRemind which caused a crash if the "-m" option was used
for a month beginning on Sunday. Doh!!!
* Version 3.0 Patch 14
* Version 3.0 Patch 14 - 1996-05-25
+ CHANGE IN COPYING POLICY
@@ -486,7 +684,7 @@ CHANGES TO REMIND
_not_ support MS Windows, and in fact do not allow Remind to run
under Windows (see COPYRIGHT).
* Version 3.0 Patch 13
* Version 3.0 Patch 13 - 1994-05-06
+ MINOR ENHANCEMENTS
@@ -521,7 +719,7 @@ CHANGES TO REMIND
- Updated the copyright notices everywhere.
* Version 3.0 Patch 12
* Version 3.0 Patch 12 - 1994-02-01
+ MINOR ENHANCEMENTS
@@ -540,7 +738,7 @@ CHANGES TO REMIND
- Fixed a problem with the '-k' option which resulted in a newline being
placed after the message text. This was giving sh(1) heartburn...
* Version 3.0 Patch 11
* Version 3.0 Patch 11 - 1993-11-26
+ MINOR ENHANCEMENTS
@@ -620,7 +818,7 @@ CHANGES TO REMIND
- Fixed the Finnish language support which was missing a few newlines.
* Version 3.0 Patch 9
* Version 3.0 Patch 9 - 1993-10-04
+ NOTES
@@ -677,7 +875,7 @@ CHANGES TO REMIND
so that newlines in the body start new paragraphs, rather than being
swallowed as white-space.
* Version 3.0 Patch 8
* Version 3.0 Patch 8 - 1993-09-08
+ MAJOR ENHANCEMENTS
@@ -729,7 +927,7 @@ CHANGES TO REMIND
range [-128, 255] (but not 0) so that char(asc(s)) works even
on machines with signed char types.
* Version 3.0 Patch 7
* Version 3.0 Patch 7 - 1993-07-22
+ MAJOR ENHANCEMENTS
@@ -772,7 +970,7 @@ CHANGES TO REMIND
- Changed kall so that "kall sh" doesn't commit suicide - patch courtesy
of Michael Salmon.
* Version 3.0 Patch 6
* Version 3.0 Patch 6 - 1993-05-05
+ MINOR ENHANCEMENTS
@@ -795,7 +993,7 @@ CHANGES TO REMIND
- Fixed a bug in the -u option which sometimes caused a core dump
(embarrassed grin!) The fix is due to Tina Hoeltig. Thanks, Tina!
* Version 3.0 Patch 5
* Version 3.0 Patch 5 - 1993-04-27
+ MAJOR ENHANCEMENTS:
@@ -830,7 +1028,7 @@ CHANGES TO REMIND
- Fixed a bug in the man page - thanks to Ed Oskiewicz.
* Version 3.0 Patch 4
* Version 3.0 Patch 4 - 1993-03-08
- Added the -g option - this sorts reminders by date/time before
issuing them. (You can see I'm running out of letters to
@@ -889,13 +1087,13 @@ CHANGES TO REMIND
- Put my new mailing address in the README files.
* Version 3.0 Patch 3
* Version 3.0 Patch 3 - 1993-02-21
- Corrected bugs in Remind and Rem2PS. No new features added. You
should NOT use patch level 2 - either stick to 3.0.1 or upgrade to
3.0.3.
* Version 3.0 Patch 2
* Version 3.0 Patch 2 - 1993-02-04
- Added the -u option to Remind so that root can run it as any user.
This simplifies the remind-all scripts, and makes them more efficient.
@@ -935,7 +1133,7 @@ CHANGES TO REMIND
- Changed Remind so that supplying the -a option causes timed reminders
not to be placed into the calendar in calendar mode.
* Version 3.0 Patch 1
* Version 3.0 Patch 1 - 1992-12-18
- Wrote the Rem2ps program to produce PostScript calendars
@@ -975,7 +1173,7 @@ CHANGES TO REMIND
- Re-formatted the WHATSNEW.30 file.
* Version 3.0
* Version 3.0 - 1992-11-09
- Total rewrite from previous versions
@@ -996,23 +1194,23 @@ CHANGES TO REMIND
- Fixed a lurking bug in trigger date calculation which, amazingly, had not
been caught in the couple of years that Remind has been out!
* Version 2.3 Patch 5
* Version 2.3 Patch 5 - 1992-04-11
- Added the "c+n" option for printing a calendar by
weeks instead of months, courtesy Dennis Cottel (dennis@peanuts.nosc.mil).
* Version 2.3 Patch 4
* Version 2.3 Patch 4 - 1991-11-06
- Made the init.c file nicer. Made the Makefile
prettier. Added "make test", "make tar" and "make shar" Makefile targets.
* Version 2.3 Patch 3
* Version 2.3 Patch 3 - 1991-09-11
- Added a command-line option for Remind to process
queued reminders in the foreground. This makes automatic termination
of Remind processes from within X-Windows and Sunview easier.
* Version 2.3 Patch 2
* Version 2.3 Patch 2 - 1991-07-19
- Fixed up a problem with timed reminders which resulted
in cursor not starting from left side of screen on some systems.
@@ -1029,7 +1227,7 @@ CHANGES TO REMIND
- Made the Makefile more portable
* Version 2.3 Patch 1
* Version 2.3 Patch 1 - 1991-03-08
- Added the "-t" command-line option to get Remind
to trigger all non-expired reminders.
@@ -1043,7 +1241,7 @@ CHANGES TO REMIND
- Added manual pages for "kall" and "rem".
* Version 2.3
* Version 2.3 - 1991-02-20
- Added the UNTIL keyword for forcing reminders to expire.
@@ -1065,28 +1263,28 @@ CHANGES TO REMIND
- Modified the calendar and SimpleCalendar formats so that the % escape
substitutions ARE performed.
* Version 2.2 - Patch 5
* Version 2.2 - Patch 5 - 1990-12-03
- Added the BEFORE, AFTER and SKIP tokens to make the
handling of holidays more sensible. Also corrected a few more bugs.
* Version 2.2 - Patch 3
* Version 2.2 - Patch 3 - 1990-11-28
- Added the MSG or RUN tokens in an OMIT command; also
allowed RUN-type reminders to be explicitly included in the calendar by
using the %" escape sequence.
* Version 2.2
* Version 2.2 - 1990-11-16
- Added the AT keyword, the timed reminders daemon, and the
calendar facility.
* Version 2.1
* Version 2.1 - 1990-11-06
- Added the "repeat" token for repeating reminders with a period
other than 7 days. Also fixed some bugs from version 2.0
* Version 2.0
* Version 2.0 - 1990-11-01
- first public release. Included advanced date specifications,
character substitution, and the RUN keyword.

View File

@@ -27,8 +27,8 @@ RUN OFF
################################################
# Ensure required version of remind is used... #
################################################
IF version() < "03.00.19"
ERRMSG This file requires at least version 03.00.19 of Remind.%
IF version() < "03.01.08"
ERRMSG This file requires at least version 03.01.08 of Remind.%
ERRMSG This version is version [version()].
EXIT
ENDIF
@@ -93,53 +93,10 @@ SET Week_1 1
SET Week_2 8
SET Week_3 15
SET Week_4 22
FSET _last(mo) "1 " + MON((mo%12)+1)+" --7"
# Shorthand for commonly used expression...
FSET _trig() TRIGGER(TRIGDATE())
FSET _last(mo) "1 " + MON((mo%12)+1) + " --7"
# Handy function to provide SCANFROM dates...
FSET _back(days) TRIGGER(TODAY()-days)
###########################################################
# On MS-DOS systems, the standard C library functions are #
# not reliable for computing offsets from local time to #
# UTC. The following provides a work-around for the #
# sunrise() and sunset() functions. Note, however, that #
# if Daylight Savings Time is in effect for today(), the #
# sun functions return times in DST even for dates on #
# which DST is not in effect; the converse can also occur.#
# #
# Change the timezone to your timezone - the default is #
# for EST which is 5 hours (300 minutes) behind UTC. #
# The code is correct for places in which Daylight Savings#
# Time begins on the last Sunday in April and ends on the #
# last Sunday in October. #
###########################################################
IF OSTYPE() == "MSDOS"
# Eastern Standard Time
SET TimeZone -300
# Use --8 rather than --7 because we want the last day BEFORE
# the time switch occurs.
REM Sun 1 May --8 SATISFY 1
SET BegDst TRIGDATE()
REM Sun 1 Nov --8 SATISFY 1
SET EndDst TRIGDATE()
SET $CalcUTC 0
# Check out the following IF statement and figure out why it works!
IF EndDst < BegDst
# Daylight Savings Time
SET $MinsFromUTC TimeZone+60
ELSE
# Standard Time
SET $MinsFromUTC TimeZone
ENDIF
ENDIF
FSET _back(days) TODAY()-days
###########################################################
# Function which returns a string in "am/pm" format based #
@@ -209,14 +166,14 @@ FSET _mail(from, subj) "fastmail -f " + \
REM 4 July SCANFROM [_back(7)] SATISFY 1
IF WKDAYNUM(TRIGDATE()) == Sat
REM [TRIGGER(TRIGDATE())] MSG Independence day (actual)
OMIT [TRIGGER(TRIGDATE()-1)] MSG Independence day (observed)
REM [TRIGDATE()] MSG Independence day (actual)
OMIT [TRIGDATE()-1] MSG Independence day (observed)
ELSE
IF WKDAYNUM(TRIGDATE()) == Sun
REM [TRIGGER(TRIGDATE())] MSG Independence day (actual)
OMIT [TRIGGER(TRIGDATE()+1)] MSG Independence day (observed)
REM [TRIGDATE()] MSG Independence day (actual)
OMIT [TRIGDATE()+1] MSG Independence day (observed)
ELSE
OMIT [TRIGGER(TRIGDATE())] MSG Independence day
OMIT [TRIGDATE()] MSG Independence day
ENDIF
ENDIF
@@ -237,7 +194,7 @@ REM Mon 8 SATISFY 1
# But only actually trigger the delayed meeting if the previous
# Monday was a holiday
IF ISOMITTED(TRIGDATE()-7)
REM [TRIGGER(TRIGDATE())] MSG Delayed meeting
REM [TRIGDATE()] MSG Delayed meeting
ENDIF
############################################################################
@@ -328,12 +285,12 @@ REM Sat Sun SPECIAL SHADE 220
SET SaveTrig $NumTrig
SET easter EASTERDATE(YEAR(TODAY()))
REM [TRIGGER(easter-46)] MSG %"Ash Wednesday%"
REM [TRIGGER(easter-7)] MSG %"Palm Sunday%"
OMIT [TRIGGER(easter-2)] MSG %"Good Friday%"
OMIT [TRIGGER(easter)] MSG %"Easter%" Sunday
REM [TRIGGER(easter+39)] MSG %"Ascension Day%"
REM [TRIGGER(easter+49)] MSG %"Pentecost%"
REM [easter-46] MSG %"Ash Wednesday%"
REM [easter-7] MSG %"Palm Sunday%"
OMIT [easter-2] MSG %"Good Friday%"
OMIT [easter] MSG %"Easter%" Sunday
REM [easter+39] MSG %"Ascension Day%"
REM [easter+49] MSG %"Pentecost%"
# Some holidays are omitted, some are not. You may want to change
# which ones are omitted - use the general forms shown below.
@@ -345,9 +302,14 @@ REM Mon Jan [Week_3] MSG Martin Luther King - %"MLK Day%"
REM Feb 2 MSG %"Ground Hog Day%"
REM Feb 14 MSG %"Valentine's%" Day
REM Mon Feb [Week_3] SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG %"President's Day%"
OMIT [trigdate()] MSG %"President's Day%"
REM Mar 17 MSG %"St. Patrick's%" Day
REM Sun Apr 1 ++2 MSG Daylight Savings Time - %"DST starts%" %b
# The DST rules are accurate for most locations in
# North America
REM Sun Apr 1 ++2 UNTIL 1 Jan 2007 MSG Daylight Savings Time - %"DST starts%" %b
REM Sun Mar 8 ++2 FROM 1 Jan 2007 MSG Daylight Savings Time - %"DST starts%" %b
REM Apr 1 MSG %"April Fool's%" Day
REM Mon Tue Wed Thu Fri Sat 15 Apr MSG %"Income tax%" due
REM May 5 MSG %"Cinco de Mayo%"
@@ -355,23 +317,28 @@ REM Sat May [Week_1] MSG %"Kentucky Derby%"
REM Sun May [Week_2] MSG %"Mother's Day%"
REM Sat May [Week_3] MSG %"Armed Forces Day%"
REM Mon [_last(May)] SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG %"Memorial Day%"
OMIT [trigdate()] MSG %"Memorial Day%"
REM Jun 14 MSG %"Flag Day%"
REM Sun Jun [Week_3] MSG %"Father's Day%"
REM Mon Sep [Week_1] SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG %"Labor Day%"
OMIT [trigdate()] MSG %"Labor Day%"
REM Mon Oct [Week_2] MSG %"Columbus Day%"
REM Nov 11 MSG %"Veterans Day%"
REM Sun [_last(Oct)] MSG Daylight Savings Time - %"DST over%"
# The DST rules are accurate for most locations in
# North America
REM Sun [_last(Oct)] UNTIL 1 Jan 2007 MSG Daylight Savings Time - %"DST over%"
REM Sun 1 Nov FROM 1 Jan 2007 MSG Daylight Savings Time - %"DST over%"
REM Oct 30 MSG %"Mischief Night%"
REM Oct 31 MSG %"Halloween%"
REM Tue Nov 2 SCANFROM [_back(7)] \
SATISFY [(YEAR(TRIGDATE()) % 4) == 0] \
MSG %"Election%" Day
REM Thu Nov [Week_4] SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG %"Thanksgiving%" Day
OMIT [trigdate()] MSG %"Thanksgiving%" Day
REM Fri Nov [Week_4+1] SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG %"Thanksgiving%" (cont.)
OMIT [trigdate()] MSG %"Thanksgiving%" (cont.)
OMIT Dec 24 MSG %"Christmas Eve%"
OMIT Dec 25 MSG %"Christmas%" Day
@@ -409,10 +376,10 @@ REM PS Border Border moveto \
([hebday(today())] [hebmon(today())]) show
# Fill in the phases of the moon on the PostScript calendar
[trigger(moondate(0))] SPECIAL MOON 0
[trigger(moondate(1))] SPECIAL MOON 1
[trigger(moondate(2))] SPECIAL MOON 2
[trigger(moondate(3))] SPECIAL MOON 3
[moondate(0)] SPECIAL MOON 0
[moondate(1)] SPECIAL MOON 1
[moondate(2)] SPECIAL MOON 2
[moondate(3)] SPECIAL MOON 3
# The following example puts sunrise and sunset times in PostScript in the
# calendar - the sizes are hard-coded, however, and work best in landscape.
@@ -465,11 +432,11 @@ SET InIsrael 0
SET Reform 0
# Convenient function definition to save typing
FSET _h(x, y) TRIGGER(HEBDATE(x,y))
FSET _h(x, y) HEBDATE(x,y)
FSET _h2(x, y) HEBDATE(x, y, TODAY()-7)
FSET _PastSat(x, y) TRIGGER(IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)+1))
FSET _PastSun(x, y) TRIGGER(IIF(WKDAYNUM(_h2(x,y))!=0, _h2(x,y), _h2(x,y)+1))
FSET _PastMon(x, y) TRIGGER(IIF(WKDAYNUM(_h2(x,y))!=1, _h2(x,y), _h2(x,y)+1))
FSET _PastSat(x, y) IIF(WKDAYNUM(_h2(x,y))!=6, _h2(x,y), _h2(x,y)+1)
FSET _PastSun(x, y) IIF(WKDAYNUM(_h2(x,y))!=0, _h2(x,y), _h2(x,y)+1)
FSET _PastMon(x, y) IIF(WKDAYNUM(_h2(x,y))!=1, _h2(x,y), _h2(x,y)+1)
# Default values in case InIsrael and Reform are not set
SET InIsrael VALUE("InIsrael", 0)
@@ -490,7 +457,7 @@ IF !InIsrael
[_h(16, "Tishrey")] MSG %"Sukkot 2%"
ENDIF
[_h(21, "Tishrey")] ++4 MSG %"Hashana Rabba%" is %b.
[_h(21, "Tishrey")] ++4 MSG %"Hoshana Rabba%" is %b.
[_h(22, "Tishrey")] ++4 MSG %"Shemini Atzeret%" is %b.
IF InIsrael
@@ -500,7 +467,7 @@ ELSE
ENDIF
# Because Kislev can change length, we must be more careful about Chanukah
FSET _chan(x) TRIGGER(HEBDATE(24, "Kislev", today()-9)+x)
FSET _chan(x) HEBDATE(24, "Kislev", today()-9)+x
[_chan(1)] ++4 MSG %"Chanukah 1%" is %b.
[_chan(2)] MSG %"Chanukah 2%"
[_chan(3)] MSG %"Chanukah 3%"
@@ -518,15 +485,17 @@ IF !Reform
ENDIF
[_h(15, "Shvat")] ++4 MSG %"Tu B'Shvat%" is %b.
[_h(15, "Adar A")] ++4 MSG %"Purim Katan%" is %b.
[_h(14, "Adar A")] ++4 MSG %"Purim Katan%" is %b.
[_h(15, "Adar A")] ++4 MSG %"Shushan Purim Katan%" is %b.
# If Purim is on Sunday, then Fast of Esther is 11 Adar.
IF WKDAYNUM(_h2(13, "Adar")) != 6
REM [TRIGGER(_h2(13, "Adar"))] ++4 MSG %"Fast of Esther%" is %b.
REM [_h2(13, "Adar")] ++4 MSG %"Fast of Esther%" is %b.
ELSE
REM [TRIGGER(_h2(11, "Adar"))] ++4 MSG %"Fast of Esther%" is %b.
REM [_h2(11, "Adar")] ++4 MSG %"Fast of Esther%" is %b.
ENDIF
[_h(14, "Adar")] ++4 MSG %"Purim%" is %b.
[_h(15, "Adar")] ++4 MSG %"Shushan Purim%" is %b.
[_h(15, "Nisan")] ++4 MSG %"Pesach%" is %b.
IF !InIsrael
@@ -539,7 +508,13 @@ IF !InIsrael && !Reform
[_h(22, "Nisan")] MSG %"Pesach 8%"
ENDIF
[_PastSun(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b.
REM [_PastSun(27, "Nisan")] SATISFY 1
IF $Tw == 5
REM [_PastSun(26, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b.
ELSE
REM [_PastSun(27, "Nisan")] ++4 MSG %"Yom HaShoah%" is %b.
ENDIF
# If 4 Iyar is a Friday, then Yom Hazikaron is
# the Wednesday before and Yom Ha'atzmaut is on
@@ -549,7 +524,7 @@ IF WKDAYNUM(_h2(4, "Iyar")) == 5
[_h(2, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b.
[_h(3, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b.
ELSE
IF WKDAYNUM(_h2, 4, "Iyar") == 0
IF WKDAYNUM(_h2(4, "Iyar")) == 0
[_h(5, "Iyar")] ++4 MSG %"Yom Hazikaron%" is %b.
[_h(6, "Iyar")] ++4 MSG %"Yom Ha'atzmaut%" is %b.
ELSE

20
man/rem.1 Normal file
View File

@@ -0,0 +1,20 @@
.TH REM 1 "30 August 2007"
.UC4
.SH NAME
rem \- Invoke Remind with a default filename
.SH SYNOPSIS
.B rem [\fIoptions\fR] [\fIdate\fR] [\fI*rep\fR] [\fItime\fR]
.SH DESCRIPTION
\fBrem\fR is a symbolic link to \fBremind\fR. When \fBremind\fR
determines that it has been invoked as \fBrem\fR, it uses a default
filename rather than expecting a filename to be supplied on the command
line.
.PP
If the environment variable DOTREMINDERS is set, \fBremind\fR uses the
value of DOTREMINDERS as the filename. Otherwise, \fBremind\fR uses
the filename $HOME/.reminders
.PP
.SH AUTHOR
Remind was written by David F. Skoll <dfs@roaringpenguin.com>
.SH SEE ALSO
\fBremind\fR

View File

@@ -407,4 +407,4 @@ You should ensure that the values you supply for margin widths are sensible.
If they are too big for the media size, \fBRem2ps\fR will not complain,
but again, the PostScript output will probably not work.
.SH SEE ALSO
\fBRemind\fR
\fBremind\fR

View File

@@ -1,4 +1,4 @@
.TH REMIND 1 "1 July 2007"
.TH REMIND 1 "31 August 2008"
.UC 4
.SH NAME
remind \- a sophisticated reminder service
@@ -13,6 +13,13 @@ a program to be executed.
If \fIfilename\fR is specified as a single dash '-', then \fBRemind\fR
takes its input from standard input. This also implicitly enables
the \fB\-o\fR option, described below.
.PP
If \fIfilename\fR happens to
be a directory rather than a plain file, then \fBRemind\fR reads all of
the files in that directory that match the pattern "*.rem". The files
are read in sorted order; the sort order may depend on your locale, but
should match the sort order used by the shell to expand "*.rem".
.SH OPTIONS
\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".
@@ -22,16 +29,46 @@ 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
date by piping the output through \fBsort(1)\fR.
.TP
.B \-j\fR[\fIn\fR]
Runs \fBRemind\fR in "purge" mode to get rid of expired reminders.
See the section PURGE MODE for details.
.TP
.B \-r
The \fB\-r\fR option disables \fBRUN\fR directives and the \fBshell()\fR
function. As of Remind 3.00.17, using \fB\-u\fR implies \fB\-r\fR.
.TP
.B \-c\fR\fIn\fR
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 calendar will
be generated for \fIn\fR months, starting with the current month. By
default, a calendar for only the current month is produced. If \fIn\fR
starts with '+', then a calendar for \fIn\fR weeks is produced.
.B \-c\fI[flags]\fIn\fR
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
calendar will be generated for \fIn\fR months, starting with the
current month. By default, a calendar for only the current month is
produced.
.PP
You can precede \fIn\fR (if any) with a set of flags. The flags
are as follows:
.TP
.B '+'
causes a calendar for \fIn\fR
weeks to be produced.
.TP
.B 'a'
causes \fBRemind\fR to display reminders on the calendar on the
day they actually occur \fIas well as\fR on any preceding days
specified by the reminder's \fIdelta\fR.
.TP
.B 'l'
causes \fBRemind\fR to use VT100 line-drawing characters to draw
the calendar. The characters are hard-coded and will only work
on terminals that emulate the VT00 line-drawing character set.
.TP
.B 'c'
causes \fBRemind\fR to use VT100 escape sequences to approximate
SPECIAL COLOR reminders. The approximation is (of necessity) very
coarse, because the VT100 only has eight different color sequences,
each with one of two brightnesses. A color component greater than
64 is considered "on", and if any of the three color components is
greater than 128, the color is considered "bright".
.TP
.B \-w\fR\fIcol\fR[,\fIpad\fR[,\fIspc\fR]]]
The \fB\-w\fR option specifies the output width, padding and spacing
@@ -97,6 +134,11 @@ The \fB\-o\fR option causes \fBRemind\fR to ignore all \fBONCE\fR directives.
The \fB\-t\fR option causes \fBRemind\fR to trigger all non-expired reminders,
regardless of the \fIdelta\fR supplied for each reminder.
.TP
.B \-t\fR\fIn\fR
If you supply a number \fIn\fR after the \fB\-t\fR option, then
\fBRemind\fR pretends that each non-expired reminder has a \fIdelta\fR
of \fIn\fR days and triggers reminders accordingly.
.TP
.B \-h
The \fB\-h\fR option ("hush...") suppresses certain warning and information
messages. In particular, if no reminders are triggered, this mode
@@ -139,15 +181,19 @@ Dump the variable table after execution of the reminder script
.TP
.B l
Echo lines when displaying error messages
.TP
.B f
Trace the reading of reminder files
.RE
.TP
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]
Normally, reminders are issued in the order in which they are encountered
in the reminder script. The \fB\-g\fR option cause \fBRemind\fR to
sort reminders by date and time prior to issuing them. The optional
\fBa\fR and \fBd\fR characters specify the sort order (ascending or
descending) for the date, time and priority fields. See the section "Sorting
Reminders" for more information.
\fB\-g\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR[\fBa|d\fR]]]]
Normally, reminders are issued in the order in which they are
encountered in the reminder script. The \fB\-g\fR option cause
\fBRemind\fR to sort reminders by date and time prior to issuing them.
The optional \fBa\fR and \fBd\fR characters specify the sort order
(ascending or descending) for the date, time and priority fields. See
the section "Sorting Reminders" for more information.
.TP
\fB\-b\fR[\fIn\fR]
Set the time format for the calendar and simple-calendar outputs. \fIN\fR
@@ -225,6 +271,9 @@ TAG clause.
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
section "Initializing Variables on the Command Line" for more details.
.TP
\fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR
Allows you to define a function on the command line.
.PP
If you supply a \fIdate\fR on the command line, it must consist of
\fIday month year\fR, where \fIday\fR is the day of the month,
@@ -241,6 +290,10 @@ set \fBRemind\fR's notion of "now" to a particular time. Supplying
a \fItime\fR on the command line also implicitly enables the \fB\-q\fR
option and disables the \fB\-z\fR option.
.PP
If you would rather specify the date more succinctly, you can supply
it as YYYY-MM-DD or YYYY/MM/DD. You can even supply a date and
time on the command line as one argument: YYYY-MM-DD@HH:MM.
.PP
In addition, you can supply a \fIrepeat\fR parameter, which has the
form *\fInum\fR. This causes \fBRemind\fR to be run \fInum\fR times,
with the date incrementing on each iteration. You may have to enclose
@@ -261,7 +314,7 @@ very simple and almost immediately understandable:
to the baroque and obscure:
.PP
.nf
REM [trigger(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!
.fi
.PP
@@ -293,6 +346,7 @@ Its syntax is:
[\fBPRIORITY\fR \fIprio\fR]
[\fBSKIP\fR | \fBBEFORE\fR | \fBAFTER\fR]
[\fBOMIT\fR \fIomit_list\fR]
[\fBOMITFUNC\fR \fIomit_function\fR]
[\fBAT\fR \fItime\fR [\fItdelta\fR] [\fItrepeat\fR]]
[\fBSCHED\fR \fIsched_function\fR]
[\fBWARN\fR \fIwarn_function\fR]
@@ -587,6 +641,48 @@ the
.I weekday
constraints.
.PP
.B SHORT-HAND DATE SPECIFICATIONS
.PP
In addition to spelling out the day, month and year separately, you
can specify YYYY-MM-DD or YYYY/MM/DD. For example, the following statements
are equivalent:
.PP
.nf
REM 5 June 2010 MSG Cool!
REM 2010-06-05 MSG Cool!
.fi
.PP
You can also specify a date and time as YYYY-MM-DD@HH:MM. These
statements are equivalent:
.PP
.nf
REM 19 Dec 2010 AT 16:45 MSG Hi
REM 2010-12-19@16:45 MSG Hi
.fi
.PP
There's one subtlety with short-hand date specifications: The following
statements are \fInot\fR equivalent:
.PP
.nf
REM 19 Dec 2010 AT 16:45 +60 MSG Hi
REM 2010-12-19@16:45 +60 MSG Hi
.fi
.PP
In the second statement, the "+60" is a \fIdelta\fR that applies to the
date rather than a \fItdelta\fR that applies to the time. We recommend
explicitly using the AT keyword with timed reminders.
.PP
.B THE REMIND ALGORITHM
.PP
\fBRemind\fR uses the following algorithm to compute a trigger date:
Starting from the current date, it examines each day, one at a time, until
it finds a date that satisfies the date specification, or proves to
itself that no such date exists. (Actually, \fBRemind\fR merely
\fIbehaves\fR as if it used this algorithm; it would be much too slow
in practice. Internally, \fBRemind\fR uses much faster techniques to
calculate a trigger date.) See DETAILS ABOUT TRIGGER COMPUTATION for
more information.
.PP
.B BACKWARD SCANNING
.PP
Sometimes, it is necessary to specify a date as being a set amount of
@@ -684,7 +780,7 @@ Another example: Suppose you have jury duty from 30 November 1992 until
of your jury duty, as well as 2 days ahead of time:
.PP
.nf
REM 30 Nov 1992 *1 +2 UNTIL 4 Dec 1992 MSG Jury duty
REM 1992-11-30 *1 +2 UNTIL 1992-12-04 MSG Jury duty
.fi
.PP
Note that the \fIrepeat\fR of *1 is necessary; without it, the reminder
@@ -750,6 +846,50 @@ also. See "The OMIT command" for more details.)
By comparison, if we had used "\-\-1", the reminder would be triggered on
the last day of the month, regardless of the \fBOMIT\fR.
.PP
.B COMPUTED LOCAL OMITS
.PP
The \fBOMITFUNC\fR phrase of the \fBREM\fR command allows you to
supply a function that determines whether or not a date is omitted.
The function is passed a single parameter of type \fBDATE\fR, and must
return a non-zero integer if the date is considered "omitted" and 0
otherwise. Here's an example:
.PP
.nf
FSET _third(x) (day(x) % 3) || \\
(wkdaynum(x) == 0) || \\
(wkdaynum(x) == 6)
REM OMITFUNC _third AFTER MSG Working day divisible by 3
.fi
.PP
In the example above, the reminder is triggered every Monday to Friday whose
day-of-month number is divisible by three. Here's how it works:
.TP
.B o
The \fBOMITFUNC _third\fR portion causes all days for which \fB_third(x)\fR
returns non-zero to be considered "omitted". This causes all days whose
day-of-month number is \fInot\fR a multiple of three to be omitted. Note
that _third also returns non-zero if the weekday is Sunday or Saturday.
.TP
.B o
The \fBAFTER\fR keyword causes the reminder to be moved after a block of
omitted days.
.PP
The combination of OMITFUNC and AFTER keyword causes the reminder to
be issued on all days whose day-of-month number is divisible by three,
but not on Saturday or Sunday.
.PP
Note that if you use \fBOMITFUNC\fR, then a local \fBOMIT\fR is
\fIignored\fR as are \fIall global OMITs\fR. If you want to omit specific
weekdays, your omit function will need to test for them specifically. If
you want to take into account the global \fBOMIT\fR context, then your omit
function will need to test for that explicitly (using the \fBisomitted()\fR
function.)
.PP
Note that an incorrect \fBOMITFUNC\fR might cause all days to be considered
omitted. For that reason, when \fBRemind\fR searches through omitted days,
it terminates the search after the \fBSATISFY\fR iteration limit
(command-line option \fB\-x\fR.)
.PP
.B TIMED REMINDERS
.PP
Timed reminders are those that have an \fBAT\fR keyword followed
@@ -1167,6 +1307,14 @@ must create an \fBOMIT\fR command for each year. (Later, in the
description of expressions and some of the more advanced features of
\fBRemind\fR, you will see how to automate this for some cases.)
.PP
As with the REM command, you can use shorthand specifiers for dates;
the following are equivalent:
.PP
.nf
OMIT 7 Sep 1992
OMIT 1992-09-07
.fi
.PP
For convenience, you can use a \fIdelta\fR and \fBMSG\fR or \fBRUN\fR
keyword in the \fBOMIT\fR command. The following sequences are exactly
equivalent:
@@ -1182,11 +1330,11 @@ equivalent:
.PP
.B THE BEFORE, AFTER AND SKIP KEYWORDS
.PP
Normally, days that are omitted, whether by a global \fBOMIT\fR command
or the local \fBOMIT\fR keyword in a \fBREM\fR statement, only affect the
counting of the \-\fIback\fR or the +\fIdelta\fR. For example, suppose
you have a meeting every Wednesday. Suppose, too, that you have indicated
11 Nov as a holiday:
Normally, days that are omitted, whether by a global \fBOMIT\fR
command or the local \fBOMIT\fR or \fBOMITFUNC\fR keywords in a
\fBREM\fR statement, only affect the counting of the \-\fIback\fR or
the +\fIdelta\fR. For example, suppose you have a meeting every
Wednesday. Suppose, too, that you have indicated 11 Nov as a holiday:
.PP
.nf
OMIT 11 Nov +4 MSG Remembrance Day
@@ -1248,6 +1396,12 @@ administrator.)
.PP
If you specify a filename of "-" in the \fBINCLUDE\fR command, \fBRemind\fR
will begin reading from standard input.
.PP
If you specify a \fIdirectory\fR as the argument to \fBINCLDUE\fR, then
\fBRemind\fR will process all files in that directory that match the shell
patterm "*.rem". The files are processed in sorted order; the sort order
matches that used by the shell when it expands "*.rem".
.SH THE RUN COMMAND
.PP
If you include other files in your reminder script, you may not always
@@ -1829,15 +1983,49 @@ order, or 2 if sorting by time in descending order.
.B $SubsIndent
The number of spaces by which all lines (except the first) of an
\fBMSF\fR-type reminder should be indented. The default is 0.
.PP
Note: If any of the calendar modes are in effect, then the
values of $Daemon, $DontFork, $DontTrigAts, $DontQueue, $HushMode,
$IgnoreOnce, $InfDelta, and $NextMode are not meaningful.
.TP
.B $T (read-only, DATE type)
Exactly equivalent to \fBtrigdate()\fR. (See BUILT-IN FUNCTIONS.)
.TP
.B $Td (read-only)
Equivalent to \fBday(trigdate())\fR.
.TP
.B $Tm (read-only)
Equivalent to \fBmonnum(trigdate())\fR.
.TP
.B $Tw (read-only)
Equivalent to \fBwkdaynum(trigdate())\fR.
.TP
.B $Ty (read-only)
Equivalent to \fByear(trigdate())\fR.
.TP
.B $TimeSep
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
DATETIME value.
.TP
.B $UntimedFirst (read-only)
Set to 1 if the \fB\-g\fR option is used with a fourth sort character
of "d"; set to 0 otherwise.
.TP
.B $U (read-only, DATE type)
Exactly equivalent to \fBtoday()\fR. (See BUILT-IN FUNCTIONS.)
.TP
.B $Ud (read-only)
Equivalent to \fBday(today())\fR.
.TP
.B $Um (read-only)
Equivalent to \fBmonnum(today())\fR.
.TP
.B $Uw (read-only)
Equivalent to \fBwkdaynum(today())\fR.
.TP
.B $Uy (read-only)
Equivalent to \fByear(today())\fR.
.PP
Note: If any of the calendar modes are in effect, then the
values of $Daemon, $DontFork, $DontTrigAts, $DontQueue, $HushMode,
$IgnoreOnce, $InfDelta, and $NextMode are not meaningful.
.PP
.B BUILT-IN FUNCTIONS
.PP
@@ -1964,6 +2152,10 @@ actual date and time, or may be the date and time supplied on the command line.
The \fBdate()\fR function returns a \fBDATE\fR object with the year,
month and day components specified by \fIy\fR, \fIm\fR and \fId\fR.
.TP
.B datepart(dq_datetime)
Returns a \fBDATE\fR object representing the date portion of
\fIdatetime\fR.
.TP
.B datetime(args)
The \fBdatetime()\fR function can take anywhere from two to five arguments.
It always returns a DATETIME generated from its arguments.
@@ -2043,6 +2235,37 @@ for the specified year. If \fIarg\fR is a \fBDATE\fR or
\fBDATETIME\fR, then returns the date of the next Easter Sunday on or
after \fIarg\fR. (The time component of a datetime is ignored.)
.TP
.B evaltrig(s_trigger [,dq_start])
Evaluates \fItrigger\fR as if it were a REM or IFTRIG trigger specification
and returns the trigger date as a \fBDATE\fR (or as a \fBDATETIME\fR if
there is an \fBAT\fR clause.) Returns a negative \fBINT\fR if no trigger
could be computed.
.RS
.PP
Normally, \fBevaltrig\fR finds a trigger date on or after today. If
you supply the \fIstart\fR argument, then it scans starting from there.
.PP
For example, the expression:
.PP
.nf
evaltrig("Mon 1", '2008-10-07')
.fi
.PP
returns '2008-11-03', since that is the first date on or after
7 October 2008 that satisfies "Mon 1".
.PP
If you want to see how many days it is from the first Monday in October, 2008
to the first Monday in November, 2008, use:
.PP
.nf
evaltrig("Mon 1", '2008-11-01') - evaltrig("Mon 1", '2008-10-01')
.fi
.PP
and the answer is 28. The trigger argument to \fBevaltrig\fR can have
all the usual trigger clauses (\fBOMIT\fR, \fBAT\fR, \fBSKIP\fR, etc.) but
\fIcannot\fR have a \fBSATISFY\fR, \fBMSG\fR, etc. reminder-type clause.
.RE
.TP
.B filedate(s_filename)
Returns the modification date of \fIfilename\fR. If \fIfilename\fR
does not exist, or its modification date is before the year
@@ -2080,6 +2303,18 @@ environment variable is not defined. Note that the names of environment
variables are generally case-sensitive; thus, getenv("HOME") is not
the same as getenv("home").
.TP
.B hebdate(i_day, s_hebmon [,idq_yrstart [,i_jahr [,i_aflag]]])
Support for Hebrew dates - see the section "The Hebrew Calendar"
.TP
.B hebday(dq_date)
Support for Hebrew dates - see the section "The Hebrew Calendar"
.TP
.B hebmon(dq_date)
Support for Hebrew dates - see the section "The Hebrew Calendar"
.TP
.B hebyear(dq_date)
Support for Hebrew dates - see the section "The Hebrew Calendar"
.TP
.B hour(tq_time)
Returns the hour component of \fItime\fR.
.TP
@@ -2120,19 +2355,8 @@ or \fBDATETIME\fR is supplied, then the year component is used in the test.
.B isomitted(dq_date)
Returns 1 if \fIdate\fR is omitted, given the current global \fBOMIT\fR
context. Returns 0 otherwise. (If a datetime is supplied, only the
date part is used.)
.TP
.B hebdate(i_day, s_hebmon [,idq_yrstart [,i_jahr [,i_aflag]]])
Support for Hebrew dates - see the section "The Hebrew Calendar"
.TP
.B hebday(dq_date)
Support for Hebrew dates - see the section "The Hebrew Calendar"
.TP
.B hebmon(dq_date)
Support for Hebrew dates - see the section "The Hebrew Calendar"
.TP
.B hebyear(dq_date)
Support for Hebrew dates - see the section "The Hebrew Calendar"
date part is used.) Note that any local \fBOMIT\fR or \fBOMITFUNC\fR
clauses are \fInot\fR taken into account by this function.
.TP
.B language()
Returns a \fBSTRING\fR naming the language supported by \fBRemind\fR.
@@ -2234,10 +2458,11 @@ This function returns the number of \fInon-\fRomitted days between
counted. \fIend\fR is never counted.
.RS
.PP
Note that \fIend\fR must be greater than or
equal to \fIstart\fR or an error is reported. In addition to using
the global OMIT context, you can supply additional arguments that are
names of weekdays to be omitted.
Note that \fIend\fR must be greater than or equal to \fIstart\fR or an
error is reported. In addition to using the global OMIT context, you
can supply additional arguments that are names of weekdays to be
omitted. However, in a \fBREM\fR command, any local \fBOMITFUNC\fR
clause is \fInot\fR taken into account by this function.
.PP
For example, the following line sets a to 11 (assuming no global OMITs):
.PP
@@ -2260,36 +2485,30 @@ reminder will label day numbers in a calendar:
ENDIF
.fi
.PP
Here's a more complex example: Suppose your normal garbage-collection
day is Thursday, but if any of Monday through Thursday of a week is a holiday,
the collection day moves to Friday. Here's one way to solve it:
.PP
.nf
FSET prev_monday(x) x - wkdaynum(x-1)
REM Thu Fri SATISFY [wkdaynum(trigdate()) == 4 && \\
nonomitted(prev_monday(today()), today()+1) == 4 || \\
wkdaynum(trigdate()) == 5 && \\
nonomitted(prev_monday(today()), today()+1) <= 4] \\
MSG Garbage Day
.fi
.PP
Whew! (You'll need to see "THE SATISFY CLAUSE" later on.) We'd
better explain that one: The prev_monday helper function takes a date
and returns the date of the previous Monday. The REM command will
trigger on the first Thursday or Friday that satisfies one of the
following conditions:
.PP
1) Either it's a Thursday and there are exactly four non-omitted days between
the previous Monday and tomorrow, or
.PP
2) It's a Friday and there are four or fewer non-omitted days between the
previous Monday and tomorrow. We need the "or fewer" condition to handle
the case of more than one holiday in a given week. If that happens,
garbage day still only moves by one day.
.PP
Obviously, the answer you get from \fBnonomitted\fR depends on the global
OMIT context. If you use moveable OMITs, you may get inconsistent results.
.PP
Here is a more complex use for \fBnonomitted\fR. My garbage collection
follows two interleaved 14-day cycles: One Friday, garbage and paper
recycling ("Black Box") are collected. The next Friday, garbage and
plastic recycling ("Blue Box") are collected. If any of Monday-Friday
is a holiday, collection is delayed until the Saturday. Here's a way
to encode these rules:
.PP
.nf
fset _garbhol(x) wkdaynum(x) == 5 && nonomitted(x-4, x+1) < 5
REM 12 November 1999 *14 AFTER OMITFUNC _garbhol MSG Black Box
REM 19 November 1999 *14 AFTER OMITFUNC _garbhol MSG Blue Box
.fi
.PP
Here's how it works: The _garbhol(x) user-defined function returns 1
if and only if (1) \fIx\fR is a Friday and (2) there is at least one
OMITted day from the previous Monday up to and including the Friday.
.PP
The first REM statement sets up the 14-day black-box cycle. The AFTER
keyword makes it move collection to the Saturday if _garbhol returns 1.
The second REM statement sets up the 14-day blue-box cycle with a similar
adjustment made by AFTER in conjunction with _garbhol.
.RE
.TP
.B now()
@@ -2317,12 +2536,13 @@ If three arguments are supplied, returns \fIstr1\fR if \fInum\fR is 1, and
.RE
.TP
.B psmoon(i_phase [,i_size [,s_note [,i_notesize]]])
Returns a \fBSTRING\fR consisting of PostScript code to draw a moon in
the upper-left hand corner of the calendar box. \fIPhase\fR specifies
the phase of the moon, and is 0 (new moon), 1 (first quarter), 2 (full
moon) or 3 (third quarter). If \fIsize\fR is specified, it controls
the radius of the moon in PostScript units (1/72 inch.) If it is not
specified or is negative, the size of the day-number font is used.
[DEPRECATED] Returns a \fBSTRING\fR consisting of PostScript code to
draw a moon in the upper-left hand corner of the calendar box.
\fIPhase\fR specifies the phase of the moon, and is 0 (new moon), 1
(first quarter), 2 (full moon) or 3 (third quarter). If \fIsize\fR is
specified, it controls the radius of the moon in PostScript units
(1/72 inch.) If it is not specified or is negative, the size of the
day-number font is used.
.PP
.PP
.RS
@@ -2330,10 +2550,10 @@ For example, the following four lines place moon symbols on the PostScript
calendar:
.PP
.nf
REM [trigger(moondate(0))] PS [psmoon(0)]
REM [trigger(moondate(1))] PS [psmoon(1)]
REM [trigger(moondate(2))] PS [psmoon(2)]
REM [trigger(moondate(3))] PS [psmoon(3)]
REM [moondate(0)] PS [psmoon(0)]
REM [moondate(1)] PS [psmoon(1)]
REM [moondate(2)] PS [psmoon(2)]
REM [moondate(3)] PS [psmoon(3)]
.fi
.PP
If \fInote\fR is specified, the text is used to annotate the moon
@@ -2346,14 +2566,14 @@ does not check for this.) For example, if you want the time of each new
moon displayed, you could use this in your reminder script:
.PP
.nf
REM [trigger(moondate(0))] PS [psmoon(0, -1, moontime(0)+"")]
REM [moondate(0)] PS [psmoon(0, -1, moontime(0)+"")]
.fi
.PP
Note how the time is coerced to a string by concatenating the null string.
.RE
.TP
\fBpsshade(i_gray)\fR or \fBpsshade(i_red, i_green, i_blue)\fR
Returns a \fBSTRING\fR that consists of PostScript commands to
[DEPRECATED] Returns a \fBSTRING\fR that consists of PostScript commands to
shade a calendar box. \fINum\fR can range from 0 (completely black)
to 100 (completely white.) If three arguments are given, they specify
red, green and blue intensity from 0 to 100.
@@ -2398,11 +2618,38 @@ character in the output is converted to a space. Note that if \fBRUN
OFF\fR has been executed, or the \fB\-r\fR command-line option has
been used, \fBshell()\fR will result in an error, and \fIcmd\fR will
not be executed.
.RS
.PP
If \fImaxlen\fR is specified, then \fBshell()\fR returns the first
\fImaxlen\fR characters of output (rather than the first 511). If
\fImaxlen\fR is specified as a negative number, then \fIall\fR the
output from \fIcmd\fR is returned.
.RE
.TP
.B slide(d_start, i_amt [,s_wkday...])
This function is the inverse of \fBnonomitted\fR. It adds \fIamt\fR
days (which can be negative) to \fIstart\fR, \fInot counting omitted days\fR.
The optional \fIwkday\fR arguments are additional weekday names to omit.
.RS
.PP
Consider this example:
.PP
.nf
OMIT 14 May 2009
SET a slide('2009-05-13', 5, "Sat", "Sun")
.fi
.PP
In this case, \fIa\fR is set to 2009-05-21. That's because we slide forward
by 5 days, not including Thursday, May 14 or Saturday and Sunday,
May 16 and 17. You can go backwards, too, so:
.PP
.nf
OMIT 14 May 2009
SET a slide('2009-05-21', -5, "Sat", "Sun")
.fi
.PP
takes \fIa\fR back to 2009-05-13.
.RE
.TP
.B strlen(s_str)
Returns the length of \fIstr\fR.
@@ -2441,6 +2688,10 @@ than 60 degrees. The functions are available starting from version
Creates a \fBTIME\fR with the hour and minute components specified by
\fIhr\fR and \fImin\fR.
.TP
.B timepart(tq_datetime)
Returns a \fBTIME\fR object representing the time portion of
\fIdatetime\fR.
.TP
.B today()
Returns \fBRemind\fR's notion of "today." This may be the actual system
date, or a date supplied on the command line, or the date of the
@@ -2460,15 +2711,18 @@ triggerable \fBREM\fR command had an \fBAT\fR clause. If there was no
returns the integer 0.
.TP
.B trigger(d_date [,t_time [,i_utcflag]]) \fRor\fB trigger(q_datetime [,i_utcflag])
Returns a string suitable for use in a \fBREM\fR command, allowing you
to calculate trigger dates in advance. (See the section "Expression
pasting" for more information.) Note that \fBtrigger()\fR
\fIalways\fR returns its result in English, even for foreign-language
versions of \fBRemind\fR. This is to avoid problems with certain C
libraries that do not handle accented characters properly. Normally,
the \fIdate\fR and \fItime\fR are the local date and time; however, if
\fIutcflag\fR is non-zero, the \fIdate\fR and \fItime\fR are
interpreted as UTC times, and are converted to local time. Examples:
Returns a string suitable for use in a \fBREM\fR command or a SCANFROM
or UNTIL clause, allowing you to calculate trigger dates in advance.
Note that in earlier versions of \fBRemind\fR, \fBtrigger\fR was
required to convert a date into something the \fBREM\fR command could
consume. However, in this version of \fBRemind\fR, you can omit it.
Note that \fBtrigger()\fR \fIalways\fR returns its result in English,
even for foreign-language versions of \fBRemind\fR. This is to avoid
problems with certain C libraries that do not handle accented
characters properly. Normally, the \fIdate\fR and \fItime\fR are the
local date and time; however, if \fIutcflag\fR is non-zero, the
\fIdate\fR and \fItime\fR are interpreted as UTC times, and are
converted to local time. Examples:
.RS
.PP
trigger('1993/04/01')
@@ -2545,6 +2799,21 @@ Returns a string specifying the version of \fBRemind\fR. For version
\fBRemind\fR are released, the value returned by \fBversion()\fR will
strictly increase, according to the rules for string ordering.
.TP
.B weekno([dq_date, [i_wkstart, [i_daystart]]])
Returns the week number of the year. If no arguments are supplied,
returns the ISO 8601 week number for \fBtoday()\fR. If one
argument \fIdate\fR is supplied, then returns the ISO 8601 week
number for that date. If two arguments are supplied, then
\fIwkstart\fR must range from 0 to 6, and represents the first
day of the week (with 0 being Sunday and 6 being Saturday.). If
\fIwkstart\fR is not supplied, then it defaults to 1. If the
third argument \fIdaystart\fR is supplied, then it specifies
when Week 1 starts. If \fIdaystart\fR is less than or equal to 7,
then Week 1 starts on the first \fIwkstart\fR on or after
January \fIdaystart\fR. Otherwise, Week 1 starts on the first
\fIwkstart\fR on or after December \fIdaystart\fR. If omitted,
\fIdaystart\fR defaults to 29 (following the ISO 8601 definition.)
.TP
.B wkday(dqi_arg)
If \fIarg\fR is a \fBDATE\fR or \fBDATETIME\fR, returns a string
representing the day of the week of the date. If \fIarg\fR is an
@@ -2567,10 +2836,10 @@ you can "paste" an expression in. To do this, surround the expression
with square brackets. For example:
.PP
.nf
REM [trigger(mydate)] MSG foo
REM [mydate] MSG foo
.fi
.PP
This evaluates the expression "trigger(mydate)", where "mydate" is
This evaluates the expression "mydate", where "mydate" is
presumably some pre-computed variable, and then "pastes" the result
into the command-line for the parser to process.
.PP
@@ -2593,8 +2862,16 @@ exceptions:
.TP
o
If \fBRemind\fR is expecting an expression, as in the \fBSET\fR command,
or the \fBIF\fR command, then no expression pasting takes place. The
expression is simply evaluated as if the square brackets were not there.
or the \fBIF\fR command, you should \fBnot\fR include square brackets.
FOr example, use:
.PP
.nf
SET a 4+5
.fi
and not:
.nf
SET a [4+5]
.fi
.TP
o
You cannot use expression pasting for the first token on a line.
@@ -2942,7 +3219,7 @@ more complicated sequence:
.nf
REM 13 SATISFY wkdaynum(trigdate()) == 5
IF trigvalid()
REM [trigger(trigdate())] +2 MSG \\
REM [trigdate()] +2 MSG \\
Friday the 13th is %b.
ENDIF
.fi
@@ -2950,8 +3227,7 @@ more complicated sequence:
Let's see how this works. The \fBSATISFY\fR clause iterates through
all the 13ths of successive months, until a trigger date is found whose
day-of-week is Friday (== 5). If a valid date was found, we use the
calculated trigger date (converted into a trigger format with the
\fBtrigger()\fR function) to set up the next reminder.
calculated trigger date to set up the next reminder.
.PP
We could also have written:
.PP
@@ -2970,7 +3246,7 @@ could use:
.nf
# Note: SATISFY 1 is an idiom for "do nothing"
REM Mon 1 Sept SATISFY 1
OMIT [trigger(trigdate())]
OMIT [trigdate()]
.fi
.PP
\fBCAVEAT:\fR This \fIonly\fR omits the \fInext\fR Labour Day, not
@@ -3022,6 +3298,10 @@ occurring on odd-numbered days of the month with the following:
MSG Here it is!!!
.fi
.PP
Note that \fBSATISFY\fR and \fBOMITFUNC\fR can often be used to solve the
same problem, though in different ways. Sometimes a \fBSATISFY\fR is cleaner
and sometimes an \fBOMITFUNC\fR; experiment and use whichever seems clearer.
.PP
.SH DEBUGGING REMINDER SCRIPTS
.PP
Although the command-line \fB\-d\fR option is useful for debugging, it
@@ -3032,7 +3312,7 @@ under program control. The format is:
.PP
\fBDEBUG\fR [+\fIflagson\fR] [\-\fIflagsoff\fR]
.PP
\fIFlagson\fR and \fIflagsoff\fR consist of strings of the characters "extvl"
\fIFlagson\fR and \fIflagsoff\fR consist of strings of the characters "extvlf"
that correspond to the debugging options discussed in the command-line
options section. If preceded with a "+", the corresponding group of
debugging options is switched on. Otherwise, they are switched off.
@@ -3243,12 +3523,11 @@ You should not have any spaces between the \fB\-i\fR option and the equal
sign; otherwise, strange variable names are created that can only be accessed
with the \fBvalue()\fR or \fBdefined()\fR functions.
.PP
If your site uses the \fBRemind-all\fR shell script to mail reminders
to users, the script will initialize the variable \fIremind_all\fR to 1
using the \fB\-i\fR option. Thus, you can detect when your reminder
script is being processed by \fBRemind-all\fR and can use this information
to control which reminders you want mailed to you.
You can also define a function on the command line by using:
.PP
\fB\-i\fR\fIfunc\fR(\fIargs\fR)=\fIdefinition\fR
.PP
Be sure to protect special characters from shell interpretation.
.SH MORE ABOUT POSTSCRIPT
.PP
The \fBPS\fR and \fBPSFILE\fR reminders pass PostScript code directly
@@ -3359,27 +3638,90 @@ so to run in the daemon mode in the background, use:
.PP
If you use \fBsh\fR or \fBbash\fR, you may have to use the "nohup" command
to ensure that the daemon is not killed when you log out.
.PP
.SH PURGE MODE
.PP
If you supply the \fB\-j\fR command-line option, \fBRemind\fR runs
in \fIpurge mode\fR. In this mode, it tries to purge expired reminders
from your reminder files.
.PP
In purge mode, \fBRemind\fR reads your reminder file and creates a new
file by appending ".purged" to the original file name. Note that
\fBRemind\fR \fInever\fR edits your original file; it always creates
a new .purged file.
.PP
If you invoke \fBRemind\fR against a directory instead of a file, then
a .purged file is created for each *.rem file in the directory.
.PP
Normally, \fBRemind\fR does not create .purged files for INCLUDed files.
However, if you supply a numeric argument after \fB\-j\fR, then \fBRemind\fR
will create .purged files for the specified level of INCLUDE. For example,
if you invoke \fBRemind\fR with the argument \fB\-j2\fR, then .purged
files will be created for the file (or directory) specified on the command
line, any files included by them, and any files included by those files.
However, .purged files will not be created for third-or-higher level
INCLUDE files.
.PP
Determining which reminders have expired is extremely tricky. \fBRemind\fR
does its best, but you should always compare the .purged file to the
original file and hand-merge the changes back in.
.PP
Remind annotates the .purged file as follows:
.PP
An expired reminder is prefixed with: #!P: Expired:
.PP
In situations where \fBRemind\fR cannot reliably determine that
something was expired, you may see the following comments inserted
before the problematic line:
.PP
.nf
#!P: Cannot purge SATISFY-type reminders
#!P: The next IF evaluated false...
#!P: REM statements in IF block not checked for purging.
#!P: The previous IF evaluated true.
#!P: REM statements in ELSE block not checked for purging
#!P: The next IFTRIG did not trigger.
#!P: REM statements in IFTRIG block not checked for purging.
#!P: Next line has expired, but contains expression... please verify
#!P: Next line may have expired, but contains non-constant expression
#!P! Could not parse next line: Some-Error-Message-Here
.fi
.PP
\fBRemind\fR always annotates .purged files with lines beginning with
"#!P". If such lines are encountered in the \fIoriginal\fR file,
they are not copied to the .purged file.
.PP
.SH SORTING REMINDERS
.PP
The \fB\-g\fR option causes \fBRemind\fR to sort reminders by
trigger date, time and priority before issuing them. Note that reminders are
still calculated in the order encountered in the script. However, rather
than being issued immediately, they are saved in an internal buffer.
When \fBRemind\fR has finished processing the script, it issues the
saved reminders in sorted order. The \fB\-g\fR option can be followed
by up to three characters that must all be "a" or "d". The first character
specifies the sort order by trigger date (ascending or descending),
the second specifies the sort order by trigger time and the third
specifies the sort order by priority. The default is
to sort all fields in ascending order.
The \fB\-g\fR option causes \fBRemind\fR to sort reminders by trigger
date, time and priority before issuing them. Note that reminders are
still calculated in the order encountered in the script. However,
rather than being issued immediately, they are saved in an internal
buffer. When \fBRemind\fR has finished processing the script, it
issues the saved reminders in sorted order. The \fB\-g\fR option can
be followed by up to four characters that must all be "a" or "d". The
first character specifies the sort order by trigger date (ascending or
descending), the second specifies the sort order by trigger time and
the third specifies the sort order by priority. If the fourth
character is "d", the untimed reminders are sorted before timed
reminders. The default is to sort all fields in ascending order and
to sort untimed reminders after timed reminders.
.PP
In ascending order, reminders are issued with the most imminent first.
Descending order is the reverse. Reminders are always sorted by
trigger date, and reminders with the same trigger date are then sorted
by trigger time. Non-timed reminders are always issued after timed
reminders in this mode. If two reminders have the same date and time,
then the priority is used to break ties. Reminders with the same date,
by trigger time. If two reminders have the same date and time, then
the priority is used to break ties. Reminders with the same date,
time and priority are issued in the order they were encountered.
.PP
You can define a user-defined function called SORTBANNER that takes one
@@ -3732,10 +4074,10 @@ in calendars produced by \fBRem2PS\fR, \fBtkremind\fR and \fBrem2html\fR.)
The \fBMOON\fR special replaces the \fBpsmoon()\fR function. Use it
like this:
.nf
REM [trigger(moondate(0))] SPECIAL MOON 0
REM [trigger(moondate(1))] SPECIAL MOON 1
REM [trigger(moondate(2))] SPECIAL MOON 2
REM [trigger(moondate(3))] SPECIAL MOON 3
REM [moondate(0)] SPECIAL MOON 0
REM [moondate(1)] SPECIAL MOON 1
REM [moondate(2)] SPECIAL MOON 2
REM [moondate(3)] SPECIAL MOON 3
.fi
These draw little moons on the various calendars. The complete syntax
of the \fBMOON\fR special is as follows:
@@ -3771,6 +4113,19 @@ The rest of the line is the text to put in the calendar.
.PP
The COLOR special is "doubly special", because in its normal operating
mode, \fBremind\fR treats a COLOR special just like a MSG-type reminder.
Also, if you invoke \fBRemind\fR with \fB\-cc\fR..., then it approximates
SPECIAL COLOR reminders on your terminal.
.PP
The \fBWEEK\fR special lets you place annotations such as the week
number in the calendar. For example, this would number each Monday
with the ISO 8601 week number. The week number is shown like this:
"(W\fIn\fR)" in this example, but you can put whatever text you like
after the WEEK keyword.
.nf
REM Monday SPECIAL WEEK (W[weekno()])
.fi
.SH MISCELLANEOUS
.PP
.B COMMAND ABBREVIATIONS
@@ -3851,7 +4206,7 @@ This example puts an entry in each box of a calendar showing the number
.nf
REM Tue 2 Nov SATISFY (year(trigdate())%4) == 0
IF trigvalid()
REM [trigger(trigdate())] ++5 MSG \\
REM [trigdate()] ++5 MSG \\
U.S. Presidential Election!!
ENDIF
.fi
@@ -3902,8 +4257,8 @@ in September. It can move over a range of 7 days. Consider the
following sequence:
.PP
.nf
REM Mon 1 Sept SCANFROM [trigger(today()-7)] SATISFY 1
OMIT [trigger(trigdate())]
REM Mon 1 Sept SCANFROM [today()-7] SATISFY 1
OMIT [trigdate()]
REM Mon AFTER MSG Hello
.fi
@@ -3933,7 +4288,7 @@ will trigger on Mondays and Thursdays between 23 July 2007 and
the reminder above as follows:
.PP
.nf
REM Mon Thu SCANFROM [trigger(max(today(), '2007-07-23'))] \\
REM Mon Thu SCANFROM [max(today(), '2007-07-23')] \\
UNTIL 2 Aug 2007 MSG Test
.fi
.PP
@@ -4026,8 +4381,9 @@ Hebrew dates in \fBRemind\fR change at midnight instead of sunset.
.PP
Language should be selectable at run-time, not compile-time. Don't
expect this to happen soon!
\fBRemind\fR has some built-in limits (for example,
number of global \fBOMIT\fRs.)
.PP
\fBRemind\fR has some built-in limits (for example, number of global
\fBOMIT\fRs.)
.PP
.SH BIBLIOGRAPHY
.PP
@@ -4043,4 +4399,4 @@ Catalog\fR, Jewish Publication Society of America.
.PP
.SH SEE ALSO
.PP
rem2ps, tkremind
rem, rem2ps, tkremind

View File

@@ -21,6 +21,8 @@ extra features as you become a more sophisticated \fBRemind\fR programmer.
on to \fBRemind\fR. The options it passes are
\fB\-b\fR, \fB\-g\fR, \fB\-x\fR, \fB\-i\fR and \fB\-m\fR.
See the \fBRemind\fR man page for details about the options.
Note that \fBTkRemind\fR will respect the \fB\-m\fR and
\fB\-b1\fR options and adjust its appearance accordingly.
\fIRead_file\fR is the file from which \fBTkRemind\fR reads reminders.
It is in standard \fBRemind\fR format. \fIWrite_file\fR is the file
@@ -235,6 +237,22 @@ press \fBApply Options\fR to put them into effect, \fBSave Options\fR
to put them into effect and save them in $HOME/.tkremindrc, or
\fBCancel\fR to cancel any changes you made.
.SH KEYBOARD SHORTCUTS
\fBTkRemind\fR's main window includes the following keyboard shortcuts:
.TP
.B Ctrl-Q
Quit
.TP
.B Left Arrow
Previous Month
.TP
.B Right Arrow
Next Month
.TP
.B Home
Today
.SH ODDS AND ENDS
\fBTkRemind\fR performs some basic consistency checks when you add or
preview a reminder. However, if you edit a reminder in the previewer,
@@ -268,7 +286,7 @@ for \fBTkRemind\fR (for example).
script file. It also places special comments in the reminder
file to store additional state. You can certainly mix
"hand-crafted" reminders with reminders created by \fBTkRemind\fR
if you are awared of the following rules and limitations:
if you are aware of the following rules and limitations:
.TP
o
Do not use the \fBTAG\fR keyword in hand-crafted reminders.

View File

@@ -1,4 +1,4 @@
Files in this directory:
tkremind -- Tcl/Tk graphical calendar using Remind as engine
cm2rem.tcl -- Convert Sun's "cm" calendar manager files to Remind.
cm2rem.tcl -- Convert Sun's "cm" calendar manager files to Remind.

View File

@@ -8,7 +8,7 @@
#
# This file is part of REMIND.
# Copyright (C) 1992-1998 David F. Skoll
# Copyright (C) 1999-2005 Roaring Penguin Software Inc.
# Copyright (C) 1999-2010 Roaring Penguin Software Inc.
#
#--------------------------------------------------------------
@@ -17,6 +17,38 @@ exec wish "$0" "$@"
wm withdraw .
# Our icon photo
catch {
image create photo rpicon -data {
R0lGODlhFwAgAOecABUTERYTERYUERcVEhgWExkXFBkXFRoXFRsZFhwZFxwa
GB0bGR4cGR4cGh8dGiAeHCEfHCEfHSIgHSIgHiQiHyYkISknJCooJispJywq
Jy4sKTIwLjUzMDUzMTo4Njs5Nzs5ODw7ODw7OT07OT48OkE/PUJAPkNBP0RC
QEVDQUVEQkdFQ0lIRkpJR01LSU5MSlBPTVFQTlNSUFRSUFRSUVVTUlVUUllY
VltZV1xaWF1cWmBfXmJgX2RiYGZlY2dmZGppZ2tqaG1ram9tbHFwb3Jwb3Rz
cXV0c3Z0c3Z1c3Z1dHd1dHh2dXh3dnt5eHx7eXx7en18en59e4B/foGAf4KB
f4SDgYWEgoWEg4eGhIiHhouKiI2Mio6Ni46NjJCQj5KRkJSTkZeWlpiXlpmY
l5qZmJybmp6dnKCfnqGgoKKhoKOioaSjoqinp6qpqKurqq+urbCvrrCwr7Gw
r7OysbW1tLi3tri3t7u6ur28vMTDw8TEw8XFxMbFxcfGxsfHxsrJycrKyczM
y83My83MzM3NzdDQz9LR0dPS0tPT09fX19jY19ra2dvb29zc29zc3Ojn5+jo
6Orq6uzs7O/v7/T09PX19fb29vf39/r6+vv7+/7+/v//////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////////////yH5BAEKAP8ALAAAAAAXACAAAAj+AP8JHEiwoMGD
CAcusRAAQEKDBQIcEBAAwUODAQJAsBGAwsWCBzJuUBLgI0ENGVM2dACg5UWV
KU+Y/JfRQBknPoq8ATQz4wxOQIFa6vMx5ZSgQetczJDSClKgcF6mFDEnE9I2
D0fADOChUdA1D7dmTBEUTditDQRQAnomIQaxICpoAmomoUoAGS2YIBIUDEIu
YndI8FAJaBaEMlIuSEkloxugUBBOSLkh44AvGfkAPYJQpYqMLIQEILB205DO
KW9kJHMhQAmgkaKgzsgjggM5GbEAxaNmdoAPOoz8CCAgEVAtg3wPEPMnQQAU
QWsg5AAzDZSMbIBeaoHwAUwSDAI2XMAENA8ThAPEBvAStEkc3yonrOW0aUMk
+BkBVAlaKATC8Fsp8Igid5ABgxMHtaTgggy6ZFBAADs= }
wm iconphoto . -default rpicon
}
proc missing_tcllib { pkg } {
catch { puts stderr "Could not find the '$pkg' package -- you must install tcllib.\nPlease see http://tcllib.sourceforge.net/" }
tk_dialog .err "Error: tcllib not installed" "Could not find the '$pkg' package -- you must install tcllib. Please see http://tcllib.sourceforge.net/" error 0 OK
@@ -93,8 +125,12 @@ set EditorPid -1
set AppendFile {NOSUCHFILE}
catch {set AppendFile $ReminderFile}
set SetFontsWorked 0
#---------------- DON'T CHANGE STUFF BELOW HERE ------------------
# 24-hour clock mode
set TwentyFourHourMode 0
# Is Monday in first column?
set MondayFirst 0
@@ -153,7 +189,7 @@ set HighestTagSoFar 0
proc Initialize {} {
global DayNames argc argv CommandLine ReminderFile AppendFile Remind PSCmd
global MondayFirst
global MondayFirst TwentyFourHourMode
set CommandLine "|$Remind -itkremind=1 -p -y -l"
set PSCmd "$Remind -p -l"
@@ -165,6 +201,9 @@ proc Initialize {} {
if {[regexp -- {m} [lindex $argv $i]]} {
set MondayFirst 1
}
if {"[lindex $argv $i]" == "-b1"} {
set TwentyFourHourMode 1
}
} else {
break
}
@@ -180,6 +219,14 @@ proc Initialize {} {
}
}
# If reminder file is a directory and appendfile is the same as
# reminder file, choose append file to be $ReminderFile/100-tkremind.rem
if {[file isdirectory $ReminderFile]} {
if {"$ReminderFile" == "$AppendFile"} {
set AppendFile [file join $ReminderFile "100-tkremind.rem"]
}
}
# Check system sanity
if {! [file readable $ReminderFile]} {
set ans [tk_dialog .error "TkRemind: Warning" "Can't read reminder file `$ReminderFile'" warning 0 "Create it and continue" "Exit"]
@@ -193,6 +240,12 @@ proc Initialize {} {
exit 1
}
if {[file isdirectory $ReminderFile] && ! [file exists $AppendFile]} {
if {![catch {close [open "$AppendFile" "a"]}]} {
tk_dialog .error "Created File" "Created blank file `$AppendFile'" info 0 "OK"
}
}
if {! [file writable $AppendFile]} {
tk_dialog .error Error "Can't write reminder file `$AppendFile'" error 0 Ok
exit 1
@@ -271,9 +324,14 @@ proc CalEntryOffset { firstDay } {
#***********************************************************************
proc CreateCalFrame { w dayNames } {
# Figure out reasonable height for text frames
global SetFontsWorked
set h [winfo screenheight .]
if {$h <= 480} {
set h 2
if {$SetFontsWorked} {
set h 3
} else {
set h 2
}
} elseif {$h <= 600} {
set h 4
} else {
@@ -298,10 +356,8 @@ proc CreateCalFrame { w dayNames } {
set f [expr $n+$j]
button $w.l$f -text "" -justify center -command "" \
-state disabled -relief flat -border 0 -padx 0 -pady 0
text $w.t$f -width 12 -height $h -border 1 -wrap word -relief flat \
text $w.t$f -width 12 -height $h -border 1 -spacing3 3 -wrap word -relief flat \
-state disabled -takefocus 0 -cursor {}
$w.t$f tag bind TAGGED <Enter> "TaggedEnter $w.t$f"
$w.t$f tag bind TAGGED <Leave> "TaggedLeave $w.t$f"
$w.t$f tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$f"
$w.t$f tag bind REM <ButtonPress-3> "FireEditor $w.t$f"
@@ -331,10 +387,17 @@ proc CreateCalFrame { w dayNames } {
#***********************************************************************
proc ConfigureCalFrame { w firstDay numDays } {
global CurMonth CurYear TodayMonth TodayYear TodayDay
global tk_version
set offset [CalEntryOffset $firstDay]
set first [expr $offset+1]
set last [expr $offset+$numDays]
if {$tk_version >= 8.5} {
set bg "#d9d9d9"
} else {
set bg [lindex [$w.t0 configure -background] 3]
}
for {set i 0} {$i < $first} {incr i} {
grid $w.l$i $w.t$i
$w.l$i configure -text "" -command "" -state disabled -relief flat
@@ -343,13 +406,11 @@ proc ConfigureCalFrame { w firstDay numDays } {
foreach t [$w.t$i tag names] {
$w.t$i tag delete $t
}
$w.t$i tag bind TAGGED <Enter> "TaggedEnter $w.t$i"
$w.t$i tag bind TAGGED <Leave> "TaggedLeave $w.t$i"
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
$w.t$i configure -state disabled
$w.t$i configure -background [lindex [$w.t$i configure -background] 3]
$w.l$i configure -background [lindex [$w.l$i configure -background] 3]
$w.t$i configure -background $bg
$w.l$i configure -background $bg
}
for {set i $first} {$i <= $last} {incr i} {
grid $w.l$i $w.t$i
@@ -361,13 +422,11 @@ proc ConfigureCalFrame { w firstDay numDays } {
foreach t [$w.t$i tag names] {
$w.t$i tag delete $t
}
$w.t$i tag bind TAGGED <Enter> "TaggedEnter $w.t$i"
$w.t$i tag bind TAGGED <Leave> "TaggedLeave $w.t$i"
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
$w.t$i configure -state disabled
$w.t$i configure -background [lindex [$w.t$i configure -background] 3]
$w.l$i configure -background [lindex [$w.l$i configure -background] 3]
$w.t$i configure -background $bg
$w.l$i configure -background $bg
}
set forgetIt 0
for {set i [expr $last+1]} {$i < 42} {incr i} {
@@ -388,13 +447,11 @@ proc ConfigureCalFrame { w firstDay numDays } {
foreach t [$w.t$i tag names] {
$w.t$i tag delete $t
}
$w.t$i tag bind TAGGED <Enter> "TaggedEnter $w.t$i"
$w.t$i tag bind TAGGED <Leave> "TaggedLeave $w.t$i"
$w.t$i tag bind TAGGED <ButtonPress-1> "EditTaggedReminder $w.t$i"
$w.t$i tag bind REM <ButtonPress-3> "FireEditor $w.t$i"
$w.t$i configure -state disabled
$w.t$i configure -background [lindex [$w.t$i configure -background] 3]
$w.l$i configure -background [lindex [$w.l$i configure -background] 3]
$w.t$i configure -background $bg
$w.l$i configure -background $bg
}
if { $CurMonth == $TodayMonth && $CurYear == $TodayYear } {
set n [expr $TodayDay + $offset]
@@ -435,6 +492,13 @@ proc CreateCalWindow { dayNames } {
wm iconname . ""
wm protocol . WM_DELETE_WINDOW Quit
wm deiconify .
bind . <Control-KeyPress-q> Quit
bind . <KeyPress-Left> ".b.prev flash; .b.prev invoke"
bind . <KeyPress-Right> ".b.next flash; .b.next invoke"
bind . <KeyPress-Prior> ".b.prev flash; .b.prev invoke"
bind . <KeyPress-Next> ".b.next flash; .b.next invoke"
bind . <KeyPress-Home> ".b.this flash; .b.this invoke"
if {$Option(StartIconified)} {
wm iconify .
}
@@ -701,10 +765,6 @@ proc FillCalWindow {} {
ConfigureCalWindow $monthName $year $firstWkday $daysInMonth
set offset [CalEntryOffset $firstWkday]
for {set i 1} {$i <= 31} {incr i} {
set didSomething($i) 0
}
set fntag "x"
while { [gets $file line] >= 0 } {
# File info
@@ -725,6 +785,13 @@ proc FillCalWindow {} {
set month [string trimleft $month 0]
set extratags ""
switch -exact -- $type {
"WEEK" {
set stuff [string trimleft $stuff]
set stuff [string trimright $stuff]
set label [expr $firstWkday + $day - 1]
.cal.l$label configure -text "$day $stuff"
continue
}
"SHADE" {
DoShadeSpecial $n $stuff
continue
@@ -763,12 +830,10 @@ proc FillCalWindow {} {
continue
}
.cal.t$n configure -state normal
if { $didSomething($day) } {
.cal.t$n insert end " .....\n"
}
set didSomething($day) 1
if {[regexp {TKTAG([0-9]+)} $tag all tagno]} {
.cal.t$n insert end [string trim $stuff] [list REM TAGGED "TKTAG$tagno" $extratags $fntag]
.cal.t$n tag bind "TKTAG$tagno" <Enter> "TaggedEnter .cal.t$n"
.cal.t$n tag bind "TKTAG$tagno" <Leave> "TaggedLeave .cal.t$n"
} else {
.cal.t$n insert end [string trim $stuff] [list REM $extratags $fntag]
}
@@ -1074,7 +1139,7 @@ proc CreateModifyDialog {w day firstDay args} {
# Set up: Year, Month, Day, WeekdayName
global CurYear CurMonth EnglishDayNames MonthNames OptionType SkipType
global ModifyDialogResult
global ModifyDialogResult TwentyFourHourMode
set OptionType 1
set SkipType 1
@@ -1090,12 +1155,13 @@ proc CreateModifyDialog {w day firstDay args} {
frame $w.exp -border 4
frame $w.adv -border 4
frame $w.weekend -border 4
frame $w.durationbox -border 4
frame $w.time -border 4
frame $w.hol -border 4
frame $w.msg
frame $w.buttons
pack $w.o1 $w.o2 $w.o3 -side top -anchor w -in $w.o
pack $w.o $w.exp $w.adv $w.weekend $w.time $w.hol $w.msg -side top -anchor w -pady 4 -expand 1 -fill both
pack $w.o $w.exp $w.adv $w.weekend $w.time $w.durationbox $w.hol $w.msg -side top -anchor w -pady 4 -expand 1 -fill both
pack $w.buttons -side top -anchor w -pady 4 -expand 1 -fill x
# TYPE 1 REMINDER
@@ -1194,7 +1260,11 @@ proc CreateModifyDialog {w day firstDay args} {
checkbutton $w.timebut -text "Timed reminder at"
$w.timebut deselect
menubutton $w.timehour -text "12" -menu $w.timehour.menu -relief raised
CreateDayMenu $w.timehour 1 12 0
if {$TwentyFourHourMode} {
CreateDayMenu $w.timehour 0 23 0
} else {
CreateDayMenu $w.timehour 1 12 0
}
menubutton $w.timemin -text "00" -menu $w.timemin.menu -relief raised
menu $w.timemin.menu -tearoff 0
foreach i {00 05 10 15 20 25 30 35 40 45 50 55} {
@@ -1202,10 +1272,12 @@ proc CreateModifyDialog {w day firstDay args} {
-command "$w.timemin configure -text $i"
}
menubutton $w.ampm -text "PM" -menu $w.ampm.menu -relief raised
menu $w.ampm.menu -tearoff 0
$w.ampm.menu add command -label "AM" -command "$w.ampm configure -text {AM}"
$w.ampm.menu add command -label "PM" -command "$w.ampm configure -text {PM}"
if {!$TwentyFourHourMode} {
menubutton $w.ampm -text "PM" -menu $w.ampm.menu -relief raised
menu $w.ampm.menu -tearoff 0
$w.ampm.menu add command -label "AM" -command "$w.ampm configure -text {AM}"
$w.ampm.menu add command -label "PM" -command "$w.ampm configure -text {PM}"
}
checkbutton $w.timeadvbut -text "with"
$w.timeadvbut deselect
@@ -1224,7 +1296,27 @@ proc CreateModifyDialog {w day firstDay args} {
$w.timerep.menu add command -label $i -command "$w.timerep configure -text $i"
}
label $w.timelab2 -text "minutes"
pack $w.timebut $w.timehour $w.timemin $w.ampm $w.timeadvbut $w.timeadv $w.timelab1 $w.timerepbut $w.timerep $w.timelab2 -side left -anchor w -in $w.time
if {$TwentyFourHourMode} {
pack $w.timebut $w.timehour $w.timemin $w.timeadvbut $w.timeadv $w.timelab1 $w.timerepbut $w.timerep $w.timelab2 -side left -anchor w -in $w.time
} else {
pack $w.timebut $w.timehour $w.timemin $w.ampm $w.timeadvbut $w.timeadv $w.timelab1 $w.timerepbut $w.timerep $w.timelab2 -side left -anchor w -in $w.time
}
# DURATION
checkbutton $w.durationbut -text "Duration"
$w.durationbut deselect
menubutton $w.durationh -text "1" -menu $w.durationh.menu -relief raised
menu $w.durationh.menu -tearoff 0
foreach i {0 1 2 3 4 5 6 7 8 9 10 11 12} {
$w.durationh.menu add command -label $i -command "$w.durationh configure -text $i"
}
label $w.durationcolon -text ":"
menubutton $w.durationm -text "00" -menu $w.durationm.menu -relief raised
menu $w.durationm.menu -tearoff 0
foreach i {00 15 30 45} {
$w.durationm.menu add command -label $i -command "$w.durationm configure -text $i"
}
pack $w.durationbut $w.durationh $w.durationcolon $w.durationm -side left -anchor w -in $w.durationbox
# SKIP TYPE
label $w.labhol -text "On holidays or weekends:"
@@ -1248,7 +1340,10 @@ proc CreateModifyDialog {w day firstDay args} {
pack $w.but$nbut -side left -anchor w -in $w.buttons -expand 1 -fill x
}
bind $w <KeyPress-Escape> "$w.but1 invoke"
bind $w <KeyPress-Escape> "$w.but1 flash; $w.but1 invoke"
if {$nbut >= 2} {
bind $w.entry <KeyPress-Return> "$w.but2 flash; $w.but2 invoke"
}
set ModifyDialogResult 0
# Center the window on the root
@@ -1265,7 +1360,7 @@ proc CreateModifyDialog {w day firstDay args} {
#***********************************************************************
proc RemindDialogToOptions { w } {
global OptionType SkipType repbut expbut advbut advcount
global timebut timeadvbut timerepbut
global timebut timeadvbut timerepbut durationbut
global dSaturday dSunday dMonday dTuesday dWednesday dThursday dFriday
set ans {}
lappend ans "-global-OptionType" $OptionType
@@ -1299,13 +1394,20 @@ proc RemindDialogToOptions { w } {
#***********************************************************************
proc OptionsToRemindDialog { w opts } {
global OptionType SkipType repbut expbut advbut advcount
global timebut timeadvbut timerepbut
global timebut timeadvbut timerepbut TwentyFourHourMode durationbut
global dSaturday dSunday dMonday dTuesday dWednesday dThursday dFriday
set hour ""
set ampm ""
foreach {flag value} $opts {
switch -glob -- $flag {
"-text-*" {
set win [string range $flag 6 end]
$w.$win configure -text $value
catch { $w.$win configure -text $value }
if {"$flag" == "-text-ampm"} {
set ampm $value
} elseif {"$flag" == "-text-timehour"} {
set hour $value
}
}
"-global-*" {
set win [string range $flag 8 end]
@@ -1318,6 +1420,26 @@ proc OptionsToRemindDialog { w opts } {
}
}
}
if {"$hour" != ""} {
if {$TwentyFourHourMode} {
if {"$ampm" != ""} {
if {"$ampm" == "PM" && $hour < 12} {
incr hour 12;
$w.timehour configure -text $hour
}
}
} else {
if {$hour > 12} {
incr hour -12
$w.timehour configure -text $hour
$w.ampm configure -text "PM"
} else {
if {"$ampm" == ""} {
$w.ampm configure -text "AM"
}
}
}
}
}
#---------------------------------------------------------------------------
@@ -1471,7 +1593,7 @@ proc CenterWindow {w} {
# The reminder as a string.
#---------------------------------------------------------------------------
proc CreateReminder {w} {
global DidOmit
global DidOmit TwentyFourHourMode
set body [string trim [$w.entry get]]
@@ -1484,7 +1606,7 @@ proc CreateReminder {w} {
# Delegate the first part to CreateReminder1, CreateReminder2, or
# CreateReminder3
global OptionType SkipType repbut expbut advbut advcount
global timebut timeadvbut timerepbut
global timebut timeadvbut timerepbut durationbut
set rem [CreateReminder$OptionType $w]
@@ -1508,13 +1630,15 @@ proc CreateReminder {w} {
if {$timebut} {
set hour [$w.timehour cget -text]
set min [$w.timemin cget -text]
if {[$w.ampm cget -text] == "PM"} {
if {$hour < 12} {
incr hour 12
}
} else {
if {$hour == 12} {
set hour 0
if {!$TwentyFourHourMode} {
if {[$w.ampm cget -text] == "PM"} {
if {$hour < 12} {
incr hour 12
}
} else {
if {$hour == 12} {
set hour 0
}
}
}
append rem " AT $hour:$min"
@@ -1524,6 +1648,9 @@ proc CreateReminder {w} {
if {$timerepbut} {
append rem " *[$w.timerep cget -text]"
}
if {$durationbut} {
append rem " DURATION [$w.durationh cget -text]:[$w.durationm cget -text]"
}
}
global SkipType
@@ -1734,7 +1861,7 @@ proc EditReminder {rem args} {
}
pack .edit.t -side top -fill both -expand 1
pack .edit.f -side top -fill x -expand 1
bind .edit <KeyPress-Escape> ".edit.but1 invoke"
bind .edit <KeyPress-Escape> ".edit.but1 flash; .edit.but1 invoke"
set ModifyDialogResult 0
CenterWindow .edit
tkwait visibility .edit
@@ -2196,8 +2323,9 @@ proc main {} {
global AppendFile HighestTagSoFar DayNames
catch {
puts "\nTkRemind Copyright (C) 1996-1998 David F. Skoll"
puts "Copyright (C) 1999-2007 Roaring Penguin Software Inc."
puts "Copyright (C) 1999-2010 Roaring Penguin Software Inc."
}
catch { SetFonts }
LoadOptions
CreateMoonImages
Initialize
@@ -2289,6 +2417,8 @@ proc FireEditor { w } {
return
}
}
Status "Firing up editor..."
after 1500 DisplayTime
set EditorPid [exec sh -c $editor &]
}
@@ -2630,12 +2760,13 @@ proc DoMoonSpecial { n stuff } {
3 { set image last }
}
.cal.t$n configure -state normal
.cal.t$n image create end -image $image
.cal.t$n image create 1.0 -image $image
if {$msg != ""} {
.cal.t$n insert end " $msg"
.cal.t$n insert 1.1 " $msg\n"
} else {
.cal.t$n insert 1.1 "\n"
}
.cal.t$n insert end "\n"
.cal.t$n configure -state disabled
}
#***********************************************************************
@@ -2648,7 +2779,12 @@ proc DoMoonSpecial { n stuff } {
# Displays current date and time in status window
#***********************************************************************
proc DisplayTime {} {
set msg [clock format [clock seconds] -format "%e %b %Y %I:%M%p"]
global TwentyFourHourMode
if {$TwentyFourHourMode} {
set msg [clock format [clock seconds] -format "%e %b %Y %H:%M"]
} else {
set msg [clock format [clock seconds] -format "%e %b %Y %I:%M%p"]
}
Status $msg
}
@@ -2725,7 +2861,7 @@ proc ShowTodaysReminders {} {
CenterWindow $w
# Grab the reminders
set stuff [exec -keepnewline $Remind -g -q -a -r $ReminderFile 2>/dev/null]
set stuff [exec -keepnewline $Remind -g -q -r $ReminderFile 2>/dev/null]
$w.text insert end $stuff
$w.text configure -state disabled
}
@@ -2785,4 +2921,16 @@ proc ClosePopup { w after_token mail_addr close_win ignore_or_kill tag reminder
}
}
# Adjust font defaults for screen size
proc SetFonts {} {
global SetFontsWorked
set h [winfo screenheight .]
if {$h <= 480} {
# Small screen (maybe eeepc?)
font configure TkDefaultFont -size 6
font configure TkFixedFont -size 6
}
set SetFontsWorked 1
}
main

View File

@@ -1,7 +1,6 @@
# Makefile.in for REMIND
#
VERSION=03.01.01
SHELL= /bin/sh
BETA = 1
srcdir=@srcdir@
@@ -10,7 +9,9 @@ exec_prefix=@exec_prefix@
mandir=@mandir@
bindir=@bindir@
datadir=@datadir@
datarootdir=@datarootdir@
VPATH=$(srcdir)
VERSION=@VERSION@
INSTALL=@INSTALL@
INSTALL_PROGRAM=@INSTALL_PROGRAM@
@@ -20,7 +21,8 @@ PROGS= remind rem2ps
SCRIPTS= $(srcdir)/../scripts/tkremind $(srcdir)/../scripts/cm2rem.tcl
MANS= $(srcdir)/../man/rem2ps.1 $(srcdir)/../man/remind.1 \
$(srcdir)/../man/tkremind.1 $(srcdir)/../man/cm2rem.1
$(srcdir)/../man/tkremind.1 $(srcdir)/../man/cm2rem.1 \
$(srcdir)/../man/rem.1
.SUFFIXES:
.SUFFIXES: .c .o
@@ -36,7 +38,7 @@ REMINDOBJS= $(REMINDSRCS:.c=.o)
all: remind rem2ps
test: remind
sh ../tests/test-rem
@sh ../tests/test-rem
.c.o:
@CC@ -c @CFLAGS@ @DEFS@ $(CEXTRA) $(LANGDEF) -I. -I$(srcdir) $<
@@ -47,18 +49,22 @@ rem2ps: rem2ps.o dynbuf.o
remind: $(REMINDOBJS)
@CC@ @LDFLAGS@ $(LDEXTRA) -o remind $(REMINDOBJS) @LIBS@
install: all
install-nostripped: all
-mkdir -p $(DESTDIR)$(bindir) || true
for prog in $(PROGS) $(SCRIPTS) ; do \
$(INSTALL_PROGRAM) $$prog $(DESTDIR)$(bindir) || exit 1; \
done
-strip $(DESTDIR)$(bindir)/remind || true
-strip $(DESTDIR)$(bindir)/rem2ps || true
rm -f $(DESTDIR)$(bindir)/rem > /dev/null 2>&1 || true
ln -s remind $(DESTDIR)$(bindir)/rem
-mkdir -p $(DESTDIR)$(mandir)/man1 || true
for man in $(MANS) ; do \
$(INSTALL_DATA) $$man $(DESTDIR)$(mandir)/man1 || exit 1; \
done
install: install-nostripped
strip $(DESTDIR)$(bindir)/remind || true
strip $(DESTDIR)$(bindir)/rem2ps || true
clean:
rm -f *.o *~ core *.bak $(PROGS)
@@ -74,15 +80,16 @@ depend:
# Build a tar file based on all files checked into git.
distro:
ln -s . ../remind-$(VERSION)
(cd ..; git-ls-files) | sed -e 's/^/remind-$(VERSION)\//' | xargs tar -C .. -cvf remind-$(VERSION).tar
(cd ..; git ls-files -x .gitignore) | sed -e 's/^/remind-$(VERSION)\//' | xargs tar -C .. -cvf remind-$(VERSION).tar
gzip -f -v -9 remind-$(VERSION).tar
rm -f ../remind-$(VERSION)
beta-tgz:
ln -s . ../remind-$(VERSION)-BETA-$(BETA)
(cd ..; git-ls-files) | sed -e 's/^/remind-$(VERSION)-BETA-$(BETA)\//' | xargs tar -C .. -cvf remind-$(VERSION)-BETA-$(BETA).tar
(cd ..; git ls-files -x .gitignore) | sed -e 's/^/remind-$(VERSION)-BETA-$(BETA)\//' | xargs tar -C .. -cvf remind-$(VERSION)-BETA-$(BETA).tar
gzip -f -v -9 remind-$(VERSION)-BETA-$(BETA).tar
rm -f ../remind-$(VERSION)-BETA-$(BETA)
#---------------- Stuff after this added by "make depend" -----------------

View File

@@ -28,29 +28,103 @@
/* Data structures used by the calendar */
typedef struct cal_entry {
struct cal_entry *next;
char *text;
char *pos;
char const *text;
char const *pos;
int is_color;
int r, g, b;
int time;
int priority;
char tag[TAG_LEN+1];
char passthru[PASSTHRU_LEN+1];
int duration;
char *filename;
char const *filename;
int lineno;
} CalEntry;
/* Line-drawing sequences */
struct line_drawing {
char const *graphics_on;
char const *graphics_off;
char tlr, bl, tbl, blr, tblr, tr, tb, br, tbr, tl, lr;
};
static struct line_drawing NormalDrawing = {
"", "", '+', '+', '+', '+', '+', '+', '|', '+', '+', '+', '-'
};
static struct line_drawing VT100Drawing = {
"\x1B(0", "\x1B(B",
'\x76', '\x6b', '\x75', '\x77', '\x6e', '\x6d', '\x78',
'\x6c', '\x74', '\x6a', '\x71'
};
static char *VT100Colors[2][2][2][2] /* [Br][R][G][B] */ = {
{
/*** DIM COLORS ***/
{
{
/* 0, 0, 0 = Black */ "\x1B[0;30m",
/* 0, 0, 1 = Blue */ "\x1B[0;34m"
},
{
/* 0, 1, 0 = Green */ "\x1B[0;32m",
/* 0, 1, 1 = Cyan */ "\x1B[0;36m"
}
},
{
{
/* 1, 0, 0 = Red */ "\x1B[0;31m",
/* 1, 0, 1 = Magenta */ "\x1B[0;35m"
},
{
/* 1, 1, 0 = Yellow */ "\x1B[0;33m",
/* 1, 1, 1 = White */ "\x1B[0;37m"
}
}
},
{
/*** BRIGHT COLORS ***/
{
{
/* 0, 0, 0 = Grey */ "\x1B[30;1m",
/* 0, 0, 1 = Blue */ "\x1B[34;1m"
},
{
/* 0, 1, 0 = Green */ "\x1B[32;1m",
/* 0, 1, 1 = Cyan */ "\x1B[36;1m"
}
},
{
{
/* 1, 0, 0 = Red */ "\x1B[31;1m",
/* 1, 0, 1 = Magenta */ "\x1B[35;1m"
},
{
/* 1, 1, 0 = Yellow */ "\x1B[33;1m",
/* 1, 1, 1 = White */ "\x1B[37;1m"
}
}
}
};
static struct line_drawing *linestruct;
#define DRAW(x) putchar(linestruct->x)
/* Global variables */
static CalEntry *CalColumn[7];
static CalEntry *CalPs[7];
static int ColSpaces;
static void Colorize(CalEntry const *e);
static void Decolorize(void);
static void SortCol (CalEntry **col);
static void DoCalendarOneWeek (void);
static void DoCalendarOneWeek (int nleft);
static void DoCalendarOneMonth (void);
static int WriteCalendarRow (void);
static void PrintLeft (char *s, int width, char pad);
static void PrintCentered (char *s, int width, char pad);
static void WriteWeekHeaderLine (void);
static void WritePostHeaderLine (void);
static void PrintLeft (char const *s, int width, char pad);
static void PrintCentered (char const *s, int width, char pad);
static int WriteOneCalLine (void);
static int WriteOneColLine (int col);
static void GenerateCalEntries (int col);
@@ -58,10 +132,42 @@ static void WriteCalHeader (void);
static void WriteCalTrailer (void);
static int DoCalRem (ParsePtr p, int col);
static void WriteSimpleEntries (int col, int jul);
static void WriteSolidCalLine (void);
static void WriteTopCalLine (void);
static void WriteBottomCalLine (void);
static void WriteIntermediateCalLine (void);
static void WriteCalDays (void);
static void gon(void)
{
printf("%s", linestruct->graphics_on);
}
static void goff(void)
{
printf("%s", linestruct->graphics_off);
}
static void Decolorize(void)
{
printf("%s", "\x1B[0m");
}
static void Colorize(CalEntry const *e)
{
int bright = 0;
int r, g, b;
if (e->r > 128 || e->g > 128 || e->b > 128) {
bright = 1;
}
if (e->r > 64) r = 1;
else r = 0;
if (e->g > 64) g = 1;
else g = 0;
if (e->b > 64) b = 1;
else b = 0;
printf("%s", VT100Colors[bright][r][g][b]);
}
/***************************************************************/
/* */
/* ProduceCalendar */
@@ -73,6 +179,11 @@ void ProduceCalendar(void)
{
int y, m, d;
if (UseVTChars) {
linestruct = &VT100Drawing;
} else {
linestruct = &NormalDrawing;
}
ShouldCache = 1;
ColSpaces = (CalWidth - 9) / 7;
@@ -89,13 +200,13 @@ void ProduceCalendar(void)
else JulianToday -= ((JulianToday+1)%7);
if (!DoSimpleCalendar) {
WriteIntermediateCalLine();
WriteWeekHeaderLine();
WriteCalDays();
WriteIntermediateCalLine();
}
while (CalWeeks--)
DoCalendarOneWeek();
DoCalendarOneWeek(CalWeeks);
return;
}
}
@@ -107,7 +218,7 @@ void ProduceCalendar(void)
/* Write a calendar for a single week */
/* */
/***************************************************************/
static void DoCalendarOneWeek(void)
static void DoCalendarOneWeek(nleft)
{
int y, m, d, done, i, l, wd;
char buf[81];
@@ -133,7 +244,9 @@ static void DoCalendarOneWeek(void)
}
/* Here come the first few lines... */
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
FromJulian(OrigJul+i, &y, &m, &d);
sprintf(buf, "%d %c%c%c ", d, MonthName[m][0], MonthName[m][1],
@@ -142,14 +255,20 @@ static void DoCalendarOneWeek(void)
PrintLeft(buf, ColSpaces, '*');
else
PrintLeft(buf, ColSpaces, ' ');
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
for (l=0; l<CalPad; l++) {
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
@@ -163,16 +282,24 @@ static void DoCalendarOneWeek(void)
/* Write any blank lines required */
while (LinesWritten++ < CalLines) {
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
/* Write the final line */
WriteIntermediateCalLine();
if (nleft) {
WriteIntermediateCalLine();
} else {
WriteBottomCalLine();
}
}
/***************************************************************/
@@ -229,6 +356,7 @@ static int WriteCalendarRow(void)
char buf[81];
int OrigJul = JulianToday;
int LinesWritten = 0;
int moreleft;
/* Get the date of the first day */
FromJulian(JulianToday, &y, &m, &d);
@@ -254,7 +382,9 @@ static int WriteCalendarRow(void)
/* Here come the first few lines... */
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
if (i < wd || d+i-wd>DaysInMonth(m, y))
PrintLeft("", ColSpaces, ' ');
@@ -262,14 +392,20 @@ static int WriteCalendarRow(void)
sprintf(buf, "%d", d+i-wd);
PrintLeft(buf, ColSpaces, ' ');
}
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
for (l=0; l<CalPad; l++) {
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
@@ -283,18 +419,27 @@ static int WriteCalendarRow(void)
/* Write any blank lines required */
while (LinesWritten++ < CalLines) {
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
PrintLeft("", ColSpaces, ' ');
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
WriteIntermediateCalLine();
moreleft = (d+7-wd <= DaysInMonth(m, y));
if (moreleft) {
WriteIntermediateCalLine();
} else {
WriteBottomCalLine();
}
/* Return non-zero if we have not yet finished */
return (d+7-wd <= DaysInMonth(m, y));
return moreleft;
}
/***************************************************************/
@@ -304,7 +449,7 @@ static int WriteCalendarRow(void)
/* Left-justify a piece of text. */
/* */
/***************************************************************/
static void PrintLeft(char *s, int width, char pad)
static void PrintLeft(char const *s, int width, char pad)
{
int len = strlen(s);
printf("%s", s);
@@ -318,7 +463,7 @@ static void PrintLeft(char *s, int width, char pad)
/* Center a piec of text */
/* */
/***************************************************************/
static void PrintCentered(char *s, int width, char pad)
static void PrintCentered(char const *s, int width, char pad)
{
int len = strlen(s);
int d = (width - len) / 2;
@@ -342,14 +487,18 @@ static int WriteOneCalLine(void)
{
int done = 1, i;
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
if (CalColumn[i]) {
if (WriteOneColLine(i)) done = 0;
} else {
PrintCentered("", ColSpaces, ' ');
}
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
@@ -368,8 +517,8 @@ static int WriteOneCalLine(void)
static int WriteOneColLine(int col)
{
CalEntry *e = CalColumn[col];
char *s;
char *space;
char const *s;
char const *space;
int numwritten = 0;
/* Print as many characters as possible within the column */
@@ -381,8 +530,8 @@ static int WriteOneColLine(int col)
if (!*s && e->next) {
PrintLeft("", ColSpaces, ' ');
CalColumn[col] = e->next;
free(e->text);
free(e->filename);
free((char *) e->text);
free((char *) e->filename);
free(e);
return 1;
}
@@ -394,6 +543,11 @@ static int WriteOneColLine(int col)
s++;
}
/* Colorize reminder if necessary */
if (UseVTColors && e->is_color) {
Colorize(e);
}
/* If we couldn't find a space char, print what we have. */
if (!space) {
for (s = e->pos; s - e->pos < ColSpaces; s++) {
@@ -412,6 +566,11 @@ static int WriteOneColLine(int col)
}
}
/* Decolorize reminder if necessary */
if (UseVTColors && e->is_color) {
Decolorize();
}
/* Flesh out the rest of the column */
while(numwritten++ < ColSpaces) PutChar(' ');
@@ -421,8 +580,8 @@ static int WriteOneColLine(int col)
/* If done, free memory if no next entry. */
if (!*s && !e->next) {
CalColumn[col] = e->next;
free(e->text);
free(e->filename);
free((char *) e->text);
free((char *) e->filename);
free(e);
} else {
e->pos = s;
@@ -441,7 +600,7 @@ static void GenerateCalEntries(int col)
{
int r;
Token tok;
char *s;
char const *s;
Parser p;
/* Do some initialization first... */
@@ -450,7 +609,7 @@ static void GenerateCalEntries(int col)
DestroyVars(0);
NumTriggered = 0;
r=OpenFile(InitialFile);
r=IncludeFile(InitialFile);
if (r) {
fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING], InitialFile, ErrMsg[r]);
exit(1);
@@ -550,14 +709,18 @@ static void WriteCalHeader(void)
FromJulian(JulianToday, &y, &m, &d);
sprintf(buf, "%s %d", MonthName[m], y);
WriteSolidCalLine();
WriteTopCalLine();
PutChar('|');
gon();
DRAW(tb);
goff();
PrintCentered(buf, CalWidth-2, ' ');
PutChar('|');
gon();
DRAW(tb);
goff();
PutChar('\n');
WriteIntermediateCalLine();
WritePostHeaderLine();
WriteCalDays();
WriteIntermediateCalLine();
}
@@ -585,26 +748,33 @@ static int DoCalRem(ParsePtr p, int col)
Trigger trig;
TimeTrig tim;
Value v;
int r;
int r, err;
int jul;
CalEntry *CurCol = CalColumn[col];
CalEntry *CurPs = CalPs[col];
CalEntry *e;
char *s, *s2;
DynamicBuffer buf, obuf;
char const *s, *s2;
DynamicBuffer buf, obuf, pre_buf;
Token tok;
int is_color, col_r, col_g, col_b;
is_color = 0;
DBufInit(&buf);
DBufInit(&pre_buf);
/* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if ( (r=ParseRem(p, &trig, &tim, 1)) ) return r;
/* Don't include timed reminders in calendar if -a option supplied. */
if (DontIssueAts && tim.ttime != NO_TIME) return OK;
if (trig.typ == NO_TYPE) return E_EOLN;
if (trig.typ == SAT_TYPE) {
r=DoSatRemind(&trig, &tim, p);
if (r) return r;
if (r) {
if (r == E_EXPIRED) return OK;
return r;
}
if (!LastTrigValid) return OK;
r=ParseToken(p, &buf);
if (r) return r;
FindToken(DBufValue(&buf), &tok);
@@ -626,7 +796,7 @@ static int DoCalRem(ParsePtr p, int col)
if (!LastTrigValid) return OK;
} else {
/* Calculate the trigger date */
jul = ComputeTrigger(trig.scanfrom, &trig, &r);
jul = ComputeTrigger(trig.scanfrom, &trig, &r, 1);
if (r) return r;
}
@@ -638,47 +808,63 @@ static int DoCalRem(ParsePtr p, int col)
strcpy(trig.passthru, "PSFile");
trig.typ = PASSTHRU_TYPE;
}
if (!PsCal) {
if (trig.typ == PASSTHRU_TYPE) {
if (strcmp(trig.passthru, "COLOR")) return OK;
/* Strip off the three color numbers */
DBufFree(&buf);
r=ParseToken(p, &buf);
DBufFree(&buf);
if (r) return r;
r=ParseToken(p, &buf);
DBufFree(&buf);
if (r) return r;
r=ParseToken(p, &buf);
DBufFree(&buf);
if (r) return r;
}
}
if (trig.typ == PASSTHRU_TYPE) {
if (!PsCal && strcmp(trig.passthru, "COLOR")) return OK;
if (!strcmp(trig.passthru, "COLOR")) {
is_color = 1;
/* Strip off the three color numbers */
DBufFree(&buf);
r=ParseToken(p, &buf);
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
DBufFree(&buf);
if (r) return r;
r=ParseToken(p, &buf);
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
DBufFree(&buf);
if (r) return r;
r=ParseToken(p, &buf);
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
DBufFree(&buf);
if (r) return r;
(void) sscanf(DBufValue(&pre_buf), "%d %d %d",
&col_r, &col_g, &col_b);
if (col_r < 0) col_r = 0;
else if (col_r > 255) col_r = 255;
if (col_g < 0) col_g = 0;
else if (col_g > 255) col_g = 255;
if (col_b < 0) col_b = 0;
else if (col_b > 255) col_b = 255;
/* Remove any "at" times from PS or PSFILE reminders */
if (trig.typ == PASSTHRU_TYPE && strcmp(trig.passthru, "COLOR")) {
tim.ttime = NO_TIME;
tim.duration = NO_TIME;
if (!PsCal && !DoSimpleCalendar) {
DBufFree(&pre_buf);
}
}
}
/* If trigger date == today, add it to the current entry */
DBufInit(&obuf);
if ((jul == JulianToday) ||
(DoSimpleCalendar &&
DoSimpleCalDelta &&
ShouldTriggerReminder(&trig, &tim, jul))) {
(DoSimpleCalDelta &&
ShouldTriggerReminder(&trig, &tim, jul, &err))) {
NumTriggered++;
if (DoSimpleCalendar || tim.ttime != NO_TIME) {
/* Suppress time if it's not today */
if (jul != JulianToday) {
/* Suppress time if it's not today or if it's a non-COLOR special */
if (jul != JulianToday ||
(trig.typ == PASSTHRU_TYPE &&
strcmp(trig.passthru, "COLOR"))) {
if (DBufPuts(&obuf, SimpleTime(NO_TIME)) != OK) {
DBufFree(&obuf);
DBufFree(&pre_buf);
return E_NO_MEM;
}
} else {
if (DBufPuts(&obuf, SimpleTime(tim.ttime)) != OK) {
if (DBufPuts(&obuf, CalendarTime(tim.ttime, tim.duration)) != OK) {
DBufFree(&obuf);
DBufFree(&pre_buf);
return E_NO_MEM;
}
}
@@ -688,12 +874,13 @@ static int DoCalRem(ParsePtr p, int col)
char evalBuf[64];
sprintf(evalBuf, "calprefix(%d)", trig.priority);
s2 = evalBuf;
r = EvalExpr(&s2, &v);
r = EvalExpr(&s2, &v, NULL);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&obuf, v.v.str) != OK) {
DestroyValue(v);
DBufFree(&obuf);
DBufFree(&pre_buf);
return E_NO_MEM;
}
}
@@ -710,11 +897,13 @@ static int DoCalRem(ParsePtr p, int col)
r = DoSubst(p, &obuf, &trig, &tim, jul, CAL_MODE);
}
if (r) {
DBufFree(&pre_buf);
DBufFree(&obuf);
return r;
}
if (DBufLen(&obuf) <= oldLen) {
DBufFree(&obuf);
DBufFree(&pre_buf);
return OK;
}
if (trig.typ != PASSTHRU_TYPE &&
@@ -722,12 +911,13 @@ static int DoCalRem(ParsePtr p, int col)
char evalBuf[64];
sprintf(evalBuf, "calsuffix(%d)", trig.priority);
s2 = evalBuf;
r = EvalExpr(&s2, &v);
r = EvalExpr(&s2, &v, NULL);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&obuf, v.v.str) != OK) {
DestroyValue(v);
DBufFree(&obuf);
DBufFree(&pre_buf);
return E_NO_MEM;
}
}
@@ -735,14 +925,22 @@ static int DoCalRem(ParsePtr p, int col)
}
}
s = DBufValue(&obuf);
if (!DoSimpleCalendar) while (isspace(*s)) s++;
if (!DoSimpleCalendar) while (isempty(*s)) s++;
DBufPuts(&pre_buf, s);
s = DBufValue(&pre_buf);
e = NEW(CalEntry);
e->is_color = is_color;
e->r = col_r;
e->g = col_g;
e->b = col_b;
if (!e) {
DBufFree(&obuf);
DBufFree(&pre_buf);
return E_NO_MEM;
}
e->text = StrDup(s);
DBufFree(&obuf);
DBufFree(&pre_buf);
if (!e->text) {
free(e);
return E_NO_MEM;
@@ -764,35 +962,20 @@ static int DoCalRem(ParsePtr p, int col)
}
e->lineno = LineNo;
/* Ugly hack... SPECIAL COLOR and SPECIAL HTML are not treated
as "passthru" to preserve ordering and make them behave like
a MSG-type reminder */
if (trig.typ == PASSTHRU_TYPE &&
strcmp(trig.passthru, "COLOR") &&
strcmp(trig.passthru, "HTML")) {
if (trig.typ == PASSTHRU_TYPE) {
StrnCpy(e->passthru, trig.passthru, PASSTHRU_LEN);
e->pos = e->passthru;
e->time = NO_TIME;
e->next = CurPs;
CalPs[col] = e;
SortCol(&CalPs[col]);
} else {
if (trig.typ == PASSTHRU_TYPE) {
StrnCpy(e->passthru, trig.passthru, PASSTHRU_LEN);
} else {
e->passthru[0] = 0;
}
e->pos = e->text;
if (jul == JulianToday) {
e->time = tim.ttime;
} else {
e->time = NO_TIME;
}
e->next = CurCol;
CalColumn[col] = e;
SortCol(&CalColumn[col]);
e->passthru[0] = 0;
}
e->pos = e->text;
if (jul == JulianToday) {
e->time = tim.ttime;
} else {
e->time = NO_TIME;
}
e->next = CurCol;
CalColumn[col] = e;
SortCol(&CalColumn[col]);
}
return OK;
}
@@ -806,36 +989,11 @@ static int DoCalRem(ParsePtr p, int col)
/***************************************************************/
static void WriteSimpleEntries(int col, int jul)
{
CalEntry *e = CalPs[col];
CalEntry *e = CalColumn[col];
CalEntry *n;
int y, m, d;
/* Do all the PASSTHRU entries first, if any */
FromJulian(jul, &y, &m, &d);
while(e) {
if (DoPrefixLineNo) printf("# fileinfo %d %s\n", e->lineno, e->filename);
printf("%04d/%02d/%02d ", y, m+1, d);
printf("%s %s ", e->passthru, e->tag);
if (e->duration != NO_TIME) {
printf("%d ", e->duration);
} else {
printf("* ");
}
if (e->time != NO_TIME) {
printf("%d ", e->time);
} else {
printf("* ");
}
printf("%s\n", e->text);
free(e->text);
free(e->filename);
n = e->next;
free(e);
e = n;
}
CalPs[col] = NULL;
e = CalColumn[col];
while(e) {
if (DoPrefixLineNo) printf("# fileinfo %d %s\n", e->lineno, e->filename);
printf("%04d/%02d/%02d", y, m+1, d);
@@ -856,8 +1014,8 @@ static void WriteSimpleEntries(int col, int jul)
printf("* ");
}
printf("%s\n", e->text);
free(e->text);
free(e->filename);
free((char *) e->text);
free((char *) e->filename);
n = e->next;
free(e);
e = n;
@@ -870,11 +1028,64 @@ static void WriteSimpleEntries(int col, int jul)
/* Various functions for writing different types of lines. */
/* */
/***************************************************************/
static void WriteSolidCalLine(void)
static void WriteTopCalLine(void)
{
PutChar('+');
PrintCentered("", CalWidth-2, '-');
PutChar('+');
gon();
DRAW(br);
PrintCentered("", CalWidth-2, linestruct->lr);
DRAW(bl);
goff();
PutChar('\n');
}
static void WriteBottomCalLine(void)
{
int i;
gon();
DRAW(tr);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(tlr);
} else {
DRAW(tl);
}
}
goff();
PutChar('\n');
}
static void WritePostHeaderLine(void)
{
int i;
gon();
DRAW(tbr);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(blr);
} else {
DRAW(tbl);
}
}
goff();
PutChar('\n');
}
static void WriteWeekHeaderLine(void)
{
int i;
gon();
DRAW(br);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(blr);
} else {
DRAW(bl);
}
}
goff();
PutChar('\n');
}
@@ -882,28 +1093,121 @@ static void WriteIntermediateCalLine(void)
{
int i;
PutChar('+');
gon();
DRAW(tbr);
for (i=0; i<7; i++) {
PrintCentered("", ColSpaces, '-');
PutChar('+');
PrintCentered("", ColSpaces, linestruct->lr);
if (i != 6) {
DRAW(tblr);
} else {
DRAW(tbl);
}
}
goff();
PutChar('\n');
}
static void WriteCalDays(void)
{
int i;
PutChar('|');
gon();
DRAW(tb);
goff();
for (i=0; i<7; i++) {
if (!MondayFirst)
PrintCentered(DayName[(i+6)%7], ColSpaces, ' ');
else
PrintCentered(DayName[i%7], ColSpaces, ' ');
PutChar('|');
gon();
DRAW(tb);
goff();
}
PutChar('\n');
}
/***************************************************************/
/* */
/* CalendarTime */
/* */
/* Format the time according to simple time format. */
/* Answer is returned in a static buffer. */
/* A trailing space is always added. */
/* This takes into account duration */
/* */
/***************************************************************/
char const *
CalendarTime(int tim, int duration)
{
static char buf[128];
int h, min, hh;
int h2, min2, hh2, newtim, days;
char const *ampm1;
char const *ampm2;
char daybuf[64];
buf[0] = 0;
if (duration == NO_TIME) {
/* No duration... just call into SimpleTime */
return SimpleTime(tim);
}
if (tim == NO_TIME) {
/* No time... nothing to return */
return buf;
}
h = tim/60;
min = tim % 60;
if (h == 0) hh=12;
else if (h > 12) hh=h-12;
else hh = h;
newtim = tim + duration;
/* How many days in duration? */
days = newtim / MINUTES_PER_DAY;
newtim = newtim % MINUTES_PER_DAY;
h2 = newtim/60;
min2 = newtim % 60;
if (h2 == 0) hh2=12;
else if (h2 > 12) hh2=h2-12;
else hh2 = h2;
if (days) {
sprintf(daybuf, "+%d", days);
} else {
daybuf[0] = 0;
}
if (h >= 12) {
ampm1 = L_PM;
} else {
ampm1 = L_AM;
}
if (h2 >= 12) {
ampm2 = L_PM;
} else {
ampm2 = L_AM;
}
if (!days) {
if (!strcmp(ampm1, ampm2)) {
ampm1 = "";
}
}
switch(ScFormat) {
case SC_AMPM:
sprintf(buf, "%d%c%02d%s-%d%c%02d%s%s ",
hh, TimeSep, min, ampm1, hh2, TimeSep, min2, ampm2, daybuf);
break;
case SC_MIL:
sprintf(buf, "%02d%c%02d-%02d%c%02d%s ",
h, TimeSep, min, h2, TimeSep, min2, daybuf);
break;
}
return buf;
}
/***************************************************************/
/* */
/* SimpleTime */
@@ -913,7 +1217,7 @@ static void WriteCalDays(void)
/* A trailing space is always added. */
/* */
/***************************************************************/
char *SimpleTime(int tim)
char const *SimpleTime(int tim)
{
static char buf[32];
int h, min, hh;
@@ -929,7 +1233,7 @@ char *SimpleTime(int tim)
if (h == 0) hh=12;
else if (h > 12) hh=h-12;
else hh=h;
sprintf(buf, "%2d%c%02d%s ", hh, TimeSep, min, (h>=12) ? L_PM : L_AM);
sprintf(buf, "%d%c%02d%s ", hh, TimeSep, min, (h>=12) ? L_PM : L_AM);
}
break;
@@ -964,7 +1268,7 @@ static void SortCol(CalEntry **col)
while (cur->next &&
CompareRems(0, cur->time, cur->priority,
0, cur->next->time, cur->next->priority,
SortByDate, SortByTime, SortByPrio) <= 0) {
SortByDate, SortByTime, SortByPrio, UntimedBeforeTimed) <= 0) {
next = cur->next;
/* Swap cur and next */
if (!prev) {

View File

@@ -1,5 +1,3 @@
/* src/config.h.in. Generated automatically from configure.in by autoheader. */
/* Define if utime(file, NULL) sets file's timestamp to the present. */
#undef HAVE_UTIME_NULL
@@ -12,6 +10,11 @@
/* Define if you have the <sys/file.h> header file. */
#undef HAVE_SYS_FILE_H
/* Define if you have the <glob.h> header file */
#undef HAVE_GLOB_H
#undef HAVE_GLOB
#undef HAVE_SETENV
#undef HAVE_UNSETENV

View File

@@ -87,8 +87,7 @@
/*---------------------------------------------------------------------*/
/* WANT_U_OPTION: Comment out the next define to permanently disable */
/* the -u option. If you do this, however, remind-all.[c]sh will not */
/* work. */
/* the -u option. */
/*---------------------------------------------------------------------*/
#define WANT_U_OPTION 1
@@ -149,6 +148,12 @@
/*---------------------------------------------------------------------*/
#define MAX_PRT_LEN 40
/*---------------------------------------------------------------------*/
/* MAX_STR_LEN: If non-zero, Remind will limit the maximum length */
/* of string values to avoid eating up all of memory... */
/*---------------------------------------------------------------------*/
#define MAX_STR_LEN 65535
/*---------------------------------------------------------------------*/
/* OP_STACK_SIZE: The size of the operator stack for expr. parsing */
/*---------------------------------------------------------------------*/
@@ -162,7 +167,7 @@
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */
/*---------------------------------------------------------------------*/
#define INCLUDE_NEST 8
#define INCLUDE_NEST 9
/*---------------------------------------------------------------------*/
/* IF_NEST: How many nested IFs do we handle? Maximum is the number */

View File

@@ -87,8 +87,7 @@
/*---------------------------------------------------------------------*/
/* WANT_U_OPTION: Comment out the next define to permanently disable */
/* the -u option. If you do this, however, remind-all.[c]sh will not */
/* work. */
/* the -u option. */
/*---------------------------------------------------------------------*/
#define WANT_U_OPTION 1
@@ -149,6 +148,12 @@
/*---------------------------------------------------------------------*/
#define MAX_PRT_LEN 40
/*---------------------------------------------------------------------*/
/* MAX_STR_LEN: If non-zero, Remind will limit the maximum length */
/* of string values to avoid eating up all of memory... */
/*---------------------------------------------------------------------*/
#define MAX_STR_LEN 65535
/*---------------------------------------------------------------------*/
/* OP_STACK_SIZE: The size of the operator stack for expr. parsing */
/*---------------------------------------------------------------------*/
@@ -162,7 +167,7 @@
/*---------------------------------------------------------------------*/
/* INCLUDE_NEST: How many nested INCLUDES do we handle? */
/*---------------------------------------------------------------------*/
#define INCLUDE_NEST 8
#define INCLUDE_NEST 9
/*---------------------------------------------------------------------*/
/* IF_NEST: How many nested IFs do we handle? Maximum is the number */

View File

@@ -29,12 +29,12 @@
static char const DontEscapeMe[] =
"1234567890_-=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@.,";
static int ParseTimeTrig (ParsePtr s, TimeTrig *tim);
static int ParseTimeTrig (ParsePtr s, TimeTrig *tim, int save_in_globals);
static int ParseLocalOmit (ParsePtr s, Trigger *t);
static int ParseScanFrom (ParsePtr s, Trigger *t, int type);
static int ParsePriority (ParsePtr s, Trigger *t);
static int ParseUntil (ParsePtr s, Trigger *t);
static int ShouldTriggerBasedOnWarn (Trigger *t, int jul);
static int ShouldTriggerBasedOnWarn (Trigger *t, int jul, int *err);
/***************************************************************/
/* */
@@ -48,7 +48,7 @@ int DoRem(ParsePtr p)
Trigger trig;
TimeTrig tim;
int r;
int r, err;
int jul;
DynamicBuffer buf;
Token tok;
@@ -56,12 +56,21 @@ int DoRem(ParsePtr p)
DBufInit(&buf);
/* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if ( (r=ParseRem(p, &trig, &tim, 1)) ) return r;
if (trig.typ == NO_TYPE) return E_EOLN;
if (trig.typ == NO_TYPE) {
PurgeEchoLine("%s\n%s\n", "#!P! Cannot parse next line", CurLine);
return E_EOLN;
}
if (trig.typ == SAT_TYPE) {
PurgeEchoLine("%s\n", "#!P: Cannot purge SATISFY-type reminders");
PurgeEchoLine("%s\n", CurLine);
r=DoSatRemind(&trig, &tim, p);
if (r) return r;
if (r) {
if (r == E_EXPIRED) return OK;
return r;
}
if (!LastTrigValid) return OK;
r=ParseToken(p, &buf);
if (r) return r;
FindToken(DBufValue(&buf), &tok);
@@ -87,12 +96,37 @@ int DoRem(ParsePtr p)
trig.typ = tok.val;
jul = LastTriggerDate;
if (!LastTrigValid) return OK;
if (PurgeMode) return OK;
} else {
/* Calculate the trigger date */
jul = ComputeTrigger(trig.scanfrom, &trig, &r);
if (r) return r;
jul = ComputeTrigger(trig.scanfrom, &trig, &r, 1);
if (r) {
if (PurgeMode) {
PurgeEchoLine("%s: %s\n", "#!P! Problem calculating trigger date", ErrMsg[r]);
PurgeEchoLine("%s\n", CurLine);
}
return r;
}
}
if (PurgeMode) {
if (trig.expired || jul < JulianToday) {
if (p->expr_happened) {
if (p->nonconst_expr) {
PurgeEchoLine("%s\n", "#!P: Next line may have expired, but contains non-constant expression");
PurgeEchoLine("%s\n", CurLine);
} else {
PurgeEchoLine("%s\n", "#!P: Next line has expired, but contains expression... please verify");
PurgeEchoLine("#!P: Expired: %s\n", CurLine);
}
} else {
PurgeEchoLine("#!P: Expired: %s\n", CurLine);
}
} else {
PurgeEchoLine("%s\n", CurLine);
}
return OK;
}
/* Queue the reminder, if necessary */
if (jul == JulianToday &&
!(!IgnoreOnce &&
@@ -102,7 +136,7 @@ int DoRem(ParsePtr p)
/* If we're in daemon mode, do nothing over here */
if (Daemon) return OK;
if (ShouldTriggerReminder(&trig, &tim, jul)) {
if (ShouldTriggerReminder(&trig, &tim, jul, &err)) {
if ( (r=TriggerReminder(p, &trig, &tim, jul)) )
{
return r;
@@ -120,12 +154,14 @@ int DoRem(ParsePtr p)
/* trigger structure. */
/* */
/***************************************************************/
int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
{
register int r;
DynamicBuffer buf;
Token tok;
int y, m, d;
DBufInit(&buf);
trig->y = NO_YR;
@@ -144,12 +180,16 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
trig->priority = DefaultPrio;
trig->sched[0] = 0;
trig->warn[0] = 0;
trig->omitfunc[0] = 0;
trig->tag[0] = 0;
trig->passthru[0] = 0;
tim->ttime = NO_TIME;
tim->delta = NO_DELTA;
tim->rep = NO_REP;
tim->duration = NO_TIME;
LastTriggerTime = NO_TIME;
if (save_in_globals) {
LastTriggerTime = NO_TIME;
}
while(1) {
/* Read space-delimited string */
@@ -159,6 +199,32 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
/* Figure out what we've got */
FindToken(DBufValue(&buf), &tok);
switch(tok.type) {
case T_Date:
DBufFree(&buf);
if (trig->d != NO_DAY) return E_DAY_TWICE;
if (trig->m != NO_MON) return E_MON_TWICE;
if (trig->y != NO_YR) return E_YR_TWICE;
FromJulian(tok.val, &y, &m, &d);
trig->y = y;
trig->m = m;
trig->d = d;
break;
case T_DateTime:
DBufFree(&buf);
if (trig->d != NO_DAY) return E_DAY_TWICE;
if (trig->m != NO_MON) return E_MON_TWICE;
if (trig->y != NO_YR) return E_YR_TWICE;
FromJulian(tok.val / MINUTES_PER_DAY, &y, &m, &d);
trig->y = y;
trig->m = m;
trig->d = d;
tim->ttime = (tok.val % MINUTES_PER_DAY);
if (save_in_globals) {
LastTriggerTime = tim->ttime;
}
break;
case T_WkDay:
DBufFree(&buf);
if (trig->wd & (1 << tok.val)) return E_WD_TWICE;
@@ -185,7 +251,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
case T_At:
DBufFree(&buf);
r=ParseTimeTrig(s, tim);
r=ParseTimeTrig(s, tim, save_in_globals);
if (r) return r;
break;
@@ -255,6 +321,10 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
case T_Omit:
DBufFree(&buf);
if (trig->omitfunc[0]) {
Eprint("Warning: OMIT is ignored if you use OMITFUNC");
}
r = ParseLocalOmit(s, trig);
if (r) return r;
break;
@@ -264,6 +334,16 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday;
return OK;
case T_OmitFunc:
if (trig->localomit) {
Eprint("Warning: OMIT is ignored if you use OMITFUNC");
}
r=ParseToken(s, &buf);
if (r) return r;
StrnCpy(trig->omitfunc, DBufValue(&buf), VAR_NAME_LEN);
DBufFree(&buf);
break;
case T_Warn:
r=ParseToken(s, &buf);
if(r) return r;
@@ -320,7 +400,7 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
/* ParseTimeTrig - parse the AT part of a timed reminder */
/* */
/***************************************************************/
static int ParseTimeTrig(ParsePtr s, TimeTrig *tim)
static int ParseTimeTrig(ParsePtr s, TimeTrig *tim, int save_in_globals)
{
Token tok;
int r;
@@ -352,7 +432,9 @@ static int ParseTimeTrig(ParsePtr s, TimeTrig *tim)
if (tim->ttime == NO_TIME) return E_EXPECT_TIME;
/* Save trigger time in global variable */
LastTriggerTime = tim->ttime;
if (save_in_globals) {
LastTriggerTime = tim->ttime;
}
PushToken(DBufValue(&buf), s);
DBufFree(&buf);
return OK;
@@ -441,6 +523,23 @@ static int ParseUntil(ParsePtr s, Trigger *t)
d = tok.val;
break;
case T_Date:
DBufFree(&buf);
if (y != NO_YR) {
Eprint("UNTIL: %s", ErrMsg[E_YR_TWICE]);
return E_YR_TWICE;
}
if (m != NO_MON) {
Eprint("UNTIL: %s", ErrMsg[E_MON_TWICE]);
return E_MON_TWICE;
}
if (d != NO_DAY) {
Eprint("UNTIL: %s", ErrMsg[E_DAY_TWICE]);
return E_DAY_TWICE;
}
FromJulian(tok.val, &y, &m, &d);
break;
default:
if (y == NO_YR || m == NO_MON || d == NO_DAY) {
Eprint("UNTIL: %s", ErrMsg[E_INCOMPLETE]);
@@ -516,6 +615,23 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
d = tok.val;
break;
case T_Date:
DBufFree(&buf);
if (y != NO_YR) {
Eprint("%s: %s", word, ErrMsg[E_YR_TWICE]);
return E_YR_TWICE;
}
if (m != NO_MON) {
Eprint("%s: %s", word, ErrMsg[E_MON_TWICE]);
return E_MON_TWICE;
}
if (d != NO_DAY) {
Eprint("%s: %s", word, ErrMsg[E_DAY_TWICE]);
return E_DAY_TWICE;
}
FromJulian(tok.val, &y, &m, &d);
break;
default:
if (y == NO_YR || m == NO_MON || d == NO_DAY) {
Eprint("%s: %s", word, ErrMsg[E_INCOMPLETE]);
@@ -549,14 +665,16 @@ static int ParseScanFrom(ParsePtr s, Trigger *t, int type)
int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
{
int r, y, m, d;
char PrioExpr[25];
char PrioExpr[VAR_NAME_LEN+25];
char tmpBuf[64];
DynamicBuffer buf, calRow;
char *s;
DynamicBuffer pre_buf;
char const *s;
Value v;
DBufInit(&buf);
DBufInit(&calRow);
DBufInit(&pre_buf);
if (t->typ == RUN_TYPE && RunDisabled) return E_RUN_DISABLED;
if ((t->typ == PASSTHRU_TYPE && strcmp(t->passthru, "COLOR")) ||
t->typ == CAL_TYPE ||
@@ -568,12 +686,24 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
if (t->typ == PASSTHRU_TYPE && !strcmp(t->passthru, "COLOR")) {
/* Strip off three tokens */
r = ParseToken(p, &buf);
if (!NextMode) {
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
}
DBufFree(&buf);
if (r) return r;
r = ParseToken(p, &buf);
if (!NextMode) {
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
}
DBufFree(&buf);
if (r) return r;
r = ParseToken(p, &buf);
if (!NextMode) {
DBufPuts(&pre_buf, DBufValue(&buf));
DBufPutc(&pre_buf, ' ');
}
DBufFree(&buf);
if (r) return r;
t->typ = MSG_TYPE;
@@ -595,12 +725,14 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
if ( (r=DoSubst(p, &buf, t, tim, jul, CAL_MODE)) ) return r;
if (!DBufLen(&buf)) {
DBufFree(&buf);
DBufFree(&pre_buf);
return OK;
}
FromJulian(jul, &y, &m, &d);
sprintf(tmpBuf, "%04d/%02d/%02d ", y, m+1, d);
if (DBufPuts(&calRow, tmpBuf) != OK) {
DBufFree(&calRow);
DBufFree(&pre_buf);
return E_NO_MEM;
}
/* If DoSimpleCalendar==1, output *all* simple calendar fields */
@@ -608,6 +740,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
/* ignore passthru field when in NextMode */
if (DBufPuts(&calRow, "* ") != OK) {
DBufFree(&calRow);
DBufFree(&pre_buf);
return E_NO_MEM;
}
if (t->tag[0]) {
@@ -617,6 +750,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
}
if (DBufPuts(&calRow, tmpBuf) != OK) {
DBufFree(&calRow);
DBufFree(&pre_buf);
return E_NO_MEM;
}
if (tim->duration != NO_TIME) {
@@ -626,6 +760,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
}
if (DBufPuts(&calRow, tmpBuf) != OK) {
DBufFree(&calRow);
DBufFree(&pre_buf);
return E_NO_MEM;
}
if (tim->ttime != NO_TIME) {
@@ -635,52 +770,63 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
}
if (DBufPuts(&calRow, tmpBuf) != OK) {
DBufFree(&calRow);
DBufFree(&pre_buf);
return E_NO_MEM;
}
}
if (DBufPuts(&calRow, SimpleTime(tim->ttime)) != OK) {
DBufFree(&calRow);
DBufFree(&pre_buf);
return E_NO_MEM;
}
printf("%s%s\n", DBufValue(&calRow), DBufValue(&buf));
printf("%s%s%s\n", DBufValue(&calRow), DBufValue(&pre_buf), DBufValue(&buf));
DBufFree(&buf);
DBufFree(&pre_buf);
DBufFree(&calRow);
return OK;
}
/* Put the substituted string into the substitution buffer */
if (UserFuncExists("msgprefix") == 1) {
sprintf(PrioExpr, "msgprefix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&buf, v.v.str) != OK) {
DBufFree(&buf);
DestroyValue(v);
return E_NO_MEM;
/* Put the substituted string into the substitution buffer */
/* Don't use msgprefix() on RUN-type reminders */
if (t->typ != RUN_TYPE) {
if (UserFuncExists("msgprefix") == 1) {
sprintf(PrioExpr, "msgprefix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v, NULL);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&buf, v.v.str) != OK) {
DBufFree(&buf);
DestroyValue(v);
return E_NO_MEM;
}
}
DestroyValue(v);
}
DestroyValue(v);
}
}
if ( (r=DoSubst(p, &buf, t, tim, jul, NORMAL_MODE)) ) return r;
if (UserFuncExists("msgsuffix") == 1) {
sprintf(PrioExpr, "msgsuffix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&buf, v.v.str) != OK) {
DBufFree(&buf);
DestroyValue(v);
return E_NO_MEM;
if (t->typ != RUN_TYPE) {
if (UserFuncExists("msgsuffix") == 1) {
sprintf(PrioExpr, "msgsuffix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v, NULL);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
if (DBufPuts(&buf, v.v.str) != OK) {
DBufFree(&buf);
DestroyValue(v);
return E_NO_MEM;
}
}
DestroyValue(v);
}
DestroyValue(v);
}
}
if ((!MsgCommand && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) {
if (DBufPutc(&buf, '\n') != OK) {
DBufFree(&buf);
@@ -726,7 +872,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
DBufFree(&buf);
NumTriggered++;
return OK;
}
}
/***************************************************************/
/* */
@@ -734,12 +880,13 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
/* */
/* Return 1 if we should trigger a reminder, based on today's */
/* date and the trigger. Return 0 if reminder should not be */
/* triggered. */
/* triggered. Sets *err non-zero in event of an error. */
/* */
/***************************************************************/
int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul)
int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul, int *err)
{
int r;
int r, omit;
*err = 0;
/* Handle the ONCE modifier in the reminder. */
if (!IgnoreOnce && t->once !=NO_ONCE && FileAccessDate == JulianToday)
@@ -763,7 +910,12 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul)
/* If there's a "warn" function, it overrides any deltas */
if (t->warn[0] != 0) {
return ShouldTriggerBasedOnWarn(t, jul);
if (DeltaOffset) {
if (jul <= JulianToday + DeltaOffset) {
return 1;
}
}
return ShouldTriggerBasedOnWarn(t, jul, err);
}
/* Move back by delta days, if any */
@@ -772,15 +924,28 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul)
jul = jul + t->delta;
else {
r = t->delta;
while(r && jul > JulianToday) {
int iter = 0;
int max = MaxSatIter;
if (max < r*2) max = r*2;
while(iter++ < max) {
if (!r || (jul <= JulianToday)) {
break;
}
jul--;
if (!IsOmitted(jul, t->localomit)) r--;
*err = IsOmitted(jul, t->localomit, t->omitfunc, &omit);
if (*err) return 0;
if (!omit) r--;
}
if (iter > max) {
*err = E_CANT_TRIG;
Eprint("Delta: Bad OMITFUNC? %s", ErrMsg[E_CANT_TRIG]);
return 0;
}
}
}
/* Should we trigger the reminder? */
return (jul <= JulianToday);
return (jul <= JulianToday + DeltaOffset);
}
/***************************************************************/
@@ -794,16 +959,20 @@ int DoSatRemind(Trigger *trig, TimeTrig *tim, ParsePtr p)
{
int iter, jul, r;
Value v;
char *s, *t;
char const *s;
char const *t;
t = p->pos;
iter = 0;
jul = trig->scanfrom;
while (iter++ < MaxSatIter) {
jul = ComputeTrigger(jul, trig, &r);
jul = ComputeTrigger(jul, trig, &r, 1);
if (r) {
if (r == E_CANT_TRIG) return OK; else return r;
}
if (jul == -1) {
return E_EXPIRED;
}
s = p->pos;
r = EvaluateExpr(p, &v);
t = p->pos;
@@ -827,7 +996,7 @@ int DoSatRemind(Trigger *trig, TimeTrig *tim, ParsePtr p)
static int ParsePriority(ParsePtr s, Trigger *t)
{
int p, r;
char *u;
char const *u;
DynamicBuffer buf;
DBufInit(&buf);
@@ -865,14 +1034,14 @@ static int ParsePriority(ParsePtr s, Trigger *t)
/* Execute the '-k' command, escaping shell chars in message. */
/* */
/***************************************************************/
int DoMsgCommand(char *cmd, char *msg)
int DoMsgCommand(char const *cmd, char const *msg)
{
int r;
int i, l;
DynamicBuffer execBuffer;
DynamicBuffer buf;
char *s;
char const *s;
DBufInit(&buf);
DBufInit(&execBuffer);
@@ -926,12 +1095,12 @@ finished:
/* function. */
/* */
/***************************************************************/
static int ShouldTriggerBasedOnWarn(Trigger *t, int jul)
static int ShouldTriggerBasedOnWarn(Trigger *t, int jul, int *err)
{
char buffer[VAR_NAME_LEN+32];
int i;
char *s;
int r;
char const *s;
int r, omit;
Value v;
int lastReturnVal = 0; /* Silence compiler warning */
@@ -943,7 +1112,7 @@ static int ShouldTriggerBasedOnWarn(Trigger *t, int jul)
for (i=1; ; i++) {
sprintf(buffer, "%s(%d)", t->warn, i);
s = buffer;
r = EvalExpr(&s, &v);
r = EvalExpr(&s, &v, NULL);
if (r) {
Eprint("%s: `%s': %s", ErrMsg[M_BAD_WARN_FUNC],
t->warn, ErrMsg[r]);
@@ -969,9 +1138,21 @@ static int ShouldTriggerBasedOnWarn(Trigger *t, int jul)
if (JulianToday + v.v.val == jul) return 1;
} else {
int j = jul;
while (v.v.val) {
int iter = 0;
int max = MaxSatIter;
if (max < v.v.val * 2) max = v.v.val*2;
while(iter++ <= max) {
j--;
if (!IsOmitted(j, t->localomit)) v.v.val++;
*err = IsOmitted(j, t->localomit, t->omitfunc, &omit);
if (*err) return 0;
if (!omit) v.v.val++;
if (!v.v.val) {
break;
}
}
if (iter > max) {
Eprint("Delta: Bad OMITFUNC? %s", ErrMsg[E_CANT_TRIG]);
return 0;
}
if (j == JulianToday) return 1;
}

View File

@@ -54,11 +54,12 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int jul,
int d, m, y;
int tim = tt->ttime;
int h, min, hh, ch, cmin, chh;
char *pm, *cpm;
char const *pm, *cpm;
int tdiff, adiff, mdiff, hdiff;
char *mplu, *hplu, *when, *plu;
char const *mplu, *hplu, *when, *plu;
int has_quote = 0;
char *ss, *os;
char *ss;
char *os;
char s[256];
int origLen = DBufLen(dbuf);
@@ -649,7 +650,7 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int jul,
/* default triggers and a mode of NORMAL_MODE. */
/* */
/***************************************************************/
int DoSubstFromString(char *source, DynamicBuffer *dbuf,
int DoSubstFromString(char const *source, DynamicBuffer *dbuf,
int jul, int tim)
{
Trigger tempTrig;

View File

@@ -42,7 +42,7 @@ static int DBufMakeRoom(DynamicBuffer *dbuf, int n)
}
/* Allocate memory */
buf = (char *) malloc(size);
buf = malloc(size);
if (!buf) return E_NO_MEM;
/* Copy contents */
@@ -102,7 +102,7 @@ int DBufPutcFN(DynamicBuffer *dbuf, char c)
%DESCRIPTION:
Appends a string to the buffer.
**********************************************************************/
int DBufPuts(DynamicBuffer *dbuf, char *str)
int DBufPuts(DynamicBuffer *dbuf, char const *str)
{
int l = strlen(str);
if (!l) return OK;

View File

@@ -25,7 +25,7 @@ typedef struct {
void DBufInit(DynamicBuffer *dbuf);
int DBufPutcFN(DynamicBuffer *dbuf, char c);
int DBufPuts(DynamicBuffer *dbuf, char *str);
int DBufPuts(DynamicBuffer *dbuf, char const *str);
void DBufFree(DynamicBuffer *dbuf);
int DBufGets(DynamicBuffer *dbuf, FILE *fp);

View File

@@ -117,6 +117,8 @@
#define E_EXPECTING_NUMBER 97
#define M_BAD_WARN_FUNC 98
#define E_CANT_CONVERT_TZ 99
#define E_NO_MATCHING_REMS 100
#define E_STRING_TOO_LONG 101
#ifdef MK_GLOBALS
#undef EXTERN
@@ -189,7 +191,7 @@ EXTERN char *ErrMsg[]
"Too many partial OMITs",
"Too many full OMITs",
"Warning: PUSH-OMIT-CONTEXT without matching POP-OMIT-CONTEXT",
"Error reading file",
"Error reading",
"Expecting end-of-line",
"Invalid Hebrew date",
"IIF needs odd number of arguments",
@@ -230,7 +232,9 @@ EXTERN char *ErrMsg[]
"%d reminder(s) queued for later today.\n",
"Expecting number",
"Bad function in WARN clause",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
}
#endif /* MK_GLOBALS */
;

View File

@@ -41,8 +41,7 @@ static int Multiply(void), Divide(void), Mod(void), Add(void),
UnMinus(void), LogNot(void),
Compare(int);
static int MakeValue (char *s, Value *v, Var *locals);
static int ParseLiteralDate (char **s, int *jul, int *tim);
static int MakeValue (char const *s, Value *v, Var *locals, ParsePtr p);
/* Binary operators - all left-associative */
@@ -73,9 +72,7 @@ Operator UnOp[] = {
};
#define NUM_UN_OPS (sizeof(UnOp) / sizeof(Operator))
/* Functions have the same definitions as operators, except the prec field
is used to indicate how many arguments are needed. */
extern Operator Func[];
extern BuiltinFunc Func[];
Operator OpStack[OP_STACK_SIZE];
Value ValStack[VAL_STACK_SIZE];
@@ -132,10 +129,10 @@ static void CleanStack(void)
/* PeekChar - peek ahead to next char. */
/* */
/***************************************************************/
static char PeekChar(char **s)
static char PeekChar(char const **s)
{
char *t = *s;
while (*t && isspace(*t)) t++;
char const *t = *s;
while (*t && isempty(*t)) t++;
return *t;
}
@@ -146,14 +143,14 @@ static char PeekChar(char **s)
/* Read a token. */
/* */
/***************************************************************/
static int ParseExprToken(DynamicBuffer *buf, char **in)
static int ParseExprToken(DynamicBuffer *buf, char const **in)
{
char c;
DBufFree(buf);
/* Skip white space */
while (**in && isspace(**in)) (*in)++;
while (**in && isempty(**in)) (*in)++;
if (!**in) return OK;
@@ -204,6 +201,46 @@ static int ParseExprToken(DynamicBuffer *buf, char **in)
if (c == '\"') {
if (!**in) return E_MISS_QUOTE;
while (**in) {
/* Allow backslash-escapes */
if (**in == '\\') {
int r;
(*in)++;
if (!**in) {
DBufFree(buf);
return E_MISS_QUOTE;
}
switch(**in) {
case 'a':
r = DBufPutc(buf, '\a');
break;
case 'b':
r = DBufPutc(buf, '\b');
break;
case 'f':
r = DBufPutc(buf, '\f');
break;
case 'n':
r = DBufPutc(buf, '\n');
break;
case 'r':
r = DBufPutc(buf, '\r');
break;
case 't':
r = DBufPutc(buf, '\t');
break;
case 'v':
r = DBufPutc(buf, '\v');
break;
default:
r = DBufPutc(buf, **in);
}
(*in)++;
if (r != OK) {
DBufFree(buf);
return E_NO_MEM;
}
continue;
}
c = *(*in)++;
if (DBufPutc(buf, c) != OK) {
DBufFree(buf);
@@ -246,7 +283,7 @@ static int ParseExprToken(DynamicBuffer *buf, char **in)
(*in)++;
}
/* Chew up any remaining white space */
while (**in && isspace(**in)) (*in)++;
while (**in && isempty(**in)) (*in)++;
/* Peek ahead - is it '('? Then we have a function call */
if (**in == '(') {
@@ -266,14 +303,14 @@ static int ParseExprToken(DynamicBuffer *buf, char **in)
/* Put the result into value pointed to by v. */
/* */
/***************************************************************/
int EvalExpr(char **e, Value *v)
int EvalExpr(char const **e, Value *v, ParsePtr p)
{
int r;
OpStackPtr = 0;
ValStackPtr = 0;
r = Evaluate(e, NULL);
r = Evaluate(e, NULL, p);
/* Put last character parsed back onto input stream */
if (DBufLen(&ExprBuf)) (*e)--;
@@ -289,15 +326,16 @@ int EvalExpr(char **e, Value *v)
}
/* Evaluate - do the actual work of evaluation. */
int Evaluate(char **s, Var *locals)
int Evaluate(char const **s, Var *locals, ParsePtr p)
{
int OpBase, ValBase;
int r;
Operator *f;
Operator *o;
BuiltinFunc *f;
int args; /* Number of function arguments */
Operator op, op2;
Value va;
char *ufname = NULL; /* Stop GCC from complaining about use of uninit var */
char const *ufname = NULL; /* Stop GCC from complaining about use of uninit var */
OpBase = OpStackPtr;
ValBase = ValStackPtr;
@@ -313,7 +351,7 @@ int Evaluate(char **s, Var *locals)
if (*DBufValue(&ExprBuf) == '(') { /* Parenthesized expression */
DBufFree(&ExprBuf);
r = Evaluate(s, locals); /* Leaves the last parsed token in ExprBuf */
r = Evaluate(s, locals, p); /* Leaves the last parsed token in ExprBuf */
if (r) return r;
r = OK;
if (*DBufValue(&ExprBuf) != ')') {
@@ -336,10 +374,12 @@ int Evaluate(char **s, Var *locals)
}
args = 0;
if (PeekChar(s) == ')') { /* Function has no arguments */
if (f) r = CallFunc(f, 0);
else {
r = CallUserFunc(ufname, 0);
free(ufname);
if (f) {
if (!f->is_constant && (p != NULL)) p->nonconst_expr = 1;
r = CallFunc(f, 0);
} else {
r = CallUserFunc(ufname, 0, p);
free((char *) ufname);
}
if (r) return r;
r = ParseExprToken(&ExprBuf, s); /* Guaranteed to be right paren. */
@@ -347,33 +387,35 @@ int Evaluate(char **s, Var *locals)
} else { /* Function has some arguments */
while(1) {
args++;
r = Evaluate(s, locals);
r = Evaluate(s, locals, p);
if (r) {
if (!f) free(ufname);
if (!f) free((char *) ufname);
return r;
}
if (*DBufValue(&ExprBuf) == ')') break;
else if (*DBufValue(&ExprBuf) != ',') {
if (!f) free(ufname);
if (!f) free((char *) ufname);
Eprint("%s: `%c'", ErrMsg[E_EXPECT_COMMA],
*DBufValue(&ExprBuf));
DBufFree(&ExprBuf);
return E_EXPECT_COMMA;
}
}
if (f) r = CallFunc(f, args);
else {
r = CallUserFunc(ufname, args);
free(ufname);
if (f) {
if (!f->is_constant && (p != NULL)) p->nonconst_expr = 1;
r = CallFunc(f, args);
} else {
r = CallUserFunc(ufname, args, p);
free((char *) ufname);
}
DBufFree(&ExprBuf);
if (r) return r;
}
} else { /* Unary operator */
f = FindFunc(DBufValue(&ExprBuf), UnOp, NUM_UN_OPS);
if (f) {
o = FindOperator(DBufValue(&ExprBuf), UnOp, NUM_UN_OPS);
if (o) {
DBufFree(&ExprBuf);
PushOpStack(*f);
PushOpStack(*o);
continue; /* Still looking for an atomic vlue */
} else if (!ISID(*DBufValue(&ExprBuf)) &&
*DBufValue(&ExprBuf) != '$' &&
@@ -384,7 +426,7 @@ int Evaluate(char **s, Var *locals)
DBufFree(&ExprBuf);
return E_ILLEGAL_CHAR;
} else { /* Must be a literal value */
r = MakeValue(DBufValue(&ExprBuf), &va, locals);
r = MakeValue(DBufValue(&ExprBuf), &va, locals, p);
DBufFree(&ExprBuf);
if (r) return r;
PushValStack(va);
@@ -419,13 +461,13 @@ int Evaluate(char **s, Var *locals)
return OK;
}
/* Must be a binary operator */
f = FindFunc(DBufValue(&ExprBuf), BinOp, NUM_BIN_OPS);
o = FindOperator(DBufValue(&ExprBuf), BinOp, NUM_BIN_OPS);
DBufFree(&ExprBuf);
if (!f) return E_EXPECTING_BINOP;
if (!o) return E_EXPECTING_BINOP;
/* While operators of higher or equal precedence are on the stack,
pop them off and evaluate */
while (OpStackPtr > OpBase && OpStack[OpStackPtr-1].prec >= f->prec) {
while (OpStackPtr > OpBase && OpStack[OpStackPtr-1].prec >= o->prec) {
PopOpStack(op2);
if (r) return r;
if (DebugFlag & DB_PRTEXPR)
@@ -437,7 +479,7 @@ int Evaluate(char **s, Var *locals)
return r;
}
}
PushOpStack(*f);
PushOpStack(*o);
}
}
@@ -448,7 +490,7 @@ int Evaluate(char **s, Var *locals)
/* a date or the value of a symbol. */
/* */
/***************************************************************/
static int MakeValue(char *s, Value *v, Var *locals)
static int MakeValue(char const *s, Value *v, Var *locals, ParsePtr p)
{
int len;
int h, m, r;
@@ -456,7 +498,7 @@ static int MakeValue(char *s, Value *v, Var *locals)
if (*s == '\"') { /* It's a literal string "*/
len = strlen(s)-1;
v->type = STR_TYPE;
v->v.str = (char *) malloc(len);
v->v.str = malloc(len);
if (! v->v.str) {
v->type = ERR_TYPE;
return E_NO_MEM;
@@ -503,6 +545,7 @@ static int MakeValue(char *s, Value *v, Var *locals)
v->v.val = len;
return OK;
} else if (*s == '$') { /* A system variable */
if (p) p->nonconst_expr = 1;
if (DebugFlag & DB_PRTEXPR)
fprintf(ErrFp, "%s => ", s);
r = GetSysVar(s+1, v);
@@ -513,10 +556,11 @@ static int MakeValue(char *s, Value *v, Var *locals)
Putc('\n', ErrFp);
}
return r;
} else /* Must be a symbol */
} else { /* Must be a symbol */
if (DebugFlag & DB_PRTEXPR)
fprintf(ErrFp, "%s => ", s);
r = GetVarValue(s, v, locals);
}
r = GetVarValue(s, v, locals, p);
if (! (DebugFlag & DB_PRTEXPR)) return r;
if (r == OK) {
PrintValue(v, ErrFp);
@@ -533,7 +577,7 @@ static int MakeValue(char *s, Value *v, Var *locals)
int DoCoerce(char type, Value *v)
{
int h, d, m, y, i, k;
char *s;
char const *s;
/* Do nothing if value is already the right type */
if (type == v->type) return OK;
@@ -697,6 +741,8 @@ static int Add(void)
Value v1, v2, v3;
int r;
size_t l1, l2;
PopValStack(v2);
if ( (r = FnPopValStack(&v1)) ) {
DestroyValue(v2);
@@ -751,7 +797,13 @@ static int Add(void)
return r;
}
v3.type = STR_TYPE;
v3.v.str = (char *) malloc(strlen(v1.v.str) + strlen(v2.v.str) + 1);
l1 = strlen(v1.v.str);
l2 = strlen(v2.v.str);
if (MaxStringLen && (l1 + l2 > MaxStringLen)) {
DestroyValue(v1); DestroyValue(v2);
return E_STRING_TOO_LONG;
}
v3.v.str = malloc(l1 + l2 + 1);
if (!v3.v.str) {
DestroyValue(v1); DestroyValue(v2);
return E_NO_MEM;
@@ -1078,7 +1130,28 @@ static int LogNot(void)
/* Find a function. */
/* */
/***************************************************************/
Operator *FindFunc(char *name, Operator where[], int num)
Operator *FindOperator(char const *name, Operator where[], int num)
{
int top=num-1, bot=0;
int mid, r;
while (top >= bot) {
mid = (top + bot) / 2;
r = StrCmpi(name, where[mid].name);
if (!r) return &where[mid];
else if (r > 0) bot = mid+1;
else top = mid-1;
}
return NULL;
}
/***************************************************************/
/* */
/* FindFunc */
/* */
/* Find a function. */
/* */
/***************************************************************/
BuiltinFunc *FindFunc(char const *name, BuiltinFunc where[], int num)
{
int top=num-1, bot=0;
int mid, r;
@@ -1102,7 +1175,7 @@ Operator *FindFunc(char *name, Operator where[], int num)
void PrintValue (Value *v, FILE *fp)
{
int y, m, d;
char *s;
char const *s;
if (v->type == STR_TYPE) {
s=v->v.str;
@@ -1154,7 +1227,7 @@ int CopyValue(Value *dest, const Value *src)
/* and tim; update s. */
/* */
/***************************************************************/
static int ParseLiteralDate(char **s, int *jul, int *tim)
int ParseLiteralDate(char const **s, int *jul, int *tim)
{
int y, m, d;
int hour, min;

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <sys/stat.h>
@@ -30,6 +31,10 @@
#include <stdlib.h>
#include <unistd.h>
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
#include "types.h"
#include "protos.h"
#include "globals.h"
@@ -42,20 +47,34 @@
/* Define the structures needed by the file caching system */
typedef struct cache {
struct cache *next;
char *text;
char const *text;
int LineNo;
} CachedLine;
typedef struct cheader {
struct cheader *next;
char *filename;
char const *filename;
CachedLine *cache;
int ownedByMe;
} CachedFile;
/* A linked list of filenames if we INCLUDE /some/directory/ */
typedef struct fname_chain {
struct fname_chain *next;
char const *filename;
} FilenameChain;
/* Cache filename chains for directories */
typedef struct directory_fname_chain {
struct directory_fname_chain *next;
FilenameChain *chain;
char const *dirname;
} DirectoryFilenameChain;
/* Define the structures needed by the INCLUDE file system */
typedef struct {
char *filename;
char const *filename;
FilenameChain *chain;
int LineNo;
unsigned int IfFlags;
int NumIfs;
@@ -66,6 +85,7 @@ typedef struct {
static CachedFile *CachedFiles = (CachedFile *) NULL;
static CachedLine *CLine = (CachedLine *) NULL;
static DirectoryFilenameChain *CachedDirectoryChains = NULL;
static FILE *fp;
@@ -73,9 +93,52 @@ static IncludeStruct IStack[INCLUDE_NEST];
static int IStackPtr = 0;
static int ReadLineFromFile (void);
static int CacheFile (const char *fname);
static int CacheFile (char const *fname);
static void DestroyCache (CachedFile *cf);
static int CheckSafety (void);
static int PopFile (void);
static void OpenPurgeFile(char const *fname, char const *mode)
{
DynamicBuffer fname_buf;
if (PurgeFP != NULL && PurgeFP != stdout) {
fclose(PurgeFP);
}
PurgeFP = NULL;
/* Do not open a purge file if we're below purge
include depth */
if (IStackPtr-2 >= PurgeIncludeDepth) {
PurgeFP = NULL;
return;
}
DBufInit(&fname_buf);
if (DBufPuts(&fname_buf, fname) != OK) return;
if (DBufPuts(&fname_buf, ".purged") != OK) return;
PurgeFP = fopen(DBufValue(&fname_buf), mode);
if (!PurgeFP) {
fprintf(ErrFp, "Cannot open `%s' for writing: %s\n", DBufValue(&fname_buf), strerror(errno));
}
DBufFree(&fname_buf);
}
static void FreeChainItem(FilenameChain *chain)
{
if (chain->filename) free((void *) chain->filename);
free(chain);
}
static void FreeChain(FilenameChain *chain)
{
FilenameChain *next;
while(chain) {
next = chain->next;
FreeChainItem(chain);
chain = next;
}
}
/***************************************************************/
/* */
@@ -137,14 +200,32 @@ static int ReadLineFromFile(void)
}
if (feof(fp)) {
FCLOSE(fp);
if ((DBufLen(&buf) == 0) &&
(DBufLen(&LineBuffer) == 0) && PurgeMode) {
if (PurgeFP != NULL && PurgeFP != stdout) fclose(PurgeFP);
PurgeFP = NULL;
}
}
l = DBufLen(&buf);
if (l && (DBufValue(&buf)[l-1] == '\\')) {
DBufValue(&buf)[l-1] = 0;
if (DBufPuts(&LineBuffer, DBufValue(&buf)) != OK) {
DBufFree(&buf);
DBufFree(&LineBuffer);
return E_NO_MEM;
if (PurgeMode) {
if (DBufPuts(&LineBuffer, DBufValue(&buf)) != OK) {
DBufFree(&buf);
DBufFree(&LineBuffer);
return E_NO_MEM;
}
if (DBufPutc(&LineBuffer, '\n') != OK) {
DBufFree(&buf);
DBufFree(&LineBuffer);
return E_NO_MEM;
}
} else {
DBufValue(&buf)[l-1] = '\n';
if (DBufPuts(&LineBuffer, DBufValue(&buf)) != OK) {
DBufFree(&buf);
DBufFree(&LineBuffer);
return E_NO_MEM;
}
}
continue;
}
@@ -171,11 +252,18 @@ static int ReadLineFromFile(void)
/* ShouldCache is 1, cache the file */
/* */
/***************************************************************/
int OpenFile(const char *fname)
int OpenFile(char const *fname)
{
CachedFile *h = CachedFiles;
int r;
if (PurgeMode) {
if (PurgeFP != NULL && PurgeFP != stdout) {
fclose(PurgeFP);
}
PurgeFP = NULL;
}
/* Assume we own the file for now */
RunDisabled &= ~RUN_NOTOWNER;
@@ -183,6 +271,9 @@ int OpenFile(const char *fname)
while (h) {
if (!strcmp(fname, h->filename)) {
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Found in cache\n", fname);
}
CLine = h->cache;
STRSET(FileName, fname);
LineNo = 0;
@@ -197,8 +288,20 @@ int OpenFile(const char *fname)
/* If it's a dash, then it's stdin */
if (!strcmp(fname, "-")) {
fp = stdin;
if (PurgeMode) {
PurgeFP = stdout;
}
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `-': Reading stdin\n");
}
} else {
fp = fopen(fname, "r");
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Reading `%s': Opening file on disk\n", fname);
}
if (PurgeMode) {
OpenPurgeFile(fname, "w");
}
}
if (!fp || !CheckSafety()) return E_CANT_OPEN;
CLine = NULL;
@@ -212,8 +315,10 @@ int OpenFile(const char *fname)
if (strcmp(fname, "-")) {
fp = fopen(fname, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN;
if (PurgeMode) OpenPurgeFile(fname, "w");
} else {
fp = stdin;
if (PurgeMode) PurgeFP = stdout;
}
}
}
@@ -230,18 +335,25 @@ int OpenFile(const char *fname)
/* Returns an indication of success or failure. */
/* */
/***************************************************************/
static int CacheFile(const char *fname)
static int CacheFile(char const *fname)
{
int r;
CachedFile *cf;
CachedLine *cl;
char *s;
char const *s;
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Caching file `%s' in memory\n", fname);
}
cl = NULL;
/* Create a file header */
cf = NEW(CachedFile);
cf->cache = NULL;
if (!cf) { ShouldCache = 0; FCLOSE(fp); return E_NO_MEM; }
if (!cf) {
ShouldCache = 0;
FCLOSE(fp);
return E_NO_MEM;
}
cf->filename = StrDup(fname);
if (!cf->filename) {
ShouldCache = 0;
@@ -267,7 +379,7 @@ static int CacheFile(const char *fname)
}
/* Skip blank chars */
s = DBufValue(&LineBuffer);
while (isspace(*s)) s++;
while (isempty(*s)) s++;
if (*s && *s!=';' && *s!='#') {
/* Add the line to the cache */
if (!cl) {
@@ -311,13 +423,33 @@ static int CacheFile(const char *fname)
return OK;
}
/***************************************************************/
/* */
/* NextChainedFile - move to the next chained file in a glob */
/* list. */
/* */
/***************************************************************/
static int NextChainedFile(IncludeStruct *i)
{
while(i->chain) {
FilenameChain *cur = i->chain;
i->chain = i->chain->next;
if (OpenFile(cur->filename) == OK) {
return OK;
} else {
Eprint("%s: %s", ErrMsg[E_CANT_OPEN], cur->filename);
}
}
return E_EOF;
}
/***************************************************************/
/* */
/* PopFile - we've reached the end. Pop up to the previous */
/* file, or return E_EOF */
/* */
/***************************************************************/
int PopFile(void)
static int PopFile(void)
{
IncludeStruct *i;
@@ -326,8 +458,21 @@ int PopFile(void)
if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]);
if (!IStackPtr) return E_EOF;
i = &IStack[IStackPtr-1];
if (i->chain) {
int oldRunDisabled = RunDisabled;
if (NextChainedFile(i) == OK) {
return OK;
}
RunDisabled = oldRunDisabled;
}
if (IStackPtr <= 1) {
return E_EOF;
}
IStackPtr--;
i = &IStack[IStackPtr];
LineNo = i->LineNo;
IfFlags = i->IfFlags;
@@ -343,13 +488,15 @@ int PopFile(void)
if (strcmp(i->filename, "-")) {
fp = fopen(i->filename, "r");
if (!fp || !CheckSafety()) return E_CANT_OPEN;
if (PurgeMode) OpenPurgeFile(i->filename, "a");
} else {
fp = stdin;
if (PurgeMode) PurgeFP = stdout;
}
if (fp != stdin)
(void) fseek(fp, i->offset, 0); /* Trust that it works... */
}
free(i->filename);
free((char *) i->filename);
return OK;
}
@@ -361,13 +508,13 @@ int PopFile(void)
/* */
/***************************************************************/
int DoInclude(ParsePtr p)
{
{
DynamicBuffer buf;
int r, e;
DBufInit(&buf);
if ( (r=ParseToken(p, &buf)) ) return r;
e = VerifyEoln(p);
e = VerifyEoln(p);
if (e) Eprint("%s", ErrMsg[e]);
if ( (r=IncludeFile(DBufValue(&buf))) ) {
DBufFree(&buf);
@@ -379,6 +526,126 @@ int DoInclude(ParsePtr p)
return OK;
}
#ifdef HAVE_GLOB
static int SetupGlobChain(char const *dirname, IncludeStruct *i)
{
DynamicBuffer pattern;
char *dir;
size_t l;
int r;
glob_t glob_buf;
DirectoryFilenameChain *dc = CachedDirectoryChains;
i->chain = NULL;
if (!*dirname) return E_CANT_OPEN;
dir = StrDup(dirname);
if (!dir) return E_NO_MEM;
/* Strip trailing slashes off directory */
l = strlen(dir);
while(l) {
if (*(dir+l-1) == '/') {
l--;
*(dir+l) = 0;
} else {
break;
}
}
/* Repair root directory :-) */
if (!l) {
*dir = '/';
}
/* Check the cache */
while(dc) {
if (!strcmp(dc->dirname, dir)) {
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Found cached directory listing for `%s'\n",
dir);
}
free(dir);
i->chain = dc->chain;
return OK;
}
dc = dc->next;
}
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Scanning directory `%s' for *.rem files\n", dir);
}
if (ShouldCache) {
dc = malloc(sizeof(DirectoryFilenameChain));
if (dc) {
dc->dirname = StrDup(dir);
if (!dc->dirname) {
free(dc);
dc = NULL;
}
}
if (dc) {
if (DebugFlag & DB_TRACE_FILES) {
fprintf(ErrFp, "Caching directory `%s' listing\n", dir);
}
dc->chain = NULL;
dc->next = CachedDirectoryChains;
CachedDirectoryChains = dc;
}
}
DBufInit(&pattern);
DBufPuts(&pattern, dir);
DBufPuts(&pattern, "/*.rem");
free(dir);
r = glob(DBufValue(&pattern), 0, NULL, &glob_buf);
DBufFree(&pattern);
if (r == GLOB_NOMATCH) {
globfree(&glob_buf);
return OK;
}
if (r != 0) {
globfree(&glob_buf);
return -1;
}
/* Add the files to the chain backwards to preserve sort order */
for (r=glob_buf.gl_pathc-1; r>=0; r--) {
FilenameChain *ch = malloc(sizeof(FilenameChain));
if (!ch) {
globfree(&glob_buf);
FreeChain(i->chain);
i->chain = NULL;
return E_NO_MEM;
}
/* TODO: stat the file and only add if it's a plain file and
readable by us */
ch->filename = StrDup(glob_buf.gl_pathv[r]);
if (!ch->filename) {
globfree(&glob_buf);
FreeChain(i->chain);
i->chain = NULL;
free(ch);
return E_NO_MEM;
}
ch->next = i->chain;
i->chain = ch;
}
if (dc) {
dc->chain = i->chain;
}
globfree(&glob_buf);
return OK;
}
#endif
/***************************************************************/
/* */
/* IncludeFile */
@@ -387,22 +654,28 @@ int DoInclude(ParsePtr p)
/* inclusion. */
/* */
/***************************************************************/
int IncludeFile(const char *fname)
int IncludeFile(char const *fname)
{
IncludeStruct *i;
int r;
int oldRunDisabled;
struct stat statbuf;
FreshLine = 1;
if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE;
i = &IStack[IStackPtr];
i->filename = StrDup(FileName);
if (!i->filename) return E_NO_MEM;
if (FileName) {
i->filename = StrDup(FileName);
if (!i->filename) return E_NO_MEM;
} else {
i->filename = NULL;
}
i->LineNo = LineNo;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
i->CLine = CLine;
i->offset = -1L;
i->chain = NULL;
if (RunDisabled & RUN_NOTOWNER) {
i->ownedByMe = 0;
} else {
@@ -415,15 +688,52 @@ int IncludeFile(const char *fname)
IStackPtr++;
#ifdef HAVE_GLOB
/* If it's a directory, set up the glob chain here. */
if (stat(fname, &statbuf) == 0) {
FilenameChain *fc;
if (S_ISDIR(statbuf.st_mode)) {
if (SetupGlobChain(fname, i) == OK) { /* Glob succeeded */
if (!i->chain) { /* Oops... no matching files */
if (!Hush) {
Eprint("%s: %s", fname, ErrMsg[E_NO_MATCHING_REMS]);
}
PopFile();
return E_NO_MATCHING_REMS;
}
while(i->chain) {
fc = i->chain;
i->chain = i->chain->next;
/* Munch first file */
oldRunDisabled = RunDisabled;
if (!OpenFile(fc->filename)) {
return OK;
}
Eprint("%s: %s", ErrMsg[E_CANT_OPEN], fc->filename);
RunDisabled = oldRunDisabled;
}
/* Couldn't open anything... bail */
return PopFile();
} else {
if (!Hush) {
Eprint("%s: %s", fname, ErrMsg[E_NO_MATCHING_REMS]);
}
}
return E_NO_MATCHING_REMS;
}
}
#endif
oldRunDisabled = RunDisabled;
/* Try to open the new file */
if (!OpenFile(fname)) {
return OK;
}
RunDisabled = oldRunDisabled;
/* Ugh! We failed! */
if ( (r=PopFile()) ) return r;
Eprint("%s: %s", ErrMsg[E_CANT_OPEN], fname);
/* Ugh! We failed! */
PopFile();
return E_CANT_OPEN;
}
@@ -432,7 +742,7 @@ int IncludeFile(const char *fname)
/* GetAccessDate - get the access date of a file. */
/* */
/***************************************************************/
int GetAccessDate(char *file)
int GetAccessDate(char const *file)
{
struct stat statbuf;
struct tm *t1;
@@ -457,10 +767,10 @@ static void DestroyCache(CachedFile *cf)
{
CachedLine *cl, *cnext;
CachedFile *temp;
if (cf->filename) free(cf->filename);
if (cf->filename) free((char *) cf->filename);
cl = cf->cache;
while (cl) {
if (cl->text) free (cl->text);
if (cl->text) free ((char *) cl->text);
cnext = cl->next;
free(cl);
cl = cnext;
@@ -488,7 +798,7 @@ static void DestroyCache(CachedFile *cf)
/***************************************************************/
int TopLevel(void)
{
return !IStackPtr;
return IStackPtr <= 1;
}
/***************************************************************/

File diff suppressed because it is too large Load Diff

View File

@@ -37,7 +37,7 @@ EXTERN int CurMon;
EXTERN int CurYear;
EXTERN int LineNo;
EXTERN int FreshLine;
EXTERN INIT( char *MsgCommand, NULL);
EXTERN INIT( char const *MsgCommand, NULL);
EXTERN INIT( int ShowAllErrors, 0);
EXTERN INIT( int DebugFlag, 0);
EXTERN INIT( int DoCalendar, 0);
@@ -53,15 +53,17 @@ EXTERN INIT( int CalMonths, 0);
EXTERN INIT( int Hush, 0);
EXTERN INIT( int NextMode, 0);
EXTERN INIT( int InfiniteDelta, 0);
EXTERN INIT( int DeltaOffset, 0);
EXTERN INIT( int RunDisabled, 0);
EXTERN INIT( int IgnoreOnce, 0);
EXTERN INIT( int SortByTime, 0);
EXTERN INIT( int SortByDate, 0);
EXTERN INIT( int SortByPrio, 0);
EXTERN INIT( int UntimedBeforeTimed, 0);
EXTERN INIT( int DefaultPrio, NO_PRIORITY);
EXTERN INIT( long SysTime, -1L);
EXTERN char *InitialFile;
EXTERN char const *InitialFile;
EXTERN int FileAccessDate;
EXTERN INIT( int DontFork, 0);
@@ -74,21 +76,27 @@ EXTERN INIT( char TimeSep, TIMESEP);
EXTERN INIT( int SynthesizeTags, 0);
EXTERN INIT( int ScFormat, SC_AMPM);
EXTERN INIT( int MaxSatIter, 150);
EXTERN INIT( int MaxStringLen, MAX_STR_LEN);
EXTERN INIT( char *FileName, NULL);
EXTERN INIT( int UseStdin, 0);
EXTERN INIT( int PurgeMode, 0);
EXTERN INIT( int PurgeIncludeDepth, 0);
EXTERN FILE *ErrFp;
EXTERN INIT( FILE *PurgeFP, NULL);
EXTERN INIT( int NumIfs, 0);
EXTERN INIT( unsigned int IfFlags, 0);
EXTERN INIT( int LastTriggerDate, 0);
EXTERN INIT( int LastTrigValid, 0);
EXTERN INIT( int LastTriggerTime, 0);
EXTERN INIT( int ShouldCache, 0);
EXTERN char *CurLine;
EXTERN char const *CurLine;
EXTERN INIT( int NumTriggered, 0);
EXTERN int ArgC;
EXTERN char **ArgV;
EXTERN char const **ArgV;
EXTERN INIT( int CalLines, CAL_LINES);
EXTERN INIT( int CalPad, 1);
EXTERN INIT( int UseVTChars, 0);
EXTERN INIT( int UseVTColors, 0);
/* Latitude and longitude */
EXTERN INIT( int LatDeg, LAT_DEG);

View File

@@ -55,7 +55,7 @@
#define ADAR2ADARA 1
#define ADAR2BOTH 2
static char *HebMonthNames[] = {
static char const *HebMonthNames[] = {
"Tishrey", "Heshvan", "Kislev", "Tevet", "Shvat", "Adar A", "Adar B",
"Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul", "Adar"};
@@ -137,7 +137,7 @@ int DaysInHebYear(int y)
/* given the LENGTH of the Hebrew year. */
/* */
/***************************************************************/
char *DaysInHebMonths(int ylen)
char const *DaysInHebMonths(int ylen)
{
static char monlen[13] =
{30, 29, 30, 29, 30, 0, 29, 30, 29, 30, 29, 30, 29};
@@ -166,7 +166,7 @@ char *DaysInHebMonths(int ylen)
int HebToJul(int hy, int hm, int hd)
{
int ylen;
char *monlens;
char const *monlens;
int rh;
int m;
@@ -201,7 +201,7 @@ void JulToHeb(int jul, int *hy, int *hm, int *hd)
int y, m, d;
int rh;
int ylen;
char *monlen;
char const *monlen;
/* Get the common year */
FromJulian(jul, &y, &m, &d);
y += 3763; /* Over-estimate a bit to be on the safe side below... */
@@ -232,7 +232,7 @@ void JulToHeb(int jul, int *hy, int *hm, int *hd)
/* year. */
/* */
/***************************************************************/
int HebNameToNum(const char *mname)
int HebNameToNum(char const *mname)
{
int i;
int m=-1;
@@ -254,7 +254,7 @@ int HebNameToNum(const char *mname)
/* year. */
/* */
/***************************************************************/
char *HebMonthName(int m, int y)
char const *HebMonthName(int m, int y)
{
if (m != ADARA && m != ADARB) return HebMonthNames[m];
@@ -293,7 +293,7 @@ char *HebMonthName(int m, int y)
int GetValidHebDate(int yin, int min, int din, int adarbehave,
int *mout, int *dout, int jahr)
{
char *monlen;
char const *monlen;
int ylen;
*mout = min;
@@ -448,7 +448,7 @@ int GetNextHebrewDate(int julstart, int hm, int hd,
/***************************************************************/
int ComputeJahr(int y, int m, int d, int *ans)
{
char *monlen;
char const *monlen;
int len;
*ans = JAHR_NONE;

View File

@@ -8,10 +8,11 @@
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-1998 by David F. Skoll */
/* Copyright (C) 1999-2007 by Roaring Penguin Software Inc. */
/* Copyright (C) 1999-2010 by Roaring Penguin Software Inc. */
/* */
/***************************************************************/
#include "version.h"
#include "config.h"
#define L_IN_INIT 1
@@ -28,7 +29,6 @@
#include "protos.h"
#include "expr.h"
#include "err.h"
#include "version.h"
#include "globals.h"
/***************************************************************
@@ -52,6 +52,7 @@
* -h = Hush mode
* -f = Do not fork
* -dchars = Debugging mode: Chars are:
* f = Trace file openings
* e = Echo input lines
* x = Display expression evaluation
* t = Display trigger dates
@@ -66,9 +67,13 @@
* -kcmd = Run 'cmd' for MSG-type reminders instead of printing to stdout
* -iVAR=EXPR = Initialize and preserve VAR.
* -m = Start calendar with Monday instead of Sunday.
* -j[n] = Purge all junk from reminder files (n = INCLUDE depth)
* A minus sign alone indicates to take input from stdin
*
**************************************************************/
#if defined(__APPLE__) || defined(__CYGWIN__)
static void rkrphgvba(int x);
#endif
/* For parsing an integer */
#define PARSENUM(var, s) \
@@ -79,10 +84,41 @@ while (isdigit(*(s))) { \
s++; \
}
static void ChgUser(char *u);
static void InitializeVar(char *str);
static void ChgUser(char const *u);
static void InitializeVar(char const *str);
static char *BadDate = "Illegal date on command line\n";
static char const *BadDate = "Illegal date on command line\n";
static DynamicBuffer default_filename_buf;
/***************************************************************/
/* */
/* DefaultFilename */
/* */
/* If we're invoked as "rem" rather than "remind", use a */
/* default filename. Use $DOTREMINDERS or $HOME/.reminders */
/* */
/***************************************************************/
static char const *DefaultFilename(void)
{
char const *s;
DBufInit(&default_filename_buf);
s = getenv("DOTREMINDERS");
if (s) {
return s;
}
s = getenv("HOME");
if (!s) {
fprintf(stderr, "HOME environment variable not set. Unable to determine reminder file.\n");
exit(1);
}
DBufPuts(&default_filename_buf, s);
DBufPuts(&default_filename_buf, "/.reminders");
return DBufValue(&default_filename_buf);
}
/***************************************************************/
/* */
@@ -91,12 +127,26 @@ static char *BadDate = "Illegal date on command line\n";
/* Initialize the system - called only once at beginning! */
/* */
/***************************************************************/
void InitRemind(int argc, char *argv[])
void InitRemind(int argc, char const *argv[])
{
char *arg;
char const *arg;
int i;
int y, m, d, rep;
Token tok;
int InvokedAsRem = 0;
char const *s;
int weeks;
int jul, tim;
#if defined(__APPLE__)
rkrphgvba(0);
#elif defined(__CYGWIN__)
rkrphgvba(1);
#endif
jul = NO_DATE;
tim = NO_TIME;
/* Initialize global dynamic buffers */
DBufInit(&Banner);
@@ -105,6 +155,8 @@ void InitRemind(int argc, char *argv[])
DBufPuts(&Banner, L_BANNER);
PurgeFP = NULL;
/* Make sure remind is not installed set-uid or set-gid */
if (getgid() != getegid() ||
getuid() != geteuid()) {
@@ -125,6 +177,19 @@ void InitRemind(int argc, char *argv[])
JulianToday = RealToday;
FromJulian(JulianToday, &CurYear, &CurMon, &CurDay);
/* See if we were invoked as "rem" rather than "remind" */
if (argv[0]) {
s = strrchr(argv[0], '/');
if (!s) {
s = argv[0];
} else {
s++;
}
if (!strcmp(s, "rem")) {
InvokedAsRem = 1;
}
}
/* Parse the command-line options */
i = 1;
while (i < argc) {
@@ -141,6 +206,13 @@ void InitRemind(int argc, char *argv[])
while (*arg) {
switch(*arg++) {
case 'j':
case 'J':
PurgeMode = 1;
if (*arg) {
PARSENUM(PurgeIncludeDepth, arg);
}
break;
case 'i':
case 'I':
InitializeVar(arg);
@@ -176,9 +248,15 @@ void InitRemind(int argc, char *argv[])
case 't':
case 'T':
InfiniteDelta = 1;
if (!*arg) {
InfiniteDelta = 1;
} else {
PARSENUM(DeltaOffset, arg);
if (DeltaOffset < 0) {
DeltaOffset = 0;
}
}
break;
case 'e':
case 'E':
ErrFp = stdout;
@@ -194,6 +272,7 @@ void InitRemind(int argc, char *argv[])
SortByDate = SORT_ASCEND;
SortByTime = SORT_ASCEND;
SortByPrio = SORT_ASCEND;
UntimedBeforeTimed = 0;
if (*arg) {
if (*arg == 'D' || *arg == 'd')
SortByDate = SORT_DESCEND;
@@ -209,6 +288,11 @@ void InitRemind(int argc, char *argv[])
SortByPrio = SORT_DESCEND;
arg++;
}
if (*arg) {
if (*arg == 'D' || *arg == 'd')
UntimedBeforeTimed = 1;
arg++;
}
break;
case 'u':
@@ -223,11 +307,11 @@ void InitRemind(int argc, char *argv[])
if (*arg == '0') {
PARSENUM(Daemon, arg);
if (Daemon == 0) Daemon = -1;
else if (Daemon < 5) Daemon = 5;
else if (Daemon < 1) Daemon = 1;
else if (Daemon > 60) Daemon = 60;
} else {
PARSENUM(Daemon, arg);
if (Daemon<5) Daemon=5;
if (Daemon<1) Daemon=1;
else if (Daemon>60) Daemon=60;
}
break;
@@ -249,8 +333,33 @@ void InitRemind(int argc, char *argv[])
case 'c':
case 'C':
DoCalendar = 1;
if (*arg == '+') {
arg++;
weeks = 0;
/* Parse the flags */
while(*arg) {
if (*arg == 'a' ||
*arg == 'A') {
DoSimpleCalDelta = 1;
arg++;
continue;
}
if (*arg == '+') {
weeks = 1;
arg++;
continue;
}
if (*arg == 'l' || *arg == 'L') {
UseVTChars = 1;
arg++;
continue;
}
if (*arg == 'c' || *arg == 'C') {
UseVTColors = 1;
arg++;
continue;
}
break;
}
if (weeks) {
PARSENUM(CalWeeks, arg);
if (!CalWeeks) CalWeeks = 1;
} else {
@@ -262,12 +371,21 @@ void InitRemind(int argc, char *argv[])
case 's':
case 'S':
DoSimpleCalendar = 1;
if (*arg == 'a' || *arg == 'A') {
weeks = 0;
while(*arg) {
if (*arg == 'a' || *arg == 'A') {
DoSimpleCalDelta = 1;
arg++;
continue;
}
if (*arg == '+') {
arg++;
weeks = 1;
continue;
}
break;
}
if (*arg == '+') {
arg++;
if (weeks) {
PARSENUM(CalWeeks, arg);
if (!CalWeeks) CalWeeks = 1;
} else {
@@ -317,11 +435,12 @@ void InitRemind(int argc, char *argv[])
case 'D':
while (*arg) {
switch(*arg++) {
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
case 'v': case 'V': DebugFlag |= DB_DUMP_VARS; break;
case 'l': case 'L': DebugFlag |= DB_PRTLINE; break;
case 'e': case 'E': DebugFlag |= DB_ECHO_LINE; break;
case 'x': case 'X': DebugFlag |= DB_PRTEXPR; break;
case 't': case 'T': DebugFlag |= DB_PRTTRIG; break;
case 'v': case 'V': DebugFlag |= DB_DUMP_VARS; break;
case 'l': case 'L': DebugFlag |= DB_PRTLINE; break;
case 'f': case 'F': DebugFlag |= DB_TRACE_FILES; break;
default:
fprintf(ErrFp, ErrMsg[M_BAD_DB_FLAG], *(arg-1));
}
@@ -360,11 +479,15 @@ void InitRemind(int argc, char *argv[])
}
/* Get the filename. */
if (i >= argc) {
Usage();
exit(1);
if (!InvokedAsRem) {
if (i >= argc) {
Usage();
exit(1);
}
InitialFile = argv[i++];
} else {
InitialFile = DefaultFilename();
}
InitialFile = argv[i++];
/* Get the date, if any */
if (i < argc) {
@@ -381,18 +504,32 @@ void InitRemind(int argc, char *argv[])
}
break;
case T_DateTime:
if (SysTime != -1L) Usage();
if (m != NO_MON || d != NO_DAY || y != NO_YR || jul != NO_DATE) Usage();
SysTime = (tok.val % MINUTES_PER_DAY) * 60;
DontQueue = 1;
Daemon = 0;
jul = tok.val / MINUTES_PER_DAY;
break;
case T_Date:
if (m != NO_MON || d != NO_DAY || y != NO_YR || jul != NO_DATE) Usage();
jul = tok.val;
break;
case T_Month:
if (m != NO_MON) Usage();
if (m != NO_MON || jul != NO_DATE) Usage();
else m = tok.val;
break;
case T_Day:
if (d != NO_DAY) Usage();
if (d != NO_DAY || jul != NO_DATE) Usage();
else d = tok.val;
break;
case T_Year:
if (y != NO_YR) Usage();
if (y != NO_YR || jul != NO_DATE) Usage();
else y = tok.val;
break;
@@ -401,7 +538,8 @@ void InitRemind(int argc, char *argv[])
else rep = tok.val;
break;
default: Usage();
default:
Usage();
}
}
@@ -411,6 +549,9 @@ void InitRemind(int argc, char *argv[])
Daemon = 0;
}
if (jul != NO_DATE) {
FromJulian(jul, &y, &m, &d);
}
/* Must supply date in the form: day, mon, yr OR mon, yr */
if (m != NO_MON || y != NO_YR || d != NO_DAY) {
if (m == NO_MON || y == NO_YR) {
@@ -456,7 +597,7 @@ void InitRemind(int argc, char *argv[])
void Usage(void)
{
fprintf(ErrFp, "\nREMIND %s (%s version) Copyright 1992-1998 David F. Skoll\n", VERSION, L_LANGNAME);
fprintf(ErrFp, "Copyright 1999-2007 Roaring Penguin Software Inc.\n");
fprintf(ErrFp, "Copyright 1999-2010 Roaring Penguin Software Inc.\n");
#ifdef BETA
fprintf(ErrFp, ">>>> BETA VERSION <<<<\n");
#endif
@@ -464,29 +605,30 @@ void Usage(void)
fprintf(ErrFp, "Options:\n");
fprintf(ErrFp, " -n Output next occurrence of reminders in simple format\n");
fprintf(ErrFp, " -r Disable RUN directives\n");
fprintf(ErrFp, " -c[n] Produce a calendar for n (default 1) months\n");
fprintf(ErrFp, " -c+[n] Produce a calendar for n (default 1) weeks\n");
fprintf(ErrFp, " -c[a][n] Produce a calendar for n (default 1) months\n");
fprintf(ErrFp, " -c[a]+[n] Produce a calendar for n (default 1) weeks\n");
fprintf(ErrFp, " -w[n[,p[,s]]] Specify width, padding and spacing of calendar\n");
fprintf(ErrFp, " -s[a][+][n] Produce `simple calendar' for n (1) months (weeks)\n");
fprintf(ErrFp, " -p[a][n] Same as -s, but input compatible with rem2ps\n");
fprintf(ErrFp, " -l Prefix each simple calendar line with line number and filename comment\n");
fprintf(ErrFp, " -v Verbose mode\n");
fprintf(ErrFp, " -o Ignore ONCE directives\n");
fprintf(ErrFp, " -t Trigger all future reminders regardless of delta\n");
fprintf(ErrFp, " -t[n] Trigger all future (or those within `n' days)\n");
fprintf(ErrFp, " -h `Hush' mode - be very quiet\n");
fprintf(ErrFp, " -a Don't trigger timed reminders immediately - just queue them\n");
fprintf(ErrFp, " -q Don't queue timed reminders\n");
fprintf(ErrFp, " -f Trigger timed reminders by staying in foreground\n");
fprintf(ErrFp, " -z[n] Enter daemon mode, waking every n (5) minutes.\n");
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 f=tracefiles\n");
fprintf(ErrFp, " -e Divert messages normally sent to stderr to stdout\n");
fprintf(ErrFp, " -b[n] Time format for cal: 0=am/pm, 1=24hr, 2=none\n");
fprintf(ErrFp, " -x[n] Iteration limit for SATISFY clause (def=150)\n");
fprintf(ErrFp, " -kcmd Run `cmd' for MSG-type reminders\n");
fprintf(ErrFp, " -g[ddd] Sort reminders by date, time and priority before issuing\n");
fprintf(ErrFp, " -g[dddd] Sort reminders by date, time, priority, and 'timedness'\n");
fprintf(ErrFp, " -ivar=val Initialize var to val and preserve var\n");
fprintf(ErrFp, " -m Start calendar with Monday rather than Sunday\n");
fprintf(ErrFp, " -y Synthesize tags for tagless reminders\n");
fprintf(ErrFp, " -j[n] Run in 'purge' mode. [n = INCLUDE depth]\n");
exit(1);
}
#endif /* L_USAGE_OVERRIDE */
@@ -500,12 +642,15 @@ void Usage(void)
/* USER environment variables. */
/* */
/***************************************************************/
static void ChgUser(char *user)
static void ChgUser(char const *user)
{
uid_t myuid;
struct passwd *pwent;
static char *home, *shell, *username, *logname;
static char *home;
static char *shell;
static char *username;
static char *logname;
myuid = getuid();
@@ -559,6 +704,20 @@ static void ChgUser(char *user)
putenv(logname);
}
}
static void
DefineFunction(char const *str)
{
Parser p;
int r;
CreateParser(str, &p);
r = DoFset(&p);
DestroyParser(&p);
if (r != OK) {
fprintf(ErrFp, "-i option: %s: %s\n", str, ErrMsg[r]);
}
}
/***************************************************************/
/* */
/* InitializeVar */
@@ -566,22 +725,34 @@ static void ChgUser(char *user)
/* Initialize and preserve a variable */
/* */
/***************************************************************/
static void InitializeVar(char *str)
static void InitializeVar(char const *str)
{
char *varname, *expr;
char const *expr;
char const *ostr = str;
char varname[VAR_NAME_LEN+1];
Value val;
int r;
/* Scan for an '=' sign */
varname = str;
while (*str && *str != '=') str++;
r = 0;
while (*str && *str != '=') {
if (r < VAR_NAME_LEN) {
varname[r++] = *str;
}
if (*str == '(') {
/* Do a function definition if we see a paren */
DefineFunction(ostr);
return;
}
str++;
}
varname[r] = 0;
if (!*str) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_EQ]);
return;
}
*str = 0;
if (!*varname) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[E_MISS_VAR]);
return;
@@ -592,7 +763,7 @@ static void InitializeVar(char *str)
return;
}
r=EvalExpr(&expr, &val);
r=EvalExpr(&expr, &val, NULL);
if (r) {
fprintf(ErrFp, ErrMsg[M_I_OPTION], ErrMsg[r]);
return;
@@ -614,3 +785,46 @@ static void InitializeVar(char *str)
return;
}
static char const pmsg1[] = {
0x4c, 0x62, 0x68, 0x20, 0x6e, 0x63, 0x63, 0x72, 0x6e, 0x65, 0x20,
0x67, 0x62, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x68, 0x61, 0x61, 0x76,
0x61, 0x74, 0x20, 0x45, 0x72, 0x7a, 0x76, 0x61, 0x71, 0x20, 0x62,
0x61, 0x20, 0x6e, 0x61, 0x20, 0x4e, 0x63, 0x63, 0x79, 0x72, 0x20,
0x63, 0x65, 0x62, 0x71, 0x68, 0x70, 0x67, 0x2e, 0x20, 0x20, 0x56,
0x27, 0x71, 0x20, 0x65, 0x6e, 0x67, 0x75, 0x72, 0x65, 0x20, 0x67,
0x75, 0x6e, 0x67, 0x0a, 0x6c, 0x62, 0x68, 0x20, 0x71, 0x76, 0x71,
0x61, 0x27, 0x67, 0x2e, 0x20, 0x20, 0x45, 0x72, 0x7a, 0x76, 0x61,
0x71, 0x20, 0x72, 0x6b, 0x72, 0x70, 0x68, 0x67, 0x76, 0x62, 0x61,
0x20, 0x6a, 0x76, 0x79, 0x79, 0x20, 0x70, 0x62, 0x61, 0x67, 0x76,
0x61, 0x68, 0x72, 0x20, 0x7a, 0x62, 0x7a, 0x72, 0x61, 0x67, 0x6e,
0x65, 0x76, 0x79, 0x6c, 0x2e, 0x0a, 0x00
};
static char const pmsg2[] = {
0x4c, 0x62, 0x68, 0x20, 0x6e, 0x63, 0x63, 0x72, 0x6e, 0x65, 0x20,
0x67, 0x62, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x68, 0x61, 0x61, 0x76,
0x61, 0x74, 0x20, 0x45, 0x72, 0x7a, 0x76, 0x61, 0x71, 0x20, 0x62,
0x61, 0x20, 0x6e, 0x20, 0x5a, 0x76, 0x70, 0x65, 0x62, 0x66, 0x62,
0x73, 0x67, 0x20, 0x66, 0x6c, 0x66, 0x67, 0x72, 0x7a, 0x2e, 0x20,
0x20, 0x56, 0x27, 0x71, 0x20, 0x65, 0x6e, 0x67, 0x75, 0x72, 0x65,
0x20, 0x67, 0x75, 0x6e, 0x67, 0x0a, 0x6c, 0x62, 0x68, 0x20, 0x71,
0x76, 0x71, 0x61, 0x27, 0x67, 0x2e, 0x20, 0x20, 0x45, 0x72, 0x7a,
0x76, 0x61, 0x71, 0x20, 0x72, 0x6b, 0x72, 0x70, 0x68, 0x67, 0x76,
0x62, 0x61, 0x20, 0x6a, 0x76, 0x79, 0x79, 0x20, 0x70, 0x62, 0x61,
0x67, 0x76, 0x61, 0x68, 0x72, 0x20, 0x7a, 0x62, 0x7a, 0x72, 0x61,
0x67, 0x6e, 0x65, 0x76, 0x79, 0x6c, 0x2e, 0x0a, 0x00
};
#if defined(__APPLE__) || defined(__CYGWIN__)
static void
rkrphgvba(int x)
{
char const *s = (x ? pmsg2 : pmsg1);
while(*s) {
int c = (int) *s++;
c=isalpha(c)?tolower(c)<0x6e?c+13:c-13:c;
putchar(c);
}
sleep(5);
}
#endif

View File

@@ -307,7 +307,9 @@ EXTERN char *ErrMsg[] =
"%d viesti(\xE4) t\xE4m\xE4n p\xE4iv\xE4n jonossa.\n",
"Numero puuttuu",
"Virheellinen funktio WARN-lausekkeessa",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
#elif IBMEXTENDED
"Ok",
@@ -409,7 +411,10 @@ EXTERN char *ErrMsg[] =
"%d viesti(\x84) t\x84m\x84n p\x84iv\x84n jonossa.\n",
"Numero puuttuu"
"Virheellinen funktio WARN-lausekkeessa",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
#else
"Ok",
"Puuttuva ']'",
@@ -510,7 +515,10 @@ EXTERN char *ErrMsg[] =
"%d viesti({) t{m{n p{iv{n jonossa.\n",
"Numero puuttuu",
"Virheellinen funktio WARN-lausekkeessa",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
#endif
};

View File

@@ -244,7 +244,10 @@ EXTERN char *ErrMsg[] =
"%d rappel(s) en file pour aujourd'hui.\n",
"Nombre attendu",
"Fonction ill\351gale apr\350s WARN",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
#else /* ISOLATIN1 */
"Ok",
"']' manquant",
@@ -345,7 +348,9 @@ EXTERN char *ErrMsg[] =
"%d rappel(s) en file pour aujourd'hui.\n",
"Nombre attendu",
"Fonction illegale apres WARN",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
#endif /* ISOLATIN1 */
};
#endif /* MK_GLOBALS */

View File

@@ -279,7 +279,9 @@ EXTERN char *ErrMsg[] =
"%d Przypomnienia zakolejkowane na p\363\274niej.\n",
"Spodziewana liczba",
"Illegal function in WARN clause (NEEDS TRANSLATION TO POLISH)",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
#else /* ISOLATIN1 */
"OK",
"Brakujacy ']'",
@@ -380,7 +382,9 @@ EXTERN char *ErrMsg[] =
"%d Przypomnienia zakolejkowane na pozniej.\n",
"Spodziewana liczba",
"Illegal function in WARN clause (NEEDS TRANSLATION TO POLISH)",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
#endif /* ISOLATIN1 */
};
#endif /* MK_GLOBALS */

View File

@@ -245,7 +245,9 @@ EXTERN char *ErrMsg[] =
"%d compromisso(s) colocados na fila para mais tarde.\n",
"Esperando numero",
"Funcao ilegal na clausula WARN",
"Can't convert between time zones"
"Can't convert between time zones",
"No files matching *.rem",
"String too long"
};
#endif /* MK_GLOBALS */

View File

@@ -7,7 +7,7 @@
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-1998 by David F. Skoll */
/* Copyright (C) 1999-2000 by Roaring Penguin Software Inc. */
/* Copyright (C) 1999-2010 by Roaring Penguin Software Inc. */
/* */
/***************************************************************/
@@ -62,14 +62,20 @@ int main(int argc, char *argv[])
/* Set up global vars */
ArgC = argc;
ArgV = argv;
ArgV = (char const **) argv;
InitRemind(argc, argv);
InitRemind(argc, (char const **) argv);
if (DoCalendar || (DoSimpleCalendar && (!NextMode || PsCal))) {
ProduceCalendar();
return 0;
}
/* Are we purging old reminders? Then just run through the loop once! */
if (PurgeMode) {
DoReminders();
return 0;
}
/* Not doing a calendar. Do the regular remind loop */
ShouldCache = (Iterations > 1);
@@ -123,6 +129,17 @@ int main(int argc, char *argv[])
return 0;
}
void PurgeEchoLine(char const *fmt, ...)
{
va_list argptr;
va_start(argptr, fmt);
if (PurgeFP != NULL) {
(void) vfprintf(PurgeFP, fmt, argptr);
}
va_end(argptr);
}
/***************************************************************/
/* */
/* DoReminders */
@@ -134,8 +151,9 @@ static void DoReminders(void)
{
int r;
Token tok;
char *s;
char const *s;
Parser p;
int purge_handled;
if (!UseStdin) {
FileAccessDate = GetAccessDate(InitialFile);
@@ -148,7 +166,7 @@ static void DoReminders(void)
exit(1);
}
r=OpenFile(InitialFile);
r=IncludeFile(InitialFile);
if (r) {
fprintf(ErrFp, "%s %s: %s\n", ErrMsg[E_ERR_READING],
InitialFile, ErrMsg[r]);
@@ -173,23 +191,40 @@ static void DoReminders(void)
ShouldIgnoreLine())
{
/*** IGNORE THE LINE ***/
if (PurgeMode) {
if (strncmp(CurLine, "#!P", 3)) {
PurgeEchoLine("%s\n", CurLine);
}
}
}
else {
purge_handled = 0;
/* Create a parser to parse the line */
CreateParser(s, &p);
switch(tok.type) {
case T_Empty:
case T_Comment:
if (!strncmp(CurLine, "#!P", 3)) {
purge_handled = 1;
}
break;
case T_Rem: r=DoRem(&p); break;
case T_Rem: r=DoRem(&p); purge_handled = 1; break;
case T_ErrMsg: r=DoErrMsg(&p); break;
case T_If: r=DoIf(&p); break;
case T_IfTrig: r=DoIfTrig(&p); break;
case T_Else: r=DoElse(&p); break;
case T_EndIf: r=DoEndif(&p); break;
case T_Include: r=DoInclude(&p); break;
case T_Include:
/* In purge mode, include closes file, so we
need to echo it here! */
if (PurgeMode) {
PurgeEchoLine("%s\n", CurLine);
}
r=DoInclude(&p);
purge_handled = 1;
break;
case T_Exit: DoExit(&p); break;
case T_Flush: r=DoFlush(&p); break;
case T_Set: r=DoSet(&p); break;
@@ -204,32 +239,42 @@ static void DoReminders(void)
DestroyParser(&p);
CreateParser(s, &p);
r=DoRem(&p);
purge_handled = 1;
}
break;
case T_Pop: r=PopOmitContext(&p); break;
case T_Preserve: r=DoPreserve(&p); break;
case T_Push: r=PushOmitContext(&p); break;
case T_RemType: if (tok.val == RUN_TYPE) {
r=DoRun(&p);
r=DoRun(&p);
} else {
CreateParser(CurLine, &p);
r=DoRem(&p);
purge_handled = 1;
}
break;
} else {
CreateParser(CurLine, &p);
r=DoRem(&p);
break;
}
/* 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); r=DoRem(&p); break;
default: CreateParser(CurLine, &p); purge_handled = 1; r=DoRem(&p); break;
}
if (r && (!Hush || r != E_RUN_DISABLED)) {
Eprint("%s", ErrMsg[r]);
}
if (PurgeMode) {
if (!purge_handled) {
PurgeEchoLine("%s\n", CurLine);
} else {
if (r) {
PurgeEchoLine("#!P! Could not parse next line: %s\n", ErrMsg[r]);
PurgeEchoLine("%s\n", CurLine);
}
}
}
/* Destroy the parser - free up resources it may be tying up */
DestroyParser(&p);
}
@@ -330,7 +375,7 @@ int ParseChar(ParsePtr p, int *err, int peek)
return *(p->epos++);
}
}
free(p->etext); /* End of substituted expression */
free((void *) p->etext); /* End of substituted expression */
p->etext = NULL;
p->epos = NULL;
p->isnested = 0;
@@ -345,8 +390,9 @@ int ParseChar(ParsePtr p, int *err, int peek)
return *(p->pos++);
}
}
p->expr_happened = 1;
p->pos++;
r = EvalExpr(&(p->pos), &val);
r = EvalExpr(&(p->pos), &val, p);
if (r) {
*err = r;
DestroyParser(p);
@@ -382,7 +428,7 @@ int ParseNonSpaceChar(ParsePtr p, int *err, int peek)
ch = ParseChar(p, err, 1);
if (*err) return 0;
while (isspace(ch)) {
while (isempty(ch)) {
ParseChar(p, err, 0); /* Guaranteed to work */
ch = ParseChar(p, err, 1);
if (*err) return 0;
@@ -406,12 +452,12 @@ int ParseToken(ParsePtr p, DynamicBuffer *dbuf)
c = ParseChar(p, &err, 0);
if (err) return err;
while (c && isspace(c)) {
while (c && isempty(c)) {
c = ParseChar(p, &err, 0);
if (err) return err;
}
if (!c) return OK;
while (c && !isspace(c)) {
while (c && !isempty(c)) {
if (DBufPutc(dbuf, c) != OK) {
DBufFree(dbuf);
return E_NO_MEM;
@@ -442,7 +488,7 @@ int ParseIdentifier(ParsePtr p, DynamicBuffer *dbuf)
c = ParseChar(p, &err, 0);
if (err) return err;
while (c && isspace(c)) {
while (c && isempty(c)) {
c = ParseChar(p, &err, 0);
if (err) return err;
}
@@ -483,13 +529,13 @@ int EvaluateExpr(ParsePtr p, Value *v)
int r;
if (p->isnested) return E_PARSE_ERR; /* Can't nest expressions */
while (isspace(*p->pos)) (p->pos)++;
while (isempty(*p->pos)) (p->pos)++;
if (!p->pos) return E_PARSE_ERR; /* Missing expression */
if (*p->pos == BEG_OF_EXPR) {
(p->pos)++;
bracketed = 1;
}
r = EvalExpr(&(p->pos), v);
r = EvalExpr(&(p->pos), v, p);
if (r) return r;
if (bracketed) {
if (*p->pos != END_OF_EXPR) return E_MISS_END;
@@ -503,21 +549,23 @@ int EvaluateExpr(ParsePtr p, Value *v)
/* Eprint - print an error message. */
/* */
/***************************************************************/
void Eprint(const char *fmt, ...)
void Eprint(char const *fmt, ...)
{
va_list argptr;
/* Check if more than one error msg. from this line */
if (!FreshLine && !ShowAllErrors) return;
if (FreshLine) {
if (FreshLine && FileName) {
FreshLine = 0;
if (strcmp(FileName, "-"))
(void) fprintf(ErrFp, "%s(%d): ", FileName, LineNo);
else
(void) fprintf(ErrFp, "-stdin-(%d): ", LineNo);
if (DebugFlag & DB_PRTLINE) OutputLine(ErrFp);
} else fprintf(ErrFp, " ");
} else if (FileName) {
fprintf(ErrFp, " ");
}
va_start(argptr, fmt);
(void) vfprintf(ErrFp, fmt, argptr);
@@ -536,8 +584,8 @@ void Eprint(const char *fmt, ...)
/***************************************************************/
void OutputLine(FILE *fp)
{
register char *s = CurLine;
register char c = 0;
char const *s = CurLine;
char c = 0;
while (*s) {
if (*s == '\n') Putc('\\', fp);
@@ -554,7 +602,7 @@ void OutputLine(FILE *fp)
/* Create a parser given a string buffer */
/* */
/***************************************************************/
void CreateParser(char *s, ParsePtr p)
void CreateParser(char const *s, ParsePtr p)
{
p->text = s;
p->pos = s;
@@ -563,6 +611,8 @@ void CreateParser(char *s, ParsePtr p)
p->etext = NULL;
p->allownested = 1;
p->tokenPushed = NULL;
p->expr_happened = 0;
p->nonconst_expr = 0;
DBufInit(&p->pushedToken);
}
@@ -576,7 +626,7 @@ void CreateParser(char *s, ParsePtr p)
void DestroyParser(ParsePtr p)
{
if (p->isnested && p->etext) {
free(p->etext);
free((void *) p->etext);
p->etext = NULL;
p->isnested = 0;
}
@@ -589,10 +639,10 @@ void DestroyParser(ParsePtr p)
/* on a per-parser basis. */
/* */
/***************************************************************/
int PushToken(const char *tok, ParsePtr p)
int PushToken(char const *tok, ParsePtr p)
{
DBufFree(&p->pushedToken);
if (DBufPuts(&p->pushedToken, (char *) tok) != OK ||
if (DBufPuts(&p->pushedToken, tok) != OK ||
DBufPutc(&p->pushedToken, ' ') != OK) {
DBufFree(&p->pushedToken);
return E_NO_MEM;
@@ -666,10 +716,15 @@ int DoIf(ParsePtr p)
Eprint("%s", ErrMsg[r]);
} else
if ( (v.type != STR_TYPE && v.v.val) ||
(v.type == STR_TYPE && strcmp(v.v.str, "")) )
(v.type == STR_TYPE && strcmp(v.v.str, "")) ) {
syndrome = IF_TRUE | BEFORE_ELSE;
else
} else {
syndrome = IF_FALSE | BEFORE_ELSE;
if (PurgeMode) {
PurgeEchoLine("%s\n", "#!P: The next IF evaluated false...");
PurgeEchoLine("%s\n", "#!P: REM statements in IF block not checked for purging.");
}
}
}
NumIfs++;
@@ -689,6 +744,8 @@ int DoElse(ParsePtr p)
{
unsigned syndrome;
int was_ignoring = ShouldIgnoreLine();
if (!NumIfs) return E_ELSE_NO_IF;
syndrome = IfFlags >> (2 * NumIfs - 2);
@@ -696,6 +753,10 @@ int DoElse(ParsePtr p)
if ((syndrome & IF_ELSE_MASK) == AFTER_ELSE) return E_ELSE_NO_IF;
IfFlags |= AFTER_ELSE << (2 * NumIfs - 2);
if (PurgeMode && ShouldIgnoreLine() && !was_ignoring) {
PurgeEchoLine("%s\n", "#!P: The previous IF evaluated true.");
PurgeEchoLine("%s\n", "#!P: REM statements in ELSE block not checked for purging");
}
return VerifyEoln(p);
}
@@ -720,7 +781,7 @@ int DoEndif(ParsePtr p)
/***************************************************************/
int DoIfTrig(ParsePtr p)
{
int r;
int r, err;
unsigned syndrome;
Trigger trig;
TimeTrig tim;
@@ -730,15 +791,20 @@ int DoIfTrig(ParsePtr p)
if (NumIfs >= IF_NEST) return E_NESTED_IF;
if (ShouldIgnoreLine()) syndrome = IF_TRUE | BEFORE_ELSE;
else {
if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if ( (r=ParseRem(p, &trig, &tim, 1)) ) return r;
if (trig.typ != NO_TYPE) return E_PARSE_ERR;
jul = ComputeTrigger(trig.scanfrom, &trig, &r);
jul = ComputeTrigger(trig.scanfrom, &trig, &r, 1);
if (r) syndrome = IF_TRUE | BEFORE_ELSE;
else {
if (ShouldTriggerReminder(&trig, &tim, jul))
if (ShouldTriggerReminder(&trig, &tim, jul, &err)) {
syndrome = IF_TRUE | BEFORE_ELSE;
else
} else {
syndrome = IF_FALSE | BEFORE_ELSE;
if (PurgeMode) {
PurgeEchoLine("%s\n", "#!P: The next IFTRIG did not trigger.");
PurgeEchoLine("%s\n", "#!P: REM statements in IFTRIG block not checked for purging.");
}
}
}
}
NumIfs++;
@@ -859,6 +925,11 @@ int DoDebug(ParsePtr p)
else DebugFlag &= ~DB_PRTLINE;
break;
case 'f':
case 'F':
if (val) DebugFlag |= DB_TRACE_FILES;
else DebugFlag &= ~DB_TRACE_FILES;
break;
}
}
}
@@ -880,7 +951,7 @@ int DoBanner(ParsePtr p)
DBufInit(&buf);
c = ParseChar(p, &err, 0);
if (err) return err;
while (isspace(c)) {
while (isempty(c)) {
c = ParseChar(p, &err, 0);
if (err) return err;
}
@@ -960,6 +1031,8 @@ void DoExit(ParsePtr p)
int r;
Value v;
if (PurgeMode) return;
r = EvaluateExpr(p, &v);
if (r || v.type != INT_TYPE) exit(99);
exit(v.v.val);
@@ -977,10 +1050,12 @@ int DoErrMsg(ParsePtr p)
TimeTrig tt;
Trigger t;
int r;
char *s;
char const *s;
DynamicBuffer buf;
if (PurgeMode) return OK;
DBufInit(&buf);
t.typ = MSG_TYPE;
tt.ttime = SystemTime(0) / 60;
@@ -988,7 +1063,7 @@ int DoErrMsg(ParsePtr p)
return r;
}
s = DBufValue(&buf);
while (isspace(*s)) s++;
while (isempty(*s)) s++;
fprintf(ErrFp, "%s\n", s);
DBufFree(&buf);
return OK;
@@ -1079,7 +1154,7 @@ int CalcMinsFromUTC(int jul, int tim, int *mins, int *isdst)
/* A macro safe ONLY if used with arg with no side effects! */
#define ISBLANK(c) (isspace(c) && (c) != '\n')
void FillParagraph(char *s)
void FillParagraph(char const *s)
{
int line = 0;
@@ -1087,7 +1162,7 @@ void FillParagraph(char *s)
int doublespace = 1;
int pendspace;
int len;
char *t;
char const *t;
int roomleft;

View File

@@ -20,6 +20,7 @@
#include "protos.h"
#include "globals.h"
#include "err.h"
#include "expr.h"
static int BexistsIntArray (int array[], int num, int key);
static void InsertIntoSortedArray (int *array, int num, int key);
@@ -113,12 +114,12 @@ int PushOmitContext(ParsePtr p)
context->numfull = NumFullOmits;
context->numpart = NumPartialOmits;
context->fullsave = (int *) malloc(NumFullOmits * sizeof(int));
context->fullsave = malloc(NumFullOmits * sizeof(int));
if (NumFullOmits && !context->fullsave) {
free(context);
return E_NO_MEM;
}
context->partsave = (int *) malloc(NumPartialOmits * sizeof(int));
context->partsave = malloc(NumPartialOmits * sizeof(int));
if (NumPartialOmits && !context->partsave) {
free(context->fullsave);
free(context);
@@ -177,26 +178,57 @@ int PopOmitContext(ParsePtr p)
/* */
/* IsOmitted */
/* */
/* Return non-zero if date is OMITted, zero if it is not. */
/* Set *omit to non-zero if date is omitted, else 0. Returns */
/* OK or an error code. */
/* */
/***************************************************************/
int IsOmitted(int jul, int localomit)
int IsOmitted(int jul, int localomit, char const *omitfunc, int *omit)
{
int y, m, d;
/* If we have an omitfunc, we *only* use it and ignore local/global
OMITs */
if (omitfunc && *omitfunc && UserFuncExists(omitfunc)) {
char expr[VAR_NAME_LEN + 32];
char const *s;
int r;
Value v;
FromJulian(jul, &y, &m, &d);
sprintf(expr, "%s('%04d-%02d-%02d')",
omitfunc, y, m+1, d);
s = expr;
r = EvalExpr(&s, &v, NULL);
if (r) return r;
if (v.type == INT_TYPE && v.v.val != 0) {
*omit = 1;
} else {
*omit = 0;
}
return OK;
}
/* Is it omitted because of local omits? */
if (localomit & (1 << (jul % 7))) return 1;
if (localomit & (1 << (jul % 7))) {
*omit = 1;
return OK;
}
/* Is it omitted because of fully-specified omits? */
if (BexistsIntArray(FullOmitArray, NumFullOmits, jul)) return 1;
if (BexistsIntArray(FullOmitArray, NumFullOmits, jul)) {
*omit = 1;
return OK;
}
/* Get the syndrome */
FromJulian(jul, &y, &m, &d);
if (BexistsIntArray(PartialOmitArray, NumPartialOmits, (m << 5) + d))
return 1;
if (BexistsIntArray(PartialOmitArray, NumPartialOmits, (m << 5) + d)) {
*omit = 1;
return OK;
}
/* Not omitted */
return 0;
*omit = 0;
return OK;
}
/***************************************************************/
@@ -262,6 +294,14 @@ int DoOmit(ParsePtr p)
if ( (r=ParseToken(p, &buf)) ) return r;
FindToken(DBufValue(&buf), &tok);
switch (tok.type) {
case T_Date:
DBufFree(&buf);
if (y != NO_YR) return E_YR_TWICE;
if (m != NO_MON) return E_MON_TWICE;
if (d != NO_DAY) return E_DAY_TWICE;
FromJulian(tok.val, &y, &m, &d);
break;
case T_Year:
DBufFree(&buf);
if (y != NO_YR) return E_YR_TWICE;
@@ -279,7 +319,7 @@ int DoOmit(ParsePtr p)
if (d != NO_DAY) return E_DAY_TWICE;
d = tok.val;
break;
case T_Delta:
DBufFree(&buf);
break;

View File

@@ -14,51 +14,54 @@
#define STRSET(x, str) { if (x) free(x); (x) = StrDup(str); }
/* Define a general malloc routine for creating pointers to objects */
#define NEW(type) ((type *) malloc(sizeof(type)))
#define NEW(type) (malloc(sizeof(type)))
/* Characters to ignore */
#define isempty(c) (isspace(c) || ((c) == '\\'))
#include "dynbuf.h"
int CallUserFunc (char *name, int nargs);
int CallUserFunc (char const *name, int nargs, ParsePtr p);
int DoFset (ParsePtr p);
void ProduceCalendar (void);
char *SimpleTime (int tim);
char const *SimpleTime (int tim);
char const *CalendarTime (int tim, int duration);
int DoRem (ParsePtr p);
int DoFlush (ParsePtr p);
void DoExit (ParsePtr p);
int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim);
int ParseRem (ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals);
int TriggerReminder (ParsePtr p, Trigger *t, TimeTrig *tim, int jul);
int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int jul);
int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int jul, int *err);
int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int jul, int mode);
int DoSubstFromString (char *source, DynamicBuffer *dbuf, int jul, int tim);
int EvalExpr (char **e, Value *v);
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int jul, int tim);
int ParseLiteralDate (char const **s, int *jul, int *tim);
int EvalExpr (char const **e, Value *v, ParsePtr p);
int DoCoerce (char type, Value *v);
void PrintValue (Value *v, FILE *fp);
int CopyValue (Value *dest, const Value *src);
int ReadLine (void);
int OpenFile (const char *fname);
int PopFile (void);
int OpenFile (char const *fname);
int DoInclude (ParsePtr p);
int IncludeFile (const char *fname);
int GetAccessDate (char *file);
int SetAccessDate (char *fname, int jul);
int IncludeFile (char const *fname);
int GetAccessDate (char const *file);
int SetAccessDate (char const *fname, int jul);
int TopLevel (void);
int CallFunc (Operator *f, int nargs);
void InitRemind (int argc, char *argv[]);
int CallFunc (BuiltinFunc *f, int nargs);
void InitRemind (int argc, char const *argv[]);
void Usage (void);
int main (int argc, char *argv[]);
int Julian (int year, int month, int day);
void FromJulian (int jul, int *y, int *m, int *d);
int ParseChar (ParsePtr p, int *err, int peek);
int ParseToken (ParsePtr p, DynamicBuffer *dbuf);
int ParseIdentifier (ParsePtr p, DynamicBuffer *dbuf);
int EvaluateExpr (ParsePtr p, Value *v);
int Evaluate (char **s, Var *locals);
int Evaluate (char const **s, Var *locals, ParsePtr p);
int FnPopValStack (Value *val);
void Eprint (const char *fmt, ...);
void Eprint (char const *fmt, ...);
void OutputLine (FILE *fp);
void CreateParser (char *s, ParsePtr p);
void CreateParser (char const *s, ParsePtr p);
void DestroyParser (ParsePtr p);
int PushToken (const char *tok, ParsePtr p);
int PushToken (char const *tok, ParsePtr p);
long SystemTime (int realtime);
int SystemDate (int *y, int *m, int *d);
int DoIf (ParsePtr p);
@@ -76,60 +79,62 @@ int DoClear (ParsePtr p);
int DestroyOmitContexts (void);
int PushOmitContext (ParsePtr p);
int PopOmitContext (ParsePtr p);
int IsOmitted (int jul, int localomit);
int IsOmitted (int jul, int localomit, char const *omitfunc, int *omit);
int DoOmit (ParsePtr p);
int QueueReminder (ParsePtr p, Trigger *trig, TimeTrig *tim, const char *sched);
int QueueReminder (ParsePtr p, Trigger *trig, TimeTrig *tim, char const *sched);
void HandleQueuedReminders (void);
char *FindInitialToken (Token *tok, char *s);
void FindToken (const char *s, Token *tok);
void FindNumericToken (const char *s, Token *t);
int ComputeTrigger (int today, Trigger *trig, int *err);
char *StrnCpy (char *dest, const char *source, int n);
int StrMatch (const char *s1, const char *s2, int n);
int StrinCmp (const char *s1, const char *s2, int n);
char *StrDup (const char *s);
int StrCmpi (const char *s1, const char *s2);
Var *FindVar (const char *str, int create);
int DeleteVar (const char *str);
int SetVar (const char *str, Value *val);
int GetVarValue (const char *str, Value *val, Var *locals);
char const *FindInitialToken (Token *tok, char const *s);
void FindToken (char const *s, Token *tok);
void FindNumericToken (char const *s, Token *t);
int ComputeTrigger (int today, Trigger *trig, int *err, int save_in_globals);
char *StrnCpy (char *dest, char const *source, int n);
int StrMatch (char const *s1, char const *s2, int n);
int StrinCmp (char const *s1, char const *s2, int n);
char *StrDup (char const *s);
int StrCmpi (char const *s1, char const *s2);
Var *FindVar (char const *str, int create);
int DeleteVar (char const *str);
int SetVar (char const *str, Value *val);
int GetVarValue (char const *str, Value *val, Var *locals, ParsePtr p);
int DoSet (Parser *p);
int DoUnset (Parser *p);
int DoDump (ParsePtr p);
void DumpVarTable (void);
void DestroyVars (int all);
int PreserveVar (char *name);
int PreserveVar (char const *name);
int DoPreserve (Parser *p);
int DoSatRemind (Trigger *trig, TimeTrig *tim, ParsePtr p);
int DoMsgCommand (char *cmd, char *msg);
int DoMsgCommand (char const *cmd, char const *msg);
int ParseNonSpaceChar (ParsePtr p, int *err, int peek);
unsigned int HashVal (const char *str);
unsigned int HashVal (char const *str);
int DateOK (int y, int m, int d);
Operator *FindFunc (char *name, Operator where[], int num);
int InsertIntoSortBuffer (int jul, int tim, char *body, int typ, int prio);
void IssueSortedReminders (void);
int UserFuncExists (char *fn);
Operator *FindOperator (char const *name, Operator where[], int num);
BuiltinFunc *FindFunc (char const *name, BuiltinFunc where[], int num);
int InsertIntoSortBuffer (int jul, int tim, char const *body, int typ, int prio);
void IssueSortedReminders (void);
int UserFuncExists (char const *fn);
void JulToHeb (int jul, int *hy, int *hm, int *hd);
int HebNameToNum (const char *mname);
char *HebMonthName (int m, int y);
int HebNameToNum (char const *mname);
char const *HebMonthName (int m, int y);
int RoshHashana (int i);
long DaysToHebYear (int y);
int DaysInHebYear (int y);
char *DaysInHebMonths (int ylen);
char const *DaysInHebMonths (int ylen);
int HebToJul (int hy, int hm, int hd);
int GetValidHebDate (int yin, int min, int din, int adarbehave, int *mout, int *dout, int yahr);
int GetNextHebrewDate (int julstart, int hm, int hd, int yahr, int adarbehave, int *ans);
int ComputeJahr (int y, int m, int d, int *ans);
int GetSysVar (const char *name, Value *val);
int SetSysVar (const char *name, Value *val);
void DumpSysVarByName (const char *name);
int GetSysVar (char const *name, Value *val);
int SetSysVar (char const *name, Value *val);
void DumpSysVarByName (char const *name);
int CalcMinsFromUTC (int jul, int tim, int *mins, int *isdst);
void FillParagraph (char *s);
void FillParagraph (char const *s);
void LocalToUTC (int locdate, int loctime, int *utcdate, int *utctime);
void UTCToLocal (int utcdate, int utctime, int *locdate, int *loctime);
int MoonPhase (int date, int time);
void HuntPhase (int startdate, int starttim, int phas, int *date, int *time);
int CompareRems (int dat1, int tim1, int prio1, int dat2, int tim2, int prio2, int bydate, int bytime, int byprio);
int CompareRems (int dat1, int tim1, int prio1, int dat2, int tim2, int prio2, int bydate, int bytime, int byprio, int untimed_first);
void SigIntHandler (int d);
void GotSigInt (void);
void SynthesizeTag(char *);
void PurgeEchoLine(char const *fmt, ...);

View File

@@ -40,7 +40,8 @@ typedef struct queuedrem {
int typ;
int RunDisabled;
int ntrig;
char *text;
char const *text;
char passthru[PASSTHRU_LEN+1];
char sched[VAR_NAME_LEN+1];
char tag[TAG_LEN+1];
TimeTrig tt;
@@ -68,7 +69,7 @@ static void reread (void);
/* */
/***************************************************************/
int QueueReminder(ParsePtr p, Trigger *trig,
TimeTrig *tim, const char *sched)
TimeTrig *tim, char const *sched)
{
QueuedRem *qelem;
@@ -89,6 +90,7 @@ int QueueReminder(ParsePtr p, Trigger *trig,
}
NumQueued++;
qelem->typ = trig->typ;
strcpy(qelem->passthru, trig->passthru);
qelem->tt = *tim;
qelem->next = QueueHead;
qelem->RunDisabled = RunDisabled;
@@ -179,13 +181,30 @@ void HandleQueuedReminders(void)
if (Daemon > 0 && SleepTime > 60*Daemon) SleepTime = 60*Daemon;
/* Wake up once a minute to recalibrate sleep time in
case of laptop hibernation */
if (Daemon <= 0) {
if (SleepTime > 60) {
SleepTime = 60;
}
}
if (Daemon >= 0) {
sleep(SleepTime);
} else {
DaemonWait(SleepTime);
}
if (Daemon> 0 && SleepTime) CheckInitialFile();
/* If not in daemon mode and day has rolled around,
exit -- not much we can do. */
if (!Daemon) {
int y, m, d;
if (RealToday != SystemDate(&y, &m, &d)) {
exit(0);
}
}
if (Daemon > 0 && SleepTime) CheckInitialFile();
if (Daemon && !q) {
if (Daemon < 0) {
@@ -200,36 +219,45 @@ void HandleQueuedReminders(void)
}
/* Trigger the reminder */
CreateParser(q->text, &p);
trig.typ = q->typ;
RunDisabled = q->RunDisabled;
if (Daemon < 0) {
printf("NOTE reminder %s ",
SimpleTime(q->tt.ttime));
printf("%s ", SimpleTime(SystemTime(0)/60));
if (!*q->tag) {
printf("*");
} else {
printf("%s", q->tag);
/* Do NOT trigger the reminder if tt.nexttime is more than a
minute in the past. This can happen if the clock is
changed or a laptop awakes from hibernation.
However, DO triger if tt.nexttime == tt.ttime so all
queued reminders are triggered at least once. */
if ((SystemTime(0) - (q->tt.nexttime * 60) <= 60) ||
(q->tt.nexttime == q->tt.ttime)) {
/* Trigger the reminder */
CreateParser(q->text, &p);
trig.typ = q->typ;
strcpy(trig.passthru, q->passthru);
RunDisabled = q->RunDisabled;
if (Daemon < 0) {
printf("NOTE reminder %s",
SimpleTime(q->tt.ttime));
printf("%s", SimpleTime(SystemTime(0)/60));
if (!*q->tag) {
printf("*");
} else {
printf("%s", q->tag);
}
printf("\n");
}
printf("\n");
}
/* Set up global variables so some functions like trigdate()
and trigtime() work correctly */
LastTriggerDate = JulianToday;
LastTriggerTime = q->tt.ttime;
LastTrigValid = 1;
(void) TriggerReminder(&p, &trig, &q->tt, JulianToday);
if (Daemon < 0) {
printf("NOTE endreminder\n");
/* Set up global variables so some functions like trigdate()
and trigtime() work correctly */
LastTriggerDate = JulianToday;
LastTriggerTime = q->tt.ttime;
LastTrigValid = 1;
(void) TriggerReminder(&p, &trig, &q->tt, JulianToday);
if (Daemon < 0) {
printf("NOTE endreminder\n");
}
fflush(stdout);
DestroyParser(&p);
}
fflush(stdout);
/* Calculate the next trigger time */
q->tt.nexttime = CalculateNextTime(q);
DestroyParser(&p);
}
exit(0);
}
@@ -322,8 +350,11 @@ void GotSigInt(void)
q->tt.rep, q->tt.delta, q->sched);
if (*q->sched) printf("(%d)", q->ntrig+1);
printf("%s", NL);
printf("Text: %s %s%s%s", ((q->typ == MSG_TYPE) ? "MSG" :
((q->typ == MSF_TYPE) ? "MSF" :"RUN")),
printf("Text: %s %s%s%s%s%s", ((q->typ == MSG_TYPE) ? "MSG" :
((q->typ == MSF_TYPE) ? "MSF" :
((q->typ == RUN_TYPE) ? "RUN" : "SPECIAL"))),
q->passthru,
(*(q->passthru)) ? " " : "",
q->text,
NL, NL);
}
@@ -365,7 +396,7 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
/* Use LineBuffer for temp. string storage. */
int r;
Value v;
char *s;
char const *s;
int LastTime = -1;
int ThisTime;
@@ -380,7 +411,7 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
char exprBuf[VAR_NAME_LEN+32];
sprintf(exprBuf, "%s(%d)", q->sched, q->ntrig);
s = exprBuf;
r = EvalExpr(&s, &v);
r = EvalExpr(&s, &v, NULL);
if (r) {
q->sched[0] = 0;
return NO_TIME;
@@ -389,7 +420,11 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
ThisTime = v.v.val;
} else if (v.type == INT_TYPE) {
if (v.v.val > 0)
ThisTime = q->tt.nexttime + v.v.val;
if (LastTime >= 0) {
ThisTime = LastTime + v.v.val;
} else {
ThisTime = q->tt.nexttime + v.v.val;
}
else
ThisTime = q->tt.ttime + v.v.val;
@@ -400,6 +435,10 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
}
if (ThisTime < 0) ThisTime = 0; /* Can't be less than 00:00 */
if (ThisTime > (MINUTES_PER_DAY-1)) ThisTime = (MINUTES_PER_DAY-1); /* or greater than 11:59 */
if (DebugFlag & DB_PRTEXPR) {
fprintf(ErrFp, "SCHED: Considering %02d%c%02d\n",
ThisTime / 60, TimeSep, ThisTime % 60);
}
if (ThisTime > q->tt.nexttime) return ThisTime;
if (ThisTime <= LastTime) {
q->sched[0] = 0;
@@ -486,6 +525,6 @@ static void DaemonWait(unsigned int sleeptime)
/***************************************************************/
static void reread(void)
{
execvp(ArgV[0], ArgV);
execvp(ArgV[0], (char **) ArgV);
}

View File

@@ -10,6 +10,7 @@
/* */
/***************************************************************/
#include "version.h"
#include "config.h"
#include "dynbuf.h"
@@ -20,16 +21,27 @@
#include <unistd.h>
#include <stdlib.h>
#include "rem2ps.h"
#include "version.h"
#define NEW(type) ((type *) malloc(sizeof(type)))
#define NEW(type) (malloc(sizeof(type)))
#define SPECIAL_NORMAL 0
#define SPECIAL_POSTSCRIPT 1
#define SPECIAL_PSFILE 2
#define SPECIAL_MOON 3
#define SPECIAL_SHADE 4
#define SPECIAL_COLOR 5
#define SPECIAL_COLOR 4
#define SPECIAL_WEEK 5
#define SPECIAL_SHADE 6
/* Array holding how specials sort */
static int SpecialSortOrder[] = {
0, /* NORMAL */
1, /* POSTSCRIPT */
1, /* PSFILE */
2, /* MOON */
0, /* COLOR */
4, /* WEEK */
5 /* SHADE */
};
typedef struct calentry {
struct calentry *next;
@@ -38,13 +50,13 @@ typedef struct calentry {
} CalEntry;
typedef struct {
char *name;
char const *name;
int xsize, ysize;
} PageType;
char DayName[7][33];
char *SmallCalLoc[] = {
char const *SmallCalLoc[] = {
"",
"bt",
"tb",
@@ -52,7 +64,7 @@ char *SmallCalLoc[] = {
};
#define NUMSMALL (sizeof(SmallCalLoc)/sizeof(SmallCalLoc[0]))
char *SmallLocation;
char const *SmallLocation;
int SmallCol1, SmallCol2;
PageType Pages[] =
@@ -88,20 +100,20 @@ char PortraitMode;
char NoSmallCal;
char UseISO;
char *HeadFont="Helvetica";
char *TitleFont="Helvetica";
char *DayFont="Helvetica-BoldOblique";
char *EntryFont="Helvetica";
char *SmallFont="Helvetica";
char *LineWidth = "1";
char const *HeadFont="Helvetica";
char const *TitleFont="Helvetica";
char const *DayFont="Helvetica-BoldOblique";
char const *EntryFont="Helvetica";
char const *SmallFont="Helvetica";
char const *LineWidth = "1";
char *HeadSize="14";
char *TitleSize="14";
char *DaySize="14";
char *EntrySize="8";
char *BorderSize = "6";
char const *HeadSize="14";
char const *TitleSize="14";
char const *DaySize="14";
char const *EntrySize="8";
char const *BorderSize = "6";
char *UserProlog = NULL;
char const *UserProlog = NULL;
int validfile = 0;
@@ -116,15 +128,15 @@ int FillPage;
int Verbose = 0;
void Init (int argc, char *argv[]);
void Usage (char *s);
void Usage (char const *s);
void DoPsCal (void);
int DoQueuedPs (void);
void DoSmallCal (char *m, int days, int first, int col, int which);
void DoSmallCal (char const *m, int days, int first, int col, int which);
void WriteProlog (void);
void WriteCalEntry (void);
void WriteOneEntry (CalEntry *c);
void GetSmallLocations (void);
char *EatToken(char *in, char *out, int maxlen);
char const *EatToken(char const *in, char *out, int maxlen);
/***************************************************************/
/* */
@@ -182,10 +194,10 @@ void DoPsCal(void)
int i;
int is_ps;
int firstcol;
char *startOfBody;
char const *startOfBody;
char passthru[PASSTHRU_LEN+1];
DynamicBuffer buf;
CalEntry *c, *d;
CalEntry *c, *d, *p;
/* Read the month and year name, followed by # days in month and 1st day of
month */
@@ -306,6 +318,7 @@ void DoPsCal(void)
if (!strcmp(passthru, "PostScript") ||
!strcmp(passthru, "PSFile") ||
!strcmp(passthru, "MOON") ||
!strcmp(passthru, "WEEK") ||
!strcmp(passthru, "SHADE")) {
is_ps = 1;
}
@@ -324,6 +337,8 @@ void DoPsCal(void)
c->special = SPECIAL_SHADE;
} else if (!strcmp(passthru, "MOON")) {
c->special = SPECIAL_MOON;
} else if (!strcmp(passthru, "WEEK")) {
c->special = SPECIAL_WEEK;
} else {
c->special = SPECIAL_PSFILE;
}
@@ -332,8 +347,23 @@ void DoPsCal(void)
PsEntries[DayNum] = c;
} else {
d = PsEntries[DayNum];
while(d->next) d = d->next;
d->next = c;
p = NULL;
/* Slot it into the right place */
while (d->next && (SpecialSortOrder[c->special] <= SpecialSortOrder[d->special])) {
p = d;
d = d->next;
}
if (SpecialSortOrder[c->special] <= SpecialSortOrder[d->special]) {
c->next = d->next;
d->next = c;
} else {
if (p) {
p->next = c;
} else {
PsEntries[DayNum] = c;
}
c->next = d;
}
}
} else if (!strcmp(passthru, "*") ||
!strcmp(passthru, "COLOR")) {
@@ -388,7 +418,7 @@ void WriteProlog(void)
int i;
int x = CurPage->xsize;
int y = CurPage->ysize;
char *isostuff;
char const *isostuff;
FILE *fp;
int nread;
char buffer[512];
@@ -574,7 +604,7 @@ void WriteCalEntry(void)
void WriteOneEntry(CalEntry *c)
{
int ch, i;
char *s = c->entry;
char const *s = c->entry;
printf(" [");
@@ -635,7 +665,8 @@ void WriteOneEntry(CalEntry *c)
/***************************************************************/
void Init(int argc, char *argv[])
{
char *s, *t;
char const *s;
char const *t;
int i=1;
int j;
int offset;
@@ -791,7 +822,7 @@ void Init(int argc, char *argv[])
/* Usage - print usage information */
/* */
/***************************************************************/
void Usage(char *s)
void Usage(char const *s)
{
if (s) fprintf(stderr, "Rem2PS: %s\n\n", s);
@@ -820,7 +851,7 @@ void Usage(char *s)
/* month. */
/* */
/***************************************************************/
void DoSmallCal(char *m, int days, int first, int col, int which)
void DoSmallCal(char const *m, int days, int first, int col, int which)
{
/* Do the small calendar */
int i, j;
@@ -885,7 +916,8 @@ int DoQueuedPs(void)
FILE *fp;
int fnoff;
char buffer[512];
char *size, *extra;
char const *size, *extra;
char const *s;
int num, r, g, b, phase, fontsize, moonsize;
unsigned char c;
@@ -953,6 +985,22 @@ int DoQueuedPs(void)
r/255.0, g/255.0, b/255.0);
break;
case SPECIAL_WEEK: /* Week number */
printf("gsave Border Border 2 div moveto /EntryFont findfont EntrySize 1.2 div scalefont setfont (");
s = e->entry+fnoff;
while(*s && isspace(*s)) {
s++;
}
while(*s) {
if (*s == '\\' || *s == '(' || *s == ')') {
PutChar('\\');
}
PutChar(*s);
s++;
}
printf(") show grestore\n");
break;
case SPECIAL_MOON: /* Moon phase */
num = sscanf(e->entry+fnoff, "%d %d %d", &phase, &moonsize,
&fontsize);
@@ -1060,7 +1108,7 @@ int DoQueuedPs(void)
void GetSmallLocations(void)
{
char c;
char *s = SmallLocation;
char const *s = SmallLocation;
int colfirst, collast;
/* Figure out the first and last columns */
@@ -1122,7 +1170,7 @@ void GetSmallLocations(void)
/* Read a space-delimited token into an output buffer. */
/* */
/***************************************************************/
char *EatToken(char *in, char *out, int maxlen)
char const *EatToken(char const *in, char *out, int maxlen)
{
int i = 0;

View File

@@ -25,7 +25,7 @@
/* The structure of a sorted entry */
typedef struct sortrem {
struct sortrem *next;
char *text;
char const *text;
int trigdate;
int trigtime;
int typ;
@@ -35,7 +35,7 @@ typedef struct sortrem {
/* The sorted reminder queue */
static Sortrem *SortedQueue = (Sortrem *) NULL;
static Sortrem *MakeSortRem (int jul, int tim, char *body, int typ, int prio);
static Sortrem *MakeSortRem (int jul, int tim, char const *body, int typ, int prio);
static void IssueSortBanner (int jul);
/***************************************************************/
@@ -45,7 +45,7 @@ static void IssueSortBanner (int jul);
/* Create a new Sortrem entry - return NULL on failure. */
/* */
/***************************************************************/
static Sortrem *MakeSortRem(int jul, int tim, char *body, int typ, int prio)
static Sortrem *MakeSortRem(int jul, int tim, char const *body, int typ, int prio)
{
Sortrem *new = NEW(Sortrem);
if (!new) return NULL;
@@ -71,7 +71,7 @@ static Sortrem *MakeSortRem(int jul, int tim, char *body, int typ, int prio)
/* Insert a reminder into the sort buffer */
/* */
/***************************************************************/
int InsertIntoSortBuffer(int jul, int tim, char *body, int typ, int prio)
int InsertIntoSortBuffer(int jul, int tim, char const *body, int typ, int prio)
{
Sortrem *new = MakeSortRem(jul, tim, body, typ, prio);
Sortrem *cur = SortedQueue, *prev = NULL;
@@ -83,6 +83,7 @@ int InsertIntoSortBuffer(int jul, int tim, char *body, int typ, int prio)
SortByDate = 0;
SortByTime = 0;
SortByPrio = 0;
UntimedBeforeTimed = 0;
return E_NO_MEM;
}
@@ -94,8 +95,8 @@ int InsertIntoSortBuffer(int jul, int tim, char *body, int typ, int prio)
while (cur) {
ShouldGoAfter = CompareRems(new->trigdate, new->trigtime, new->priority,
cur->trigdate, cur->trigtime, cur->priority,
SortByDate, SortByTime, SortByPrio);
SortByDate, SortByTime, SortByPrio, UntimedBeforeTimed);
if (ShouldGoAfter <= 0) {
prev = cur;
cur = cur->next;
@@ -154,7 +155,7 @@ void IssueSortedReminders(void)
break;
}
free(cur->text);
free((char *) cur->text);
free(cur);
cur = next;
}
@@ -173,14 +174,14 @@ static void IssueSortBanner(int jul)
char BanExpr[64];
int y, m, d;
Value v;
char *s = BanExpr;
char const *s = BanExpr;
DynamicBuffer buf;
if (UserFuncExists("sortbanner") != 1) return;
FromJulian(jul, &y, &m, &d);
sprintf(BanExpr, "sortbanner('%04d/%02d/%02d')", y, m+1, d);
y = EvalExpr(&s, &v);
y = EvalExpr(&s, &v, NULL);
if (y) return;
if (DoCoerce(STR_TYPE, &v)) return;
DBufInit(&buf);
@@ -203,19 +204,27 @@ static void IssueSortBanner(int jul)
/***************************************************************/
int CompareRems(int dat1, int tim1, int prio1,
int dat2, int tim2, int prio2,
int bydate, int bytime, int byprio)
int bydate, int bytime, int byprio,
int untimed_first)
{
int dafter, tafter, pafter;
int dafter, tafter, pafter, uafter;
dafter = (bydate != SORT_DESCEND) ? 1 : -1;
tafter = (bytime != SORT_DESCEND) ? 1 : -1;
pafter = (byprio != SORT_DESCEND) ? 1 : -1;
uafter = (untimed_first) ? -1 : 1;
if (dat1 < dat2) return dafter;
if (dat1 > dat2) return -dafter;
if (tim1 == NO_TIME && tim2 != NO_TIME) return -1;
if (tim1 != NO_TIME && tim2 == NO_TIME) return 1;
if (tim1 == NO_TIME && tim2 != NO_TIME) {
return -uafter;
}
if (tim1 != NO_TIME && tim2 == NO_TIME) {
return uafter;
}
if (tim1 < tim2) return tafter;
if (tim1 > tim2) return -tafter;

View File

@@ -74,6 +74,7 @@ Token TokArray[] = {
{ "november", 3, T_Month, 10 },
{ "october", 3, T_Month, 9 },
{ "omit", 3, T_Omit, 0 },
{ "omitfunc", 8, T_OmitFunc, 0 },
{ "once", 3, T_Once, 0 },
{ "pop-omit-context", 3, T_Pop, 0 },
{ "preserve", 8, T_Preserve, 0 },
@@ -128,7 +129,7 @@ Token NonEnglishToks[] = {
};
#endif
static int TokStrCmp (const Token *t, const char *s);
static int TokStrCmp (Token const *t, char const *s);
/***************************************************************/
/* */
@@ -138,16 +139,16 @@ static int TokStrCmp (const Token *t, const char *s);
/* left square bracket, return a T_Illegal type. */
/* */
/***************************************************************/
char *FindInitialToken(Token *tok, char *s)
char const *FindInitialToken(Token *tok, char const *s)
{
DynamicBuffer buf;
DBufInit(&buf);
tok->type = T_Illegal;
while (isspace(*s)) s++;
while (isempty(*s)) s++;
while (*s && !isspace(*s)) {
while (*s && !isempty(*s)) {
if (DBufPutc(&buf, *s++) != OK) return s;
}
@@ -165,7 +166,7 @@ char *FindInitialToken(Token *tok, char *s)
/* Given a string, which token is it? */
/* */
/***************************************************************/
void FindToken(const char *s, Token *tok)
void FindToken(char const *s, Token *tok)
{
int top, bot, mid, r, max;
int l;
@@ -252,15 +253,33 @@ void FindToken(const char *s, Token *tok)
/* Rep - *n */
/* */
/***************************************************************/
void FindNumericToken(const char *s, Token *t)
void FindNumericToken(char const *s, Token *t)
{
int mult = 1, hour, min;
char const *s_orig = s;
t->type = T_Illegal;
t->val = 0;
if (isdigit(*s)) {
PARSENUM(t->val, s);
/* If we hit a '-' or a '/', we may have a date or a datetime */
if (*s == '-' || *s == '/') {
char const *p = s_orig;
int jul, tim;
if (ParseLiteralDate(&p, &jul, &tim) == OK) {
if (*p) return;
if (tim == NO_TIME) {
t->type = T_Date;
t->val = jul;
return;
}
t->type = T_DateTime;
t->val = MINUTES_PER_DAY * jul + tim;
}
return;
}
/* If we hit a comma, swallow it. This allows stuff
like Jan 6, 1998 */
if (*s == ',') {
@@ -335,10 +354,10 @@ void FindNumericToken(const char *s, Token *t)
/* Compare a token to a string. */
/* */
/***************************************************************/
static int TokStrCmp(const Token *t, const char *s)
static int TokStrCmp(Token const *t, char const *s)
{
register int r;
char *tk = t->name;
char const *tk = t->name;
while(*tk && *s && !(*s == ',' && *(s+1) == 0)) {
r = *tk - tolower(*s);
tk++;

View File

@@ -206,16 +206,27 @@ static int NextSimpleTrig(int startdate, Trigger *trig, int *err)
*err = E_BAD_DATE;
return -1;
}
/* Back up a year in case we'll cross a year boundary*/
if (y > BASE) {
y--;
}
/* Move up to the first valid year */
while (trig->d > DaysInMonth(trig->m, y)) y++;
/* Try last year */
j = Julian(y, trig->m, trig->d);
while(! (trig->wd & (1 << (j%7)))) j++;
if (j >= startdate) return j;
/* Try this year */
y++;
j = Julian(y, trig->m, trig->d);
while(! (trig->wd & (1 << (j%7)))) j++;
if (j >= startdate) return j;
/* Must be next year */
y = y + 1;
y++;
while (trig->d > DaysInMonth(trig->m, y)) y++;
j = Julian(y, trig->m, trig->d);
while(! (trig->wd & (1 << (j%7)))) j++;
@@ -286,16 +297,33 @@ static int JYear(int jul)
/***************************************************************/
static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart)
{
int simple, mod;
int simple, mod, omit;
/* First: Have we passed the UNTIL date? */
if (trig->until != NO_UNTIL &&
trig->until < start) return -1; /* expired */
trig->until < start) {
trig->expired = 1;
return -1; /* expired */
}
/* Next: If it's an "AFTER"-type skip, back up
until we're at the start of a block of holidays */
if (trig->skip == AFTER_SKIP)
while (IsOmitted(start-1, trig->localomit)) start--;
if (trig->skip == AFTER_SKIP) {
int iter = 0;
while (iter++ <= MaxSatIter) {
*err = IsOmitted(start-1, trig->localomit, trig->omitfunc, &omit);
if (*err) return -2;
if (!omit) {
break;
}
start--;
}
if (iter > MaxSatIter) {
/* omitfunc must have returned "true" too often */
*err = E_CANT_TRIG;
return -2;
}
}
/* Find the next simple trigger */
simple = NextSimpleTrig(start, trig, err);
@@ -309,12 +337,29 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
/* If there's a BACK, back up... */
if (trig->back != NO_BACK) {
mod = trig->back;
if (mod < 0) simple += mod;
else
while(mod) {
simple--;
if (!IsOmitted(simple, trig->localomit)) mod--;
if (mod < 0) {
simple += mod;
}
else {
int iter = 0;
int max = MaxSatIter;
if (max < mod*2) {
max = mod*2;
}
while(iter++ <= max) {
if (!mod) {
break;
}
simple--;
*err = IsOmitted(simple, trig->localomit, trig->omitfunc, &omit);
if (*err) return -2;
if (!omit) mod--;
}
if (iter > max) {
*err = E_CANT_TRIG;
return -2;
}
}
}
/* If there's a REP, calculate the next occurrence */
@@ -327,12 +372,38 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
}
/* If it's a "BEFORE"-type skip, back up */
if (trig->skip == BEFORE_SKIP)
while(IsOmitted(simple, trig->localomit)) simple--;
if (trig->skip == BEFORE_SKIP) {
int iter = 0;
while(iter++ <= MaxSatIter) {
*err = IsOmitted(simple, trig->localomit, trig->omitfunc, &omit);
if (*err) return -2;
if (!omit) {
break;
}
simple--;
}
if (iter > MaxSatIter) {
*err = E_CANT_TRIG;
return -2;
}
}
/* If it's an "AFTER"-type skip, jump ahead */
if (trig->skip == AFTER_SKIP)
while (IsOmitted(simple, trig->localomit)) simple++;
if (trig->skip == AFTER_SKIP) {
int iter = 0;
while (iter++ <= MaxSatIter) {
*err = IsOmitted(simple, trig->localomit, trig->omitfunc, &omit);
if (*err) return -2;
if (!omit) {
break;
}
simple++;
}
if (iter > MaxSatIter) {
*err = E_CANT_TRIG;
return -2;
}
}
/* Return the date */
return simple;
@@ -346,15 +417,16 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
/* today's date. */
/* */
/***************************************************************/
int ComputeTrigger(int today, Trigger *trig, int *err)
int ComputeTrigger(int today, Trigger *trig, int *err, int save_in_globals)
{
int nattempts = 0,
start = today,
nextstart,
y, m, d,
nextstart = 0,
y, m, d, omit,
result;
LastTrigValid = 0;
trig->expired = 0;
if (save_in_globals) LastTrigValid = 0;
/* Assume everything works */
*err = OK;
@@ -373,14 +445,15 @@ int ComputeTrigger(int today, Trigger *trig, int *err)
*err = E_REP_FULSPEC;
return -1;
}
while (nattempts++ < TRIG_ATTEMPTS) {
result = GetNextTriggerDate(trig, start, err, &nextstart);
/* If there's an error, die immediately */
if (*err) return -1;
if (result == -1) {
trig->expired = 1;
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",
FileName, LineNo, ErrMsg[E_EXPIRED]);
@@ -389,10 +462,18 @@ int ComputeTrigger(int today, Trigger *trig, int *err)
}
/* If result is >= today, great! */
if (trig->skip == SKIP_SKIP) {
*err = IsOmitted(result, trig->localomit, trig->omitfunc, &omit);
if (*err) return -1;
} else {
omit = 0;
}
if (result >= today &&
(trig->skip != SKIP_SKIP || !IsOmitted(result, trig->localomit))) {
LastTriggerDate = result; /* Save in global var */
LastTrigValid = 1;
(trig->skip != SKIP_SKIP || !omit)) {
if (save_in_globals) {
LastTriggerDate = result; /* Save in global var */
LastTrigValid = 1;
}
if (DebugFlag & DB_PRTTRIG) {
FromJulian(result, &y, &m, &d);
fprintf(ErrFp, "%s(%d): Trig = %s, %d %s, %d\n",
@@ -409,22 +490,36 @@ int ComputeTrigger(int today, Trigger *trig, int *err)
if (trig->back == NO_BACK &&
trig->skip == NO_SKIP &&
trig->rep == NO_REP) {
trig->expired = 1;
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",
FileName, LineNo, ErrMsg[E_EXPIRED]);
}
if (result != -1) {
LastTriggerDate = result;
LastTrigValid = 1;
if (save_in_globals) {
LastTriggerDate = result;
LastTrigValid = 1;
}
}
return -1;
}
if (trig->skip == SKIP_SKIP &&
omit &&
nextstart <= start &&
result >= start) {
nextstart = result + 1;
}
/* Keep scanning... unless there's no point in doing it.*/
if (nextstart <= start) {
if (result != -1) {
LastTriggerDate = result;
LastTrigValid = 1;
if (save_in_globals) {
LastTriggerDate = result;
LastTrigValid = 1;
}
}
trig->expired = 1;
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",
FileName, LineNo, ErrMsg[E_EXPIRED]);

View File

@@ -24,12 +24,27 @@ typedef struct {
/* Define the type of operators */
typedef struct {
char *name;
char const *name;
char prec;
char type;
int (*func)(void);
} Operator;
/* Structure for passing in Nargs and out RetVal from functions */
typedef struct {
int nargs;
Value retval;
} func_info;
/* Define the type of user-functions */
typedef struct {
char const *name;
char minargs;
char maxargs;
char is_constant;
int (*func)(func_info *);
} BuiltinFunc;
/* Define the structure of a variable */
typedef struct var {
struct var *next;
@@ -40,6 +55,7 @@ typedef struct var {
/* A trigger */
typedef struct {
int expired;
int wd;
int d;
int m;
@@ -56,6 +72,7 @@ typedef struct {
int priority;
char sched[VAR_NAME_LEN+1]; /* Scheduling function */
char warn[VAR_NAME_LEN+1]; /* Warning function */
char omitfunc[VAR_NAME_LEN+1]; /* OMITFUNC function */
char tag[TAG_LEN+1];
char passthru[PASSTHRU_LEN+1];
} Trigger;
@@ -71,14 +88,16 @@ typedef struct {
/* The parse pointer */
typedef struct {
char isnested; /* Is it a nested expression? */
char isnested; /* Is it a nested expression? */
char allownested;
char *text; /* Start of text */
char *pos; /* Current position */
char *etext; /* Substituted text */
char *epos; /* Position in substituted text */
char const *text; /* Start of text */
char const *pos; /* Current position */
char const *etext; /* Substituted text */
char const *epos; /* Position in substituted text */
DynamicBuffer pushedToken; /* Pushed-back token */
char *tokenPushed; /* NULL if no pushed-back token */
char const *tokenPushed; /* NULL if no pushed-back token */
char expr_happened; /* Did we encounter an [expression] ? */
char nonconst_expr; /* Did we encounter a non-constant [expression] ? */
} Parser;
typedef Parser *ParsePtr; /* Pointer to parser structure */
@@ -116,11 +135,12 @@ typedef Parser *ParsePtr; /* Pointer to parser structure */
/* DEFINES for debugging flags */
#define DB_PRTLINE 1
#define DB_PRTEXPR 2
#define DB_PRTTRIG 4
#define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16
#define DB_PRTLINE 1
#define DB_PRTEXPR 2
#define DB_PRTTRIG 4
#define DB_DUMP_VARS 8
#define DB_ECHO_LINE 16
#define DB_TRACE_FILES 32
/* Enumeration of the tokens */
enum TokTypes
@@ -130,7 +150,7 @@ enum TokTypes
T_IfTrig, T_ErrMsg,
T_Set, T_UnSet, T_Fset, T_Omit, T_Banner, T_Exit,
T_WkDay,
T_Month, T_Time,
T_Month, T_Time, T_Date, T_DateTime,
T_Skip, T_At, T_RemType, T_Until, T_Year, T_Day, T_Rep, T_Delta, T_Back,
T_Once,
T_Empty,
@@ -146,7 +166,8 @@ enum TokTypes
T_Warn,
T_Tag,
T_Duration,
T_LongTime
T_LongTime,
T_OmitFunc
};
/* The structure of a token */

View File

@@ -29,7 +29,7 @@
typedef struct udf_struct {
struct udf_struct *next;
char name[VAR_NAME_LEN+1];
char *text;
char const *text;
Var *locals;
char IsActive;
int nargs;
@@ -40,14 +40,14 @@ static UserFunc *FuncHash[FUNC_HASH_SIZE];
/* Access to built-in functions */
extern int NumFuncs;
extern Operator Func[];
extern BuiltinFunc Func[];
/* We need access to the expression evaluation stack */
extern Value ValStack[];
extern int ValStackPtr;
static void DestroyUserFunc (UserFunc *f);
static void FUnset (char *name);
static void FUnset (char const *name);
static void FSet (UserFunc *f);
static int SetUpLocalVars (UserFunc *f);
static void DestroyLocalVals (UserFunc *f);
@@ -142,6 +142,11 @@ int DoFset(ParsePtr p)
}
}
/* Allow an optional = sign: FSET f(x) = x*x */
c = ParseNonSpaceChar(p, &r, 1);
if (c == '=') {
c = ParseNonSpaceChar(p, &r, 0);
}
/* Copy the text over */
if (p->isnested) {
Eprint("%s", ErrMsg[E_CANTNEST_FDEF]);
@@ -184,7 +189,7 @@ static void DestroyUserFunc(UserFunc *f)
}
/* Free the function definition */
if (f->text) free(f->text);
if (f->text) free( (char *) f->text);
/* Free the data structure itself */
free(f);
@@ -198,7 +203,7 @@ static void DestroyUserFunc(UserFunc *f)
/* it exists. */
/* */
/***************************************************************/
static void FUnset(char *name)
static void FUnset(char const *name)
{
UserFunc *cur, *prev;
int h;
@@ -238,12 +243,12 @@ static void FSet(UserFunc *f)
/* Call a user-defined function. */
/* */
/***************************************************************/
int CallUserFunc(char *name, int nargs)
int CallUserFunc(char const *name, int nargs, ParsePtr p)
{
UserFunc *f;
int h = HashVal(name) % FUNC_HASH_SIZE;
int i;
char *s;
char const *s;
/* Search for the function */
f = FuncHash[h];
@@ -294,9 +299,9 @@ int CallUserFunc(char *name, int nargs)
s = f->text;
/* Skip the opening bracket, if there's one */
while (isspace(*s)) s++;
while (isempty(*s)) s++;
if (*s == BEG_OF_EXPR) s++;
h = Evaluate(&s, f->locals);
h = Evaluate(&s, f->locals, p);
f->IsActive = 0;
DestroyLocalVals(f);
if (DebugFlag &DB_PRTEXPR) {
@@ -356,7 +361,7 @@ static void DestroyLocalVals(UserFunc *f)
/* it is defined, or -1 if it is not defined. */
/* */
/***************************************************************/
int UserFuncExists(char *fn)
int UserFuncExists(char const *fn)
{
UserFunc *f;
int h = HashVal(fn) % FUNC_HASH_SIZE;

View File

@@ -30,9 +30,9 @@
/* Just like strncpy EXCEPT we ALWAYS copy the trailing 0. */
/* */
/***************************************************************/
char *StrnCpy(char *dest, const char *source, int n)
char *StrnCpy(char *dest, char const *source, int n)
{
register char *odest = dest;
char *odest = dest;
while (n-- && (*dest++ = *source++)) ;
if (*(dest-1)) *dest = 0;
@@ -48,7 +48,7 @@ char *StrnCpy(char *dest, const char *source, int n)
/* of the first string, whichever is greater. */
/* */
/***************************************************************/
int StrMatch(const char *s1, const char *s2, int n)
int StrMatch(char const *s1, char const *s2, int n)
{
int l;
if ((l = strlen(s1)) < n) return 0;
@@ -60,7 +60,7 @@ int StrMatch(const char *s1, const char *s2, int n)
/* StrinCmp - compare strings, case-insensitive */
/* */
/***************************************************************/
int StrinCmp(const char *s1, const char *s2, int n)
int StrinCmp(char const *s1, char const *s2, int n)
{
register int r;
while (n && *s1 && *s2) {
@@ -80,10 +80,10 @@ int StrinCmp(const char *s1, const char *s2, int n)
/* Like ANSI strdup */
/* */
/***************************************************************/
char *StrDup(const char *s)
char *StrDup(char const *s)
{
char *ret = (char *) malloc(strlen(s)+1);
if (!ret) return (char *) NULL;
char *ret = malloc(strlen(s)+1);
if (!ret) return NULL;
strcpy(ret, s);
return ret;
}
@@ -95,7 +95,7 @@ char *StrDup(const char *s)
/* Compare strings, case insensitive. */
/* */
/***************************************************************/
int StrCmpi(const char *s1, const char *s2)
int StrCmpi(char const *s1, char const *s2)
{
int r;
while (*s1 && *s2) {

243
src/var.c
View File

@@ -36,6 +36,110 @@ static Var *VHashTbl[VAR_HASH_SIZE];
typedef int (*SysVarFunc)(int, Value *);
static int trig_date_func(int do_set, Value *val)
{
val->type = DATE_TYPE;
if (!LastTrigValid) {
val->v.val = 0;
} else {
val->v.val = LastTriggerDate;
}
return OK;
}
static int trig_day_func(int do_set, Value *val)
{
int y, m, d;
val->type = INT_TYPE;
if (!LastTrigValid) {
val->v.val = -1;
return OK;
}
FromJulian(LastTriggerDate, &y, &m, &d);
val->v.val = d;
return OK;
}
static int trig_mon_func(int do_set, Value *val)
{
int y, m, d;
val->type = INT_TYPE;
if (!LastTrigValid) {
val->v.val = -1;
return OK;
}
FromJulian(LastTriggerDate, &y, &m, &d);
val->v.val = m+1;
return OK;
}
static int trig_year_func(int do_set, Value *val)
{
int y, m, d;
val->type = INT_TYPE;
if (!LastTrigValid) {
val->v.val = -1;
return OK;
}
FromJulian(LastTriggerDate, &y, &m, &d);
val->v.val = y;
return OK;
}
static int trig_wday_func(int do_set, Value *val)
{
val->type = INT_TYPE;
if (!LastTrigValid) {
val->v.val = -1;
return OK;
}
val->v.val = (LastTriggerDate + 1) % 7;
return OK;
}
static int today_date_func(int do_set, Value *val)
{
val->type = DATE_TYPE;
val->v.val = JulianToday;
return OK;
}
static int today_day_func(int do_set, Value *val)
{
int y, m, d;
val->type = INT_TYPE;
FromJulian(JulianToday, &y, &m, &d);
val->v.val = d;
return OK;
}
static int today_mon_func(int do_set, Value *val)
{
int y, m, d;
val->type = INT_TYPE;
FromJulian(JulianToday, &y, &m, &d);
val->v.val = m+1;
return OK;
}
static int today_year_func(int do_set, Value *val)
{
int y, m, d;
val->type = INT_TYPE;
FromJulian(JulianToday, &y, &m, &d);
val->v.val = y;
return OK;
}
static int today_wday_func(int do_set, Value *val)
{
val->type = INT_TYPE;
val->v.val = (JulianToday + 1) % 7;
return OK;
}
static int date_sep_func(int do_set, Value *val)
{
if (!do_set) {
@@ -80,7 +184,7 @@ static int time_sep_func(int do_set, Value *val)
/* Given a string, compute the hash value. */
/* */
/***************************************************************/
unsigned int HashVal(const char *str)
unsigned int HashVal(char const *str)
{
register unsigned int i=0;
register unsigned int j=1;
@@ -102,7 +206,7 @@ unsigned int HashVal(const char *str)
/* string. If create is 1, create the variable. */
/* */
/***************************************************************/
Var *FindVar(const char *str, int create)
Var *FindVar(char const *str, int create)
{
register int h;
register Var *v;
@@ -139,7 +243,7 @@ Var *FindVar(const char *str, int create)
/* string and delete it. */
/* */
/***************************************************************/
int DeleteVar(const char *str)
int DeleteVar(char const *str)
{
register int h;
register Var *v;
@@ -168,7 +272,7 @@ int DeleteVar(const char *str)
/* Set the indicate variable to the specified value. */
/* */
/***************************************************************/
int SetVar(const char *str, Value *val)
int SetVar(char const *str, Value *val)
{
Var *v = FindVar(str, 1);
@@ -186,7 +290,7 @@ int SetVar(const char *str, Value *val)
/* Get a copy of the value of the variable. */
/* */
/***************************************************************/
int GetVarValue(const char *str, Value *val, Var *locals)
int GetVarValue(char const *str, Value *val, Var *locals, ParsePtr p)
{
Var *v;
@@ -198,6 +302,8 @@ int GetVarValue(const char *str, Value *val, Var *locals)
v = v->next;
}
/* Global variable... mark expression as non-constant */
if (p) p->nonconst_expr = 1;
v=FindVar(str, 0);
if (!v) {
@@ -223,6 +329,11 @@ int DoSet (Parser *p)
r = ParseIdentifier(p, &buf);
if (r) return r;
/* Allow optional equals-sign: SET var = value */
if (ParseNonSpaceChar(p, &r, 1) == '=') {
ParseNonSpaceChar(p, &r, 0);
}
r = EvaluateExpr(p, &v);
if (r) {
DBufFree(&buf);
@@ -281,6 +392,8 @@ int DoDump(ParsePtr p)
Var *v;
DynamicBuffer buf;
if (PurgeMode) return OK;
DBufInit(&buf);
r = ParseToken(p, &buf);
if (r) return r;
@@ -383,7 +496,7 @@ void DestroyVars(int all)
/* Given the name of a variable, "preserve" it. */
/* */
/***************************************************************/
int PreserveVar(char *name)
int PreserveVar(char const *name)
{
Var *v;
@@ -440,7 +553,7 @@ int DoPreserve (Parser *p)
/* The structure of a system variable */
typedef struct {
char *name;
char const *name;
char modifiable;
int type;
void *value;
@@ -457,49 +570,62 @@ typedef struct {
/* All of the system variables sorted alphabetically */
static SysVar SysVarArr[] = {
/* name mod type value min/mal max */
{ "CalcUTC", 1, INT_TYPE, &CalculateUTC, 0, 1 },
{ "CalMode", 0, INT_TYPE, &DoCalendar, 0, 0 },
{ "Daemon", 0, INT_TYPE, &Daemon, 0, 0 },
{ "DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0 },
{ "DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999 },
{ "DontFork", 0, INT_TYPE, &DontFork, 0, 0 },
{ "DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 },
{ "DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 },
{ "EndSent", 1, STR_TYPE, &EndSent, 0, 0 },
{ "EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 },
{ "FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 },
{ "FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 },
{ "FormWidth", 1, INT_TYPE, &FormWidth, 20, 132 },
{ "HushMode", 0, INT_TYPE, &Hush, 0, 0 },
{ "IgnoreOnce", 0, INT_TYPE, &IgnoreOnce, 0, 0 },
{ "InfDelta", 0, INT_TYPE, &InfiniteDelta, 0, 0 },
{ "LatDeg", 1, INT_TYPE, &LatDeg, -90, 90 },
{ "LatMin", 1, INT_TYPE, &LatMin, -59, 59 },
{ "LatSec", 1, INT_TYPE, &LatSec, -59, 59 },
{ "Location", 1, STR_TYPE, &Location, 0, 0 },
{ "LongDeg", 1, INT_TYPE, &LongDeg, -180, 180 },
{ "LongMin", 1, INT_TYPE, &LongMin, -59, 59 },
{ "LongSec", 1, INT_TYPE, &LongSec, -59, 59 },
{ "MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY },
{ "MinsFromUTC", 1, INT_TYPE, &MinsFromUTC, -13*60, 13*60 },
{ "NextMode", 0, INT_TYPE, &NextMode, 0, 0 },
{ "NumQueued", 0, INT_TYPE, &NumQueued, 0, 0 },
{ "NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 },
{ "PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo,0, 0 },
{ "PSCal", 0, INT_TYPE, &PsCal, 0, 0 },
{ "RunOff", 0, INT_TYPE, &RunDisabled, 0, 0 },
{ "SimpleCal", 0, INT_TYPE, &DoSimpleCalendar, 0, 0 },
{ "SortByDate", 0, INT_TYPE, &SortByDate, 0, 0},
{ "SortByPrio", 0, INT_TYPE, &SortByPrio, 0, 0},
{ "SortByTime", 0, INT_TYPE, &SortByTime, 0, 0},
{ "SubsIndent", 1, INT_TYPE, &SubsIndent, 0, 132},
{ "TimeSep", 1, SPECIAL_TYPE, time_sep_func, 0, 0 }
/* name mod type value min/mal max */
{"CalcUTC", 1, INT_TYPE, &CalculateUTC, 0, 1 },
{"CalMode", 0, INT_TYPE, &DoCalendar, 0, 0 },
{"Daemon", 0, INT_TYPE, &Daemon, 0, 0 },
{"DateSep", 1, SPECIAL_TYPE, date_sep_func, 0, 0 },
{"DefaultPrio", 1, INT_TYPE, &DefaultPrio, 0, 9999},
{"DeltaOffset", 0, INT_TYPE, &DeltaOffset, 0, 0 },
{"DontFork", 0, INT_TYPE, &DontFork, 0, 0 },
{"DontQueue", 0, INT_TYPE, &DontQueue, 0, 0 },
{"DontTrigAts", 0, INT_TYPE, &DontIssueAts, 0, 0 },
{"EndSent", 1, STR_TYPE, &EndSent, 0, 0 },
{"EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 },
{"FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 },
{"FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 },
{"FormWidth", 1, INT_TYPE, &FormWidth, 20, 132 },
{"HushMode", 0, INT_TYPE, &Hush, 0, 0 },
{"IgnoreOnce", 0, INT_TYPE, &IgnoreOnce, 0, 0 },
{"InfDelta", 0, INT_TYPE, &InfiniteDelta, 0, 0 },
{"LatDeg", 1, INT_TYPE, &LatDeg, -90, 90 },
{"LatMin", 1, INT_TYPE, &LatMin, -59, 59 },
{"LatSec", 1, INT_TYPE, &LatSec, -59, 59 },
{"Location", 1, STR_TYPE, &Location, 0, 0 },
{"LongDeg", 1, INT_TYPE, &LongDeg, -180, 180 },
{"LongMin", 1, INT_TYPE, &LongMin, -59, 59 },
{"LongSec", 1, INT_TYPE, &LongSec, -59, 59 },
{"MaxSatIter", 1, INT_TYPE, &MaxSatIter, 10, ANY },
{"MaxStringLen", 1, INT_TYPE, &MaxStringLen, -1, ANY },
{"MinsFromUTC", 1, INT_TYPE, &MinsFromUTC, -780, 780 },
{"NextMode", 0, INT_TYPE, &NextMode, 0, 0 },
{"NumQueued", 0, INT_TYPE, &NumQueued, 0, 0 },
{"NumTrig", 0, INT_TYPE, &NumTriggered, 0, 0 },
{"PrefixLineNo", 0, INT_TYPE, &DoPrefixLineNo, 0, 0 },
{"PSCal", 0, INT_TYPE, &PsCal, 0, 0 },
{"RunOff", 0, INT_TYPE, &RunDisabled, 0, 0 },
{"SimpleCal", 0, INT_TYPE, &DoSimpleCalendar, 0, 0 },
{"SortByDate", 0, INT_TYPE, &SortByDate, 0, 0 },
{"SortByPrio", 0, INT_TYPE, &SortByPrio, 0, 0 },
{"SortByTime", 0, INT_TYPE, &SortByTime, 0, 0 },
{"SubsIndent", 1, INT_TYPE, &SubsIndent, 0, 132 },
{"T", 0, SPECIAL_TYPE, trig_date_func, 0, 0 },
{"Td", 0, SPECIAL_TYPE, trig_day_func, 0, 0 },
{"TimeSep", 1, SPECIAL_TYPE, time_sep_func, 0, 0 },
{"Tm", 0, SPECIAL_TYPE, trig_mon_func, 0, 0 },
{"Tw", 0, SPECIAL_TYPE, trig_wday_func, 0, 0 },
{"Ty", 0, SPECIAL_TYPE, trig_year_func, 0, 0 },
{"U", 0, SPECIAL_TYPE, today_date_func, 0, 0 },
{"Ud", 0, SPECIAL_TYPE, today_day_func, 0, 0 },
{"Um", 0, SPECIAL_TYPE, today_mon_func, 0, 0 },
{"UntimedFirst", 0, INT_TYPE, &UntimedBeforeTimed, 0, 0 },
{"Uw", 0, SPECIAL_TYPE, today_wday_func, 0, 0 },
{"Uy", 0, SPECIAL_TYPE, today_year_func, 0, 0 }
};
#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) )
static SysVar *FindSysVar (const char *name);
static void DumpSysVar (const char *name, const SysVar *v);
static SysVar *FindSysVar (char const *name);
static void DumpSysVar (char const *name, const SysVar *v);
/***************************************************************/
/* */
/* SetSysVar */
@@ -507,7 +633,7 @@ static void DumpSysVar (const char *name, const SysVar *v);
/* Set a system variable to the indicated value. */
/* */
/***************************************************************/
int SetSysVar(const char *name, Value *value)
int SetSysVar(char const *name, Value *value)
{
SysVar *v = FindSysVar(name);
if (!v) return E_NOSUCH_VAR;
@@ -542,7 +668,7 @@ int SetSysVar(const char *name, Value *value)
/* Get the value of a system variable */
/* */
/***************************************************************/
int GetSysVar(const char *name, Value *val)
int GetSysVar(char const *name, Value *val)
{
SysVar *v = FindSysVar(name);
@@ -577,7 +703,7 @@ int GetSysVar(const char *name, Value *val)
/* Find a system var with specified name. */
/* */
/***************************************************************/
static SysVar *FindSysVar(const char *name)
static SysVar *FindSysVar(char const *name)
{
int top=NUMSYSVARS-1, bottom=0;
int mid=(top + bottom) / 2;
@@ -601,7 +727,7 @@ static SysVar *FindSysVar(const char *name)
/* If name is "", dump all system variables. */
/* */
/***************************************************************/
void DumpSysVarByName(const char *name)
void DumpSysVarByName(char const *name)
{
int i;
SysVar *v;
@@ -623,7 +749,7 @@ void DumpSysVarByName(const char *name)
/* Dump the system variable. */
/* */
/***************************************************************/
static void DumpSysVar(const char *name, const SysVar *v)
static void DumpSysVar(char const *name, const SysVar *v)
{
char buffer[VAR_NAME_LEN+10];
@@ -638,12 +764,11 @@ static void DumpSysVar(const char *name, const SysVar *v)
Value val;
SysVarFunc f = (SysVarFunc) v->value;
f(0, &val);
if (DoCoerce(STR_TYPE, &val) == OK) {
fprintf(ErrFp, "\"%s\"\n", val.v.str);
}
PrintValue(&val, ErrFp);
Putc('\n', ErrFp);
DestroyValue(val);
} else if (v->type == STR_TYPE) {
char *s = *((char **)v->value);
char const *s = *((char **)v->value);
int y;
Putc('"', ErrFp);
for (y=0; y<MAX_PRT_LEN && *s; y++) {
@@ -657,6 +782,12 @@ static void DumpSysVar(const char *name, const SysVar *v)
Putc('"', ErrFp);
if (*s) fprintf(ErrFp, "...");
Putc('\n', ErrFp);
} else if (v->type == DATE_TYPE) {
Value val;
val.type = DATE_TYPE;
val.v.val = * (int *) v->value;
PrintValue(&val, ErrFp);
Putc('\n', ErrFp);
} else {
if (!v->modifiable) fprintf(ErrFp, "%d\n", *((int *)v->value));
else {

View File

@@ -1,13 +0,0 @@
/***************************************************************/
/* */
/* VERSION.H */
/* */
/* What version of remind do we have? */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992-1998 by David F. Skoll */
/* Copyright (C) 1999-2007 by Roaring Penguin Software Inc. */
/* */
/***************************************************************/
#define VERSION "03.01.01"

1
src/version.h.in Normal file
View File

@@ -0,0 +1 @@
#define VERSION "@VERSION@"

17
tests/colors.rem Normal file
View File

@@ -0,0 +1,17 @@
REM 28 SPECIAL COLOR 0 0 0 Black
REM 28 SPECIAL COLOR 65 0 0 Dim Red
REM 28 SPECIAL COLOR 0 65 0 Dim Green
REM 28 SPECIAL COLOR 0 0 65 Dim Blue
REM 28 SPECIAL COLOR 0 65 65 Dim Cyan
REM 28 SPECIAL COLOR 65 0 65 Dim Magenta
REM 28 SPECIAL COLOR 65 65 0 Dim Yellow
REM 28 SPECIAL COLOR 65 65 65 Dim White
REM 28 SPECIAL COLOR 129 0 0 Bright Red
REM 28 SPECIAL COLOR 0 129 0 Bright Green
REM 28 SPECIAL COLOR 0 0 129 Bright Blue
REM 28 SPECIAL COLOR 0 129 129 Bright Cyan
REM 28 SPECIAL COLOR 129 0 129 Bright Magenta
REM 28 SPECIAL COLOR 129 129 0 Bright Yellow
REM 28 SPECIAL COLOR 129 129 129 Bright White

1
tests/file.ps Normal file
View File

@@ -0,0 +1 @@
(Second-Bit-Of-PS)

1
tests/file2.ps Normal file
View File

@@ -0,0 +1 @@
(Fourth-Bit-Of-PS)

1
tests/include_dir/01.rem Normal file
View File

@@ -0,0 +1 @@
REM 15 MSG 01

1
tests/include_dir/02.rem Normal file
View File

@@ -0,0 +1 @@
REM 15 MSG 02

View File

@@ -0,0 +1 @@
REM MSG IGNORE

View File

@@ -0,0 +1 @@
REM 15 MSG You can't read this file.

View File

@@ -0,0 +1 @@
REM MSG IGNORE

5
tests/include_test.rem Normal file
View File

@@ -0,0 +1,5 @@
INCLUDE include_dir
INCLUDE include_dir_no_rems
INCLUDE nonexistent_include_dir
REM 15 MSG Whee!!!!

7
tests/purge_dir/f1.rem Normal file
View File

@@ -0,0 +1,7 @@
# This is f1.rem
INCLUDE [filedir()]/f2.rem
INCLUDE [filedir()]/f2.rem
REM 1 Oct 1991 MSG old1.
REM Monday UNTIL 1 Oct 1991 MSG old2.

6
tests/purge_dir/f2.rem Normal file
View File

@@ -0,0 +1,6 @@
# This is f2.rem
REM 3 feb 2012 MSG new
REM 3 1998 MSG old
INCLUDE [filedir()]/f3.rem

92
tests/purge_dir/f3.rem Normal file
View File

@@ -0,0 +1,92 @@
# This is f3.rem
REM Mon MSG repeat
REM Mon SATISFY [1] MSG repeat
IF 0
REM 1991 MSG wookie
ENDIF
IF 1
REM 1991 MSG wookie
ENDIF
IFTRIG 1991
REM MSG wookie
ENDIF
# More complex conditional statements
IF 1
IF 0
REM 1991 MSG wookie
ELSE
REM 1991 MSG wookie
ENDIF
ELSE
IF 1
REM 1991 MSG wookie
ELSE
REM 1991 MSG wookie
ENDIF
ENDIF
REM [1990+1] MSG old-with-constant-expression
REM [1990+1] \
MSG Continued line-old-with-constant-expression
REM 1990 \
MSG expired-continued-line
set y 1990
REM [y+1] MSG old-with-nonconstant-expression
# A comment that should be preserved
#!P A comment that should be nuked because it \
starts with #!P
REM [y+1] \
MSG Continued-line-old-with-nonconstant-expression
OMIT 25 Dec MSG woaaahh!
OMIT 24 Dec
OMIT 1 Jan 1992 MSG woaaahah... expired
OMIT 2 Jan 1992
# Complicated expressions
SET a 3
FSET const(x) x+3
FSET nonconst(x) x+a
REM [const(5)] Jan 1992 MSG expired... should be commented out
REM [const(a)] Jan 1992 MSG nonconstant expression
REM [nonconst(5)] Jan 1992 MSG nonconstant expression
REM [value("a")] Jan 1992 MSG nonconstant expression
IF 0
# A comment in a false IF block
#!P This should be nuked
ENDIF
# Busted line
REM [0/0] Jan 1992 MSG ouch
ERRMSG blorky
FLUSH
SET a 1
FSET a(x) x*x
UNSET a
CLEAR-OMIT-CONTEXT
PUSH-OMIT-CONTEXT
POP-OMIT-CONTEXT
BANNER wow
DEBUG +x
DEBUG -x
DUMP $
EXIT 0
PRESERVE i

3
tests/runinc.rem Normal file
View File

@@ -0,0 +1,3 @@
set s shell("echo 3")
run on
set s shell("echo 3")

6
tests/runtest.rem Normal file
View File

@@ -0,0 +1,6 @@
run off
set a shell("echo 2")
run on
set a shell("echo 2")
run off
include ../tests/runinc.rem

30
tests/shade.rem Normal file
View File

@@ -0,0 +1,30 @@
set $MinsFromUTC -240
set $CalcUTC 0
set $Location "Ottawa"
set $LongDeg 75
set $LongMin 39
set $LongSec 0
set $LatDeg 45
set $LatMin 24
set $LatSec 0
IF $PSCAL
[trigger(moondate(0))] SPECIAL MOON 0 -1 -1 [moontime(0)]
[trigger(moondate(1))] SPECIAL MOON 1 -1 -1 [moontime(1)]
[trigger(moondate(2))] SPECIAL MOON 2 -1 -1 [moontime(2)]
[trigger(moondate(3))] SPECIAL MOON 3 -1 -1 [moontime(3)]
ENDIF
REM 4 PS (First-Bit-Of-PS)
REM 4 PSFILE file.ps
REM 4 PS (Third-Bit-Of-PS)
REM 4 PSFILE file2.ps
REM Mon SPECIAL SHADE 255 255 255
REM Tue SPECIAL SHADE 255 255 204
REM Wed SPECIAL SHADE 255 204 255
REM Thu SPECIAL SHADE 204 255 255
REM Fri SPECIAL SHADE 255 204 204
REM Sat SPECIAL SHADE 204 255 204
REM Sun SPECIAL SHADE 204 204 255

View File

@@ -11,8 +11,76 @@
# Copyright (C) 1999-2000 Roaring Penguin Software Inc.
# ---------------------------------------------------------------------------
DIR=`dirname $0`
cd $DIR
if test $? != 0 ; then
echo "Unable to cd $DIR" >&2
exit 1
fi
chmod 000 include_dir/04cantread.rem
TEST_GETENV="foo bar baz" ; export TEST_GETENV
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 > ../tests/test.out
echo "Test 1" > ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -e -dxte ../tests/test.rem 16 feb 1991 >> ../tests/test.out
echo "" >> ../tests/test.out
echo "Test 2" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l ../tests/test2.rem 1 aug 2007 >> ../tests/test.out
echo "" >> ../tests/test.out
echo "Test 3" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -s ../tests/test2.rem 1 aug 2007 >> ../tests/test.out
echo "" >> ../tests/test.out
echo "Test 4" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -sa ../tests/test2.rem 1 aug 2007 >> ../tests/test.out
echo "Test 5" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l -b0 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out
echo "Test 6" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l -b1 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out
echo "Test 7" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -p -l -b2 ../tests/test3.rem 1 aug 2007 >> ../tests/test.out
echo "Test 8" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -df -p -l -b2 ../tests/include_dir 1 aug 2007 >> ../tests/test.out 2>&1
echo "Test 9" >> ../tests/test.out
echo "" >> ../tests/test.out
../src/remind -df -p -l -b2 ../tests/nonexistent_include_dir 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind -df -p -l -b2 ../tests/include_dir_no_rems 1 aug 2007 >> ../tests/test.out 2>&1
../src/remind -df -p -l -b2 ../tests/include_test.rem 1 aug 2007 >> ../tests/test.out 2>&1
chmod 644 include_dir/04cantread.rem
echo "Color Test" >> ../tests/test.out
../src/remind -ccl ../tests/colors.rem 1 aug 2007 >> ../tests/test.out
echo "MON WKDAY DAY across year test" >> ../tests/test.out
echo 'REM Mon 29 Dec MSG x' | ../src/remind -dt - 1 Jan 2000 >> ../tests/test.out 2>&1
echo "Sort Test" >> ../tests/test.out
(echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind -gaaa - 1 Jan 2000 >> ../tests/test.out 2>&1
(echo "REM AT 12:00 MSG Untimed"; echo "REM MSG Timed") | ../src/remind -gaaad - 1 Jan 2000 >> ../tests/test.out 2>&1
echo "Purge Test" >> ../tests/test.out
../src/remind -j999 ../tests/purge_dir/f1.rem >> ../tests/test.out 2>&1
echo "F1" >> ../tests/test.out
cat ../tests/purge_dir/f1.rem.purged >> ../tests/test.out
echo "F2" >> ../tests/test.out
cat ../tests/purge_dir/f2.rem.purged >> ../tests/test.out
echo "F3" >> ../tests/test.out
cat ../tests/purge_dir/f3.rem.purged >> ../tests/test.out
rm -f ../tests/purge_dir/*.rem.purged >> ../tests/test.out 2>&1
../src/remind ../tests/runtest.rem >> ../tests/test.out 2>&1
../src/remind -p ../tests/shade.rem 1 August 2009 | ../src/rem2ps -e -l -c3 >> ../tests/test.out 2>&1
cmp -s ../tests/test.out ../tests/test.cmp
if [ "$?" = "0" ]; then
echo "Remind: Acceptance test PASSED"

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,10 @@
#
# ./test-rem # From WITHIN Remind source directory!
# Don't evaluate SATISFY expressions if reminder has expired
REM Wed UNTIL 15 Feb 1991 SATISFY [trigdate() > '1990-01-01'] MSG wookie
REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())]
fset _h(x, y) trigger(hebdate(x,y))
@@ -41,6 +45,9 @@ fset _i(x,y,z,a) trigger(hebdate(x,y,z,a))
[_i(29, "Adar A", today(), 5755)] MSG Leap
[_i(29, "Adar A", today(), 5756)] MSG Illegal
# This causes a parse error on version 03.01.01
REM 1990-01-01 SATISFY 1
# Test each possible case of the basic reminders.
REM MSG Every Day
@@ -128,7 +135,7 @@ CLEAR-OMIT-CONTEXT
REM 1 Mar -1 MSG 1 mar -1
REM 1 Mar --1 MSG 1 mar --1
REM 28 Feb BEFORE MSG 28 Feb BEFORE
REM 28 Feb SKIP MSG 28 Feb SKIP
REM 28 Feb SKIP MSG 28 Feb SKIP
REM 28 Feb AFTER MSG 28 Feb AFTER
POP-OMIT-CONTEXT
@@ -140,7 +147,7 @@ REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91
# Test BACK
CLEAR-OMIT-CONTEXT
REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1
@@ -159,6 +166,25 @@ REM Fri SCANFROM [trigger(today()-7)] SATISFY 1
OMIT [trigger(trigdate())]
REM Fri after MSG 16 Feb 1991
CLEAR-OMIT-CONTEXT
# Test omitfunc
fset _ofunc(x) (day(x) < 7 || day(x) % 2)
REM 1 March OMITFUNC _ofunc AFTER MSG OmitFunc Test
REM 8 March OMITFUNC _ofunc -1 MSG OmitFunc Test 2
# omitfunc ignores local/global omits
fset _ofunc(x) 0
OMIT 1 March
OMIT 2 March 1991
REM 1 March OMIT Sun OMITFUNC _ofunc AFTER MSG Should trigger 1 March
REM 1 March OMIT Sun AFTER MSG Should trigger 4 March
# Test shorthand reminders
REM 1991-02-28 MSG Feb 28
REM 1991/02/28@14:45 MSG Feb 28
REM Wed UNTIL 1991-01-01 MSG Expired
REM Wed SCANFROM 1991-02-26 MSG SCANFROM
set a000 abs(1)
set a001 abs(-1)
set a002 asc("foo")
@@ -250,4 +276,9 @@ set a078 easterdate(today())
set a079 easterdate(1992)
set a080 easterdate(1995)
set a081 ""
OMIT 1991-03-11
set a082 slide('1991-03-01', 7, "Sat", "Sun")
set a083 slide('1991-04-01', -7, "Sat")
set a084 nonomitted('1991-03-01', '1991-03-13', "Sat", "Sun")
set a085 nonomitted('1991-03-24', '1991-04-01', "Sat")
dump

View File

@@ -1,830 +0,0 @@
# Test file for REMIND
#
# Use this file to test the date calculation routines
# of the REMIND program by typing:
#
# ./test-rem # From WITHIN Remind source directory!
REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())]
.\test.rem(8): Trig = Saturday, 16 February, 1991
Reminders for Saturday, 16th February, 1991:
today() => 1991/02/16
hebday(1991/02/16) => 2
today() => 1991/02/16
hebmon(1991/02/16) => "Adar"
today() => 1991/02/16
hebyear(1991/02/16) => 5751
Today is 2 Adar 5751
fset _h(x, y) trigger(hebdate(x,y))
[_h(1, "Tishrey")] MSG Rosh Hashana 1
Entering UserFN _h(1, "Tishrey")
x => 1
y => "Tishrey"
hebdate(1, "Tishrey") => 1991/09/09
trigger(1991/09/09) => "9 September 1991"
Leaving UserFN _h() => "9 September 1991"
.\test.rem(11): Trig = Monday, 9 September, 1991
[_h(2, "Tishrey")] MSG Rosh Hashana 2
Entering UserFN _h(2, "Tishrey")
x => 2
y => "Tishrey"
hebdate(2, "Tishrey") => 1991/09/10
trigger(1991/09/10) => "10 September 1991"
Leaving UserFN _h() => "10 September 1991"
.\test.rem(12): Trig = Tuesday, 10 September, 1991
[_h(3, "Tishrey")] MSG Tzom Gedalia
Entering UserFN _h(3, "Tishrey")
x => 3
y => "Tishrey"
hebdate(3, "Tishrey") => 1991/09/11
trigger(1991/09/11) => "11 September 1991"
Leaving UserFN _h() => "11 September 1991"
.\test.rem(13): Trig = Wednesday, 11 September, 1991
[_h(10, "Tishrey")] MSG Yom Kippur
Entering UserFN _h(10, "Tishrey")
x => 10
y => "Tishrey"
hebdate(10, "Tishrey") => 1991/09/18
trigger(1991/09/18) => "18 September 1991"
Leaving UserFN _h() => "18 September 1991"
.\test.rem(14): Trig = Wednesday, 18 September, 1991
[_h(15, "Tishrey")] MSG Sukkot 1
Entering UserFN _h(15, "Tishrey")
x => 15
y => "Tishrey"
hebdate(15, "Tishrey") => 1991/09/23
trigger(1991/09/23) => "23 September 1991"
Leaving UserFN _h() => "23 September 1991"
.\test.rem(15): Trig = Monday, 23 September, 1991
[_h(25, "Kislev")] MSG Channuka
Entering UserFN _h(25, "Kislev")
x => 25
y => "Kislev"
hebdate(25, "Kislev") => 1991/12/02
trigger(1991/12/02) => "2 December 1991"
Leaving UserFN _h() => "2 December 1991"
.\test.rem(16): Trig = Monday, 2 December, 1991
[_h(10, "Tevet")] MSG Asara B'Tevet
Entering UserFN _h(10, "Tevet")
x => 10
y => "Tevet"
hebdate(10, "Tevet") => 1991/12/17
trigger(1991/12/17) => "17 December 1991"
Leaving UserFN _h() => "17 December 1991"
.\test.rem(17): Trig = Tuesday, 17 December, 1991
[_h(15, "Shvat")] MSG Tu B'Shvat
Entering UserFN _h(15, "Shvat")
x => 15
y => "Shvat"
hebdate(15, "Shvat") => 1992/01/20
trigger(1992/01/20) => "20 January 1992"
Leaving UserFN _h() => "20 January 1992"
.\test.rem(18): Trig = Monday, 20 January, 1992
[_h(15, "Adar A")] MSG Purim Katan
Entering UserFN _h(15, "Adar A")
x => 15
y => "Adar A"
hebdate(15, "Adar A") => 1992/02/19
trigger(1992/02/19) => "19 February 1992"
Leaving UserFN _h() => "19 February 1992"
.\test.rem(19): Trig = Wednesday, 19 February, 1992
[_h(14, "Adar")] MSG Purim
Entering UserFN _h(14, "Adar")
x => 14
y => "Adar"
hebdate(14, "Adar") => 1991/02/28
trigger(1991/02/28) => "28 February 1991"
Leaving UserFN _h() => "28 February 1991"
.\test.rem(20): Trig = Thursday, 28 February, 1991
[_h(15, "Nisan")] MSG Pesach
Entering UserFN _h(15, "Nisan")
x => 15
y => "Nisan"
hebdate(15, "Nisan") => 1991/03/30
trigger(1991/03/30) => "30 March 1991"
Leaving UserFN _h() => "30 March 1991"
.\test.rem(21): Trig = Saturday, 30 March, 1991
[_h(27, "Nisan")] MSG Yom HaShoah
Entering UserFN _h(27, "Nisan")
x => 27
y => "Nisan"
hebdate(27, "Nisan") => 1991/04/11
trigger(1991/04/11) => "11 April 1991"
Leaving UserFN _h() => "11 April 1991"
.\test.rem(22): Trig = Thursday, 11 April, 1991
[_h(4, "Iyar")] MSG Yom HaZikaron
Entering UserFN _h(4, "Iyar")
x => 4
y => "Iyar"
hebdate(4, "Iyar") => 1991/04/18
trigger(1991/04/18) => "18 April 1991"
Leaving UserFN _h() => "18 April 1991"
.\test.rem(23): Trig = Thursday, 18 April, 1991
[_h(5, "Iyar")] MSG Yom Ha'atzmaut
Entering UserFN _h(5, "Iyar")
x => 5
y => "Iyar"
hebdate(5, "Iyar") => 1991/04/19
trigger(1991/04/19) => "19 April 1991"
Leaving UserFN _h() => "19 April 1991"
.\test.rem(24): Trig = Friday, 19 April, 1991
[_h(28, "Iyar")] MSG Yom Yerushalayim
Entering UserFN _h(28, "Iyar")
x => 28
y => "Iyar"
hebdate(28, "Iyar") => 1991/05/12
trigger(1991/05/12) => "12 May 1991"
Leaving UserFN _h() => "12 May 1991"
.\test.rem(25): Trig = Sunday, 12 May, 1991
[_h(6, "Sivan")] MSG Shavuot
Entering UserFN _h(6, "Sivan")
x => 6
y => "Sivan"
hebdate(6, "Sivan") => 1991/05/19
trigger(1991/05/19) => "19 May 1991"
Leaving UserFN _h() => "19 May 1991"
.\test.rem(26): Trig = Sunday, 19 May, 1991
[_h(9, "Av")] MSG Tish'a B'Av
Entering UserFN _h(9, "Av")
x => 9
y => "Av"
hebdate(9, "Av") => 1991/07/20
trigger(1991/07/20) => "20 July 1991"
Leaving UserFN _h() => "20 July 1991"
.\test.rem(27): Trig = Saturday, 20 July, 1991
# Test some jahrzeit cases
fset _i(x,y,z,a) trigger(hebdate(x,y,z,a))
[_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete
today() => 1991/02/16
Entering UserFN _i(30, "Heshvan", 1991/02/16, 5759)
x => 30
y => "Heshvan"
z => 1991/02/16
a => 5759
hebdate(30, "Heshvan", 1991/02/16, 5759) => 1991/11/07
trigger(1991/11/07) => "7 November 1991"
Leaving UserFN _i() => "7 November 1991"
.\test.rem(31): Trig = Thursday, 7 November, 1991
[_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective
today() => 1991/02/16
Entering UserFN _i(30, "Heshvan", 1991/02/16, 5760)
x => 30
y => "Heshvan"
z => 1991/02/16
a => 5760
hebdate(30, "Heshvan", 1991/02/16, 5760) => 1991/11/07
trigger(1991/11/07) => "7 November 1991"
Leaving UserFN _i() => "7 November 1991"
.\test.rem(32): Trig = Thursday, 7 November, 1991
[_i(30, "Heshvan", today(), 5761)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(30, "Heshvan", 1991/02/16, 5761)
x => 30
y => "Heshvan"
z => 1991/02/16
a => 5761
hebdate(30, "Heshvan", 1991/02/16, 5761) => .\test.rem(33): 30 Heshvan 5761: Invalid Hebrew date
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
[_i(30, "Kislev", today(), 5759)] MSG Complete-Complete
today() => 1991/02/16
Entering UserFN _i(30, "Kislev", 1991/02/16, 5759)
x => 30
y => "Kislev"
z => 1991/02/16
a => 5759
hebdate(30, "Kislev", 1991/02/16, 5759) => 1991/12/07
trigger(1991/12/07) => "7 December 1991"
Leaving UserFN _i() => "7 December 1991"
.\test.rem(35): Trig = Saturday, 7 December, 1991
[_i(30, "Kislev", today(), 5760)] MSG Complete-Defective
today() => 1991/02/16
Entering UserFN _i(30, "Kislev", 1991/02/16, 5760)
x => 30
y => "Kislev"
z => 1991/02/16
a => 5760
hebdate(30, "Kislev", 1991/02/16, 5760) => 1991/12/07
trigger(1991/12/07) => "7 December 1991"
Leaving UserFN _i() => "7 December 1991"
.\test.rem(36): Trig = Saturday, 7 December, 1991
[_i(30, "Kislev", today(), 5761)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(30, "Kislev", 1991/02/16, 5761)
x => 30
y => "Kislev"
z => 1991/02/16
a => 5761
hebdate(30, "Kislev", 1991/02/16, 5761) => .\test.rem(37): 30 Kislev 5761: Invalid Hebrew date
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
[_i(30, "Adar A", today(), 5755)] MSG Leap
today() => 1991/02/16
Entering UserFN _i(30, "Adar A", 1991/02/16, 5755)
x => 30
y => "Adar A"
z => 1991/02/16
a => 5755
hebdate(30, "Adar A", 1991/02/16, 5755) => 1992/03/05
trigger(1992/03/05) => "5 March 1992"
Leaving UserFN _i() => "5 March 1992"
.\test.rem(39): Trig = Thursday, 5 March, 1992
[_i(30, "Adar A", today(), 5756)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(30, "Adar A", 1991/02/16, 5756)
x => 30
y => "Adar A"
z => 1991/02/16
a => 5756
hebdate(30, "Adar A", 1991/02/16, 5756) => .\test.rem(40): No Adar A in 5756
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
[_i(29, "Adar A", today(), 5755)] MSG Leap
today() => 1991/02/16
Entering UserFN _i(29, "Adar A", 1991/02/16, 5755)
x => 29
y => "Adar A"
z => 1991/02/16
a => 5755
hebdate(29, "Adar A", 1991/02/16, 5755) => 1991/03/15
trigger(1991/03/15) => "15 March 1991"
Leaving UserFN _i() => "15 March 1991"
.\test.rem(41): Trig = Friday, 15 March, 1991
[_i(29, "Adar A", today(), 5756)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(29, "Adar A", 1991/02/16, 5756)
x => 29
y => "Adar A"
z => 1991/02/16
a => 5756
hebdate(29, "Adar A", 1991/02/16, 5756) => .\test.rem(42): No Adar A in 5756
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
# Test each possible case of the basic reminders.
REM MSG Every Day
.\test.rem(46): Trig = Saturday, 16 February, 1991
Every Day
REM 18 MSG Every 18th
.\test.rem(48): Trig = Monday, 18 February, 1991
REM 15 MSG Every 15th
.\test.rem(49): Trig = Friday, 15 March, 1991
REM Feb MSG February
.\test.rem(51): Trig = Saturday, 16 February, 1991
February
REM Jan MSG January
.\test.rem(52): Trig = Wednesday, 1 January, 1992
REM March MSG March
.\test.rem(53): Trig = Friday, 1 March, 1991
REM 13 Jan MSG 13 Jan
.\test.rem(55): Trig = Monday, 13 January, 1992
REM 15 Feb MSG 15 Feb
.\test.rem(56): Trig = Saturday, 15 February, 1992
REM 28 Feb MSG 28 Feb
.\test.rem(57): Trig = Thursday, 28 February, 1991
REM 29 Feb MSG 29 Feb
.\test.rem(58): Trig = Saturday, 29 February, 1992
REM 5 Mar MSG 5 Mar
.\test.rem(59): Trig = Tuesday, 5 March, 1991
REM 1990 MSG 1990
.\test.rem(61): Expired
REM 1991 MSG 1991
.\test.rem(62): Trig = Saturday, 16 February, 1991
1991
REM 1992 MSG 1991
.\test.rem(63): Trig = Wednesday, 1 January, 1992
REM 1 1990 MSG 1 1990
.\test.rem(65): Expired
REM 29 1991 MSG 29 1991
.\test.rem(66): Trig = Friday, 29 March, 1991
REM 29 1992 MSG 29 1992
.\test.rem(67): Trig = Wednesday, 29 January, 1992
REM 16 1991 MSG 16 1991
.\test.rem(68): Trig = Saturday, 16 February, 1991
16 1991
REM Jan 1990 MSG Jan 1990
.\test.rem(70): Expired
REM Feb 1991 MSG Feb 1991
.\test.rem(71): Trig = Saturday, 16 February, 1991
Feb 1991
REM Dec 1991 MSG Dec 1991
.\test.rem(72): Trig = Sunday, 1 December, 1991
REM May 1992 MSG May 1992
.\test.rem(73): Trig = Friday, 1 May, 1992
REM 1 Jan 1991 MSG 1 Jan 1991
.\test.rem(75): Expired
REM 16 Feb 1991 MSG 16 Feb 1991
.\test.rem(76): Trig = Saturday, 16 February, 1991
16 Feb 1991
REM 29 Dec 1992 MSG 29 Dec 1992
.\test.rem(77): Trig = Tuesday, 29 December, 1992
REM Sun MSG Sun
.\test.rem(79): Trig = Sunday, 17 February, 1991
REM Fri Sat Tue MSG Fri Sat Tue
.\test.rem(80): Trig = Saturday, 16 February, 1991
Fri Sat Tue
REM Sun 16 MSG Sun 16
.\test.rem(82): Trig = Sunday, 17 February, 1991
REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1
.\test.rem(83): Trig = Friday, 1 March, 1991
REM Sun Feb MSG Sun Feb
.\test.rem(85): Trig = Sunday, 17 February, 1991
REM Mon Tue March MSG Mon Tue March
.\test.rem(86): Trig = Monday, 4 March, 1991
REM Sun 16 Feb MSG Sun 16 Feb
.\test.rem(88): Trig = Sunday, 17 February, 1991
REM Mon Tue 10 March MSG Mon Tue 10 March
.\test.rem(89): Trig = Monday, 11 March, 1991
REM Sat Sun 1991 MSG Sat Sun 1991
.\test.rem(91): Trig = Saturday, 16 February, 1991
Sat Sun 1991
REM Mon Tue 1992 MSG Mon Tue 1992
.\test.rem(92): Trig = Monday, 6 January, 1992
REM Sun 16 1991 MSG Sun 16 1991
.\test.rem(94): Trig = Sunday, 17 February, 1991
REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992
.\test.rem(95): Trig = Wednesday, 1 January, 1992
REM Mon Feb 1991 MSG Mon Feb 1991
.\test.rem(97): Trig = Monday, 18 February, 1991
REM Tue Jan 1992 MSG Tue Jan 1992
.\test.rem(98): Trig = Tuesday, 7 January, 1992
REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991
.\test.rem(100): Trig = Sunday, 17 February, 1991
REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992
.\test.rem(101): Trig = Tuesday, 28 January, 1992
# Try some Backs
CLEAR-OMIT-CONTEXT
REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun
.\test.rem(105): Trig = Thursday, 28 February, 1991
REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun
.\test.rem(106): Trig = Thursday, 28 February, 1991
OMIT 28 Feb
REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted)
.\test.rem(109): Trig = Wednesday, 27 February, 1991
REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted)
.\test.rem(110): Trig = Thursday, 28 February, 1991
CLEAR-OMIT-CONTEXT
# Try out UNTIL
REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991
.\test.rem(115): Trig = Wednesday, 20 February, 1991
# Try playing with the OMIT context
OMIT 28 Feb 1991
REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
.\test.rem(120): Trig = Wednesday, 27 February, 1991
REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
.\test.rem(121): Trig = Thursday, 28 February, 1991
REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
.\test.rem(122): Trig = Wednesday, 27 February, 1991
REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
.\test.rem(123): Trig = Friday, 28 February, 1992
REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
.\test.rem(124): Trig = Friday, 1 March, 1991
PUSH-OMIT-CONTEXT
CLEAR-OMIT-CONTEXT
REM 1 Mar -1 MSG 1 mar -1
.\test.rem(128): Trig = Thursday, 28 February, 1991
REM 1 Mar --1 MSG 1 mar --1
.\test.rem(129): Trig = Thursday, 28 February, 1991
REM 28 Feb BEFORE MSG 28 Feb BEFORE
.\test.rem(130): Trig = Thursday, 28 February, 1991
REM 28 Feb SKIP MSG 28 Feb SKIP
.\test.rem(131): Trig = Thursday, 28 February, 1991
REM 28 Feb AFTER MSG 28 Feb AFTER
.\test.rem(132): Trig = Thursday, 28 February, 1991
POP-OMIT-CONTEXT
REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
.\test.rem(135): Trig = Wednesday, 27 February, 1991
REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
.\test.rem(136): Trig = Thursday, 28 February, 1991
REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
.\test.rem(137): Trig = Wednesday, 27 February, 1991
REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
.\test.rem(138): Trig = Friday, 28 February, 1992
REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
.\test.rem(139): Trig = Friday, 1 March, 1991
REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91
.\test.rem(142): Trig = Wednesday, 13 March, 1991
# Test BACK
CLEAR-OMIT-CONTEXT
REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1
.\test.rem(146): Trig = Monday, 18 February, 1991
OMIT 17 Feb 1991
REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted)
.\test.rem(149): Trig = Monday, 18 February, 1991
18 Feb 1991 +1 (17Feb91 omitted)
REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted)
.\test.rem(150): Trig = Monday, 18 February, 1991
CLEAR-OMIT-CONTEXT
# Test the scanfrom clause
REM Fri SATISFY 1
.\test.rem(154): Trig = Friday, 22 February, 1991
OMIT [trigger(trigdate())]
trigdate() => 1991/02/22
trigger(1991/02/22) => "22 February 1991"
REM Fri after MSG 23 Feb 1991
.\test.rem(156): Trig = Saturday, 23 February, 1991
CLEAR-OMIT-CONTEXT
REM Fri SCANFROM [trigger(today()-7)] SATISFY 1
today() => 1991/02/16
1991/02/16 - 7 => 1991/02/09
trigger(1991/02/09) => "9 February 1991"
.\test.rem(158): Trig = Friday, 15 February, 1991
OMIT [trigger(trigdate())]
trigdate() => 1991/02/15
trigger(1991/02/15) => "15 February 1991"
REM Fri after MSG 16 Feb 1991
.\test.rem(160): Trig = Saturday, 16 February, 1991
16 Feb 1991
CLEAR-OMIT-CONTEXT
set a000 abs(1)
abs(1) => 1
set a001 abs(-1)
- 1 => -1
abs(-1) => 1
set a002 asc("foo")
asc("foo") => 102
set a003 baseyr()
baseyr() => 1990
set a004 char(66,55,66,77,66)
char(66, 55, 66, 77, 66) => "B7BMB"
set a005 choose(3, "foo", "bar", "baz", "blech")
choose(3, "foo", "bar", "baz", "blech") => "baz"
set a006 coerce("string", 1)
coerce("string", 1) => "1"
set a007 coerce("string", today())
today() => 1991/02/16
coerce("string", 1991/02/16) => "1991/02/16"
set a008 coerce("string", 11:44)
coerce("string", 11:44) => "11:44"
set a009 coerce("int", "badnews")
coerce("int", "badnews") => Can't coerce
.\test.rem(171): Can't coerce
set a010 coerce("int", "12")
coerce("int", "12") => 12
set a011 coerce("int", 11:44)
coerce("int", 11:44) => 704
set a012 coerce("int", today())
today() => 1991/02/16
coerce("int", 1991/02/16) => 411
set a013 date(1992, 2, 2)
date(1992, 2, 2) => 1992/02/02
set a014 date(1993, 2, 29)
date(1993, 2, 29) => Bad date specification
.\test.rem(176): Bad date specification
set a015 day(today())
today() => 1991/02/16
day(1991/02/16) => 16
set a016 daysinmon(2, 1991)
daysinmon(2, 1991) => 28
set a017 daysinmon(2, 1992)
daysinmon(2, 1992) => 29
set a018 defined("a017")
defined("a017") => 1
set a019 defined("a019")
defined("a019") => 0
set a020 filename()
filename() => ".\test.rem"
set a021 getenv("TEST_GETENV")
getenv("TEST_GETENV") => "foo bar baz"
set a022 hour(11:22)
hour(11:22) => 11
set a023 iif(1, 1, 0)
iif(1, 1, 0) => 1
set a024 iif(0, 1, 0)
iif(0, 1, 0) => 0
set a025 index("barfoobar", "foo")
index("barfoobar", "foo") => 4
set a026 index("barfoobar", "bar", 2)
index("barfoobar", "bar", 2) => 7
set a027 isleap(today())
today() => 1991/02/16
isleap(1991/02/16) => 0
set a028 isleap(1992)
isleap(1992) => 1
omit [trigger(today())]
today() => 1991/02/16
trigger(1991/02/16) => "16 February 1991"
set a030 isomitted(today())
today() => 1991/02/16
isomitted(1991/02/16) => 1
clear
set a029 isomitted(today())
today() => 1991/02/16
isomitted(1991/02/16) => 0
set a031 lower("FOOBARBAZ")
lower("FOOBARBAZ") => "foobarbaz"
set a032 max(1, 2, 34, 1, 3)
max(1, 2, 34, 1, 3) => 34
set a033 max("foo", "bar", "baz")
max("foo", "bar", "baz") => "foo"
set a034 max(today(), today()+1, today()-1)
today() => 1991/02/16
today() => 1991/02/16
1991/02/16 + 1 => 1991/02/17
today() => 1991/02/16
1991/02/16 - 1 => 1991/02/15
max(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/17
set a035 min(1, 2, 34, 1, 3)
min(1, 2, 34, 1, 3) => 1
set a036 min("foo", "bar", "baz")
min("foo", "bar", "baz") => "bar"
set a037 min(today(), today()+1, today()-1)
today() => 1991/02/16
today() => 1991/02/16
1991/02/16 + 1 => 1991/02/17
today() => 1991/02/16
1991/02/16 - 1 => 1991/02/15
min(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/15
set a038 minute(11:33)
minute(11:33) => 33
set a039 mon(today())
today() => 1991/02/16
mon(1991/02/16) => "February"
set a040 monnum(today())
today() => 1991/02/16
monnum(1991/02/16) => 2
set a041 ord(3)
ord(3) => "3rd"
set a042 ord(4)
ord(4) => "4th"
set a043 ostype()
ostype() => "MSDOS"
set a044 plural(2)
plural(2) => "s"
set a045 plural(2, "ies")
plural(2, "ies") => "iess"
set a046 plural(2, "y", "ies")
plural(2, "y", "ies") => "ies"
set a047 sgn(-2)
- 2 => -2
sgn(-2) => -1
set a048 shell("echo foo")
shell("echo foo") => "foo"
set a049 strlen("sadjflkhsldkfhsdlfjhk")
strlen("sadjflkhsldkfhsdlfjhk") => 21
set a050 substr(a049, 2)
a049 => 21
substr(21, 2) => Type mismatch
.\test.rem(214): Type mismatch
set a051 substr(a050, 2, 6)
a050 => .\test.rem(215): Undefined variable: a050
set a052 time(1+2, 3+4)
1 + 2 => 3
3 + 4 => 7
time(3, 7) => 03:07
rem 10 jan 1992 AT 11:22 CAL
.\test.rem(217): Trig = Friday, 10 January, 1992
set a053 trigdate()
trigdate() => 1992/01/10
set a054 trigtime()
trigtime() => 11:22
set a055 trigvalid()
trigvalid() => 1
set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh")
upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a057 value("a05"+"6")
"a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version()
version() => "03.00.19"
set a059 wkday(today())
today() => 1991/02/16
wkday(1991/02/16) => "Saturday"
set a060 wkdaynum(today())
today() => 1991/02/16
wkdaynum(1991/02/16) => 6
set a061 year(today())
today() => 1991/02/16
year(1991/02/16) => 1991
set a062 1+2*(3+4-(5*7/2))
3 + 4 => 7
5 * 7 => 35
35 / 2 => 17
7 - 17 => -10
2 * -10 => -20
1 + -20 => -19
set a063 1>=2
1 >= 2 => 0
set a064 1<2 || 3 > 4
1 < 2 => 1
3 > 4 => 0
1 || 0 => 1
set a065 1 && 1
1 && 1 => 1
set a066 !a065
a065 => 1
! 1 => 0
set a067 typeof(2)
typeof(2) => "INT"
set a068 typeof("foo")
typeof("foo") => "STRING"
set a069 typeof(11:33)
typeof(11:33) => "TIME"
set a070 typeof(today())
today() => 1991/02/16
typeof(1991/02/16) => "DATE"
fset g(x,y) max(x,y)
fset h(x,y) min(g(x+y, x*y), g(x-y, x/y))
set a071 g(1, 2)
Entering UserFN g(1, 2)
x => 1
y => 2
max(1, 2) => 2
Leaving UserFN g() => 2
set a072 h(2, 3)
Entering UserFN h(2, 3)
x => 2
y => 3
2 + 3 => 5
x => 2
y => 3
2 * 3 => 6
Entering UserFN g(5, 6)
x => 5
y => 6
max(5, 6) => 6
Leaving UserFN g() => 6
x => 2
y => 3
2 - 3 => -1
x => 2
y => 3
2 / 3 => 0
Entering UserFN g(-1, 0)
x => -1
y => 0
max(-1, 0) => 0
Leaving UserFN g() => 0
min(6, 0) => 0
Leaving UserFN h() => 0
set a073 h("foo", 11:33)
Entering UserFN h("foo", 11:33)
x => "foo"
y => 11:33
"foo" + 11:33 => "foo11:33"
x => "foo"
y => 11:33
"foo" * 11:33 => Type mismatch
.\test.rem(240): `*': Type mismatch
Leaving UserFN h() => Type mismatch
set a074 dosubst("%a %b %c %d %e %f %g %h", '1992/5/5')
dosubst("%a %b %c %d %e %f %g %h", 1992/05/05) => "on Tuesday, 5 May, 1992 in 444 days' tim"...
msg [a074]%
.\test.rem(242): Trig = Saturday, 16 February, 1991
a074 => "on Tuesday, 5 May, 1992 in 444 days' tim"...
on Tuesday, 5 May, 1992 in 444 days' time on Tuesday 5 on 05/05/1992 on 05/05/1992 on Tuesday, 5 May on 05/05
set a075 dosubst("%i %j %k %l %m %n %o %p", '1992/5/5')
dosubst("%i %j %k %l %m %n %o %p", 1992/05/05) => "on 05/05 on Tuesday, May 5th, 1992 on Tu"...
msg [a075]%
.\test.rem(244): Trig = Saturday, 16 February, 1991
a075 => "on 05/05 on Tuesday, May 5th, 1992 on Tu"...
on 05/05 on Tuesday, May 5th, 1992 on Tuesday, May 5th on 1992/05/05 May 5 s
set a076 dosubst("%q %r %s %t %u %v %w %x", '1992/5/5')
dosubst("%q %r %s %t %u %v %w %x", 1992/05/05) => "s' 05 th 05 on Tuesday, 5th May, 1992 on"...
msg [a076]%
.\test.rem(246): Trig = Saturday, 16 February, 1991
a076 => "s' 05 th 05 on Tuesday, 5th May, 1992 on"...
s' 05 th 05 on Tuesday, 5th May, 1992 on Tuesday, 5th May Tuesday 444
set a077 dosubst("%y %z", '1992/5/5')
dosubst("%y %z", 1992/05/05) => "1992 92
"
msg [a077]%
.\test.rem(248): Trig = Saturday, 16 February, 1991
a077 => "1992 92
"
1992 92
set a078 easterdate(today())
today() => 1991/02/16
easterdate(1991/02/16) => 1991/03/31
set a079 easterdate(1992)
easterdate(1992) => 1992/04/19
set a080 easterdate(1995)
easterdate(1995) => 1995/04/16
set a081 ""
dump
Variable Value
a017 29
a036 "bar"
a055 1
a074 "on Tuesday, 5 May, 1992 in 444 days' tim"...
a008 "11:44"
a027 0
a046 "ies"
a065 1
a018 1
a037 1991/02/15
a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a075 "on 05/05 on Tuesday, May 5th, 1992 on Tu"...
a028 1
a047 -1
a066 0
a019 0
a038 33
a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a076 "s' 05 th 05 on Tuesday, 5th May, 1992 on"...
a029 0
a048 "foo"
a067 "INT"
a039 "February"
a058 "03.00.19"
a077 "1992 92
"
a049 21
a068 "STRING"
a059 "Saturday"
a078 1991/03/31
a069 "TIME"
a079 1992/04/19
a000 1
a010 12
a001 1
a020 ".\test.rem"
a011 704
a030 1
a002 102
a021 "foo bar baz"
a040 2
a012 411
a031 "foobarbaz"
a003 1990
a022 11
a041 "3rd"
a060 6
a013 1992/02/02
a032 34
a070 "DATE"
a004 "B7BMB"
a023 1
a042 "4th"
a061 1991
a080 1995/04/16
a033 "foo"
a052 03:07
a071 2
a005 "baz"
a024 0
a043 "MSDOS"
a062 -19
a081 ""
a015 16
a034 1991/02/17
a053 1992/01/10
a072 0
a006 "1"
a025 4
a044 "s"
a063 0
a016 28
a035 1
a054 11:22
a007 "1991/02/16"
a026 7
a045 "iess"
a064 1

View File

@@ -1,830 +0,0 @@
# Test file for REMIND
#
# Use this file to test the date calculation routines
# of the REMIND program by typing:
#
# ./test-rem # From WITHIN Remind source directory!
REM MSG Today is [hebday(today())] [hebmon(today())] [hebyear(today())]
.\test.rem(8): Trig = Saturday, 16 February, 1991
Reminders for Saturday, 16th February, 1991:
today() => 1991/02/16
hebday(1991/02/16) => 2
today() => 1991/02/16
hebmon(1991/02/16) => "Adar"
today() => 1991/02/16
hebyear(1991/02/16) => 5751
Today is 2 Adar 5751
fset _h(x, y) trigger(hebdate(x,y))
[_h(1, "Tishrey")] MSG Rosh Hashana 1
Entering UserFN _h(1, "Tishrey")
x => 1
y => "Tishrey"
hebdate(1, "Tishrey") => 1991/09/09
trigger(1991/09/09) => "9 September 1991"
Leaving UserFN _h() => "9 September 1991"
.\test.rem(11): Trig = Monday, 9 September, 1991
[_h(2, "Tishrey")] MSG Rosh Hashana 2
Entering UserFN _h(2, "Tishrey")
x => 2
y => "Tishrey"
hebdate(2, "Tishrey") => 1991/09/10
trigger(1991/09/10) => "10 September 1991"
Leaving UserFN _h() => "10 September 1991"
.\test.rem(12): Trig = Tuesday, 10 September, 1991
[_h(3, "Tishrey")] MSG Tzom Gedalia
Entering UserFN _h(3, "Tishrey")
x => 3
y => "Tishrey"
hebdate(3, "Tishrey") => 1991/09/11
trigger(1991/09/11) => "11 September 1991"
Leaving UserFN _h() => "11 September 1991"
.\test.rem(13): Trig = Wednesday, 11 September, 1991
[_h(10, "Tishrey")] MSG Yom Kippur
Entering UserFN _h(10, "Tishrey")
x => 10
y => "Tishrey"
hebdate(10, "Tishrey") => 1991/09/18
trigger(1991/09/18) => "18 September 1991"
Leaving UserFN _h() => "18 September 1991"
.\test.rem(14): Trig = Wednesday, 18 September, 1991
[_h(15, "Tishrey")] MSG Sukkot 1
Entering UserFN _h(15, "Tishrey")
x => 15
y => "Tishrey"
hebdate(15, "Tishrey") => 1991/09/23
trigger(1991/09/23) => "23 September 1991"
Leaving UserFN _h() => "23 September 1991"
.\test.rem(15): Trig = Monday, 23 September, 1991
[_h(25, "Kislev")] MSG Channuka
Entering UserFN _h(25, "Kislev")
x => 25
y => "Kislev"
hebdate(25, "Kislev") => 1991/12/02
trigger(1991/12/02) => "2 December 1991"
Leaving UserFN _h() => "2 December 1991"
.\test.rem(16): Trig = Monday, 2 December, 1991
[_h(10, "Tevet")] MSG Asara B'Tevet
Entering UserFN _h(10, "Tevet")
x => 10
y => "Tevet"
hebdate(10, "Tevet") => 1991/12/17
trigger(1991/12/17) => "17 December 1991"
Leaving UserFN _h() => "17 December 1991"
.\test.rem(17): Trig = Tuesday, 17 December, 1991
[_h(15, "Shvat")] MSG Tu B'Shvat
Entering UserFN _h(15, "Shvat")
x => 15
y => "Shvat"
hebdate(15, "Shvat") => 1992/01/20
trigger(1992/01/20) => "20 January 1992"
Leaving UserFN _h() => "20 January 1992"
.\test.rem(18): Trig = Monday, 20 January, 1992
[_h(15, "Adar A")] MSG Purim Katan
Entering UserFN _h(15, "Adar A")
x => 15
y => "Adar A"
hebdate(15, "Adar A") => 1992/02/19
trigger(1992/02/19) => "19 February 1992"
Leaving UserFN _h() => "19 February 1992"
.\test.rem(19): Trig = Wednesday, 19 February, 1992
[_h(14, "Adar")] MSG Purim
Entering UserFN _h(14, "Adar")
x => 14
y => "Adar"
hebdate(14, "Adar") => 1991/02/28
trigger(1991/02/28) => "28 February 1991"
Leaving UserFN _h() => "28 February 1991"
.\test.rem(20): Trig = Thursday, 28 February, 1991
[_h(15, "Nisan")] MSG Pesach
Entering UserFN _h(15, "Nisan")
x => 15
y => "Nisan"
hebdate(15, "Nisan") => 1991/03/30
trigger(1991/03/30) => "30 March 1991"
Leaving UserFN _h() => "30 March 1991"
.\test.rem(21): Trig = Saturday, 30 March, 1991
[_h(27, "Nisan")] MSG Yom HaShoah
Entering UserFN _h(27, "Nisan")
x => 27
y => "Nisan"
hebdate(27, "Nisan") => 1991/04/11
trigger(1991/04/11) => "11 April 1991"
Leaving UserFN _h() => "11 April 1991"
.\test.rem(22): Trig = Thursday, 11 April, 1991
[_h(4, "Iyar")] MSG Yom HaZikaron
Entering UserFN _h(4, "Iyar")
x => 4
y => "Iyar"
hebdate(4, "Iyar") => 1991/04/18
trigger(1991/04/18) => "18 April 1991"
Leaving UserFN _h() => "18 April 1991"
.\test.rem(23): Trig = Thursday, 18 April, 1991
[_h(5, "Iyar")] MSG Yom Ha'atzmaut
Entering UserFN _h(5, "Iyar")
x => 5
y => "Iyar"
hebdate(5, "Iyar") => 1991/04/19
trigger(1991/04/19) => "19 April 1991"
Leaving UserFN _h() => "19 April 1991"
.\test.rem(24): Trig = Friday, 19 April, 1991
[_h(28, "Iyar")] MSG Yom Yerushalayim
Entering UserFN _h(28, "Iyar")
x => 28
y => "Iyar"
hebdate(28, "Iyar") => 1991/05/12
trigger(1991/05/12) => "12 May 1991"
Leaving UserFN _h() => "12 May 1991"
.\test.rem(25): Trig = Sunday, 12 May, 1991
[_h(6, "Sivan")] MSG Shavuot
Entering UserFN _h(6, "Sivan")
x => 6
y => "Sivan"
hebdate(6, "Sivan") => 1991/05/19
trigger(1991/05/19) => "19 May 1991"
Leaving UserFN _h() => "19 May 1991"
.\test.rem(26): Trig = Sunday, 19 May, 1991
[_h(9, "Av")] MSG Tish'a B'Av
Entering UserFN _h(9, "Av")
x => 9
y => "Av"
hebdate(9, "Av") => 1991/07/20
trigger(1991/07/20) => "20 July 1991"
Leaving UserFN _h() => "20 July 1991"
.\test.rem(27): Trig = Saturday, 20 July, 1991
# Test some jahrzeit cases
fset _i(x,y,z,a) trigger(hebdate(x,y,z,a))
[_i(30, "Heshvan", today(), 5759)] MSG Complete-Complete
today() => 1991/02/16
Entering UserFN _i(30, "Heshvan", 1991/02/16, 5759)
x => 30
y => "Heshvan"
z => 1991/02/16
a => 5759
hebdate(30, "Heshvan", 1991/02/16, 5759) => 1991/11/07
trigger(1991/11/07) => "7 November 1991"
Leaving UserFN _i() => "7 November 1991"
.\test.rem(31): Trig = Thursday, 7 November, 1991
[_i(30, "Heshvan", today(), 5760)] MSG Complete-Defective
today() => 1991/02/16
Entering UserFN _i(30, "Heshvan", 1991/02/16, 5760)
x => 30
y => "Heshvan"
z => 1991/02/16
a => 5760
hebdate(30, "Heshvan", 1991/02/16, 5760) => 1991/11/07
trigger(1991/11/07) => "7 November 1991"
Leaving UserFN _i() => "7 November 1991"
.\test.rem(32): Trig = Thursday, 7 November, 1991
[_i(30, "Heshvan", today(), 5761)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(30, "Heshvan", 1991/02/16, 5761)
x => 30
y => "Heshvan"
z => 1991/02/16
a => 5761
hebdate(30, "Heshvan", 1991/02/16, 5761) => .\test.rem(33): 30 Heshvan 5761: Invalid Hebrew date
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
[_i(30, "Kislev", today(), 5759)] MSG Complete-Complete
today() => 1991/02/16
Entering UserFN _i(30, "Kislev", 1991/02/16, 5759)
x => 30
y => "Kislev"
z => 1991/02/16
a => 5759
hebdate(30, "Kislev", 1991/02/16, 5759) => 1991/12/07
trigger(1991/12/07) => "7 December 1991"
Leaving UserFN _i() => "7 December 1991"
.\test.rem(35): Trig = Saturday, 7 December, 1991
[_i(30, "Kislev", today(), 5760)] MSG Complete-Defective
today() => 1991/02/16
Entering UserFN _i(30, "Kislev", 1991/02/16, 5760)
x => 30
y => "Kislev"
z => 1991/02/16
a => 5760
hebdate(30, "Kislev", 1991/02/16, 5760) => 1991/12/07
trigger(1991/12/07) => "7 December 1991"
Leaving UserFN _i() => "7 December 1991"
.\test.rem(36): Trig = Saturday, 7 December, 1991
[_i(30, "Kislev", today(), 5761)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(30, "Kislev", 1991/02/16, 5761)
x => 30
y => "Kislev"
z => 1991/02/16
a => 5761
hebdate(30, "Kislev", 1991/02/16, 5761) => .\test.rem(37): 30 Kislev 5761: Invalid Hebrew date
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
[_i(30, "Adar A", today(), 5755)] MSG Leap
today() => 1991/02/16
Entering UserFN _i(30, "Adar A", 1991/02/16, 5755)
x => 30
y => "Adar A"
z => 1991/02/16
a => 5755
hebdate(30, "Adar A", 1991/02/16, 5755) => 1992/03/05
trigger(1992/03/05) => "5 March 1992"
Leaving UserFN _i() => "5 March 1992"
.\test.rem(39): Trig = Thursday, 5 March, 1992
[_i(30, "Adar A", today(), 5756)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(30, "Adar A", 1991/02/16, 5756)
x => 30
y => "Adar A"
z => 1991/02/16
a => 5756
hebdate(30, "Adar A", 1991/02/16, 5756) => .\test.rem(40): No Adar A in 5756
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
[_i(29, "Adar A", today(), 5755)] MSG Leap
today() => 1991/02/16
Entering UserFN _i(29, "Adar A", 1991/02/16, 5755)
x => 29
y => "Adar A"
z => 1991/02/16
a => 5755
hebdate(29, "Adar A", 1991/02/16, 5755) => 1991/03/15
trigger(1991/03/15) => "15 March 1991"
Leaving UserFN _i() => "15 March 1991"
.\test.rem(41): Trig = Friday, 15 March, 1991
[_i(29, "Adar A", today(), 5756)] MSG Illegal
today() => 1991/02/16
Entering UserFN _i(29, "Adar A", 1991/02/16, 5756)
x => 29
y => "Adar A"
z => 1991/02/16
a => 5756
hebdate(29, "Adar A", 1991/02/16, 5756) => .\test.rem(42): No Adar A in 5756
Invalid Hebrew date
Leaving UserFN _i() => Invalid Hebrew date
# Test each possible case of the basic reminders.
REM MSG Every Day
.\test.rem(46): Trig = Saturday, 16 February, 1991
Every Day
REM 18 MSG Every 18th
.\test.rem(48): Trig = Monday, 18 February, 1991
REM 15 MSG Every 15th
.\test.rem(49): Trig = Friday, 15 March, 1991
REM Feb MSG February
.\test.rem(51): Trig = Saturday, 16 February, 1991
February
REM Jan MSG January
.\test.rem(52): Trig = Wednesday, 1 January, 1992
REM March MSG March
.\test.rem(53): Trig = Friday, 1 March, 1991
REM 13 Jan MSG 13 Jan
.\test.rem(55): Trig = Monday, 13 January, 1992
REM 15 Feb MSG 15 Feb
.\test.rem(56): Trig = Saturday, 15 February, 1992
REM 28 Feb MSG 28 Feb
.\test.rem(57): Trig = Thursday, 28 February, 1991
REM 29 Feb MSG 29 Feb
.\test.rem(58): Trig = Saturday, 29 February, 1992
REM 5 Mar MSG 5 Mar
.\test.rem(59): Trig = Tuesday, 5 March, 1991
REM 1990 MSG 1990
.\test.rem(61): Expired
REM 1991 MSG 1991
.\test.rem(62): Trig = Saturday, 16 February, 1991
1991
REM 1992 MSG 1991
.\test.rem(63): Trig = Wednesday, 1 January, 1992
REM 1 1990 MSG 1 1990
.\test.rem(65): Expired
REM 29 1991 MSG 29 1991
.\test.rem(66): Trig = Friday, 29 March, 1991
REM 29 1992 MSG 29 1992
.\test.rem(67): Trig = Wednesday, 29 January, 1992
REM 16 1991 MSG 16 1991
.\test.rem(68): Trig = Saturday, 16 February, 1991
16 1991
REM Jan 1990 MSG Jan 1990
.\test.rem(70): Expired
REM Feb 1991 MSG Feb 1991
.\test.rem(71): Trig = Saturday, 16 February, 1991
Feb 1991
REM Dec 1991 MSG Dec 1991
.\test.rem(72): Trig = Sunday, 1 December, 1991
REM May 1992 MSG May 1992
.\test.rem(73): Trig = Friday, 1 May, 1992
REM 1 Jan 1991 MSG 1 Jan 1991
.\test.rem(75): Expired
REM 16 Feb 1991 MSG 16 Feb 1991
.\test.rem(76): Trig = Saturday, 16 February, 1991
16 Feb 1991
REM 29 Dec 1992 MSG 29 Dec 1992
.\test.rem(77): Trig = Tuesday, 29 December, 1992
REM Sun MSG Sun
.\test.rem(79): Trig = Sunday, 17 February, 1991
REM Fri Sat Tue MSG Fri Sat Tue
.\test.rem(80): Trig = Saturday, 16 February, 1991
Fri Sat Tue
REM Sun 16 MSG Sun 16
.\test.rem(82): Trig = Sunday, 17 February, 1991
REM Mon Tue Wed Thu Fri 1 MSG Mon Tue Wed Thu Fri 1
.\test.rem(83): Trig = Friday, 1 March, 1991
REM Sun Feb MSG Sun Feb
.\test.rem(85): Trig = Sunday, 17 February, 1991
REM Mon Tue March MSG Mon Tue March
.\test.rem(86): Trig = Monday, 4 March, 1991
REM Sun 16 Feb MSG Sun 16 Feb
.\test.rem(88): Trig = Sunday, 17 February, 1991
REM Mon Tue 10 March MSG Mon Tue 10 March
.\test.rem(89): Trig = Monday, 11 March, 1991
REM Sat Sun 1991 MSG Sat Sun 1991
.\test.rem(91): Trig = Saturday, 16 February, 1991
Sat Sun 1991
REM Mon Tue 1992 MSG Mon Tue 1992
.\test.rem(92): Trig = Monday, 6 January, 1992
REM Sun 16 1991 MSG Sun 16 1991
.\test.rem(94): Trig = Sunday, 17 February, 1991
REM Mon Tue Wed Thu Fri 1 1992 MSG Mon Tue Wed Thu Fri 1 1992
.\test.rem(95): Trig = Wednesday, 1 January, 1992
REM Mon Feb 1991 MSG Mon Feb 1991
.\test.rem(97): Trig = Monday, 18 February, 1991
REM Tue Jan 1992 MSG Tue Jan 1992
.\test.rem(98): Trig = Tuesday, 7 January, 1992
REM Sun Mon 16 Feb 1991 MSG Sun Mon 16 Feb 1991
.\test.rem(100): Trig = Sunday, 17 February, 1991
REM Tue 28 Jan 1992 MSG Tue 28 Jan 1992
.\test.rem(101): Trig = Tuesday, 28 January, 1992
# Try some Backs
CLEAR-OMIT-CONTEXT
REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun
.\test.rem(105): Trig = Thursday, 28 February, 1991
REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun
.\test.rem(106): Trig = Thursday, 28 February, 1991
OMIT 28 Feb
REM 1 -1 OMIT sat sun MSG 1 -1 OMIT Sat Sun (28 Feb omitted)
.\test.rem(109): Trig = Wednesday, 27 February, 1991
REM 1 --1 OMIT sat sun MSG 1 --1 OMIT Sat Sun (28 Feb omitted)
.\test.rem(110): Trig = Thursday, 28 February, 1991
CLEAR-OMIT-CONTEXT
# Try out UNTIL
REM Wed UNTIL 21 Feb 1991 MSG Wed UNTIL 21 Feb 1991
.\test.rem(115): Trig = Wednesday, 20 February, 1991
# Try playing with the OMIT context
OMIT 28 Feb 1991
REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
.\test.rem(120): Trig = Wednesday, 27 February, 1991
REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
.\test.rem(121): Trig = Thursday, 28 February, 1991
REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
.\test.rem(122): Trig = Wednesday, 27 February, 1991
REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
.\test.rem(123): Trig = Friday, 28 February, 1992
REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
.\test.rem(124): Trig = Friday, 1 March, 1991
PUSH-OMIT-CONTEXT
CLEAR-OMIT-CONTEXT
REM 1 Mar -1 MSG 1 mar -1
.\test.rem(128): Trig = Thursday, 28 February, 1991
REM 1 Mar --1 MSG 1 mar --1
.\test.rem(129): Trig = Thursday, 28 February, 1991
REM 28 Feb BEFORE MSG 28 Feb BEFORE
.\test.rem(130): Trig = Thursday, 28 February, 1991
REM 28 Feb SKIP MSG 28 Feb SKIP
.\test.rem(131): Trig = Thursday, 28 February, 1991
REM 28 Feb AFTER MSG 28 Feb AFTER
.\test.rem(132): Trig = Thursday, 28 February, 1991
POP-OMIT-CONTEXT
REM 1 Mar -1 MSG 1 mar -1 (28feb91 omitted)
.\test.rem(135): Trig = Wednesday, 27 February, 1991
REM 1 Mar --1 MSG 1 mar --1 (28Feb91 omitted)
.\test.rem(136): Trig = Thursday, 28 February, 1991
REM 28 Feb BEFORE MSG 28 Feb BEFORE (28Feb91 omitted)
.\test.rem(137): Trig = Wednesday, 27 February, 1991
REM 28 Feb SKIP MSG 28 Feb SKIP (28Feb91 omitted)
.\test.rem(138): Trig = Friday, 28 February, 1992
REM 28 Feb AFTER MSG 28 Feb AFTER (28Feb91 omitted)
.\test.rem(139): Trig = Friday, 1 March, 1991
REM 13 March 1991 *1 UNTIL 19 March 1991 MSG 13-19 Mar 91
.\test.rem(142): Trig = Wednesday, 13 March, 1991
# Test BACK
CLEAR-OMIT-CONTEXT
REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1
.\test.rem(146): Trig = Monday, 18 February, 1991
OMIT 17 Feb 1991
REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1 (17Feb91 omitted)
.\test.rem(149): Trig = Monday, 18 February, 1991
18 Feb 1991 +1 (17Feb91 omitted)
REM 18 Feb 1991 ++1 MSG 18 Feb 1991 ++1 (17Feb91 omitted)
.\test.rem(150): Trig = Monday, 18 February, 1991
CLEAR-OMIT-CONTEXT
# Test the scanfrom clause
REM Fri SATISFY 1
.\test.rem(154): Trig = Friday, 22 February, 1991
OMIT [trigger(trigdate())]
trigdate() => 1991/02/22
trigger(1991/02/22) => "22 February 1991"
REM Fri after MSG 23 Feb 1991
.\test.rem(156): Trig = Saturday, 23 February, 1991
CLEAR-OMIT-CONTEXT
REM Fri SCANFROM [trigger(today()-7)] SATISFY 1
today() => 1991/02/16
1991/02/16 - 7 => 1991/02/09
trigger(1991/02/09) => "9 February 1991"
.\test.rem(158): Trig = Friday, 15 February, 1991
OMIT [trigger(trigdate())]
trigdate() => 1991/02/15
trigger(1991/02/15) => "15 February 1991"
REM Fri after MSG 16 Feb 1991
.\test.rem(160): Trig = Saturday, 16 February, 1991
16 Feb 1991
CLEAR-OMIT-CONTEXT
set a000 abs(1)
abs(1) => 1
set a001 abs(-1)
- 1 => -1
abs(-1) => 1
set a002 asc("foo")
asc("foo") => 102
set a003 baseyr()
baseyr() => 1990
set a004 char(66,55,66,77,66)
char(66, 55, 66, 77, 66) => "B7BMB"
set a005 choose(3, "foo", "bar", "baz", "blech")
choose(3, "foo", "bar", "baz", "blech") => "baz"
set a006 coerce("string", 1)
coerce("string", 1) => "1"
set a007 coerce("string", today())
today() => 1991/02/16
coerce("string", 1991/02/16) => "1991/02/16"
set a008 coerce("string", 11:44)
coerce("string", 11:44) => "11:44"
set a009 coerce("int", "badnews")
coerce("int", "badnews") => Can't coerce
.\test.rem(171): Can't coerce
set a010 coerce("int", "12")
coerce("int", "12") => 12
set a011 coerce("int", 11:44)
coerce("int", 11:44) => 704
set a012 coerce("int", today())
today() => 1991/02/16
coerce("int", 1991/02/16) => 411
set a013 date(1992, 2, 2)
date(1992, 2, 2) => 1992/02/02
set a014 date(1993, 2, 29)
date(1993, 2, 29) => Bad date specification
.\test.rem(176): Bad date specification
set a015 day(today())
today() => 1991/02/16
day(1991/02/16) => 16
set a016 daysinmon(2, 1991)
daysinmon(2, 1991) => 28
set a017 daysinmon(2, 1992)
daysinmon(2, 1992) => 29
set a018 defined("a017")
defined("a017") => 1
set a019 defined("a019")
defined("a019") => 0
set a020 filename()
filename() => ".\test.rem"
set a021 getenv("TEST_GETENV")
getenv("TEST_GETENV") => "foo bar baz"
set a022 hour(11:22)
hour(11:22) => 11
set a023 iif(1, 1, 0)
iif(1, 1, 0) => 1
set a024 iif(0, 1, 0)
iif(0, 1, 0) => 0
set a025 index("barfoobar", "foo")
index("barfoobar", "foo") => 4
set a026 index("barfoobar", "bar", 2)
index("barfoobar", "bar", 2) => 7
set a027 isleap(today())
today() => 1991/02/16
isleap(1991/02/16) => 0
set a028 isleap(1992)
isleap(1992) => 1
omit [trigger(today())]
today() => 1991/02/16
trigger(1991/02/16) => "16 February 1991"
set a030 isomitted(today())
today() => 1991/02/16
isomitted(1991/02/16) => 1
clear
set a029 isomitted(today())
today() => 1991/02/16
isomitted(1991/02/16) => 0
set a031 lower("FOOBARBAZ")
lower("FOOBARBAZ") => "foobarbaz"
set a032 max(1, 2, 34, 1, 3)
max(1, 2, 34, 1, 3) => 34
set a033 max("foo", "bar", "baz")
max("foo", "bar", "baz") => "foo"
set a034 max(today(), today()+1, today()-1)
today() => 1991/02/16
today() => 1991/02/16
1991/02/16 + 1 => 1991/02/17
today() => 1991/02/16
1991/02/16 - 1 => 1991/02/15
max(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/17
set a035 min(1, 2, 34, 1, 3)
min(1, 2, 34, 1, 3) => 1
set a036 min("foo", "bar", "baz")
min("foo", "bar", "baz") => "bar"
set a037 min(today(), today()+1, today()-1)
today() => 1991/02/16
today() => 1991/02/16
1991/02/16 + 1 => 1991/02/17
today() => 1991/02/16
1991/02/16 - 1 => 1991/02/15
min(1991/02/16, 1991/02/17, 1991/02/15) => 1991/02/15
set a038 minute(11:33)
minute(11:33) => 33
set a039 mon(today())
today() => 1991/02/16
mon(1991/02/16) => "February"
set a040 monnum(today())
today() => 1991/02/16
monnum(1991/02/16) => 2
set a041 ord(3)
ord(3) => "3rd"
set a042 ord(4)
ord(4) => "4th"
set a043 ostype()
ostype() => "OS/2"
set a044 plural(2)
plural(2) => "s"
set a045 plural(2, "ies")
plural(2, "ies") => "iess"
set a046 plural(2, "y", "ies")
plural(2, "y", "ies") => "ies"
set a047 sgn(-2)
- 2 => -2
sgn(-2) => -1
set a048 shell("echo foo")
shell("echo foo") => "foo"
set a049 strlen("sadjflkhsldkfhsdlfjhk")
strlen("sadjflkhsldkfhsdlfjhk") => 21
set a050 substr(a049, 2)
a049 => 21
substr(21, 2) => Type mismatch
.\test.rem(214): Type mismatch
set a051 substr(a050, 2, 6)
a050 => .\test.rem(215): Undefined variable: a050
set a052 time(1+2, 3+4)
1 + 2 => 3
3 + 4 => 7
time(3, 7) => 03:07
rem 10 jan 1992 AT 11:22 CAL
.\test.rem(217): Trig = Friday, 10 January, 1992
set a053 trigdate()
trigdate() => 1992/01/10
set a054 trigtime()
trigtime() => 11:22
set a055 trigvalid()
trigvalid() => 1
set a056 upper("sdfjhsdf ksjdfh kjsdfh ksjdfh")
upper("sdfjhsdf ksjdfh kjsdfh ksjdfh") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a057 value("a05"+"6")
"a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version()
version() => "03.00.19"
set a059 wkday(today())
today() => 1991/02/16
wkday(1991/02/16) => "Saturday"
set a060 wkdaynum(today())
today() => 1991/02/16
wkdaynum(1991/02/16) => 6
set a061 year(today())
today() => 1991/02/16
year(1991/02/16) => 1991
set a062 1+2*(3+4-(5*7/2))
3 + 4 => 7
5 * 7 => 35
35 / 2 => 17
7 - 17 => -10
2 * -10 => -20
1 + -20 => -19
set a063 1>=2
1 >= 2 => 0
set a064 1<2 || 3 > 4
1 < 2 => 1
3 > 4 => 0
1 || 0 => 1
set a065 1 && 1
1 && 1 => 1
set a066 !a065
a065 => 1
! 1 => 0
set a067 typeof(2)
typeof(2) => "INT"
set a068 typeof("foo")
typeof("foo") => "STRING"
set a069 typeof(11:33)
typeof(11:33) => "TIME"
set a070 typeof(today())
today() => 1991/02/16
typeof(1991/02/16) => "DATE"
fset g(x,y) max(x,y)
fset h(x,y) min(g(x+y, x*y), g(x-y, x/y))
set a071 g(1, 2)
Entering UserFN g(1, 2)
x => 1
y => 2
max(1, 2) => 2
Leaving UserFN g() => 2
set a072 h(2, 3)
Entering UserFN h(2, 3)
x => 2
y => 3
2 + 3 => 5
x => 2
y => 3
2 * 3 => 6
Entering UserFN g(5, 6)
x => 5
y => 6
max(5, 6) => 6
Leaving UserFN g() => 6
x => 2
y => 3
2 - 3 => -1
x => 2
y => 3
2 / 3 => 0
Entering UserFN g(-1, 0)
x => -1
y => 0
max(-1, 0) => 0
Leaving UserFN g() => 0
min(6, 0) => 0
Leaving UserFN h() => 0
set a073 h("foo", 11:33)
Entering UserFN h("foo", 11:33)
x => "foo"
y => 11:33
"foo" + 11:33 => "foo11:33"
x => "foo"
y => 11:33
"foo" * 11:33 => Type mismatch
.\test.rem(240): `*': Type mismatch
Leaving UserFN h() => Type mismatch
set a074 dosubst("%a %b %c %d %e %f %g %h", '1992/5/5')
dosubst("%a %b %c %d %e %f %g %h", 1992/05/05) => "on Tuesday, 5 May, 1992 in 444 days' tim"...
msg [a074]%
.\test.rem(242): Trig = Saturday, 16 February, 1991
a074 => "on Tuesday, 5 May, 1992 in 444 days' tim"...
on Tuesday, 5 May, 1992 in 444 days' time on Tuesday 5 on 05/05/1992 on 05/05/1992 on Tuesday, 5 May on 05/05
set a075 dosubst("%i %j %k %l %m %n %o %p", '1992/5/5')
dosubst("%i %j %k %l %m %n %o %p", 1992/05/05) => "on 05/05 on Tuesday, May 5th, 1992 on Tu"...
msg [a075]%
.\test.rem(244): Trig = Saturday, 16 February, 1991
a075 => "on 05/05 on Tuesday, May 5th, 1992 on Tu"...
on 05/05 on Tuesday, May 5th, 1992 on Tuesday, May 5th on 1992/05/05 May 5 s
set a076 dosubst("%q %r %s %t %u %v %w %x", '1992/5/5')
dosubst("%q %r %s %t %u %v %w %x", 1992/05/05) => "s' 05 th 05 on Tuesday, 5th May, 1992 on"...
msg [a076]%
.\test.rem(246): Trig = Saturday, 16 February, 1991
a076 => "s' 05 th 05 on Tuesday, 5th May, 1992 on"...
s' 05 th 05 on Tuesday, 5th May, 1992 on Tuesday, 5th May Tuesday 444
set a077 dosubst("%y %z", '1992/5/5')
dosubst("%y %z", 1992/05/05) => "1992 92
"
msg [a077]%
.\test.rem(248): Trig = Saturday, 16 February, 1991
a077 => "1992 92
"
1992 92
set a078 easterdate(today())
today() => 1991/02/16
easterdate(1991/02/16) => 1991/03/31
set a079 easterdate(1992)
easterdate(1992) => 1992/04/19
set a080 easterdate(1995)
easterdate(1995) => 1995/04/16
set a081 ""
dump
Variable Value
a017 29
a036 "bar"
a055 1
a074 "on Tuesday, 5 May, 1992 in 444 days' tim"...
a008 "11:44"
a027 0
a046 "ies"
a065 1
a018 1
a037 1991/02/15
a056 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a075 "on 05/05 on Tuesday, May 5th, 1992 on Tu"...
a028 1
a047 -1
a066 0
a019 0
a038 33
a057 "SDFJHSDF KSJDFH KJSDFH KSJDFH"
a076 "s' 05 th 05 on Tuesday, 5th May, 1992 on"...
a029 0
a048 "foo"
a067 "INT"
a039 "February"
a058 "03.00.19"
a077 "1992 92
"
a049 21
a068 "STRING"
a059 "Saturday"
a078 1991/03/31
a069 "TIME"
a079 1992/04/19
a000 1
a010 12
a001 1
a020 ".\test.rem"
a011 704
a030 1
a002 102
a021 "foo bar baz"
a040 2
a012 411
a031 "foobarbaz"
a003 1990
a022 11
a041 "3rd"
a060 6
a013 1992/02/02
a032 34
a070 "DATE"
a004 "B7BMB"
a023 1
a042 "4th"
a061 1991
a080 1995/04/16
a033 "foo"
a052 03:07
a071 2
a005 "baz"
a024 0
a043 "OS/2"
a062 -19
a081 ""
a015 16
a034 1991/02/17
a053 1992/01/10
a072 0
a006 "1"
a025 4
a044 "s"
a063 0
a016 28
a035 1
a054 11:22
a007 "1991/02/16"
a026 7
a045 "iess"
a064 1

43
tests/test2.rem Normal file
View File

@@ -0,0 +1,43 @@
# Make things sane
set $TimeSep ":"
set $DateSep "/"
set $LatDeg 45
set $LatMin 24
set $LatSec 0
set $Location "Ottawa"
set $LongDeg 75
set $LongMin 39
set $LongSec 0
clear-omit-context
omit 14 aug
# Test the SPECIAL COLOR escapes
REM WED +2 SPECIAL COLOR 0 0 255 %"Blue Wednesday%" is %b
REM THU SPECIAL COLOR 255 0 0 Red Thursday
# Test SPECIAL SHADE
REM FRI SPECIAL SHADE 0 255 0
# Test SPECIAL MOON
REM 12 AUG SPECIAL MOON 0
# Test nonomitted
REM MSG [nonomitted('2007-08-01', today())] NonOmit-1
REM MSG [nonomitted('2007-08-01', today(), "Sat", "Sun")] NonOmit-2
# Test SPECIAL COLOR with an AT clause
REM 20 AUG AT 13:45 SPECIAL COLOR 6 7 8 Mooo!
# Test SPECIAL PostScript with and without AT clause
REM 21 AUG AT 1:55 SPECIAL PostScript (wookie) show
REM 22 AUG SPECIAL PostScript (cabbage) show
# Test a random SPECIAL with and without AT
REM 23 AUG AT 16:44 SPECIAL blort snoo glup
REM 24 AUG SPECIAL blort gulp wookie
# Bug discovered by Paul Pelzl
OMIT 13 August
REM 6 August 2007 *7 SKIP SATISFY [1] MSG Blort

29
tests/test3.rem Normal file
View File

@@ -0,0 +1,29 @@
REM 1 AT 11:00 MSG Wookie
REM 1 AT 12:00 MSG Cookie
REM 1 AT 13:00 MSG Snookie
REM 1 AT 11:00 DURATION 0:45 MSG Lettuce
REM 1 AT 12:00 DURATION 0:45 MSG Cabbage
REM 1 AT 13:00 DURATION 0:45 MSG Tomato
REM 1 AT 11:00 DURATION 1:45 MSG Apple
REM 1 AT 12:00 DURATION 2:45 MSG Pear
REM 1 AT 13:00 DURATION 3:45 MSG Grape
REM 1 AT 11:00 DURATION 14:45 MSG Green
REM 1 AT 12:00 DURATION 14:45 MSG Blue
REM 1 AT 13:00 DURATION 14:45 MSG Red
REM 1 AT 11:00 DURATION 24:45 MSG Yellow
REM 1 AT 12:00 DURATION 24:45 MSG Orange
REM 1 AT 13:00 DURATION 24:45 MSG Magenta
REM 1 AT 11:00 DURATION 36:45 MSG Purple
REM 1 AT 12:00 DURATION 36:45 MSG Black
REM 1 AT 13:00 DURATION 36:45 MSG Brown
REM 1 AT 11:00 DURATION 48:45 MSG Sad
REM 1 AT 12:00 DURATION 48:45 MSG Happy
REM 1 AT 13:00 DURATION 48:45 MSG Strange

View File

@@ -1,6 +1,6 @@
#!/bin/sh
echo "Unconfiguring Remind..."
echo rm -f config.cache config.log config.status src/Makefile src/config.h
rm -f config.cache config.log config.status src/Makefile src/config.h
echo rm -f config.cache config.log config.status src/Makefile src/config.h src/version.h
rm -f config.cache config.log config.status src/Makefile src/config.h src/version.h
exit 0

View File

@@ -1,95 +0,0 @@
# Makefile for installing WWW server calendar scripts
# This file is part of REMIND.
# Copyright (C) 1992-1997 by David F. Skoll
# Copyright (C) 1999 by Roaring Penguin Software Inc.
# The complete name of your www host. Example: www.mycompany.com
WWWHOST = magnesium.roaringpenguin.com
# WWWHOST = www.mycompany.com
# The complete path to where the scripts actually live, as seen by
# the UNIX operating system.
SCRIPTDIR = /var/www/cgi-bin
# SCRIPTDIR = /home/dfs/Remind/www/TEST
# Where the scripts live as seen by the web browser. If
# they live in WWWHOST/cgi-bin, supply /cgi-bin
CGIDIR = /cgi-bin
# The complete path to the directory containing the HTML file "calendar.html".
# This is a sample file containing links to all the scripts. This path
# should be the path as seen by the UNIX operating system
HTMLDIR = /var/www/html/remind
# Where you stick images, as seen by UNIX
IMAGEDIR = /var/www/html/remind
# Where images are, as seen by web browers
IMAGEBASE = /remind
# Location. Typically, the name of your city or town. Example: Ottawa
LOCATION = Ottawa
# LOCATION = New York
# Where do Remind and Rem2PS executables live?
REMIND = /usr/local/bin/remind
REM2PS = /usr/local/bin/rem2ps
# If your Web server requires CGI programs to have a .cgi suffix, use
# the next line. Otherwise, comment it out
CGISUFFIX=.cgi
# Don't change stuff below here.
# --------------------------------------------------------------------
# Construct a rotten mean nasty sed script to do the dirty work
SEDSCRIPT = -e 's/Ottawa/$(LOCATION)/g' \
-e 's@http://your.machine/your.dir@http://$(WWWHOST)$(CGIDIR)@g' \
-e 's@%SCRIPTDIR%@$(SCRIPTDIR)@g' \
-e 's@%REMIND%@$(REMIND)@g' \
-e 's@%IMAGEBASE%@$(IMAGEBASE)@g' \
-e 's@%REM2PS%@$(REM2PS)@g' \
-e 's@cal_dispatch@cal_dispatch$(CGISUFFIX)@g' \
-e 's@rem2html@rem2html$(CGISUFFIX)@g'
SEDSCRIPT2 = -e 's/Ottawa/$(LOCATION)/g' \
-e 's@http://your.machine/your.dir@http://$(WWWHOST)$(CGIDIR)@g' \
-e 's@%SCRIPTDIR%@$(SCRIPTDIR)@g' \
-e 's@%REMIND%@$(REMIND)@g' \
-e 's@%IMAGEBASE%@$(IMAGEBASE)@g' \
-e 's@%REM2PS%@$(REM2PS)@g'
all:
@echo "Edit the Makefile; then type 'make install' to install"
@echo "the www server scripts."
# OK, it's abominable. But it works...
install:
-mkdir $(SCRIPTDIR)
cp calps hebdate hebps hebhtml moon sunrise sunset $(SCRIPTDIR)
sed $(SEDSCRIPT) < cal_dispatch-DIST > $(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
sed $(SEDSCRIPT) < hebdate.rem-DIST > $(SCRIPTDIR)/hebdate.rem
sed $(SEDSCRIPT) < moon.rem-DIST > $(SCRIPTDIR)/moon.rem
sed $(SEDSCRIPT) < sunrise.rem-DIST > $(SCRIPTDIR)/sunrise.rem
sed $(SEDSCRIPT) < sunset.rem-DIST > $(SCRIPTDIR)/sunset.rem
sed $(SEDSCRIPT) < calendar.html-DIST > $(HTMLDIR)/calendar.html
sed $(SEDSCRIPT) < hebhtml > $(SCRIPTDIR)/hebhtml
sed $(SEDSCRIPT2) < rem2html > $(SCRIPTDIR)/rem2html$(CGISUFFIX)
chmod 644 $(SCRIPTDIR)/sunrise.rem
chmod 644 $(SCRIPTDIR)/moon.rem
chmod 644 $(SCRIPTDIR)/hebdate.rem
chmod 644 $(SCRIPTDIR)/sunset.rem
chmod 644 $(HTMLDIR)/calendar.html
chmod 755 $(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
chmod 755 $(SCRIPTDIR)/rem2html$(CGISUFFIX)
chmod 755 $(SCRIPTDIR)/calps $(SCRIPTDIR)/hebdate \
$(SCRIPTDIR)/hebps $(SCRIPTDIR)/moon $(SCRIPTDIR)/sunrise \
$(SCRIPTDIR)/sunset $(SCRIPTDIR)/hebhtml $(SCRIPTDIR)/rem2html$(CGISUFFIX)
-mkdir $(IMAGEDIR)
cp firstquarter.png fullmoon.png lastquarter.png newmoon.png $(IMAGEDIR)
chmod 644 $(IMAGEDIR)/firstquarter.png $(IMAGEDIR)/fullmoon.png $(IMAGEDIR)/lastquarter.png $(IMAGEDIR)/newmoon.png

100
www/Makefile.in Normal file
View File

@@ -0,0 +1,100 @@
# Makefile.in for installing WWW server calendar scripts
# This file is part of REMIND.
# Copyright (C) 1992-1997 by David F. Skoll
# Copyright (C) 1999-2007 by Roaring Penguin Software Inc.
# The complete path to where the scripts actually live, as seen by
# the UNIX operating system.
SCRIPTDIR = /var/www/cgi-bin
# Where the scripts live as seen by the web browser.
CGIDIR = /cgi-bin
# The complete path to the directory containing the HTML file "calendar.html".
# This is a sample file containing links to all the scripts. This path
# should be the path as seen by the UNIX operating system
HTMLDIR = /var/www/remind
# Where you stick images and CSS files, as seen by UNIX
IMAGEDIR = /var/www/remind/resources
# Where images are, as seen by web browers
IMAGEBASE = /remind/resources
# Set by configure - don't touch.
srcdir=@srcdir@
prefix=@prefix@
exec_prefix=@exec_prefix@
mandir=@mandir@
bindir=@bindir@
datadir=@datadir@
datarootdir=@datarootdir@
# Where do Remind and Rem2PS executables live?
REMIND = $(bindir)/remind
REM2PS = $(bindir)/rem2ps
# If your Web server requires CGI programs to have a .cgi suffix, use
# the next line. Otherwise, comment it out
CGISUFFIX=.cgi
# Don't change stuff below here.
# --------------------------------------------------------------------
# Construct a rotten mean nasty sed script to do the dirty work
SEDSCRIPT = -e 's@%CGIDIR%@$(CGIDIR)@g' \
-e 's@%SCRIPTDIR%@$(SCRIPTDIR)@g' \
-e 's@%REMIND%@$(REMIND)@g' \
-e 's@%IMAGEBASE%@$(IMAGEBASE)@g' \
-e 's@%REM2PS%@$(REM2PS)@g' \
-e 's@cal_dispatch@cal_dispatch$(CGISUFFIX)@g' \
-e 's@rem2html@rem2html$(CGISUFFIX)@g'
SEDSCRIPT2 = -e 's@%CGIDIR%@$(CGIDIR)@g' \
-e 's@%SCRIPTDIR%@$(SCRIPTDIR)@g' \
-e 's@%REMIND%@$(REMIND)@g' \
-e 's@%IMAGEBASE%@$(IMAGEBASE)@g' \
-e 's@%REM2PS%@$(REM2PS)@g'
all:
@echo "Edit the Makefile; then type 'make install' to install"
@echo "the www server scripts."
# OK, it's abominable. But it works...
install:
-mkdir -p $(DESTDIR)$(SCRIPTDIR)
-mkdir -p $(DESTDIR)$(HTMLDIR)
cp calps hebdate hebps hebhtml moon sunrise sunset $(DESTDIR)$(SCRIPTDIR)
sed $(SEDSCRIPT) < cal_dispatch-DIST > $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
sed $(SEDSCRIPT) < hebdate.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/hebdate.rem
sed $(SEDSCRIPT) < moon.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/moon.rem
sed $(SEDSCRIPT) < sunrise.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/sunrise.rem
sed $(SEDSCRIPT) < sunset.rem-DIST > $(DESTDIR)$(SCRIPTDIR)/sunset.rem
sed $(SEDSCRIPT) < calendar.html-DIST > $(DESTDIR)$(HTMLDIR)/calendar.html
sed $(SEDSCRIPT) < hebhtml > $(DESTDIR)$(SCRIPTDIR)/hebhtml
sed $(SEDSCRIPT2) < rem2html > $(DESTDIR)$(SCRIPTDIR)/rem2html$(CGISUFFIX)
chmod 644 $(DESTDIR)$(SCRIPTDIR)/sunrise.rem
chmod 644 $(DESTDIR)$(SCRIPTDIR)/moon.rem
chmod 644 $(DESTDIR)$(SCRIPTDIR)/hebdate.rem
chmod 644 $(DESTDIR)$(SCRIPTDIR)/sunset.rem
chmod 644 $(DESTDIR)$(HTMLDIR)/calendar.html
chmod 755 $(DESTDIR)$(SCRIPTDIR)/cal_dispatch$(CGISUFFIX)
chmod 755 $(DESTDIR)$(SCRIPTDIR)/rem2html$(CGISUFFIX)
chmod 755 $(DESTDIR)$(SCRIPTDIR)/calps $(DESTDIR)$(SCRIPTDIR)/hebdate \
$(DESTDIR)$(SCRIPTDIR)/hebps $(DESTDIR)$(SCRIPTDIR)/moon \
$(DESTDIR)$(SCRIPTDIR)/sunrise $(DESTDIR)$(SCRIPTDIR)/sunset \
$(DESTDIR)$(SCRIPTDIR)/hebhtml \
$(DESTDIR)$(SCRIPTDIR)/rem2html$(CGISUFFIX)
-mkdir -p $(DESTDIR)$(IMAGEDIR)
cp firstquarter.png fullmoon.png lastquarter.png newmoon.png rem-default.css $(DESTDIR)$(IMAGEDIR)
chmod 644 $(DESTDIR)$(IMAGEDIR)/firstquarter.png \
$(DESTDIR)$(IMAGEDIR)/fullmoon.png \
$(DESTDIR)$(IMAGEDIR)/lastquarter.png \
$(DESTDIR)$(IMAGEDIR)/newmoon.png \
$(DESTDIR)$(IMAGEDIR)/rem-default.css

View File

@@ -1,24 +1,13 @@
REM2HTML
--------
Rem2HTML is a Perl script which transforms the output of
`remind -p ...' to an HTML table. Type `perl rem2html --help' for
usage information.
Rem2HTML is a Perl script that transforms the output of `remind -p
...' to HTML. Type `perl rem2html --help' for usage information.
Typical usage: remind -p ~/.reminders | rem2html > file.html
You may have to edit the "#!/bin/perl" line to reflect the location
of your Perl interpreter.
If you make Remind output data for more than one month (for example,
remind -p3 ~/.reminders | rem2html > file.html), then rem2html
creates a _SINGLE_ file of concatenated HTML documents. It's up
to you to split them. In this case, you're probably better off
using the --tableonly option to rem2html and adding the appropriate
head and tail with another program.
Rem2HTML was contributed by Don Schwarz <darkowl@mcs.net>. It
produces HTML compatible with most modern browsers, but *not* with
browsers which don't support tables (like Lynx, as of this writing.)
You may have to edit the "#!/usr/bin/perl" line to reflect the
location of your Perl interpreter.
--
David F. Skoll

View File

@@ -6,10 +6,6 @@
# CAL_DISPATCH -- Shell script for CGI directory to dispatch calendar
# commands.
#
# WARNING WARNING WARNING -- If your /bin/sh is really GNU's "bash",
# make sure you don't have a buggy version which treats char 0xFF as
# a command separator -- it is a security risk!
#########################
#

View File

@@ -1,28 +1,28 @@
<HTML>
<!-- Sample HTML file with links to the calendar stuff -->
<HEAD>
<TITLE>David Skoll's Calendar Server</TITLE>
<TITLE>Remind Calendar Server</TITLE>
</HEAD>
<BODY>
<H1>Roaring Penguin's Calendar Server</H1>
<H1>Remind Calendar Server</H1>
<a HREF="http://your.machine/your.dir/cal_dispatch?sunrise">
<a HREF="%CGIDIR%/cal_dispatch?sunrise">
Sunrise Information</a><P>
<a HREF="http://your.machine/your.dir/cal_dispatch?sunset">
<a HREF="%CGIDIR%/cal_dispatch?sunset">
Sunset Information</a><P>
<a HREF="http://your.machine/your.dir/cal_dispatch?moon">
<a HREF="%CGIDIR%/cal_dispatch?moon">
Moon Phase Information</a><P>
<a HREF="http://your.machine/your.dir/cal_dispatch?calps">
<a HREF="%CGIDIR%/cal_dispatch?calps">
Blank PostScript Calendar</a> (Approximately 20kB)<P>
<a HREF="http://your.machine/your.dir/cal_dispatch?hebdate">
<a HREF="%CGIDIR%/cal_dispatch?hebdate">
Today's Hebrew Date</a><P>
<a HREF="http://your.machine/your.dir/cal_dispatch?hebps">
<a HREF="%CGIDIR%/cal_dispatch?hebps">
PostScript Calendar with Jewish Holidays</a> (Approximately 35 kB)<P>
<a HREF="http://your.machine/your.dir/cal_dispatch?hebhtml">
HTML Calendar with Jewish Holidays</a> (Your browser must support tables.)<P>
<a HREF="%CGIDIR%/cal_dispatch?hebhtml">
HTML Calendar with Jewish Holidays</a>
<HR>
<a HREF="http://www.roaringpenguin.com/remind.html">
<a HREF="http://www.roaringpenguin.com/remind">
Get the Remind software</a> that provides this service.<P>
</BODY>

View File

@@ -8,5 +8,5 @@
echo "Content-type: application/postscript"
echo
$REMIND -p /dev/null | $REM2PS -e -c3
$REMIND -p /dev/null | $REM2PS -e -c3 -l
exit 0

View File

@@ -65,7 +65,7 @@ IF !InIsrael
[_h(16, "Tishrey")] MSG %"Sukkot 2%"
ENDIF
[_h(21, "Tishrey")] ++12 MSG %"Hashana Rabba%" is %b.
[_h(21, "Tishrey")] ++12 MSG %"Hoshana Rabba%" is %b.
[_h(22, "Tishrey")] ++12 MSG %"Shemini Atzeret%" is %b.
IF InIsrael
@@ -158,8 +158,8 @@ IF ostart <= TODAY() && (TODAY() - ostart < 49)
ENDIF
IF !$PSCAL
REM 20 ++40 msg Also available: <a HREF="http://your.machine/your.dir/cal_dispatch?hebps">a PostScript calendar</a> (about 35KB) for %m %y, complete with Hebrew dates, Jewish holidays, and moon phases for Ottawa. (This takes a few seconds to generate.)
REM 20 ++40 msg And: <a HREF="http://your.machine/your.dir/cal_dispatch?hebhtml">an HTML version</a> of the above. (Your browser must handle tables.)
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.
ELSE
[trigger(moondate(0))] SPECIAL MOON 0
[trigger(moondate(1))] SPECIAL MOON 1

View File

@@ -12,7 +12,7 @@ echo ""
$REMIND - <<EOR
BANNER %
REM 20 ++40 RUN $REMIND -iHTML=0 -p $DIR/hebdate.rem %m %y | $REM2PS -e -c3
REM 20 ++40 RUN $REMIND -iHTML=0 -p $DIR/hebdate.rem %m %y | $REM2PS -e -c3 -l
EOR
exit 0

View File

@@ -9,11 +9,11 @@ set now now()
banner %
MSG <HEAD>%
MSG <TITLE>Moon over Ottawa</TITLE>%
MSG <TITLE>Moon over [$Location]</TITLE>%
MSG </HEAD>%
MSG <BODY>
MSG <H1>Moon over Ottawa</H1>
MSG <H1>Moon over [$Location]</H1>
set ndate moondate(0)
set ntime moontime(0)
@@ -22,12 +22,12 @@ set ftime moontime(2)
fset t(x) wkday(x)+", "+day(x)+" "+mon(x)+", "+year(x)
msg Today is %w, %d %m, %y.<P>
msg The local time in Ottawa is [now].<P>
msg The local time in [$Location] is [now].<P>
if ndate < fdate
msg The next new moon is on [t(ndate)] at [ntime], Ottawa time.<P>
msg The next full moon is on [t(fdate)] at [ftime], Ottawa time.<P>
msg The next new moon is on [t(ndate)] at [ntime], [$Location] time.<P>
msg The next full moon is on [t(fdate)] at [ftime], [$Location] time.<P>
else
msg The next full moon is on [t(fdate)] at [ftime], Ottawa time.<P>
msg The next new moon is on [t(ndate)] at [ntime], Ottawa time.<P>
msg The next full moon is on [t(fdate)] at [ftime], [$Location] time.<P>
msg The next new moon is on [t(ndate)] at [ntime], [$Location] time.<P>
endif

69
www/rem-default.css Normal file
View File

@@ -0,0 +1,69 @@
table.rem-cal {
font-family: helvetica, arial, sans-serif;
font-size: 12pt;
}
table.rem-sc-table {
font-family: helvetica, arial, sans-serif;
font-size: 10pt;
width: 95%;
float: left;
}
caption.rem-cal-caption {
font-size: 14pt;
font-weight: bold;
}
th.rem-cal-hdr {
width: 14%;
border-style: solid;
border-width: 1px;
vertical-align: top;
}
td.rem-empty, td.rem-cell, td.rem-small-calendar {
width: 14%;
height: 7em;
border-style: solid;
border-width: 1px;
vertical-align: top;
}
td.rem-today {
width: 14%;
height: 7em;
border-style: solid;
border-width: 2px;
border-color: #EE3333;
vertical-align: top;
}
table.rem-cal {
width: 100%;
border-collapse: collapse;
}
div.rem-daynumber {
float: right;
text-align: right;
vertical-align: top;
font-size: 14pt;
}
div.rem-moon {
float: left;
text-align: left;
vertical-align: top;
}
th.rem-sc-hdr {
text-align: right;
}
td.rem-sc-empty-cell, td.rem-sc-cell {
text-align: right;
width: 14%;
}
caption.rem-sc-caption {
font-size: 12pt;
}

View File

@@ -1,102 +1,558 @@
#!/usr/bin/perl
# rem2html
#
# A script to convert from the output of "remind -p" to Hyper-Text Markup
# Language (HTML), the text format used in WWW documents. By default, it
# outputs a stand-alone file which can be fed directly into a web browser.
# The output uses nested <TABLE> blocks, so it will only work in a browser
# which supports tables (Netscape, MSIE, etc, NOT Lynx).
#
# This script works well on my computer (Linux 2.0.27) under Perl 5.003 and
# 5.004. It should work fine on other unices but I have no idea whether
# it will run under VMS, OS/2, Windows, etc.
#
# [Note from David: The REMIND license prohibits you from using REMIND
# under Windows.]
#
# Rem2html puts "normal" CAL or MSG-type reminders in a <P></P> pair,
# and escapes any special HTML characters.
#
# If you want to put actual HTML code in the calendar (hyper-links, for
# example), use the "SPECIAL HTML" type. For example:
#
# REM Wed SPECIAL HTML <P> Meeting at \
# <A HREF="http://www.linuxhq.com">Linux HQ</A> </P>
#
# This file is Copyright (C) 1997-8 by Don Schwarz <darkowl@mcs.net>
# and David F. Skoll
use strict;
use warnings;
use Getopt::Long;
@months = (January,February,March,April,May,June,July,August,September,October,November,December);
my %Options;
$DefaultImageDir = "%IMAGEBASE%";
if ($DefaultImageDir =~ m@/$@) {
chop $DefaultImageDir;
}
my $rem2html_version = '2.0';
$rem2html_version = "1.0";
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
@Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);
&parse_options();
my $TIDY_PROGNAME = $0;
$TIDY_PROGNAME =~ s|^.*/||;
# Backgound color -- unfortunately, most Web browsers (as of 14 October 1999)
# do not correctly handle transparency in PNG images, so I had to make the
# moon image background white.
$Options{'bgcolor'} ||= "BGCOLOR=\"#FFFFFF\"";
# rem2html -- convert the output of "remind -p" to HTML
if (-t STDIN) {
print STDERR "(Rem2HTML: Input should not come from a terminal.)\n";
$Options{'help'} = 1;
}
=head1 NAME
if ($Options{'help'}) {
&show_usage();
} elsif ($Options{'version'}) {
print "Rem2HTML Version $rem2html_version.\n";
} else {
$successes = 0;
while(1) {
last if (!parse_input());
$successes++;
&output_header();
&output_data();
&output_footer();
}
print STDERR "Rem2HTML: Couldn't find any calendar data.\n" if (!$successes);
}
rem2html - Convert the output of "remind -p" to HTML
exit(0);
=head1 SYNOPSIS
sub show_usage {
print STDERR <<EndOfUsage;
Rem2HTML: Produce a HTML calendar from the output of Remind.
remind -p ... | rem2html [options]
Usage: rem2html [options]
=head1 OPTIONS
=over 4
=item --help, -h
Print usage information
=item --version
Print version
=item --backurl I<url>
When producing the small calendar for the previous month, make the
month name a link to I<url>.
=item --forwurl I<url>
When producing the small calendar for the next month, make the
month name a link to I<url>.
=item --imgbase I<url>
When creating URLs for images and the stylesheet, use
I<url> as the base URL.
=item --stylesheet I<url.css>
Use I<url.css> as the stylesheet. If this option is used,
I<url.css> is I<not> interpreted relative to B<imgbase>.
=item --nostyle
Produce basic HTML that does not use a CSS stylesheet.
=item --tableonly
Output results as a E<lt>tableE<gt> ... E<lt>/tableE<gt> sequence only
without any E<lt>htmlE<gt> or E<lt>bodyE<gt> tags.
=item --title I<title>
Use I<title> as the content between E<lt>titleE<gt> and E<lt>/titleE<gt>
tags.
=item --prologue I<html_text>
Insert I<html_text> right after the E<lt>bodyE<gt> tag.
=item --epilogue I<html_text>
Insert I<html_text> right before the E<lt>/bodyE<gt> tag.
=back
=head1 AUTHOR
rem2html was written by David F. Skoll with much inspiration from an
earlier version by Don Schwarz.
=cut
sub usage
{
my ($exit_status) = @_;
if (!defined($exit_status)) {
$exit_status = 1;
}
print STDERR <<"EOM";
$TIDY_PROGNAME: Produce an HTML calendar from the output of "remind -p"
Usage: remind -p ... | rem2html [options]
Options:
--help, -h Print this information
--version Version information
-p file Prepend the specified HTML file to the beginning of the
output
-a file Append the specified HTML file to the end of the output
-f[shted] font Set font for small cal, hdr, title, cal entries,day numbers
-s[hted] size Set size for header, title, calendar entries and/or day
numbers
--backurl url Make the title on the previous month's small calendar entry
a hyperlink to <url>
--forwurl url Same as --backurl, but with the next month's small calendar
--tableonly Output the results as a <TABLE> block only, no <HTML>, etc.
--border,-b size Set the border thickness of the table
--cellspace,-t size Set the line thickness of the table
--bgcolor,-g color Set the background color for the day entries
EndOfUsage
--help, -h Print usage information
--man Show man page (requires "perldoc")
--version Print version
--backurl url Make the title on the previous month's small calendar
entry a link to <url>
--forwurl url Same as --backurl, but for the next month's small calendar
--imgbase url Base URL of images and default stylesheet file
--stylesheet url.css URL of CSS stylesheet. If specified, imgbase is NOT
prepended to url.css
--nostyle Produce basic HTML that does not use a CSS stylesheet
--tableonly Output results as a <table> only, no <html>, <body>, etc.
--title string What to put in <title>...</title> tags
--prologue html_text Text to insert at the top of the body
--epilogue html_text Text to insert at the end of the body
EOM
exit($exit_status);
}
sub escape_html {
sub parse_options
{
local $SIG{__WARN__} = sub { print STDERR "$TIDY_PROGNAME: $_[0]\n"; };
if (!GetOptions(\%Options, "help|h",
"man",
"version",
"stylesheet=s",
"nostyle",
"backurl=s",
"forwurl=s",
"title=s",
"prologue=s",
"epilogue=s",
"imgbase=s",
"tableonly")) {
usage(1);
}
$Options{'title'} ||= 'HTML Calendar';
# Fix up imgbase
my $imgbase = '%IMAGEBASE%';
if ($imgbase ne '%' . 'IMAGEBASE' . '%') {
$Options{'imgbase'} ||= $imgbase;
} else {
$Options{'imgbase'} ||= '';
}
$Options{'imgbase'} =~ s|/+$||;
my $stylesheet = $Options{'imgbase'};
$stylesheet .= '/' if ($stylesheet ne '');
$stylesheet .= 'rem-default.css';
$Options{'stylesheet'} ||= $stylesheet;
}
sub start_output
{
return if ($Options{'tableonly'});
print("<html>\n<head>\n<title>" . $Options{'title'} . "</title>\n");
if (!$Options{'nostyle'}) {
if ($Options{'stylesheet'}) {
print('<link rel="stylesheet" type="text/css" href="' .
$Options{'stylesheet'} . '">' . "\n");
}
}
print("</head>\n<body>\n");
if ($Options{'prologue'}) {
print $Options{'prologue'} . "\n";
}
}
sub end_output
{
return if ($Options{'tableonly'});
if ($Options{'epilogue'}) {
print $Options{'epilogue'} . "\n";
}
print("</body>\n</html>\n");
}
sub parse_input
{
undef $days;
undef $shades;
undef $moons;
undef $classes;
undef $weeks;
my $found_data = 0;
while(<STDIN>) {
chomp;
last if /^\# rem2ps begin$/;
}
my $line;
# Month Year numdays firstday monday_first_flag
$line = <STDIN>;
return 0 unless $line;
chomp($line);
($Month, $Year, $Numdays, $Firstwkday, $Mondayfirst) = split(' ', $line);
# Day names
$line = <STDIN>;
return 0 unless $line;
chomp($line);
@Daynames = split(' ', $line);
# Prevmon prevlen
$line = <STDIN>;
return 0 unless $line;
chomp($line);
($Prevmon, $Prevlen) = split(' ', $line);
# Nextmon nextlen
$line = <STDIN>;
return 0 unless $line;
chomp($line);
($Nextmon, $Nextlen) = split(' ', $line);
$found_data = 1;
my $class;
if ($Options{'nostyle'}) {
$class = '';
} else {
$class = ' class="rem-entry"';
}
while(<STDIN>) {
chomp;
last if /^\# rem2ps end$/;
next if /^\#/;
next unless m/^(\d*).(\d*).(\d*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/;
my ($y, $m, $d, $special, $tag, $duration, $time, $body) =
($1, $2, $3, $4, $5, $6, $7, $8);
my $d1 = $d;
$d1 =~ s/^0+//;
if ($special eq 'HTML') {
push(@{$days->[$d]}, $body);
} elsif ($special eq 'HTMLCLASS') {
$classes->[$d] = $body;
} elsif ($special eq 'WEEK') {
$body =~ s/^\s+//;
$body =~ s/\s+$//;
$weeks->{$d1} = $body;
} elsif ($special eq 'MOON') {
if ($body =~ /(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
my ($phase, $moonsize, $fontsize, $msg) = ($1, $2, $3, $4);
$moons->[$d]->{'phase'} = $phase;
$moons->[$d]->{'msg'} = $msg;
} elsif ($body =~ /(\S+)/) {
$moons->[$d]->{'phase'} = $1;
$moons->[$d]->{'msg'} = '';
}
} elsif ($special eq 'SHADE') {
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)/) {
$shades->[$d] = sprintf("#%02X%02X%02X",
($1 % 256), ($2 % 256), ($3 % 256));
}
} elsif ($special eq 'COLOR') {
if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/) {
my($r, $g, $b, $text) = ($1, $2, $3, $4);
my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
$r % 256, $g % 256, $b % 256);
push(@{$days->[$d]}, "<p$class $color>" . escape_html($text) . '</p>');
}
} elsif ($special eq '*') {
push(@{$days->[$d]}, "<p$class>" . escape_html($body) . '</p>');
}
}
return $found_data;
}
sub small_calendar
{
my($month, $monlen, $url, $first_col) = @_;
if ($Mondayfirst) {
$first_col--;
if ($first_col < 0) {
$first_col = 6;
}
}
if ($Options{'nostyle'}) {
print "<td width=\"14%\">\n";
print "<table border=\"0\">\n";
print "<caption>";
} else {
print "<td class=\"rem-small-calendar\">\n";
print "<table class=\"rem-sc-table\">\n";
print "<caption class=\"rem-sc-caption\">";
}
print "<a href=\"$url\">" if ($url);
print $month;
print "</a>" if ($url);
print "</caption>\n";
my $class;
if ($Options{'nostyle'}) {
print '<tr>';
$class = ' align="right"';
} else {
print '<tr class="rem-sc-hdr-row">';
$class = ' class="rem-sc-hdr"';
}
if (!$Mondayfirst) {
print "<th$class>" . substr($Daynames[0], 0, 1) . '</th>';
}
for (my $i=1; $i<7; $i++) {
print "<th$class>" . substr($Daynames[$i], 0, 1) . '</th>';
}
if ($Mondayfirst) {
print "<th$class>" . substr($Daynames[0], 0, 1) . '</th>';
}
print("</tr>\n");
my $col = 0;
for (; $col<$first_col; $col++) {
if ($col == 0) {
print("<tr>\n");
}
if ($Options{'nostyle'}) {
print("<td align=\"right\" width=\"14%\">&nbsp;</td>");
} else {
print("<td class=\"rem-sc-empty-cell\">&nbsp;</td>");
}
}
for (my $day=1; $day <= $monlen; $day++) {
if ($col == 0) {
print("<tr>\n");
}
$col++;
if ($Options{'nostyle'}) {
print("<td align=\"right\" width=\"14%\">$day</td>");
} else {
print("<td class=\"rem-sc-cell\">$day</td>");
}
if ($col == 7) {
print("</tr>\n");
$col = 0;
}
}
if ($col) {
while ($col < 7) {
if ($Options{'nostyle'}) {
print("<td align=\"right\" width=\"14%\">&nbsp;</td>");
} else {
print("<td class=\"rem-sc-empty-cell\">&nbsp;</td>");
}
$col++;
}
print("</tr>\n");
}
print("</table>\n");
print "</td>\n";
}
sub output_calendar
{
# Which column is 1st of month in?
my $first_col = $Firstwkday;
if ($Mondayfirst) {
$first_col--;
if ($first_col < 0) {
$first_col = 6;
}
}
# Last column
my $last_col = ($first_col + $Numdays - 1) % 7;
# Start the table
my $class;
if ($Options{'nostyle'}) {
print '<table width="100%" border="1" cellspacing=\"0\"><caption>' .
$Month . ' ' . $Year . '</caption>' . "\n";
print '<tr>';
$class = ' width="14%"';
} else {
print '<table class="rem-cal"><caption class="rem-cal-caption">' .
$Month . ' ' . $Year . '</caption>' . "\n";
print '<tr class="rem-cal-hdr-row">';
$class = ' class="rem-cal-hdr"';
}
if (!$Mondayfirst) {
print "<th$class>" . $Daynames[0] . '</th>';
}
for (my $i=1; $i<7; $i++) {
print "<th$class>" . $Daynames[$i] . '</th>';
}
if ($Mondayfirst) {
print "<th$class>" . $Daynames[0] . '</th>';
}
print "</tr>\n";
# Start the calendar rows
my $col = 0;
if ($Options{'nostyle'}) {
print "<tr>\n";
} else {
print "<tr class=\"rem-cal-row\">\n";
}
if ($first_col > 0) {
small_calendar($Prevmon, $Prevlen, $Options{'backurl'},
($Firstwkday - $Prevlen + 35) % 7);
$col++;
}
if ($last_col == 6 && $first_col > 0) {
small_calendar($Nextmon, $Nextlen, $Options{'forwurl'},
($Firstwkday + $Numdays) % 7);
$col++;
}
if ($Options{'nostyle'}) {
$class = ' width="14%"';
} else {
$class = ' class="rem-empty"';
}
while ($col < $first_col) {
print("<td$class>&nbsp;</td>\n");
$col++;
}
for (my $day=1; $day<=$Numdays; $day++) {
draw_day_cell($day);
$col++;
if ($col == 7) {
$col = 0;
print "</tr>\n";
if ($day < $Numdays) {
if ($Options{'nostyle'}) {
print "<tr>\n";
} else {
print "<tr class=\"rem-cal-row\">\n";
}
}
}
}
if ($col) {
while ($col < 7) {
if ($col == 5) {
if ($first_col == 0) {
small_calendar($Prevmon, $Prevlen, $Options{'backurl'},
($Firstwkday - $Prevlen + 35) % 7);
} else {
print("<td$class>&nbsp;</td>\n");
}
} elsif ($col == 6) {
small_calendar($Nextmon, $Nextlen, $Options{'forwurl'},
($Firstwkday + $Numdays) % 7);
} else {
print("<td$class>&nbsp;</td>\n");
}
$col++;
}
print "</tr>\n";
}
# Add a row for small calendars if they were not yet done!
if ($first_col == 0 && $last_col == 6) {
if ($Options{'nostyle'}) {
print "<tr>\n";
} else {
print "<tr class=\"rem-cal-row\">\n";
}
small_calendar($Prevmon, $Prevlen, $Options{'backurl'},
($Firstwkday - $Prevlen + 35) % 7);
for (my $i=0; $i<5; $i++) {
print("<td$class>&nbsp;</td>\n");
}
small_calendar($Nextmon, $Nextlen, $Options{'forwurl'},
($Firstwkday + $Numdays) % 7);
print("</tr>\n");
}
# End the table
print "</table>\n";
}
sub draw_day_cell
{
my($day) = @_;
my $shade = $shades->[$day];
my $week = '';
if (exists($weeks->{$day})) {
$week = ' ' . $weeks->{$day};
}
my $class;
if ($Options{'nostyle'}) {
$class = $classes->[$day] || '';
} else {
$class = $classes->[$day] || "rem-cell";
}
if ($shade) {
$shade = " style=\"background: $shade;\"";
} else {
$shade = "";
}
if ($class ne '') {
print "<td class=\"$class\"$shade>\n";
} else {
print "<td valign=\"top\" $shade>\n";
}
if ($moons->[$day]) {
my $phase = $moons->[$day]->{'phase'};
my $msg = $moons->[$day]->{'msg'};
$msg ||= '';
if ($msg ne '') {
$msg = '&nbsp;' . escape_html($msg);
}
my $img;
my $alt;
my $title;
if ($phase == 0) {
$img = 'newmoon.png';
$title = 'New Moon';
$alt = 'new';
} elsif ($phase == 1) {
$img = 'firstquarter.png';
$title = 'First Quarter';
$alt = '1st';
} elsif ($phase == 2) {
$img = 'fullmoon.png';
$alt = 'full';
$title = 'Full Moon';
} else {
$img = 'lastquarter.png';
$alt = 'last';
$title = 'Last Quarter';
}
if ($Options{'imgbase'}) {
$img = $Options{'imgbase'} . '/' . $img;
}
if ($Options{'nostyle'}) {
print("<div style=\"float: left\"><img border=\"0\" width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
} else {
print("<div class=\"rem-moon\"><img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
}
}
if ($Options{'nostyle'}) {
print "<div style=\"float: right\">$day$week</div>\n";
print "<p>&nbsp;</p>\n";
} else {
print "<div class=\"rem-daynumber\">$day$week</div>\n";
}
if ($days->[$day]) {
print(join("\n", @{$days->[$day]}));
}
print "</td>\n";
}
sub escape_html
{
my($in) = @_;
$in =~ s/\&/\&amp;/g;
$in =~ s/\</\&lt;/g;
@@ -104,313 +560,35 @@ sub escape_html {
return $in;
}
sub parse_options {
%Options = ();
GetOptions (\%Options, "help|h",
"version",
"border|b=i",
"cellspace|t=i",
"backurl|bu:s", "forwurl|fu:s",
"tableonly|to",
"prologue|p=s",
"append|a=s",
"bgcolor|g=s",
"fs=s", "fh=s", "ft=s", "fe=s", "fd=s",
"sh=i", "st=i", "se=i", "sd=i",
);
$Options{'border'} = "BORDER=" . ($Options{'border'} || 1);
$Options{'cellspace'} &&= "CELLSPACING=$Options{'cellspace'}";
$Options{'bgcolor'} &&= "BGCOLOR=$Options{'bgcolor'}";
parse_options();
if ($Options{'help'}) {
usage(0);
exit(0);
} elsif ($Options{'man'}) {
system("perldoc $0");
exit(0);
} elsif ($Options{'version'}) {
print "rem2html version $rem2html_version.\n";
exit(0);
}
sub parse_input {
local $where = 0;
local $msg;
local $type;
local $day;
@days = ();
@shades = ();
@moons = ();
while (<>) {
chomp($_);
if (/rem2(html|ps) begin/) {
} elsif (!$where) {
next;
} elsif ($where == 1) {
local ($month, $year);
($month, $year, $month_length, $firstday, $mfirst) = split(" ");
$caption = "$month, $year";
for $i ( 1 .. $month_length) { push(@days, ""); }
} elsif ($where == 2) {
@DayNames = split(" ");
} elsif ($where == 3) {
@prevsc = split(" ");
} elsif ($where == 4) {
@nextsc = split(" ");
} else {
return 1 if /rem2(html|ps) end/;
next if /^\#/;
next unless m/^(\d*).(\d*).(\d*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/;
$type = $4;
$msg = $8;
$day = $3;
if ($type eq "HTML") {
$days[$day] .= "$msg ";
} elsif ($type eq "MOON") {
my($phase, $text);
if ($msg =~ /^\s*(\d+)\s+\S+\s+\S+\s+(.*)$/) {
$phase = $1;
$text = $2;
} elsif ($msg =~ /^\s*(\d+)/) {
$phase = $1;
$text = "";
} else {
next;
}
next if ($phase > 3);
if ($phase == 0) {
$text = "<IMAGE SRC=\"$DefaultImageDir/newmoon.png\" ALT=\"New Moon\"" .
"WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
} elsif ($phase == 1) {
$text = "<IMAGE SRC=\"$DefaultImageDir/firstquarter.png\" ALT=\"First Quarter\"" .
"WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
} elsif ($phase == 2) {
$text = "<IMAGE SRC=\"$DefaultImageDir/fullmoon.png\" ALT=\"Full Moon\"" .
"WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
} else {
$text = "<IMAGE SRC=\"$DefaultImageDir/lastquarter.png\" ALT=\"Last Quarter\"" .
"WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
}
$moons[$day] = $text . "</FONT> ";
} elsif ($type eq "SHADE") {
my($red, $green, $blue);
if ($msg =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s*$/) {
$red = $1;
$green = $2;
$blue = $3;
} elsif ($msg =~ /^\s*(\d+)\s*$/) {
$red = $1;
$green = $1;
$blue = $1;
} else {
next;
}
next if ($red > 255 || $green > 255 || $blue > 255);
$shades[$day] = sprintf(" BGCOLOR=\"#%02X%02X%02X\"",
$red, $green, $blue);
} elsif ($type eq "COLOR") {
my($red, $green, $blue, $stuff);
if ($msg =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s*(.*)$/) {
$red = $1;
$green = $2;
$blue = $3;
$stuff = $4;
next if ($red > 255 || $green > 255 || $blue > 255);
$days[$day] .=
sprintf("<P><font color=\"#%02x%02x%02x\">",
$red, $green, $blue) .
escape_html($stuff) . "</font></P>";
} else {
next;
}
} elsif ($type eq "*") {
$msg = &escape_html($msg);
$days[$day] .= "<P>$msg</P>";
}
}
$where++;
}
if ($where) {
return 1;
}
return 0;
if (-t STDIN) {
print STDERR "$TIDY_PROGNAME: Input should not come from a terminal.\n\n";
usage(1);
}
sub output_header {
local ($title, $dayheader);
if (!$Options{'tableonly'}) {
print <<EndOfHTML;
<HTML>
<HEAD><TITLE>Reminders for $caption</TITLE></HEAD>
<BODY>
EndOfHTML
}
print <<EndOfHTML;
<!-- This output was produced by Rem2HTML $rem2html_version (written by
Don Schwarz <darkowl\@mcs.net>) and Remind (written by David F. Skoll). -->
EndOfHTML
if ($Options{'prologue'}) {
open(PROLOGUE, "< $Options{'prologue'}");
print while ( <PROLOGUE> );
close(PROLOGUE);
}
$caption = &format_font($caption, $Options{'ft'}, $Options{'st'} || "+1");
print <<EndOfHTML;
<TABLE $Options{'border'} $Options{'cellspace'} $Options{'bgcolor'} WIDTH=100%>
<CAPTION><STRONG>$caption</STRONG></CAPTION>
<TR>
EndOfHTML
$mfirst || &print_day_header($DayNames[0]);
for($i=1; $i<7; $i++) {
&print_day_header($DayNames[$i]);
}
$mfirst && &print_day_header($DayNames[0]);
print " </TR>\n";
my $found_something = 0;
while(1) {
last if (!parse_input());
start_output() unless $found_something;
$found_something = 1;
output_calendar();
}
if ($found_something) {
end_output();
exit(0);
} else {
print STDERR "$TIDY_PROGNAME: Could not find any calendar data on STDIN.\n";
exit(1);
}
sub output_footer {
print "</TABLE>\n";
if ($Options{'append'}) {
open(EPILOGUE, "< $Options{'append'}");
print while ( <EPILOGUE> );
close(EPILOGUE);
}
unless ($Options{'tableonly'}) {
print <<EndOfHTML;
</BODY>
</HTML>
EndOfHTML
}
}
sub output_data {
local ($endday, $prevday, $nextday, $week, $weekday);
local ($element, $day, $msg, $fday);
$firstday -= $mfirst;
if ($firstday < 0) { $firstday += 7; }
$endday = $firstday + $month_length;
$endweek = $endday + (6 - ($endday % 7));
$endday %= 7;
if ( $firstday > 1 ) {
$prevday = 0;
$nextday = 1;
} elsif ($endday ? ($endday < 5) : !$firstday) {
$prevday = $endweek - 1;
$nextday = $endweek;
} else {
$prevday = 0;
$nextday = $endweek;
}
for $week ( 0..5 ) {
print " <TR>\n";
for $weekday ( 0..6 ) {
$element = ($week * 7) + ($weekday * 1);
$day = $element - $firstday + 1;
$msg = $days[$day];
$msg = $msg ? &format_font($msg, $Options{'fe'}, $Options{'se'})
: " <BR> <BR> <BR> <BR>";
$fday = &format_font($day, $Options{'fd'}, $Options{'sd'} || -1);
if ($day > 0 && $day <= $month_length) {
print <<EndOfHTML;
<TD HEIGHT="120" VALIGN=TOP WIDTH=14%$shades[$day]>
<P ALIGN=RIGHT>$moons[$day]$fday</P>
$msg
</TD>
EndOfHTML
} elsif ($element == $prevday) {
&small_calendar(@prevsc, 1, $Options{'backurl'});
} elsif ($element == $nextday) {
&small_calendar(@nextsc, 2, $Options{'forwurl'});
} else {
print " <TD WIDTH=14%><BR></TD>";
}
}
print " </TR>\n";
last if $day >= $month_length && $element >= $nextday;
}
}
sub small_calendar {
local ($scname, $scn, $which, $url) = @_;
local ($scstart, $l, $week, $weekday, $tday);
$scname = "<A HREF=\"$url\">$scname</A>", if $url;
$scname = &format_font($scname, $Options{'fs'}, -2);
if ($which == 1) {
$scstart = $firstday - ($scn % 7);
if ($scstart < 0) { $scstart += 7; }
} else {
$scstart = $firstday + ($month_length % 7);
if ($scstart > 6) { $scstart -= 7; }
}
print <<EndOfHTML;
<TD WIDTH=14% VALIGN=TOP>
<TABLE WIDTH=100%>
<CAPTION><STRONG>$scname</STRONG></CAPTION>
<TR>
EndOfHTML
$mfirst || &print_day_header(substr($DayNames[0], 0, 1), 1);
for ($i=1; $i<7; $i++) {
&print_day_header(substr($DayNames[$i], 0, 1), 1);
}
$mfirst && &print_day_header(substr($DayNames[0], 0, 1), 1);
print "</TR>\n";
for $week ( 0..5 ) {
print " <TR>\n";
for $weekday ( 0..6 ) {
$tday = ($week * 7) + ($weekday * 1) - $scstart + 1;
$tday = "", if $tday <= 0 || $tday > $scn;
print " <TD WIDTH=14%><FONT SIZE=-2>$tday</FONT></TD>\n";
}
print " </TR>\n";
last if $tday >= $scn;
}
print <<EndOfHTML;
</TABLE>
</TD>
EndOfHTML
}
sub format_font {
local ($text, $font, $size) = @_;
if (!$text) {
return "";
} elsif ($font && $size) {
return "<FONT FACE=$font SIZE=$size>$text</FONT>";
} elsif ($font) {
return "<FONT FACE=$font>$text</FONT>";
} elsif ($size) {
return "<FONT SIZE=$size>$text</FONT>";
} else {
return $text;
}
}
sub print_day_header {
local ($dheader, $small) = @_;
if ($small) {
$dheader = &format_font($dheader, $Options{'fs'}, -2);
} else {
$dheader = &format_font($dheader, $Options{'fh'}, $Options{'sh'});
}
print " <TH WIDTH=14%>$dheader</TH>\n";
}

View File

@@ -8,11 +8,11 @@ set now now()
banner %
MSG <HEAD>%
MSG <TITLE>Sunrise in Ottawa</TITLE>%
MSG <TITLE>Sunrise in [$Location]</TITLE>%
MSG </HEAD>%
MSG <BODY>
MSG <H1>Sunrise in Ottawa</H1>
MSG <H1>Sunrise in [$Location]</H1>
set tod sunrise(today())
set tom sunrise(today()+1)
@@ -34,7 +34,7 @@ set fn iif(diff==0, "now", diff <0, "ago", "from now")
set iw iif(diff<0, "was", "is")
set aw iif(tod==tom, " as well.", ".")
msg Today is %w, %d %m, %y.<P>
msg The local time in Ottawa is [now].<P>
msg The local time in [$Location] is [now].<P>
msg Sunrise today [iw] at [tod]; in other words, [hstr][astr][mstr] [fn].<P>
msg Sunrise tomorrow is at [tom][aw]<P>
msg The daylight portion of tomorrow will be [slen] today.<P>

View File

@@ -9,11 +9,11 @@ set now now()
banner %
MSG <HEAD>%
MSG <TITLE>Sunset in Ottawa</TITLE>%
MSG <TITLE>Sunset in [$Location]</TITLE>%
MSG </HEAD>%
MSG <BODY>
MSG <H1>Sunset in Ottawa</H1>
MSG <H1>Sunset in [$Location]</H1>
set tod sunset(today())
set tom sunset(today()+1)
@@ -35,7 +35,7 @@ set fn iif(diff==0, "now", diff <0, "ago", "from now")
set iw iif(diff<0, "was", "is")
set aw iif(tod==tom, " as well.", ".")
msg Today is %w, %d %m, %y.<P>
msg The local time in Ottawa is [now].<P>
msg The local time in [$Location] is [now].<P>
msg Sunset today [iw] at [tod]; in other words, [hstr][astr][mstr] [fn].<P>
msg Sunset tomorrow is at [tom][aw]<P>
msg The daylight portion of tomorrow will be [slen] today.<P>