ftp.nice.ch/pub/next/unix/calendars/remind.3.0.13.N.bs.tar.gz#/remind-3.0.13/trigger.c

This is trigger.c in view mode; [Download] [Up]

/***************************************************************/
/*                                                             */
/*  TRIGGER.C                                                  */
/*                                                             */
/*  Routines for figuring out the trigger date of a reminder   */
/*                                                             */
/*  This file is part of REMIND.                               */
/*  Copyright (C) 1992, 1993, 1994 by David F. Skoll           */
/*                                                             */
/***************************************************************/

#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include "types.h"
#include "expr.h"
#include "protos.h"
#include "globals.h"
#include "err.h"

#define GOT_DAY 1
#define GOT_MON 2
#define GOT_YR 4
#define GOT_WD 8

static int	JYear		ARGS((int jul));
static int	JMonth		ARGS((int jul));
static int NextSimpleTrig ARGS((int startdate, Trigger *trig, int *err));
static int GetNextTriggerDate ARGS((Trigger *trig, int start, int *err, int *nextstart));

/***************************************************************/
/*                                                             */
/*  NextSimpleTrig                                             */
/*                                                             */
/*  Compute the "simple" trigger date, taking into account     */
/*  ONLY the day of week, day, month and year components.      */
/*  Normally, returns -1 if the trigger has expired.  As a     */
/*  special case, if D, M, Y [WD] are specified, returns the   */
/*  Julian date, regardless of whether it's expired.  This is  */
/*  so that dates with a REP can be handled properly.          */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int NextSimpleTrig(int startdate, Trigger *trig, int *err)
#else
static int NextSimpleTrig(startdate, trig, err)
int startdate;
Trigger *trig;
int *err;
#endif
{
   int typ = 0;
   int d, m, y, j, d2, m2, y2;

   *err = 0;
   FromJulian(startdate, &y, &m, &d);
   d2 = d;
   m2 = m;
   y2 = y;

   if (trig->d != NO_DAY) typ |= GOT_DAY;
   if (trig->m != NO_MON) typ |= GOT_MON;
   if (trig->y != NO_YR) typ |= GOT_YR;
   if (trig->wd != NO_WD) typ |= GOT_WD;
   switch(typ) {
      case 0:
      case GOT_WD:
	 if (trig->wd != NO_WD)
	    while(! (trig->wd & (1 << (startdate%7)))) startdate++;
	 return startdate;

      case GOT_DAY:
	 if (d > trig->d) {
	    m++;
	    if (m == 12) { m = 0; y++; }
	 }
	 while (trig->d > DaysInMonth(m, trig->y)) m++;
	 j = Julian(y, m, trig->d);
	 return j;

      case GOT_MON:
	 if (m == trig->m) return startdate;
	 else if (m > trig->m) return Julian(y+1, trig->m, 1);
	 else return Julian(y, trig->m, 1);

      case GOT_YR:
	 if (y == trig->y) return startdate;
	 else if (y < trig->y) return Julian(trig->y, 0, 1);
	 else return -1;

      case GOT_DAY+GOT_MON:
	 if (m > trig->m || (m == trig->m && d > trig->d)) y++;
	 if (trig->d > MonthDays[trig->m]) {
	    *err = E_BAD_DATE;
	    return -1;
	 }

	 /* Take care of Feb. 29 */
	 while (trig->d > DaysInMonth(trig->m, y)) y++;
	 return Julian(y, trig->m, trig->d);

      case GOT_DAY+GOT_YR:
	if (y < trig->y) return Julian(trig->y, 0, trig->d);
	else if (y > trig->y) return -1;

	if (d > trig->d) {
	   m++;
	   if (m == 12) return -1;
	}
	while (trig->d > DaysInMonth(m, trig->y)) m++;
	return Julian(trig->y, m, trig->d);

      case GOT_MON+GOT_YR:
	 if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
	 if (y < trig->y) return Julian(trig->y, trig->m, 1);
	 if (m == trig->m) return startdate;
	 return Julian(trig->y, trig->m, 1);

      case GOT_DAY+GOT_MON+GOT_YR:
	 if (trig->d > DaysInMonth(trig->m, trig->y)) {
	    *err = E_BAD_DATE;
	    return -1;
	 }
	 return Julian(trig->y, trig->m, trig->d);

      case GOT_YR+GOT_WD:
	 if (y > trig->y) return -1;
	 if (y < trig->y) j = Julian(trig->y, 0, 1);
	 else j = startdate;
	 while(! (trig->wd & (1 << (j%7)))) j++;
	 if (JYear(j) > trig->y) return -1;
	 return j;

      case GOT_MON+GOT_WD:
	 if (m == trig->m) {
	    j = startdate;
	    while(! (trig->wd & (1 << (j%7)))) j++;
	    if (JMonth(j) == trig->m) return j;
	 }
	 if (m >= trig->m) j = Julian(y+1, trig->m, 1);
	 else j = Julian(y, trig->m, 1);
	 while(! (trig->wd & (1 << (j%7)))) j++;
	 return j; /* Guaranteed to be within the month */

      case GOT_DAY+GOT_WD:
	 if (m !=0 || y > BASE) {
	    m2 = m-1;
	    if (m2 < 0) { y2 = y-1; m2 = 11; }

	    /* If there are fewer days in previous month, no match */
	    if (trig->d <= DaysInMonth(m2, y2)) {
	       j = Julian(y2, m2, trig->d);
	       while(! (trig->wd & (1 << (j%7)))) j++;
	       if (j >= startdate) return j;

	    }
	 }

	 /* Try this month */
	 if (trig->d <= DaysInMonth(m, y)) {
	    j = Julian(y, m, trig->d);
	    while(! (trig->wd & (1 << (j%7)))) j++;
	    if (j >= startdate) return j;
	 }

         /* Argh!  Try next avail. month */
	 m2 = m+1;
	 if (m2 > 11) { m2 = 0; y++; }
	 while (trig->d > DaysInMonth(m2, y)) m2++;
	 j = Julian(y, m2, trig->d);
	 while(! (trig->wd & (1 << (j%7)))) j++;
	 return j;

      case GOT_WD+GOT_YR+GOT_DAY:
	 if (y > trig->y+1 || (y > trig->y && m>0)) return -1;
	 if (y > trig->y) {
	    j = Julian(trig->y, 11, trig->d);
	    while(! (trig->wd & (1 << (j%7)))) j++;
	    if (j >= startdate) return j;
	 } else if (y < trig->y) {
	    j = Julian(trig->y, 0, trig->d);
	    while(! (trig->wd & (1 << (j%7)))) j++;
	    return j;
	 } else {
	    /* Try last month */
	    if (m > 0) {
	       m2 = m-1;
	       while (trig->d > DaysInMonth(m2, trig->y)) m2--;
	       j = Julian(trig->y, m2, trig->d);
	       while(! (trig->wd & (1 << (j%7)))) j++;
	       if (j >= startdate) return j;
	    }
	 }
	 /* Try this month */
	 if (trig->d <= DaysInMonth(m, trig->y)) {
	    j = Julian(trig->y, m, trig->d);
	    while(! (trig->wd & (1 << (j%7)))) j++;
	    if (j >= startdate) return j;
	 }

	 /* Must be next month */
	 if (m == 11) return -1;
	 m++;
	 while (trig->d > DaysInMonth(m, trig->d)) m++;
	 j = Julian(trig->y, m, trig->d);
	 while(! (trig->wd & (1 << (j%7)))) j++;
	 return j;

      case GOT_DAY+GOT_MON+GOT_WD:
	 /* Move up to the first valid year */
	while (trig->d > DaysInMonth(trig->m, y)) y++;

	/* Try this year */
	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;
	while (trig->d > DaysInMonth(trig->m, y)) y++;
	j = Julian(y, trig->m, trig->d);
	while(! (trig->wd & (1 << (j%7)))) j++;
	return j;

      case GOT_WD+GOT_MON+GOT_YR:
	 if (y > trig->y || (y == trig->y && m > trig->m)) return -1;
	 if (trig->y > y || (trig->y == y && trig->m > m)) {
	    j = Julian(trig->y, trig->m, 1);
	    while(! (trig->wd & (1 << (j%7)))) j++;
	    return j;
	 } else {
	    j = startdate;
	    while(! (trig->wd & (1 << (j%7)))) j++;
	    FromJulian(j, &y2, &m2, &d2);
	    if (m2 == trig->m) return j; else return -1;
	 }

      case GOT_WD+GOT_DAY+GOT_MON+GOT_YR:
	 j = Julian(trig->y, trig->m, trig->d);
	 while(! (trig->wd & (1 << (j%7)))) j++;
	 return j;

      default:
	Eprint("NextSimpleTrig %s %d", ErrMsg[E_SWERR], typ);
	*err = E_SWERR;
	return -1;
   }
}

