Compare commits

..

44 Commits

Author SHA1 Message Date
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
34 changed files with 1110 additions and 324 deletions

2
configure vendored
View File

@@ -5284,7 +5284,7 @@ _ACEOF
fi
done
VERSION=03.01.05
VERSION=03.01.06
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h"

View File

@@ -45,6 +45,6 @@ if test "$GCC" = yes; then
fi
AC_CHECK_FUNCS(setenv unsetenv glob)
VERSION=03.01.05
VERSION=03.01.06
AC_SUBST(VERSION)
AC_OUTPUT(src/Makefile www/Makefile src/version.h)

View File

@@ -1,5 +1,33 @@
CHANGES TO REMIND
* 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

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
@@ -182,13 +182,14 @@ Echo lines when displaying error messages
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
@@ -337,6 +338,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]
@@ -794,6 +796,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
@@ -1226,11 +1272,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
@@ -1888,6 +1934,10 @@ $IgnoreOnce, $InfDelta, and $NextMode are not meaningful.
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.
.PP
.B BUILT-IN FUNCTIONS
.PP
@@ -2093,6 +2143,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
@@ -2170,7 +2251,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.)
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 hebdate(i_day, s_hebmon [,idq_yrstart [,i_jahr [,i_aflag]]])
Support for Hebrew dates - see the section "The Hebrew Calendar"
@@ -2284,10 +2366,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
@@ -2310,36 +2393,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()
@@ -2449,11 +2526,13 @@ 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 strlen(s_str)
Returns the length of \fIstr\fR.
@@ -2596,6 +2675,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
@@ -2644,8 +2738,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.
@@ -3073,6 +3175,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
@@ -3412,24 +3518,26 @@ to ensure that the daemon is not killed when you log out.
.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
@@ -3823,6 +3931,16 @@ 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
@@ -4079,8 +4197,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

View File

@@ -208,6 +208,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
@@ -747,6 +753,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
@@ -2796,7 +2809,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
}

View File

@@ -748,7 +748,7 @@ 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 *e;
@@ -763,7 +763,7 @@ static int DoCalRem(ParsePtr p, int col)
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;
@@ -793,7 +793,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;
}
@@ -845,7 +845,7 @@ static int DoCalRem(ParsePtr p, int col)
DBufInit(&obuf);
if ((jul == JulianToday) ||
(DoSimpleCalDelta &&
ShouldTriggerReminder(&trig, &tim, jul))) {
ShouldTriggerReminder(&trig, &tim, jul, &err))) {
NumTriggered++;
if (DoSimpleCalendar || tim.ttime != NO_TIME) {
@@ -1265,7 +1265,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

@@ -148,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 */
/*---------------------------------------------------------------------*/

View File

@@ -148,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 */
/*---------------------------------------------------------------------*/

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,7 +56,7 @@ 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 == SAT_TYPE) {
@@ -90,7 +90,7 @@ int DoRem(ParsePtr p)
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;
}
@@ -103,7 +103,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;
@@ -121,7 +121,7 @@ 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;
@@ -145,13 +145,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 */
@@ -187,7 +190,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;
@@ -257,6 +260,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;
@@ -266,6 +273,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;
@@ -322,7 +339,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;
@@ -354,7 +371,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;
@@ -551,7 +570,7 @@ 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;
DynamicBuffer pre_buf;
@@ -744,7 +763,7 @@ int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
DBufFree(&buf);
NumTriggered++;
return OK;
}
}
/***************************************************************/
/* */
@@ -752,12 +771,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)
@@ -786,7 +806,7 @@ int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul)
return 1;
}
}
return ShouldTriggerBasedOnWarn(t, jul);
return ShouldTriggerBasedOnWarn(t, jul, err);
}
/* Move back by delta days, if any */
@@ -795,9 +815,22 @@ 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;
}
}
}
@@ -824,7 +857,7 @@ int DoSatRemind(Trigger *trig, TimeTrig *tim, ParsePtr p)
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;
}
@@ -953,12 +986,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 const *s;
int r;
int r, omit;
Value v;
int lastReturnVal = 0; /* Silence compiler warning */
@@ -996,9 +1029,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

@@ -118,6 +118,7 @@
#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
@@ -232,7 +233,8 @@ EXTERN char *ErrMsg[]
"Expecting number",
"Bad function in WARN clause",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
}
#endif /* MK_GLOBALS */
;

View File

@@ -737,6 +737,8 @@ static int Add(void)
Value v1, v2, v3;
int r;
size_t l1, l2;
PopValStack(v2);
if ( (r = FnPopValStack(&v1)) ) {
DestroyValue(v2);
@@ -791,7 +793,13 @@ static int Add(void)
return r;
}
v3.type = STR_TYPE;
v3.v.str = 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;

View File

@@ -723,7 +723,7 @@ static void DestroyCache(CachedFile *cf)
/***************************************************************/
int TopLevel(void)
{
return !IStackPtr;
return IStackPtr <= 1;
}
/***************************************************************/

View File

