This is dorem.c in view mode; [Download] [Up]
/***************************************************************/
/* */
/* DOREM.C */
/* */
/* Contains routines for parsing reminders and evaluating */
/* triggers. Also contains routines for parsing OMIT */
/* commands. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992, 1993, 1994 by David F. Skoll. */
/* */
/***************************************************************/
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include "globals.h"
#include "err.h"
#include "types.h"
#include "protos.h"
#include "expr.h"
PRIVATE int ParseTimeTrig ARGS ((ParsePtr s, TimeTrig *tim));
PRIVATE int ParseLocalOmit ARGS ((ParsePtr s, Trigger *t));
PRIVATE int ParseScanFrom ARGS ((ParsePtr s, Trigger *t));
PRIVATE int ParsePriority ARGS ((ParsePtr s, Trigger *t));
PRIVATE int ParseUntil ARGS ((ParsePtr s, Trigger *t));
/***************************************************************/
/* */
/* DoRem */
/* */
/* Do the REM command. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int DoRem(ParsePtr p)
#else
int DoRem(p)
ParsePtr p;
#endif
{
Trigger trig;
TimeTrig tim;
int r;
int jul;
char buf[TOKSIZE];
Token tok;
/* Parse the trigger date and time */
if ( (r=ParseRem(p, &trig, &tim)) ) return r;
if (trig.typ == NO_TYPE) return E_EOLN;
if (trig.typ == SAT_TYPE) {
r=DoSatRemind(&trig, &tim, p);
if (r) return r;
r=ParseToken(p, buf);
if (r) return r;
FindToken(buf, &tok);
if (tok.type == T_Empty || tok.type == T_Comment) return OK;
if (tok.type != T_RemType || tok.val == SAT_TYPE) return E_PARSE_ERR;
trig.typ = tok.val;
jul = LastTriggerDate;
if (!LastTrigValid) return OK;
} else {
/* Calculate the trigger date */
jul = ComputeTrigger(trig.scanfrom, &trig, &r);
if (r) return r;
}
/* Queue the reminder, if necessary */
#ifdef HAVE_QUEUED
if (jul == JulianToday &&
!(!IgnoreOnce &&
trig.once != NO_ONCE &&
FileAccessDate == JulianToday))
QueueReminder(p, trig.typ, &tim, trig.sched);
/* If we're in daemon mode, do nothing over here */
if (Daemon) return OK;
#endif
if (ShouldTriggerReminder(&trig, &tim, jul)) {
#ifdef OS2_POPUP
if ( (r=TriggerReminder(p, &trig, &tim, jul, 0)) ) {
#else
if ( (r=TriggerReminder(p, &trig, &tim, jul)) ) {
#endif
return r;
}
}
return OK;
}
/***************************************************************/
/* */
/* ParseRem */
/* */
/* Given a parse pointer, parse line and fill in a */
/* trigger structure. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim)
#else
int ParseRem(s, trig, tim)
ParsePtr s;
Trigger *trig;
TimeTrig *tim;
#endif
{
register int r;
Token tok;
trig->y = NO_YR;
trig->m = NO_MON;
trig->d = NO_DAY;
trig->wd = NO_WD;
trig->back = NO_BACK;
trig->delta = NO_DELTA;
trig->until = NO_UNTIL;
trig->rep = NO_REP;
trig->localomit = NO_WD;
trig->skip = NO_SKIP;
trig->once = NO_ONCE;
trig->typ = NO_TYPE;
trig->scanfrom = NO_DATE;
trig->priority = DefaultPrio;
trig->sched[0] = 0;
tim->ttime = NO_TIME;
tim->delta = NO_DELTA;
tim->rep = NO_REP;
while(1) {
/* Read space-delimited string */
r = ParseToken(s, TokBuffer);
if (r) return r;
/* Figure out what we've got */
FindToken(TokBuffer, &tok);
switch(tok.type) {
case T_WkDay:
if (trig->wd & (1 << tok.val)) return E_WD_TWICE;
trig->wd |= (1 << tok.val);
break;
case T_Month:
if (trig->m != NO_MON) return E_MON_TWICE;
trig->m = tok.val;
break;
case T_Skip:
if (trig->skip != NO_SKIP) return E_SKIP_ERR;
trig->skip = tok.val;
break;
case T_Priority:
r=ParsePriority(s, trig);
if (r) return r;
break;
case T_At:
r=ParseTimeTrig(s, tim);
if (r) return r;
break;
case T_Scanfrom:
r=ParseScanFrom(s, trig);
if (r) return r;
break;
case T_RemType:
trig->typ = tok.val;
if (s->isnested) return E_CANT_NEST_RTYPE;
if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday;
return OK;
case T_Until:
r=ParseUntil(s, trig);
if (r) return r;
break;
case T_Year:
if (trig->y != NO_YR) return E_YR_TWICE;
trig->y = tok.val;
break;
case T_Day:
if (trig->d != NO_DAY) return E_DAY_TWICE;
trig->d = tok.val;
break;
case T_Rep:
if (trig->rep != NO_REP) return E_REP_TWICE;
trig->rep = tok.val;
break;
case T_Delta:
if (trig->delta != NO_DELTA) return E_DELTA_TWICE;
trig->delta = tok.val;
break;
case T_Back:
if (trig->back != NO_BACK) return E_BACK_TWICE;
trig->back = tok.val;
break;
case T_Once:
if (trig->once != NO_ONCE) return E_ONCE_TWICE;
trig->once = ONCE_ONCE;
break;
case T_Omit:
r = ParseLocalOmit(s, trig);
if (r) return r;
break;
case T_Empty:
if (trig->scanfrom == NO_DATE) trig->scanfrom = JulianToday;
return OK;
case T_Sched:
r=ParseToken(s, TokBuffer);
if(r) return r;
StrnCpy(trig->sched, TokBuffer, VAR_NAME_LEN);
break;
default:
Eprint("%s: %s", ErrMsg[E_UNKNOWN_TOKEN], TokBuffer);
return E_UNKNOWN_TOKEN;
}
}
}
/***************************************************************/
/* */
/* ParseTimeTrig - parse the AT part of a timed reminder */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int ParseTimeTrig(ParsePtr s, TimeTrig *tim)
#else
static int ParseTimeTrig(s, tim)
ParsePtr s;
TimeTrig *tim;
#endif
{
Token tok;
int r;
while(1) {
r = ParseToken(s, TokBuffer);
if (r) return r;
FindToken(TokBuffer, &tok);
switch(tok.type) {
case T_Time:
tim->ttime = tok.val;
break;
case T_Delta:
tim->delta = (tok.val > 0) ? tok.val : -tok.val;
break;
case T_Rep:
tim->rep = tok.val;
break;
default:
if (tim->ttime == NO_TIME) return E_EXPECT_TIME;
/* Save in global variable */
LastTriggerTime = tim->ttime;
PushToken(TokBuffer);
return OK;
}
}
}
/***************************************************************/
/* */
/* ParseLocalOmit - parse the local OMIT portion of a */
/* reminder. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int ParseLocalOmit(ParsePtr s, Trigger *t)
#else
static int ParseLocalOmit(s, t)
ParsePtr s;
Trigger *t;
#endif
{
Token tok;
int r;
while(1) {
r = ParseToken(s, TokBuffer);
if (r) return r;
FindToken(TokBuffer, &tok);
switch(tok.type) {
case T_WkDay:
t->localomit |= (1 << tok.val);
break;
default:
PushToken(TokBuffer);
return OK;
}
}
}
/***************************************************************/
/* */
/* ParseUntil - parse the UNTIL portion of a reminder */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int ParseUntil(ParsePtr s, Trigger *t)
#else
static int ParseUntil(s, t)
ParsePtr s;
Trigger *t;
#endif
{
int y = NO_YR,
m = NO_MON,
d = NO_DAY;
Token tok;
int r;
if (t->until != NO_UNTIL) return E_UNTIL_TWICE;
while(1) {
r = ParseToken(s, TokBuffer);
if (r) return r;
FindToken(TokBuffer, &tok);
switch(tok.type) {
case T_Year:
if (y != NO_YR) {
Eprint("UNTIL: %s", ErrMsg[E_YR_TWICE]);
return E_YR_TWICE;
}
y = tok.val;
break;
case T_Month:
if (m != NO_MON) {
Eprint("UNTIL: %s", ErrMsg[E_MON_TWICE]);
return E_MON_TWICE;
}
m = tok.val;
break;
case T_Day:
if (d != NO_DAY) {
Eprint("UNTIL: %s", ErrMsg[E_DAY_TWICE]);
return E_DAY_TWICE;
}
d = tok.val;
break;
default:
if (y == NO_YR || m == NO_MON || d == NO_DAY) {
Eprint("UNTIL: %s", ErrMsg[E_INCOMPLETE]);
return E_INCOMPLETE;
}
if (!DateOK(y, m, d)) return E_BAD_DATE;
t->until = Julian(y, m, d);
PushToken(TokBuffer);
return OK;
}
}
}
/***************************************************************/
/* */
/* ParseScanFrom - parse the SCANFROM portion of a reminder */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int ParseScanFrom(ParsePtr s, Trigger *t)
#else
static int ParseScanFrom(s, t)
ParsePtr s;
Trigger *t;
#endif
{
int y = NO_YR,
m = NO_MON,
d = NO_DAY;
Token tok;
int r;
if (t->scanfrom != NO_DATE) return E_SCAN_TWICE;
while(1) {
r = ParseToken(s, TokBuffer);
if (r) return r;
FindToken(TokBuffer, &tok);
switch(tok.type) {
case T_Year:
if (y != NO_YR) {
Eprint("SCANFROM: %s", ErrMsg[E_YR_TWICE]);
return E_YR_TWICE;
}
y = tok.val;
break;
case T_Month:
if (m != NO_MON) {
Eprint("SCANFROM: %s", ErrMsg[E_MON_TWICE]);
return E_MON_TWICE;
}
m = tok.val;
break;
case T_Day:
if (d != NO_DAY) {
Eprint("SCANFROM: %s", ErrMsg[E_DAY_TWICE]);
return E_DAY_TWICE;
}
d = tok.val;
break;
default:
if (y == NO_YR || m == NO_MON || d == NO_DAY) {
Eprint("SCANFROM: %s", ErrMsg[E_INCOMPLETE]);
return E_INCOMPLETE;
}
if (!DateOK(y, m, d)) return E_BAD_DATE;
t->scanfrom = Julian(y, m, d);
PushToken(TokBuffer);
return OK;
}
}
}
/***************************************************************/
/* */
/* TriggerReminder */
/* */
/* Trigger the reminder if it's a RUN or MSG type. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
#ifdef OS2_POPUP
PUBLIC int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul,
int AsPopUp)
#else /* ! OS2_POPUP */
PUBLIC int TriggerReminder(ParsePtr p, Trigger *t, TimeTrig *tim, int jul)
#endif /* OS2_POPUP */
#else /* ! HAVE_PROTOS */
#ifdef OS2_POPUP
int TriggerReminder(p, t, tim, jul, AsPopUp)
ParsePtr p;
Trigger *t;
TimeTrig *tim;
int jul;
int AsPopUp;
#else /* ! OS2_POPUP */
int TriggerReminder(p, t, tim, jul)
ParsePtr p;
Trigger *t;
TimeTrig *tim;
int jul;
#endif /* OS2_POPUP */
#endif /* HAVE_PROTOS */
{
int r, y, m, d;
char PrioExpr[25];
static char buf[LINELEN+TOKSIZE];
char *s, *s2;
Value v;
if (t->typ == RUN_TYPE && RunDisabled) return E_RUN_DISABLED;
if (t->typ == CAL_TYPE || t->typ == PS_TYPE || t->typ == PSF_TYPE)
return OK;
/* If it's a MSG-type reminder, and no -k option was used, issue the banner. */
if ((t->typ == MSG_TYPE || t->typ == MSF_TYPE)
&& !NumTriggered && !NextMode && !MsgCommand) {
if (!DoSubstFromString(Banner, SubstBuffer, JulianToday, NO_TIME) && *SubstBuffer)
#ifdef OS2_POPUP
if (AsPopUp)
PutlPopUp(SubstBuffer);
else
printf("%s\n", SubstBuffer);
#else
printf("%s\n", SubstBuffer);
#endif
}
/* If it's NextMode, process as a CAL-type entry, and issue simple-calendar
format. */
if (NextMode) {
if ( (r=DoSubst(p, SubstBuffer, t, tim, jul, CAL_MODE)) ) return r;
if (!*SubstBuffer) return OK;
FromJulian(jul, &y, &m, &d);
#ifdef OS2_POPUP
if (AsPopUp) {
sprintf(buf, "%04d%c%02d%c%02d %s", y, DATESEP, m+1, DATESEP, d,
SimpleTime(tim->ttime, NULL));
StartPopUp();
PutsPopUp(buf);
PutlPopUp(SubstBuffer);
}
else
printf("%04d%c%02d%c%02d %s%s\n", y, DATESEP, m+1, DATESEP, d,
SimpleTime(tim->ttime, NULL),
SubstBuffer);
#else
printf("%04d%c%02d%c%02d %s%s\n", y, DATESEP, m+1, DATESEP, d,
SimpleTime(tim->ttime, NULL),
SubstBuffer);
#endif
return OK;
}
/* Put the substituted string into the SubstBuffer */
s2 = buf;
*s2 = 0;
if (UserFuncExists("msgprefix") == 1) {
sprintf(PrioExpr, "msgprefix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
sprintf(s2, "%s", v.v.str);
s2 += strlen(s2);
}
DestroyValue(v);
}
}
if ( (r=DoSubst(p, s2, t, tim, jul, NORMAL_MODE)) ) return r;
s2 += strlen(s2);
if (UserFuncExists("msgsuffix") == 1) {
sprintf(PrioExpr, "msgsuffix(%d)", t->priority);
s = PrioExpr;
r = EvalExpr(&s, &v);
if (!r) {
if (!DoCoerce(STR_TYPE, &v)) {
sprintf(s2, "%s", v.v.str);
s2 += strlen(s2);
}
DestroyValue(v);
}
}
if ((!MsgCommand && t->typ == MSG_TYPE) || t->typ == MSF_TYPE) {
*s2++ = '\n';
}
*s2 = 0;
/* If we are sorting, just queue it up in the sort buffer */
if (SortByDate) {
if (InsertIntoSortBuffer(jul, tim->ttime, buf, t->typ, t->priority) == OK) {
NumTriggered++;
return OK;
}
}
/* If we didn't insert the reminder into the sort buffer, issue the
reminder now. */
switch(t->typ) {
case MSG_TYPE:
if (MsgCommand) {
DoMsgCommand(MsgCommand, buf);
} else {
#ifdef OS2_POPUP
if (AsPopUp)
PutlPopUp(buf);
else
printf("%s", buf);
#else
printf("%s", buf);
#endif
}
break;
case MSF_TYPE:
#ifdef OS2_POPUP
if (AsPopUp) {
StartPopUp();
FillParagraph(buf, 1);
EndPopUp();
} else {
FillParagraph(buf, 0);
}
#else
FillParagraph(buf);
#endif
break;
case RUN_TYPE:
system(buf);
break;
default: /* Unknown/illegal type? */
return E_SWERR;
}
NumTriggered++;
return OK;
}
/***************************************************************/
/* */
/* ShouldTriggerReminder */
/* */
/* Return 1 if we should trigger a reminder, based on today's */
/* date and the trigger. Return 0 if reminder should not be */
/* triggered. */
/* */
/***************************************************************/
#ifdef __TURBOC__
#pragma argsused
#endif
#ifdef HAVE_PROTOS
PUBLIC int ShouldTriggerReminder(Trigger *t, TimeTrig *tim, int jul)
#else
int ShouldTriggerReminder(t, tim, jul)
Trigger *t;
TimeTrig *tim;
int jul;
#endif
{
int r;
/* Handle the ONCE modifier in the reminder. */
if (!IgnoreOnce && t->once !=NO_ONCE && FileAccessDate == JulianToday)
return 0;
if (jul < JulianToday) return 0;
/* Don't trigger timed reminders if DontIssueAts is true, and if the
reminder is for today */
#ifdef HAVE_QUEUED
if (jul == JulianToday && DontIssueAts && tim->ttime != NO_TIME) return 0;
#endif
/* Don't trigger "old" timed reminders */
/*** REMOVED...
if (jul == JulianToday &&
tim->ttime != NO_TIME &&
tim->ttime < SystemTime(0) / 60) return 0;
*** ...UNTIL HERE */
/* If "infinite delta" option is chosen, always trigger future reminders */
if (InfiniteDelta || NextMode) return 1;
/* Move back by delta days, if any */
if (t->delta != NO_DELTA) {
if (t->delta < 0)
jul = jul + t->delta;
else {
r = t->delta;
while(r && jul > JulianToday) {
jul--;
if (!IsOmitted(jul, t->localomit)) r--;
}
}
}
/* Should we trigger the reminder? */
return (jul <= JulianToday);
}
/***************************************************************/
/* */
/* DoSatRemind */
/* */
/* Do the "satisfying..." remind calculation. */
/* */
/***************************************************************/
#ifdef __TURBOC__
#pragma argsused
#endif
#ifdef HAVE_PROTOS
PUBLIC int DoSatRemind(Trigger *trig, TimeTrig *tim, ParsePtr p)
#else
int DoSatRemind(trig, tim, p)
Trigger *trig;
TimeTrig *tim;
ParsePtr p;
#endif
{
int iter, jul, r;
Value v;
char *s, *t;
t = p->pos;
iter = 0;
jul = trig->scanfrom;
while (iter++ < MaxSatIter) {
jul = ComputeTrigger(jul, trig, &r);
if (r) {
if (r == E_CANT_TRIG) return OK; else return r;
}
s = p->pos;
r = EvaluateExpr(p, &v);
t = p->pos;
if (r) return r;
if (v.type != INT_TYPE && v.type != STR_TYPE) return E_BAD_TYPE;
if (v.type == INT_TYPE && v.v.val) return OK;
if (v.type == STR_TYPE && *v.v.str) return OK;
p->pos = s;
jul++;
}
p->pos = t;
LastTrigValid = 0;
return OK;
}
/***************************************************************/
/* */
/* ParsePriority - parse the PRIORITY portion of a reminder */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int ParsePriority(ParsePtr s, Trigger *t)
#else
static int ParsePriority(s, t)
ParsePtr s;
Trigger *t;
#endif
{
int p, r;
char *u;
r = ParseToken(s, TokBuffer);
if(r) return r;
u = TokBuffer;
if (!isdigit(*u)) return E_EXPECTING_NUMBER;
p = 0;
while (isdigit(*u)) {
p = p*10 + *u - '0';
u++;
}
if (*u) return E_EXPECTING_NUMBER;
/* Tricky! The only way p can be < 0 is if overflow occurred; thus,
E2HIGH is indeed the appropriate error message. */
if (p<0 || p>9999) return E_2HIGH;
t->priority = p;
return OK;
}
/***************************************************************/
/* */
/* DoMsgCommand */
/* */
/* Execute the '-k' command, escaping shell chars in message. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC void DoMsgCommand(char *cmd, char *msg)
#else
void DoMsgCommand(cmd, msg)
char *cmd;
char *msg;
#endif
{
#ifdef WANT_SHELL_ESCAPING
char buf[2*LINELEN+TOKSIZE];
char *s, *t;
/* Escape shell characters in msg INCLUDING WHITESPACE! */
for (t=buf, s=msg; *s; s++) {
if (isspace(*s) || strchr("\"'!$%^&*()|<>[]{}`~;?\\", *s)) *t++ = '\\';
*t++ = *s;
}
*t = 0;
/* Use SubstBuffer -- not too safe, since no check for overflow... */
sprintf(SubstBuffer, cmd, buf);
#else
sprintf(SubstBuffer, cmd, msg);
#endif /* WANT_SHELL_ESCAPING */
system(SubstBuffer);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.