/***************************************************************/
/*                                                             */
/*  JMonth - Given a Julian date, what's the month?            */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int JMonth(int jul)
#else
static int JMonth(jul)
int jul;
#endif
{
   int y, m, d;
   FromJulian(jul, &y, &m, &d);
   return m;
}

/***************************************************************/
/*                                                             */
/*  JYear - Given a Julian date, what's the year?              */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int JYear(int jul)
#else
static int JYear(jul)
int jul;
#endif
{
   int y, m, d;
   FromJulian(jul, &y, &m, &d);
   return y;
}

/***************************************************************/
/*                                                             */
/*  GetNextTriggerDate                                         */
/*                                                             */
/*  Given a trigger, compute the next trigger date.            */
/*                                                             */
/*  Returns the Julian date of next trigger, -1 if             */
/*  expired, -2 if can't compute trigger date.                 */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int GetNextTriggerDate(Trigger *trig, int start, int *err, int *nextstart)
#else
static int GetNextTriggerDate(trig, start, err, nextstart)
Trigger *trig;
int start;
int *err;
int *nextstart;
#endif
{
   int simple, mod;

/* First:  Have we passed the UNTIL date? */
   if (trig->until != NO_UNTIL &&
       trig->until < start) return -1; /* expired */

/* Next: If it's an "AFTER"-type skip, back up
   until we're at the start of a block of holidays */
   if (trig->skip == AFTER_SKIP)
      while (IsOmitted(start-1, trig->localomit)) start--;

/* Find the next simple trigger */
   simple = NextSimpleTrig(start, trig, err);

/* Problems? */
   if (*err || (simple == -1)) return -1;

/* Suggested starting point for next attempt */
   *nextstart = simple+1;

/* 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 there's a REP, calculate the next occurrence */
   if (trig->rep != NO_REP) {
      if (simple < start) {
	 mod = (start - simple) / trig->rep;
	 simple = simple + mod * trig->rep;
	 if (simple < start) simple += trig->rep;
      }
   }

/* If it's a "BEFORE"-type skip, back up */
   if (trig->skip == BEFORE_SKIP)
      while(IsOmitted(simple, trig->localomit)) simple--;

/* If it's an "AFTER"-type skip, jump ahead */
   if (trig->skip == AFTER_SKIP)
      while (IsOmitted(simple, trig->localomit)) simple++;

/* Return the date */
   return simple;
}