@@ -70,6 +70,7 @@ static int FDaysinmon (void);
static int FDefined (void);
static int FDosubst (void);
static int FEasterdate (void);
static int FEvalTrig (void);
static int FFiledate (void);
static int FFiledatetime (void);
static int FFiledir (void);
@@ -128,6 +129,7 @@ static int FRealtoday (void);
static int FToday (void);
static int FTrigger (void);
static int FTzconvert (void);
static int FWeekno (void);
static int CheckArgs (Operator *f, int nargs);
static int CleanUpAfterFunc (void);
static int SunStuff (int rise, double cosz, int jul);
@@ -163,6 +165,11 @@ extern int ValStackPtr;
from 0 to (Nargs - 1) */
#define ARG(x) (ValStack[ValStackPtr - Nargs + (x)])
#define ARGV(x) ARG(x).v.val
#define ARGSTR(x) ARG(x).v.str
#define ASSERT_TYPE(x, t) if (ARG(x).type != t) return E_BAD_TYPE
/* Macro for getting date part of a date or datetime value */
#define DATEPART(x) ((x).type == DATE_TYPE ? (x).v.val : ((x).v.val / MINUTES_PER_DAY))
@@ -202,6 +209,7 @@ Operator Func[] = {
{ "dosubst", 1, 3, FDosubst },
{ "dusk", 0, 1, FDusk },
{ "easterdate", 1, 1, FEasterdate },
{ "evaltrig", 1, 2, FEvalTrig },
{ "filedate", 1, 1, FFiledate },
{ "filedatetime", 1, 1, FFiledatetime },
{ "filedir", 0, 0, FFiledir },
@@ -258,6 +266,7 @@ Operator Func[] = {
{ "upper", 1, 1, FUpper },
{ "value", 1, 2, FValue },
{ "version", 0, 0, FVersion },
{ "weekno", 0, 3, FWeekno },
{ "wkday", 1, 1, FWkday },
{ "wkdaynum", 1, 1, FWkdaynum },
{ "year", 1, 1, FYear }
@@ -409,15 +418,37 @@ static int FBaseyr(void)
static int FDate(void)
{
int y, m, d;
if (ARG(0).type != INT_TYPE ||
ARG(1).type != INT_TYPE ||
ARG(2).type != INT_TYPE) return E_BAD_TYPE;
y = ARG(0).v.val;
m = ARG(1).v.val - 1;
d = ARG(2).v.val;
int ytemp, mtemp, dtemp;
if (!DateOK(y, m, d)) return E_BAD_DATE;
/* Any arg can be a date (in which case we use the corresponding
component) or an integer */
if (HASDATE(ARG(0))) {
FromJulian(DATEPART(ARG(0)), &ytemp, &mtemp, &dtemp);
y = ytemp;
} else {
ASSERT_TYPE(0, INT_TYPE);
y = ARGV(0);
}
if (HASDATE(ARG(1))) {
FromJulian(DATEPART(ARG(1)), &ytemp, &mtemp, &dtemp);
m = mtemp;
} else {
ASSERT_TYPE(1, INT_TYPE);
m = ARGV(1) - 1;
}
if (HASDATE(ARG(2))) {
FromJulian(DATEPART(ARG(2)), &ytemp, &mtemp, &dtemp);
d = dtemp;
} else {
ASSERT_TYPE(2, INT_TYPE);
d = ARGV(2);
}
if (!DateOK(y, m, d)) {
return E_BAD_DATE;
}
RetVal.type = DATE_TYPE;
RetVal.v.val = Julian(y, m, d);
return OK;
@@ -442,27 +473,27 @@ static int FDateTime(void)
case 2:
if (ARG(0).type != DATE_TYPE ||
ARG(1).type != TIME_TYPE) return E_BAD_TYPE;
RetVal.v.val = (MINUTES_PER_DAY * ARG(0).v.val) + ARG(1).v.val;
RetVal.v.val = (MINUTES_PER_DAY * ARGV(0)) + ARGV(1);
return OK;
case 3:
if (ARG(0).type != DATE_TYPE ||
ARG(1).type != INT_TYPE ||
ARG(2).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(1).v.val < 0 || ARG(2).v.val < 0) return E_2LOW;
if (ARG(1).v.val > 23 || ARG(2).v.val > 59) return E_2HIGH;
RetVal.v.val = (MINUTES_PER_DAY * ARG(0).v.val) + 60 * ARG(1).v.val + ARG(2).v.val;
if (ARGV(1) < 0 || ARGV(2) < 0) return E_2LOW;
if (ARGV(1) > 23 || ARGV(2) > 59) return E_2HIGH;
RetVal.v.val = (MINUTES_PER_DAY * ARGV(0)) + 60 * ARGV(1) + ARGV(2);
return OK;
case 4:
if (ARG(0).type != INT_TYPE ||
ARG(1).type != INT_TYPE ||
ARG(2).type != INT_TYPE ||
ARG(3).type != TIME_TYPE) return E_BAD_TYPE;
y = ARG(0).v.val;
m = ARG(1).v.val - 1;
d = ARG(2).v.val;
y = ARGV(0);
m = ARGV(1) - 1;
d = ARGV(2);
if (!DateOK(y, m, d)) return E_BAD_DATE;
RetVal.v.val = Julian(y, m, d) * MINUTES_PER_DAY + ARG(3).v.val;
RetVal.v.val = Julian(y, m, d) * MINUTES_PER_DAY + ARGV(3);
return OK;
case 5:
if (ARG(0).type != INT_TYPE ||
@@ -471,14 +502,14 @@ static int FDateTime(void)
ARG(3).type != INT_TYPE ||
ARG(4).type != INT_TYPE) return E_BAD_TYPE;
y = ARG(0).v.val;
m = ARG(1).v.val - 1;
d = ARG(2).v.val;
y = ARGV(0);
m = ARGV(1) - 1;
d = ARGV(2);
if (!DateOK(y, m, d)) return E_BAD_DATE;
if (ARG(3).v.val < 0 || ARG(4).v.val < 0) return E_2LOW;
if (ARG(3).v.val > 23 || ARG(4).v.val > 59) return E_2HIGH;
RetVal.v.val = Julian(y, m, d) * MINUTES_PER_DAY + ARG(3).v.val * 60 + ARG(4).v.val;
if (ARGV(3) < 0 || ARGV(4) < 0) return E_2LOW;
if (ARGV(3) > 23 || ARGV(4) > 59) return E_2HIGH;
RetVal.v.val = Julian(y, m, d) * MINUTES_PER_DAY + ARGV(3) * 60 + ARGV(4);
return OK;
default:
@@ -495,8 +526,8 @@ static int FCoerce(void)
{
char const *s;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
s = ARG(0).v.str;
ASSERT_TYPE(0, STR_TYPE);
s = ARGSTR(0);
/* Copy the value of ARG(1) into RetVal, and make ARG(1) invalid so
it won't be destroyed */
@@ -569,9 +600,9 @@ static int FMin(void)
/***************************************************************/
static int FAsc(void)
{
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
RetVal.type = INT_TYPE;
RetVal.v.val = *(ARG(0).v.str);
RetVal.v.val = *(ARGSTR(0));
return OK;
}
@@ -587,14 +618,14 @@ static int FChar(void)
/* Special case of one arg - if given ascii value 0, create empty string */
if (Nargs == 1) {
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(0).v.val < -128) return E_2LOW;
if (ARG(0).v.val > 255) return E_2HIGH;
len = ARG(0).v.val ? 2 : 1;
ASSERT_TYPE(0, INT_TYPE);
if (ARGV(0) < -128) return E_2LOW;
if (ARGV(0) > 255) return E_2HIGH;
len = ARGV(0) ? 2 : 1;
RetVal.v.str = malloc(len);
if (!RetVal.v.str) return E_NO_MEM;
RetVal.type = STR_TYPE;
*(RetVal.v.str) = ARG(0).v.val;
*(RetVal.v.str) = ARGV(0);
if (len>1) *(RetVal.v.str + 1) = 0;
return OK;
}
@@ -714,12 +745,12 @@ static int FWkday(void)
if (!HASDATE(ARG(0)) && ARG(0).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(0).type == INT_TYPE) {
if (ARG(0).v.val < 0) return E_2LOW;
if (ARG(0).v.val > 6) return E_2HIGH;
if (ARGV(0) < 0) return E_2LOW;
if (ARGV(0) > 6) return E_2HIGH;
/* Convert 0=Sun to 0=Mon */
ARG(0).v.val--;
if (ARG(0).v.val < 0) ARG(0).v.val = 6;
s = DayName[ARG(0).v.val];
ARGV(0)--;
if (ARGV(0) < 0) ARGV(0) = 6;
s = DayName[ARGV(0)];
} else s = DayName[DATEPART(ARG(0)) % 7];
return RetStrVal(s);
}
@@ -732,7 +763,7 @@ static int FMon(void)
if (!HASDATE(ARG(0)) && ARG(0).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(0).type == INT_TYPE) {
m = ARG(0).v.val - 1;
m = ARGV(0) - 1;
if (m < 0) return E_2LOW;
if (m > 11) return E_2HIGH;
} else {
@@ -784,8 +815,8 @@ static int FTime(void)
if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
h = ARG(0).v.val;
m = ARG(1).v.val;
h = ARGV(0);
m = ARGV(1);
if (h<0 || m<0) return E_2LOW;
if (h>23 || m>59) return E_2HIGH;
RetVal.type = TIME_TYPE;
@@ -803,8 +834,8 @@ static int FAbs(void)
{
int v;
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
v = ARG(0).v.val;
ASSERT_TYPE(0, INT_TYPE);
v = ARGV(0);
RetVal.type = INT_TYPE;
RetVal.v.val = (v < 0) ? (-v) : v;
return OK;
@@ -814,8 +845,8 @@ static int FSgn(void)
{
int v;
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
v = ARG(0).v.val;
ASSERT_TYPE(0, INT_TYPE);
v = ARGV(0);
RetVal.type = INT_TYPE;
if (v>0) RetVal.v.val = 1;
else if (v<0) RetVal.v.val = -1;
@@ -835,9 +866,9 @@ static int FOrd(void)
int t, u, v;
char const *s;
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, INT_TYPE);
v = ARG(0).v.val;
v = ARGV(0);
t = v % 100;
if (t < 0) t = -t;
u = t % 10;
@@ -860,33 +891,33 @@ static int FOrd(void)
/***************************************************************/
static int FPlural(void)
{
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, INT_TYPE);
switch(Nargs) {
case 1:
if (ARG(0).v.val == 1) return RetStrVal("");
if (ARGV(0) == 1) return RetStrVal("");
else return RetStrVal("s");
case 2:
if (ARG(1).type != STR_TYPE) return E_BAD_TYPE;
if (ARG(0).v.val == 1) {
ASSERT_TYPE(1, STR_TYPE);
if (ARGV(0) == 1) {
DCOPYVAL(RetVal, ARG(1));
return OK;
}
RetVal.type = STR_TYPE;
RetVal.v.str = malloc(strlen(ARG(1).v.str)+2);
RetVal.v.str = malloc(strlen(ARGSTR(1))+2);
if (!RetVal.v.str) {
RetVal.type = ERR_TYPE;
return E_NO_MEM;
}
strcpy(RetVal.v.str, ARG(1).v.str);
strcpy(RetVal.v.str, ARGSTR(1));
strcat(RetVal.v.str, "s");
return OK;
default:
if (ARG(1).type != STR_TYPE || ARG(2).type != STR_TYPE)
return E_BAD_TYPE;
if (ARG(0).v.val == 1) DCOPYVAL(RetVal, ARG(1));
if (ARGV(0) == 1) DCOPYVAL(RetVal, ARG(1));
else DCOPYVAL(RetVal, ARG(2));
return OK;
}
@@ -904,8 +935,8 @@ static int FChoose(void)
{
int v;
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
v = ARG(0).v.val;
ASSERT_TYPE(0, INT_TYPE);
v = ARGV(0);
if (v < 1) v = 1;
if (v > Nargs-1) v = Nargs-1;
DCOPYVAL(RetVal, ARG(v));
@@ -943,7 +974,7 @@ static int FUpper(void)
{
char *s;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
DCOPYVAL(RetVal, ARG(0));
s = RetVal.v.str;
while (*s) {
@@ -957,7 +988,7 @@ static int FLower(void)
{
char *s;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
DCOPYVAL(RetVal, ARG(0));
s = RetVal.v.str;
while (*s) {
@@ -1024,8 +1055,8 @@ static int FRealCurrent(void)
/***************************************************************/
static int FGetenv(void)
{
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
return RetStrVal(getenv(ARG(0).v.str));
ASSERT_TYPE(0, STR_TYPE);
return RetStrVal(getenv(ARGSTR(0)));
}
/***************************************************************/
@@ -1040,13 +1071,13 @@ static int FValue(void)
{
Var *v;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
switch(Nargs) {
case 1:
return GetVarValue(ARG(0).v.str, &RetVal, NULL);
return GetVarValue(ARGSTR(0), &RetVal, NULL);
case 2:
v = FindVar(ARG(0).v.str, 0);
v = FindVar(ARGSTR(0), 0);
if (!v) {
DCOPYVAL(RetVal, ARG(1));
return OK;
@@ -1066,11 +1097,11 @@ static int FValue(void)
/***************************************************************/
static int FDefined(void)
{
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
RetVal.type = INT_TYPE;
if (FindVar(ARG(0).v.str, 0))
if (FindVar(ARGSTR(0), 0))
RetVal.v.val = 1;
else
RetVal.v.val = 0;
@@ -1142,12 +1173,12 @@ static int FDaysinmon(void)
{
if (ARG(0).type != INT_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(0).v.val > 12 || ARG(0).v.val < 1 ||
ARG(1).v.val < BASE || ARG(1).v.val > BASE+YR_RANGE)
if (ARGV(0) > 12 || ARGV(0) < 1 ||
ARGV(1) < BASE || ARGV(1) > BASE+YR_RANGE)
return E_DOMAIN_ERR;
RetVal.type = INT_TYPE;
RetVal.v.val = DaysInMonth(ARG(0).v.val-1, ARG(1).v.val);
RetVal.v.val = DaysInMonth(ARGV(0)-1, ARGV(1));
return OK;
}
@@ -1168,7 +1199,7 @@ static int FIsleap(void)
if (HASDATE(ARG(0)))
FromJulian(DATEPART(ARG(0)), &y, &m, &d);
else
y = ARG(0).v.val;
y = ARGV(0);
RetVal.type = INT_TYPE;
RetVal.v.val = IsLeapYear(y);
@@ -1193,34 +1224,34 @@ static int FTrigger(void)
ARG(0).type != DATETIME_TYPE) return E_BAD_TYPE;
if (ARG(0).type == DATE_TYPE) {
date = ARG(0).v.val;
date = ARGV(0);
} else {
date = ARG(0).v.val / MINUTES_PER_DAY;
tim = ARG(0).v.val % MINUTES_PER_DAY;
date = ARGV(0) / MINUTES_PER_DAY;
tim = ARGV(0) % MINUTES_PER_DAY;
}
if (ARG(0).type == DATE_TYPE) {
if (Nargs > 2) {
/* Date Time UTCFlag */
if (ARG(0).type == DATETIME_TYPE) return E_BAD_TYPE;
if (ARG(2).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(1).type != TIME_TYPE) return E_BAD_TYPE;
tim = ARG(1).v.val;
if (ARG(2).v.val) {
ASSERT_TYPE(2, INT_TYPE);
ASSERT_TYPE(1, TIME_TYPE);
tim = ARGV(1);
if (ARGV(2)) {
UTCToLocal(date, tim, &date, &tim);
}
} else if (Nargs > 1) {
/* Date Time */
if (ARG(1).type != TIME_TYPE) return E_BAD_TYPE;
tim = ARG(1).v.val;
ASSERT_TYPE(1, TIME_TYPE);
tim = ARGV(1);
}
} else {
if (Nargs > 2) {
return E_2MANY_ARGS;
} else if (Nargs > 1) {
/* DateTime UTCFlag */
if (ARG(1).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(1).v.val) {
ASSERT_TYPE(1, INT_TYPE);
if (ARGV(1)) {
UTCToLocal(date, tim, &date, &tim);
}
}
@@ -1257,12 +1288,12 @@ static int FShell(void)
DBufInit(&buf);
if (RunDisabled) return E_RUN_DISABLED;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
if (Nargs >= 2) {
if (ARG(1).type != INT_TYPE) return E_BAD_TYPE;
maxlen = ARG(1).v.val;
ASSERT_TYPE(1, INT_TYPE);
maxlen = ARGV(1);
}
fp = popen(ARG(0).v.str, "r");
fp = popen(ARGSTR(0), "r");
if (!fp) return E_IO_ERR;
while (1) {
ch = getc(fp);
@@ -1285,6 +1316,8 @@ static int FShell(void)
DBufValue(&buf)[DBufLen(&buf)-1] = 0;
}
/* XXX Should we consume remaining output from cmd? */
pclose(fp);
r = RetStrVal(DBufValue(&buf));
DBufFree(&buf);
@@ -1301,9 +1334,10 @@ static int FShell(void)
static int FIsomitted(void)
{
if (!HASDATE(ARG(0))) return E_BAD_TYPE;
RetVal.type = INT_TYPE;
RetVal.v.val = IsOmitted(DATEPART(ARG(0)), 0);
return OK;
int r = IsOmitted(DATEPART(ARG(0)), 0, NULL, &RetVal.v.val);
return r;
}
/***************************************************************/
@@ -1322,9 +1356,9 @@ static int FSubstr(void)
if (ARG(0).type != STR_TYPE || ARG(1).type != INT_TYPE) return E_BAD_TYPE;
if (Nargs == 3 && ARG(2).type != INT_TYPE) return E_BAD_TYPE;
s = ARG(0).v.str;
s = ARGSTR(0);
start = 1;
while (start < ARG(1).v.val) {
while (start < ARGV(1)) {
if (!*s) break;
s++;
start++;
@@ -1332,7 +1366,7 @@ static int FSubstr(void)
if (Nargs == 2 || !*s) return RetStrVal(s);
end = start;
t = s;
while (end <= ARG(2).v.val) {
while (end <= ARGV(2)) {
if (!*s) break;
s++;
end++;
@@ -1356,12 +1390,12 @@ static int FIndex(void)
if (ARG(0).type != STR_TYPE || ARG(1).type != STR_TYPE ||
(Nargs == 3 && ARG(2).type != INT_TYPE)) return E_BAD_TYPE;
s = ARG(0).v.str;
s = ARGSTR(0);
/* If 3 args, bump up the start */
if (Nargs == 3) {
start = 1;
while (start < ARG(2).v.val) {
while (start < ARGV(2)) {
if (!*s) break;
s++;
start++;
@@ -1369,13 +1403,13 @@ static int FIndex(void)
}
/* Find the string */
s = strstr(s, ARG(1).v.str);
s = strstr(s, ARGSTR(1));
RetVal.type = INT_TYPE;
if (!s) {
RetVal.v.val = 0;
return OK;
}
RetVal.v.val = (s - ARG(0).v.str) + 1;
RetVal.v.val = (s - ARGSTR(0)) + 1;
return OK;
}
@@ -1469,10 +1503,10 @@ static int FAccess(void)
if (ARG(0).type != STR_TYPE ||
(ARG(1).type != INT_TYPE && ARG(1).type != STR_TYPE)) return E_BAD_TYPE;
if (ARG(1).type == INT_TYPE) amode = ARG(1).v.val;
if (ARG(1).type == INT_TYPE) amode = ARGV(1);
else {
amode = 0;
s = ARG(1).v.str;
s = ARGSTR(1);
while (*s) {
switch(*s++) {
case 'r':
@@ -1485,7 +1519,7 @@ static int FAccess(void)
}
}
RetVal.type = INT_TYPE;
RetVal.v.val = access(ARG(0).v.str, amode);
RetVal.v.val = access(ARGSTR(0), amode);
return OK;
}
@@ -1529,9 +1563,9 @@ static int FLanguage(void)
/***************************************************************/
static int FArgs(void)
{
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
RetVal.type = INT_TYPE;
RetVal.v.val = UserFuncExists(ARG(0).v.str);
RetVal.v.val = UserFuncExists(ARGSTR(0));
return OK;
}
@@ -1551,25 +1585,25 @@ static int FDosubst(void)
jul = NO_DATE;
tim = NO_TIME;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
if (Nargs >= 2) {
if (ARG(1).type == DATETIME_TYPE) {
jul = DATEPART(ARG(1));
tim = TIMEPART(ARG(1));
} else {
if (ARG(1).type != DATE_TYPE) return E_BAD_TYPE;
jul = ARG(1).v.val;
ASSERT_TYPE(1, DATE_TYPE);
jul = ARGV(1);
}
if (Nargs >= 3) {
if (ARG(1).type == DATETIME_TYPE) {
return E_2MANY_ARGS;
}
if (ARG(2).type != TIME_TYPE) return E_BAD_TYPE;
tim = ARG(2).v.val;
ASSERT_TYPE(2, TIME_TYPE);
tim = ARGV(2);
}
}
if ((r=DoSubstFromString(ARG(0).v.str, &buf, jul, tim))) return r;
if ((r=DoSubstFromString(ARGSTR(0), &buf, jul, tim))) return r;
r = RetStrVal(DBufValue(&buf));
DBufFree(&buf);
return r;
@@ -1593,8 +1627,8 @@ static int FHebdate(void)
int adarbehave;
if (ARG(0).type != INT_TYPE || ARG(1).type != STR_TYPE) return E_BAD_TYPE;
day = ARG(0).v.val;
mon = HebNameToNum(ARG(1).v.str);
day = ARGV(0);
mon = HebNameToNum(ARGSTR(1));
if (mon < 0) return E_BAD_HEBDATE;
if (Nargs == 2) {
r = GetNextHebrewDate(JulianToday, mon, day, 0, 0, &ans);
@@ -1604,15 +1638,15 @@ static int FHebdate(void)
return OK;
}
if (Nargs == 5) {
if (ARG(4).type != INT_TYPE) return E_BAD_TYPE;
adarbehave = ARG(4).v.val;
ASSERT_TYPE(4, INT_TYPE);
adarbehave = ARGV(4);
if (adarbehave < 0) return E_2LOW;
if (adarbehave > 2) return E_2HIGH;
} else adarbehave = 0;
if (Nargs >= 4) {
if (ARG(3).type != INT_TYPE) return E_BAD_TYPE;
jahr = ARG(3).v.val;
ASSERT_TYPE(3, INT_TYPE);
jahr = ARGV(3);
if (jahr < 0) return E_2LOW;
if (jahr > 2) {
r = ComputeJahr(jahr, mon, day, &jahr);
@@ -1622,7 +1656,7 @@ static int FHebdate(void)
if (ARG(2).type == INT_TYPE) {
year = ARG(2).v.val;
year = ARGV(2);
r = GetValidHebDate(year, mon, day, 0, &mout, &dout, jahr);
if (r) return r;
r = HebToJul(year, mout, dout);
@@ -1718,7 +1752,7 @@ static int FEasterdate(void)
int y, m, d;
int g, c, x, z, e, n;
if (ARG(0).type == INT_TYPE) {
y = ARG(0).v.val;
y = ARGV(0);
if (y < BASE) return E_2LOW;
else if (y > BASE+YR_RANGE) return E_2HIGH;
} else if (HASDATE(ARG(0))) {
@@ -1785,8 +1819,8 @@ static int FTimeStuff(int wantmins)
}
if (Nargs >= 2) {
if (HASTIME(ARG(0))) return E_2MANY_ARGS;
if (ARG(1).type != TIME_TYPE) return E_BAD_TYPE;
tim = ARG(1).v.val;
ASSERT_TYPE(1, TIME_TYPE);
tim = ARGV(1);
}
}
@@ -1992,9 +2026,9 @@ static int FFiledate(void)
RetVal.type = DATE_TYPE;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
if (stat(ARG(0).v.str, &statbuf)) {
if (stat(ARGSTR(0), &statbuf)) {
RetVal.v.val = 0;
return OK;
}
@@ -2023,9 +2057,9 @@ static int FFiledatetime(void)
RetVal.type = DATETIME_TYPE;
if (ARG(0).type != STR_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, STR_TYPE);
if (stat(ARG(0).v.str, &statbuf)) {
if (stat(ARGSTR(0), &statbuf)) {
RetVal.v.val = 0;
return OK;
}
@@ -2075,9 +2109,9 @@ static int FPsshade(void)
sprintf(s, "BoxWidth _A sub _A lineto BoxWidth _A sub BoxHeight _A sub lineto ");
s += strlen(s);
if (Nargs == 1) {
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARG(0).v.val);
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div setgray fill 0.0 setgray", ARGV(0));
} else {
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARG(0).v.val, ARG(1).v.val, ARG(2).v.val);
sprintf(s, "_A BoxHeight _A sub lineto closepath %d 100 div %d 100 div %d 100 div setrgbcolor fill 0.0 setgray", ARGV(0), ARGV(1), ARGV(2));
}
return RetStrVal(psbuff);
}
@@ -2101,20 +2135,20 @@ static int FPsmoon(void)
int size = -1;
int fontsize = -1;
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(0).v.val < 0) return E_2LOW;
if (ARG(0).v.val > 3) return E_2HIGH;
ASSERT_TYPE(0, INT_TYPE);
if (ARGV(0) < 0) return E_2LOW;
if (ARGV(0) > 3) return E_2HIGH;
if (Nargs > 1) {
if (ARG(1).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(1).v.val < -1) return E_2LOW;
size = ARG(1).v.val;
ASSERT_TYPE(1, INT_TYPE);
if (ARGV(1) < -1) return E_2LOW;
size = ARGV(1);
if (Nargs > 2) {
if (ARG(2).type != STR_TYPE) return E_BAD_TYPE;
extra = ARG(2).v.str;
ASSERT_TYPE(2, STR_TYPE);
extra = ARGSTR(2);
if (Nargs > 3) {
if (ARG(3).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(3).v.val <= 0) return E_2LOW;
fontsize = ARG(3).v.val;
ASSERT_TYPE(3, INT_TYPE);
if (ARGV(3) <= 0) return E_2LOW;
fontsize = ARGV(3);
}
}
}
@@ -2139,7 +2173,7 @@ static int FPsmoon(void)
s += strlen(s);
sprintf(s, " %s 0 360 arc closepath", sizebuf);
s += strlen(s);
switch(ARG(0).v.val) {
switch(ARGV(0)) {
case 0:
sprintf(s, " fill");
s += strlen(s);
@@ -2208,8 +2242,8 @@ static int FMoonphase(void)
case 2:
if (ARG(0).type == DATETIME_TYPE) return E_2MANY_ARGS;
if (ARG(0).type != DATE_TYPE && ARG(1).type != TIME_TYPE) return E_BAD_TYPE;
date = ARG(0).v.val;
time = ARG(1).v.val;
date = ARGV(0);
time = ARGV(1);
break;
default: return E_SWERR;
@@ -2251,9 +2285,9 @@ static int MoonStuff(int type_wanted)
startdate = JulianToday;
starttim = 0;
if (ARG(0).type != INT_TYPE) return E_BAD_TYPE;
if (ARG(0).v.val < 0) return E_2LOW;
if (ARG(0).v.val > 3) return E_2HIGH;
ASSERT_TYPE(0, INT_TYPE);
if (ARGV(0) < 0) return E_2LOW;
if (ARGV(0) > 3) return E_2HIGH;
if (Nargs >= 2) {
if (!HASDATE(ARG(1))) return E_BAD_TYPE;
startdate = DATEPART(ARG(1));
@@ -2263,12 +2297,12 @@ static int MoonStuff(int type_wanted)
if (Nargs >= 3) {
if (HASTIME(ARG(1))) return E_2MANY_ARGS;
if (ARG(2).type != TIME_TYPE) return E_BAD_TYPE;
starttim = ARG(2).v.val;
ASSERT_TYPE(2, TIME_TYPE);
starttim = ARGV(2);
}
}
HuntPhase(startdate, starttim, ARG(0).v.val, &d, &t);
HuntPhase(startdate, starttim, ARGV(0), &d, &t);
RetVal.type = type_wanted;
switch(type_wanted) {
case TIME_TYPE:
@@ -2288,7 +2322,7 @@ static int MoonStuff(int type_wanted)
static int FTimepart(void)
{
if (ARG(0).type != DATETIME_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, DATETIME_TYPE);
RetVal.type = TIME_TYPE;
RetVal.v.val = TIMEPART(ARG(0));
return OK;
@@ -2296,7 +2330,7 @@ static int FTimepart(void)
static int FDatepart(void)
{
if (ARG(0).type != DATETIME_TYPE) return E_BAD_TYPE;
ASSERT_TYPE(0, DATETIME_TYPE);
RetVal.type = DATE_TYPE;
RetVal.v.val = DATEPART(ARG(0));
return OK;
@@ -2439,10 +2473,10 @@ static int FTzconvert(void)
if (Nargs == 2) {
r = tz_convert(year, month, day, hour, minute,
ARG(1).v.str, NULL, &tm);
ARGSTR(1), NULL, &tm);
} else {
r = tz_convert(year, month, day, hour, minute,
ARG(1).v.str, ARG(2).v.str, &tm);
ARGSTR(1), ARGSTR(2), &tm);
}
if (r == -1) return E_CANT_CONVERT_TZ;
@@ -2458,7 +2492,7 @@ static int
FNonomitted(void)
{
int d1, d2, ans, localomit, i;
int omit, r;
Token tok;
if (!HASDATE(ARG(0)) ||
@@ -2478,7 +2512,9 @@ FNonomitted(void)
ans = 0;
while (d1 < d2) {
if (!IsOmitted(d1++, localomit)) {
r = IsOmitted(d1++, localomit, NULL, &omit);
if (r) return r;
if (!omit) {
ans++;
}
}
@@ -2486,3 +2522,116 @@ FNonomitted(void)
RetVal.v.val = ans;
return OK;
}
static int
FWeekno(void)
{
int jul = JulianToday;
int wkstart = 0; /* Week start on Monday */
int daystart = 29; /* First week starts on wkstart on or after Dec. 29 */
int monstart;
int candidate;
int y, m, d;
if (Nargs >= 1) {
if (!HASDATE(ARG(0))) return E_BAD_TYPE;
jul = DATEPART(ARG(0));
}
if (Nargs >= 2) {
ASSERT_TYPE(1, INT_TYPE);
if (ARGV(1) < 0) return E_2LOW;
if (ARGV(1) > 6) return E_2HIGH;
wkstart = ARGV(1);
/* Convert 0=Sun to 0=Mon */
wkstart--;
if (wkstart < 0) wkstart = 6;
if (Nargs >= 3) {
ASSERT_TYPE(2, INT_TYPE);
if (ARGV(2) < 1) return E_2LOW;
if (ARGV(2) > 31) return E_2HIGH;
daystart = ARGV(2);
}
}
RetVal.type = INT_TYPE;
/* If start day is 7, first week starts after Jan,
otherwise after Dec. */
if (daystart <= 7) {
monstart = 0;
} else {
monstart = 11;
}
FromJulian(jul, &y, &m, &d);
/* Try this year */
candidate = Julian(y, monstart, daystart);
while((candidate % 7) != wkstart) candidate++;
if (candidate <= jul) {
RetVal.v.val = ((jul - candidate) / 7) + 1;
return OK;
}
if (y-1 < BASE) return E_DATE_OVER;
/* Must be last year */
candidate = Julian(y-1, monstart, daystart);
while((candidate % 7) != wkstart) candidate++;
if (candidate <= jul) {
RetVal.v.val = ((jul - candidate) / 7) + 1;
return OK;
}
if (y-2 < BASE) return E_DATE_OVER;
/* Holy cow! */
candidate = Julian(y-2, monstart, daystart);
while((candidate % 7) != wkstart) candidate++;
RetVal.v.val = ((jul - candidate) / 7) + 1;
return OK;
}
static int
FEvalTrig(void)
{
Parser p;
Trigger trig;
TimeTrig tim;
int jul, scanfrom;
int r;
ASSERT_TYPE(0, STR_TYPE);
if (Nargs >= 2) {
if (!HASDATE(ARG(1))) return E_BAD_TYPE;
scanfrom = DATEPART(ARG(1));
} else {
scanfrom = NO_DATE;
}
CreateParser(ARGSTR(0), &p);
p.allownested = 0;
r = ParseRem(&p, &trig, &tim, 0);
if (r) return r;
if (trig.typ != NO_TYPE) return E_PARSE_ERR;
if (scanfrom == NO_DATE) {
jul = ComputeTrigger(trig.scanfrom, &trig, &r, 0);
} else {
/* Hokey... */
if (trig.scanfrom != JulianToday) {
Eprint("Warning: SCANFROM is ignored in two-argument form of evaltrig()");
}
jul = ComputeTrigger(scanfrom, &trig, &r, 0);
}
if (r) return r;
if (jul < 0) {
RetVal.type = INT_TYPE;
RetVal.v.val = jul;
} else if (tim.ttime == NO_TIME) {
RetVal.type = DATE_TYPE;
RetVal.v.val = jul;
} else {
RetVal.type = DATETIME_TYPE;
RetVal.v.val = (MINUTES_PER_DAY * jul) + tim.ttime;
}
return OK;
}

View File

@@ -59,6 +59,7 @@ 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);
@@ -75,6 +76,7 @@ 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 FILE *ErrFp;

View File

@@ -248,6 +248,7 @@ void InitRemind(int argc, char const *argv[])
SortByDate = SORT_ASCEND;
SortByTime = SORT_ASCEND;
SortByPrio = SORT_ASCEND;
UntimedBeforeTimed = 0;
if (*arg) {
if (*arg == 'D' || *arg == 'd')
SortByDate = SORT_DESCEND;
@@ -263,6 +264,11 @@ void InitRemind(int argc, char const *argv[])
SortByPrio = SORT_DESCEND;
arg++;
}
if (*arg) {
if (*arg == 'D' || *arg == 'd')
UntimedBeforeTimed = 1;
arg++;
}
break;
case 'u':
@@ -576,7 +582,7 @@ void Usage(void)
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");

View File

@@ -308,7 +308,8 @@ EXTERN char *ErrMsg[] =
"Numero puuttuu",
"Virheellinen funktio WARN-lausekkeessa",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
#elif IBMEXTENDED
"Ok",
@@ -411,7 +412,9 @@ EXTERN char *ErrMsg[] =
"Numero puuttuu"
"Virheellinen funktio WARN-lausekkeessa",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
#else
"Ok",
"Puuttuva ']'",
@@ -513,7 +516,9 @@ EXTERN char *ErrMsg[] =
"Numero puuttuu",
"Virheellinen funktio WARN-lausekkeessa",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
#endif
};

View File

@@ -245,7 +245,9 @@ EXTERN char *ErrMsg[] =
"Nombre attendu",
"Fonction ill\351gale apr\350s WARN",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
#else /* ISOLATIN1 */
"Ok",
"']' manquant",
@@ -347,7 +349,8 @@ EXTERN char *ErrMsg[] =
"Nombre attendu",
"Fonction illegale apres WARN",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
#endif /* ISOLATIN1 */
};
#endif /* MK_GLOBALS */

View File

@@ -280,7 +280,8 @@ EXTERN char *ErrMsg[] =
"Spodziewana liczba",
"Illegal function in WARN clause (NEEDS TRANSLATION TO POLISH)",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
#else /* ISOLATIN1 */
"OK",
"Brakujacy ']'",
@@ -382,7 +383,8 @@ EXTERN char *ErrMsg[] =
"Spodziewana liczba",
"Illegal function in WARN clause (NEEDS TRANSLATION TO POLISH)",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
#endif /* ISOLATIN1 */
};
#endif /* MK_GLOBALS */

View File

@@ -246,7 +246,8 @@ EXTERN char *ErrMsg[] =
"Esperando numero",
"Funcao ilegal na clausula WARN",
"Can't convert between time zones",
"No files matching *.rem"
"No files matching *.rem",
"String too long"
};
#endif /* MK_GLOBALS */

View File

@@ -724,7 +724,7 @@ int DoEndif(ParsePtr p)
/***************************************************************/
int DoIfTrig(ParsePtr p)
{
int r;
int r, err;
unsigned syndrome;
Trigger trig;
TimeTrig tim;
@@ -734,12 +734,12 @@ 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
syndrome = IF_FALSE | BEFORE_ELSE;

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);
@@ -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);
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;
}
/***************************************************************/

View File

@@ -26,9 +26,9 @@ 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 const *source, DynamicBuffer *dbuf, int jul, int tim);
int EvalExpr (char const **e, Value *v);
@@ -75,14 +75,14 @@ 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, char const *sched);
void HandleQueuedReminders (void);
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 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);
@@ -128,7 +128,7 @@ 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 *);

View File

@@ -30,6 +30,7 @@
#define SPECIAL_MOON 3
#define SPECIAL_SHADE 4
#define SPECIAL_COLOR 5
#define SPECIAL_WEEK 6
typedef struct calentry {
struct calentry *next;
@@ -306,6 +307,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 +326,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;
}
@@ -887,6 +891,7 @@ int DoQueuedPs(void)
int fnoff;
char buffer[512];
char const *size, *extra;
char const *s;
int num, r, g, b, phase, fontsize, moonsize;
unsigned char c;
@@ -954,6 +959,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);

View File

@@ -83,6 +83,7 @@ int InsertIntoSortBuffer(int jul, int tim, char const *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 const *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;
@@ -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 },

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,7 +297,7 @@ 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 &&
@@ -294,8 +305,22 @@ static int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart
/* 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 +334,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 +369,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 +414,15 @@ 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 = 0,
y, m, d,
y, m, d, omit,
result;
LastTrigValid = 0;
if (save_in_globals) LastTrigValid = 0;
/* Assume everything works */
*err = OK;
@@ -373,8 +441,8 @@ int ComputeTrigger(int today, Trigger *trig, int *err)
*err = E_REP_FULSPEC;
return -1;
}
while (nattempts++ < TRIG_ATTEMPTS) {
result = GetNextTriggerDate(trig, start, err, &nextstart);
@@ -389,10 +457,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",
@@ -414,14 +490,16 @@ int ComputeTrigger(int today, Trigger *trig, int *err)
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 &&
IsOmitted(result, trig->localomit) &&
omit &&
nextstart <= start &&
result >= start) {
nextstart = result + 1;
@@ -430,8 +508,10 @@ int ComputeTrigger(int today, Trigger *trig, int *err)
/* 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;
}
}
if (DebugFlag & DB_PRTTRIG) {
fprintf(ErrFp, "%s(%d): %s\n",

View File

@@ -56,6 +56,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;
@@ -148,7 +149,8 @@ enum TokTypes
T_Warn,
T_Tag,
T_Duration,
T_LongTime
T_LongTime,
T_OmitFunc
};
/* The structure of a token */

View File

@@ -223,7 +223,7 @@ int DoSet (Parser *p)
r = ParseIdentifier(p, &buf);
if (r) return r;
/* Allow option equals-sign: SET var = value */
/* Allow optional equals-sign: SET var = value */
if (ParseNonSpaceChar(p, &r, 1) == '=') {
ParseNonSpaceChar(p, &r, 0);
}
@@ -488,6 +488,7 @@ static SysVar SysVarArr[] = {
{ "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, -13*60, 13*60 },
{ "NextMode", 0, INT_TYPE, &NextMode, 0, 0 },
{ "NumQueued", 0, INT_TYPE, &NumQueued, 0, 0 },
@@ -500,7 +501,8 @@ static SysVar SysVarArr[] = {
{ "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 }
{ "TimeSep", 1, SPECIAL_TYPE, time_sep_func, 0, 0 },
{ "UntimedFirst", 0, INT_TYPE, &UntimedBeforeTimed, 0, 0 }
};
#define NUMSYSVARS ( sizeof(SysVarArr) / sizeof(SysVar) )

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

View File

@@ -60,6 +60,15 @@ 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
../src/remind ../tests/runtest.rem >> ../tests/test.out 2>&1
cmp -s ../tests/test.out ../tests/test.cmp
if [ "$?" = "0" ]; then
echo "Remind: Acceptance test PASSED"

View File

@@ -431,7 +431,7 @@ REM 1 Mar --1 MSG 1 mar --1
../tests/test.rem(133): Trig = Thursday, 28 February, 1991
REM 28 Feb BEFORE MSG 28 Feb BEFORE
../tests/test.rem(134): Trig = Thursday, 28 February, 1991
REM 28 Feb SKIP MSG 28 Feb SKIP
REM 28 Feb SKIP MSG 28 Feb SKIP
../tests/test.rem(135): Trig = Thursday, 28 February, 1991
REM 28 Feb AFTER MSG 28 Feb AFTER
../tests/test.rem(136): Trig = Thursday, 28 February, 1991
@@ -451,7 +451,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
../tests/test.rem(146): Trig = Wednesday, 13 March, 1991
# Test BACK
CLEAR-OMIT-CONTEXT
REM 18 Feb 1991 +1 MSG 18 Feb 1991 +1
@@ -488,6 +488,190 @@ REM Fri after MSG 16 Feb 1991
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
Entering UserFN _ofunc(1991-02-15)
x => 1991-02-15
day(1991-02-15) => 15
15 < 7 => 0
x => 1991-02-15
day(1991-02-15) => 15
15 % 2 => 1
0 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-02-14)
x => 1991-02-14
day(1991-02-14) => 14
14 < 7 => 0
x => 1991-02-14
day(1991-02-14) => 14
14 % 2 => 0
0 || 0 => 0
Leaving UserFN _ofunc() => 0
Entering UserFN _ofunc(1991-03-01)
x => 1991-03-01
day(1991-03-01) => 1
1 < 7 => 1
x => 1991-03-01
day(1991-03-01) => 1
1 % 2 => 1
1 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-02)
x => 1991-03-02
day(1991-03-02) => 2
2 < 7 => 1
x => 1991-03-02
day(1991-03-02) => 2
2 % 2 => 0
1 || 0 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-03)
x => 1991-03-03
day(1991-03-03) => 3
3 < 7 => 1
x => 1991-03-03
day(1991-03-03) => 3
3 % 2 => 1
1 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-04)
x => 1991-03-04
day(1991-03-04) => 4
4 < 7 => 1
x => 1991-03-04
day(1991-03-04) => 4
4 % 2 => 0
1 || 0 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-05)
x => 1991-03-05
day(1991-03-05) => 5
5 < 7 => 1
x => 1991-03-05
day(1991-03-05) => 5
5 % 2 => 1
1 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-06)
x => 1991-03-06
day(1991-03-06) => 6
6 < 7 => 1
x => 1991-03-06
day(1991-03-06) => 6
6 % 2 => 0
1 || 0 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-07)
x => 1991-03-07
day(1991-03-07) => 7
7 < 7 => 0
x => 1991-03-07
day(1991-03-07) => 7
7 % 2 => 1
0 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-08)
x => 1991-03-08
day(1991-03-08) => 8
8 < 7 => 0
x => 1991-03-08
day(1991-03-08) => 8
8 % 2 => 0
0 || 0 => 0
Leaving UserFN _ofunc() => 0
../tests/test.rem(169): Trig = Friday, 8 March, 1991
REM 8 March OMITFUNC _ofunc -1 MSG OmitFunc Test 2
Entering UserFN _ofunc(1991-03-07)
x => 1991-03-07
day(1991-03-07) => 7
7 < 7 => 0
x => 1991-03-07
day(1991-03-07) => 7
7 % 2 => 1
0 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-06)
x => 1991-03-06
day(1991-03-06) => 6
6 < 7 => 1
x => 1991-03-06
day(1991-03-06) => 6
6 % 2 => 0
1 || 0 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-05)
x => 1991-03-05
day(1991-03-05) => 5
5 < 7 => 1
x => 1991-03-05
day(1991-03-05) => 5
5 % 2 => 1
1 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-04)
x => 1991-03-04
day(1991-03-04) => 4
4 < 7 => 1
x => 1991-03-04
day(1991-03-04) => 4
4 % 2 => 0
1 || 0 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-03)
x => 1991-03-03
day(1991-03-03) => 3
3 < 7 => 1
x => 1991-03-03
day(1991-03-03) => 3
3 % 2 => 1
1 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-02)
x => 1991-03-02
day(1991-03-02) => 2
2 < 7 => 1
x => 1991-03-02
day(1991-03-02) => 2
2 % 2 => 0
1 || 0 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-03-01)
x => 1991-03-01
day(1991-03-01) => 1
1 < 7 => 1
x => 1991-03-01
day(1991-03-01) => 1
1 % 2 => 1
1 || 1 => 1
Leaving UserFN _ofunc() => 1
Entering UserFN _ofunc(1991-02-28)
x => 1991-02-28
day(1991-02-28) => 28
28 < 7 => 0
x => 1991-02-28
day(1991-02-28) => 28
28 % 2 => 0
0 || 0 => 0
Leaving UserFN _ofunc() => 0
../tests/test.rem(170): Trig = Thursday, 28 February, 1991
# 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
../tests/test.rem(176): Warning: OMIT is ignored if you use OMITFUNC
Entering UserFN _ofunc(1991-02-15)
Leaving UserFN _ofunc() => 0
Entering UserFN _ofunc(1991-03-01)
Leaving UserFN _ofunc() => 0
../tests/test.rem(176): Trig = Friday, 1 March, 1991
REM 1 March OMIT Sun AFTER MSG Should trigger 4 March
../tests/test.rem(177): Trig = Monday, 4 March, 1991
set a000 abs(1)
abs(1) => 1
set a001 abs(-1)
@@ -510,7 +694,7 @@ set a008 coerce("string", 11:44)
coerce("string", 11:44) => "11:44"
set a009 coerce("int", "badnews")
coerce("int", "badnews") => Can't coerce
../tests/test.rem(175): Can't coerce
../tests/test.rem(188): Can't coerce
set a010 coerce("int", "12")
coerce("int", "12") => 12
set a011 coerce("int", 11:44)
@@ -522,7 +706,7 @@ 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
../tests/test.rem(180): Bad date specification
../tests/test.rem(193): Bad date specification
set a015 day(today())
today() => 1991-02-16
day(1991-02-16) => 16
@@ -617,15 +801,15 @@ strlen("sadjflkhsldkfhsdlfjhk") => 21
set a050 substr(a049, 2)
a049 => 21
substr(21, 2) => Type mismatch
../tests/test.rem(218): Type mismatch
../tests/test.rem(231): Type mismatch
set a051 substr(a050, 2, 6)
a050 => ../tests/test.rem(219): Undefined variable: a050
a050 => ../tests/test.rem(232): 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
../tests/test.rem(221): Trig = Friday, 10 January, 1992
../tests/test.rem(234): Trig = Friday, 10 January, 1992
set a053 trigdate()
trigdate() => 1992-01-10
set a054 trigtime()
@@ -638,7 +822,7 @@ set a057 value("a05"+"6")
"a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version()
version() => "03.01.05"
version() => "03.01.06"
set a059 wkday(today())
today() => 1991-02-16
wkday(1991-02-16) => "Saturday"
@@ -717,31 +901,31 @@ y => 11:33
x => "foo"
y => 11:33
"foo" * 11:33 => Type mismatch
../tests/test.rem(244): `*': Type mismatch
../tests/test.rem(257): `*': 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]%
../tests/test.rem(246): Trig = Saturday, 16 February, 1991
../tests/test.rem(259): 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]%
../tests/test.rem(248): Trig = Saturday, 16 February, 1991
../tests/test.rem(261): 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]%
../tests/test.rem(250): Trig = Saturday, 16 February, 1991
../tests/test.rem(263): 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]%
../tests/test.rem(252): Trig = Saturday, 16 February, 1991
../tests/test.rem(265): Trig = Saturday, 16 February, 1991
a077 => "1992 92
"
1992 92
@@ -779,7 +963,7 @@ dump
a048 "foo"
a067 "INT"
a039 "February"
a058 "03.01.05"
a058 "03.01.06"
a077 "1992 92
"
a049 21
@@ -1922,4 +2106,23 @@ Color Test
(0x(B (0x(B (0x(BBright (0x(B (0x(B (0x(B (0x(B (0x(B
(0x(B (0x(B (0x(BWhite (0x(B (0x(B (0x(B (0x(B (0x(B
(0mqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqvqqqqqqqqqqj(B
MON WKDAY DAY across year test
-(1): Trig = Monday, 3 January, 2000
No reminders.
Sort Test
Reminders for Saturday, 1st January, 2000:
Untimed
Timed
Reminders for Saturday, 1st January, 2000:
Timed
Untimed
../tests/runtest.rem(2): shell(): RUN disabled
../tests/runinc.rem(1): shell(): RUN disabled
../tests/runinc.rem(3): shell(): RUN disabled
No reminders.

View File

@@ -132,7 +132,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
@@ -144,7 +144,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
@@ -163,6 +163,19 @@ 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
set a000 abs(1)
set a001 abs(-1)
set a002 asc("foo")

View File

@@ -9,7 +9,7 @@ my %Options;
my $rem2html_version = '2.0';
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst,
my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
@Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);
my $TIDY_PROGNAME = $0;
@@ -186,6 +186,7 @@ sub parse_input
undef $shades;
undef $moons;
undef $classes;
undef $weeks;
my $found_data = 0;
while(<STDIN>) {
@@ -232,10 +233,16 @@ sub parse_input
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);
@@ -362,7 +369,6 @@ sub output_calendar
# Last column
my $last_col = ($first_col + $Numdays - 1) % 7;
# Start the table
my $class;
if ($Options{'nostyle'}) {
@@ -475,6 +481,10 @@ 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] || '';
@@ -529,10 +539,10 @@ sub draw_day_cell
}
if ($Options{'nostyle'}) {
print "<div style=\"float: right\">$day</div>\n";
print "<div style=\"float: right\">$day$week</div>\n";
print "<p>&nbsp;</p>\n";
} else {
print "<div class=\"rem-daynumber\">$day</div>\n";
print "<div class=\"rem-daynumber\">$day$week</div>\n";
}
if ($days->[$day]) {
print(join("\n", @{$days->[$day]}));