/***************************************************************/
/*                                                             */
/*  ComputeTrigger                                             */
/*                                                             */
/*  The main function.  Compute the next trigger date given    */
/*  today's date.                                              */
/*                                                             */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int ComputeTrigger(int today, Trigger *trig, int *err)
#else
int ComputeTrigger(today, trig, err)
int today;
Trigger *trig;
int *err;
#endif
{
   int nattempts = 0,
       start = today,
       nextstart,
       y, m, d,
       result;

   LastTrigValid = 0;
/* Assume everything works */
   *err = OK;

/* But check for obvious problems... */
   if (trig->localomit == 1 + 2 + 4 + 8 + 16 + 32 + 64) {
      *err = E_2MANY_LOCALOMIT;
      return -1;
   }

   if (trig->rep != NO_REP &&
       (trig->d == NO_DAY ||
	trig->m == NO_MON ||
	trig->y == NO_YR)) {
      Eprint("%s", ErrMsg[E_REP_FULSPEC]);
      *err = E_REP_FULSPEC;
      return -1;
   }
       
   
   while (nattempts++ < TRIG_ATTEMPTS) {
      result = GetNextTriggerDate(trig, start, err, &nextstart);

      /* If there's an error, die immediately */
      if (*err) return -1;
      if (result == -1) {
         if (DebugFlag & DB_PRTTRIG) {
	    fprintf(ErrFp, "%s(%d): %s\n",
	       FileName, LineNo, ErrMsg[E_EXPIRED]);
	 }
         return -1;
      }

      /* If result is >= today, great! */
      if (result >= today &&
         (trig->skip != SKIP_SKIP || !IsOmitted(result, trig->localomit))) {
	 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",
	            FileName, LineNo,
		    DayName[result % 7],
		    d,
		    MonthName[m],
		    y);
	 }
	 return result;
      }

      /* If it's a simple trigger, no point in rescanning */
      if (trig->back == NO_BACK &&
          trig->skip == NO_SKIP &&
	  trig->rep == NO_REP) {
          if (DebugFlag & DB_PRTTRIG) {
	     fprintf(ErrFp, "%s(%d): %s\n",
	             FileName, LineNo, ErrMsg[E_EXPIRED]);
	     }
	     if (result != -1) {
	        LastTriggerDate = result;
		LastTrigValid = 1;
	     }
	     return -1;
	  }
      /* Keep scanning... unless there's no point in doing it.*/
      if (nextstart <= start) {
      	 if (result != -1) {
	    LastTriggerDate = result;
	    LastTrigValid = 1;
	 }
         if (DebugFlag & DB_PRTTRIG) {
	    fprintf(ErrFp, "%s(%d): %s\n",
	              FileName, LineNo, ErrMsg[E_EXPIRED]);
         }
	 return -1;
      }
      else start = nextstart;

   }

   /* We failed - too many attempts or trigger has expired*/
   *err = E_CANT_TRIG;
   return -1;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.