home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / TIMEXSRC.ZIP / BA_EVCTL.C < prev    next >
Text File  |  1990-03-29  |  23KB  |  784 lines

  1. /* ba_evctl.c -- Background Agent event control
  2.  
  3.         February 1990   Mark E. Mallett, Personal Workstation Magazine
  4.  
  5. This file contains routines that control events.
  6.  
  7.  
  8. Included are:
  9.  
  10.         schedule_events The event scheduler thread
  11.  
  12. */
  13.  
  14. #define    INCL_BASE        /* Get all DOS defs */
  15.  
  16. #include <stdio.h>
  17. #include <io.h>
  18. #include <sys/types.h>
  19. #include <time.h>
  20. #include <string.h>
  21. #include <memory.h>
  22. #include <os2.h>
  23. #include <dos.h>
  24.  
  25. #include "timex.h"
  26. #include "ba_timex.h"
  27.  
  28.  
  29. /* Local Definitions */
  30.  
  31.  
  32. /* External data referenced */
  33.  
  34. extern  HSEM    Eventsem;       /* Event list interlock semaphore */
  35. extern  PEVENT  *PendingL;      /* List of pending events */
  36. extern  int     Priclass;       /* My priority class */
  37. extern  int     Prival;         /* My priority value */
  38.  
  39. /* External routines used */
  40.  
  41. extern  void    log_begin( void );
  42. extern  void    log_end( void );
  43. extern  long    tmclock( struct tm *tmP );
  44. extern struct tm *xlocaltime( time_t * );    /* Fix for buggy MSC one. */
  45.  
  46.  
  47. /* Local data publicly available */
  48.  
  49.  
  50. /* Local routines and forward declarations */
  51.  
  52.         void    action( PEVENT *peventP );
  53.         void    act_run( PEVENT *peventP );
  54.         void    calcntime( PEVENT *peventP );
  55.  
  56. static void calc_dayof( EVENT *eventP, struct tm *tmP, int op );
  57. static void calc_hour( EVENT *eventP, struct tm *tmP, int op );
  58. static void calc_min( EVENT *eventP, struct tm *tmP, int op );
  59. static void calc_month( EVENT *eventP, struct tm *tmP, int op );
  60. static void calc_sec( EVENT *eventP, struct tm *tmP, int op );
  61. static void calc_year( EVENT *eventP, struct tm *tmP, int op );
  62.  
  63.  
  64. /* Private data */
  65.  
  66.     /* Table of days per month */
  67. static  int     Mdays[12] = {
  68.         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  69.                             };
  70.  
  71. /*
  72.  
  73. *//* schedule_events()
  74.  
  75.     Schedule the events
  76.  
  77. Accepts :
  78.  
  79.  
  80. Returns :
  81.  
  82.  
  83. Notes :
  84.  
  85.     This is the routine that handles the scheduling of pending
  86. events.  Since the events on the pending list (PendingL) are in
  87. due-time order, only the first event need be looked at to determine
  88. the next schedule time.  This routine waits for that event to come
  89. due (in small increments, so that external changes can be caught
  90. in reasonable time), and schedules it.
  91.  
  92.  
  93. */
  94.  
  95. void
  96. schedule_events()
  97. {
  98.     time_t        curtime;    /* Current time (in seconds) */
  99.     LONG        sleeptime;    /* Milliseconds to sleep */
  100.     PEVENT        *peventP;    /* Ptr to next pending event */
  101.  
  102.     /* Loop forever, scheduling events. */
  103.     for( ; ; ) {
  104.         /* Figure out how long to sleep until the next event is ready. */
  105.     if ( ( peventP = PendingL ) == NULL )
  106.         /* No next event ... use default time */
  107.         sleeptime = SLEEPINCR;
  108.     else {
  109.         /* Get difference between due time and time now */
  110.         curtime = time( (time_t *)NULL );
  111.         sleeptime = peventP->pe_ntime - curtime;
  112.  
  113.             /* Interesting compiler problem: MSC 5.1 doesn't do the
  114.                 multiplication by 1000 without the (long) cast.  It
  115.                 compiles fine, but has no effect. */
  116.             if ( sleeptime < ( 0x7fffffff/1000 ) )
  117.                 sleeptime = (long)sleeptime * 1000;
  118.  
  119.         if ( sleeptime > SLEEPINCR )
  120.         sleeptime = SLEEPINCR;
  121.     }
  122.  
  123.     /* If we must sleep (positive sleeptime), then sleep. */
  124.         if ( sleeptime > 0 )
  125.             DosSleep( sleeptime );
  126.  
  127.     /* Check to see if first event is ready to go. */
  128.         if ( ( peventP = PendingL ) != NULL ) {
  129.         curtime = time( (time_t *)NULL );
  130.         if ( curtime >= peventP->pe_ntime ) {
  131.         /* First event appears ready.  Grab access to the data
  132.            before proceeding. */
  133.                 get_sem( "schedule_events", "event list", Eventsem );
  134.  
  135.         /* Run the first event if it is still ready */
  136.         curtime = time( (time_t *)NULL );
  137.         if ( ( ( peventP = PendingL ) != NULL ) &&
  138.              ( curtime >= peventP->pe_ntime ) ) {
  139.  
  140.             /* Do the action for the event */
  141.                     action( peventP );
  142.  
  143.             /* Remove the event from the event list */
  144.             unschedule( peventP );
  145.  
  146.             /* Schedule the event again. */
  147.             schedule( peventP );
  148.         }
  149.  
  150.         /* Release access to the events */
  151.                 rel_sem( "schedule_events", "event list", Eventsem );
  152.         }
  153.     }
  154.     }
  155. }
  156. /*
  157.  
  158. *//* action( peventP )
  159.  
  160.     Perform the action for an event
  161.  
  162. Accepts :
  163.  
  164.     peventP        A pending event structure
  165.  
  166. Returns :
  167.  
  168.     < nothing >
  169.  
  170. */
  171.  
  172. void
  173. action( peventP )
  174.     PEVENT        *peventP;    /* Event to make act */
  175. {
  176.     /* If the event specifies a particular priority, setup our
  177.        priority before doing the action. */
  178.     if ( peventP->pe_eventP->ev_priclass != -1 )
  179.         set_priority( 0, peventP->pe_eventP->ev_priclass,
  180.               peventP->pe_eventP->ev_prival );
  181.  
  182.     /* Dispatch according to the action type */
  183.     switch( peventP->pe_eventP->ev_acttype ) {
  184.     case    ACTION_RUN:        /* Run a program */
  185.             act_run( peventP );
  186.         break;
  187.  
  188.     default:            /* Error.. */
  189.         warning( EC_UNKACTION, "unknown action type in action()" );
  190.         break;
  191.     }
  192.  
  193.     /* Reset program priority */
  194.     if ( peventP->pe_eventP->ev_priclass != -1 )
  195.         set_priority( 0, Priclass, Prival );
  196.  
  197. }
  198. /*
  199.  
  200. *//* act_run( peventP )
  201.  
  202.     Perform a "RUN" action for an event.
  203.  
  204. Accepts :
  205.  
  206.     peventP        The run event
  207.  
  208. Returns :
  209.  
  210.     <nothing>
  211.  
  212. */
  213.  
  214. void
  215. act_run( peventP )
  216.     PEVENT        *peventP;    /* Ptr to event */
  217. {
  218.     int        status;        /* Status code */
  219.     RESULTCODES    result;        /* Result from prog run */
  220.     char        *cmdP;        /* Ptr to command line */
  221.         char            *argP;          /* Ptr to arg string */
  222.     char        progname[128];    /* Name of program */
  223.         char            args[128];      /* Program arguments */
  224.     char        failbuf[256];    /* Failure string buffer */
  225.  
  226.     /* Extract the program name from the action string */
  227.     cmdP = peventP->pe_eventP->ev_actargP;
  228.     cmdP = gettoken( cmdP, &progname[0], 128, "", " \t" );
  229.  
  230.     /* Get argument string and double-NUL-terminate it */
  231.     argP = &args[0];
  232.     strcpy( argP, &progname[0] );       /* Command name */
  233.     argP += strlen( argP );
  234.     *argP++ = NUL;                      /* Name must be nul-terminated */
  235.     strcpy( argP, cmdP );               /* Arg string */
  236.     argP += strlen( argP );
  237.     argP[1] = NUL;                      /* Must be double-nul'ed */
  238.  
  239.     /* Log the attempt to start the program */
  240.     log_begin();                        /* Redirect to log file */
  241.     log( "Starting RUN of \"%s\" with args \"%s\"", &progname[0],
  242.                  &args[ strlen( &args[0] ) +1] );
  243.  
  244.     /* Run the program */
  245.     status = DosExecPgm( &failbuf[0],   /* Failure filename buffer */
  246.                          256,           /* Size of fail buff */
  247.                          EXEC_SYNC,     /* Flags */
  248.                          &args[0],      /* Command line args */
  249.                          0,             /* Environment string */
  250.                          &result,       /* Results */
  251.                          &progname[0]   /* Program */
  252.                        );
  253.  
  254.     if ( status != 0 )
  255.         warning( EC_NOTOK, "DosExecPgm failed: %d", status );
  256.     else
  257.     /* Success; log the program termination. */
  258.         log( "Program terminated with status %d", result.codeResult );
  259.  
  260.     log_end();                          /* End log redirect */
  261. }
  262. /*
  263.  
  264. *//* schedule( peventP )
  265.  
  266.         Schedule an event
  267.  
  268. Accepts :
  269.  
  270.         peventP         Ptr to pending event structure
  271.  
  272. Returns :
  273.  
  274.         < nothing >
  275.  
  276. Notes :
  277.  
  278.         This routine adds the event to the event list, in proper
  279.         time-ordered place.
  280. */
  281.  
  282.  
  283. void
  284. schedule(
  285.         PEVENT          *peventP        /* Ptr to pending event struct */
  286. ) {
  287.  
  288.         time_t          ntime;          /* Next event fire time */
  289.         PEVENT          *nextP;         /* Ptr to next one on list */
  290.         PEVENT          *prevP;         /* Previous one */
  291.  
  292.     /* Calculate the next fire time */
  293.     calcntime( peventP );
  294.     ntime = peventP->pe_ntime;
  295.  
  296.     /* Find the place to put the new event */
  297.     prevP = NULL;
  298.     for( nextP = PendingL; nextP != NULL;
  299.                  prevP = nextP, nextP = nextP->pe_nextP ) {
  300.         if ( ntime < nextP->pe_ntime )
  301.             break;
  302.     }
  303.  
  304.     /* Link in the new event */
  305.     peventP->pe_nextP = nextP;
  306.     peventP->pe_prevP = prevP;
  307.     if ( nextP != NULL )
  308.        nextP->pe_prevP = peventP;
  309.     if ( prevP != NULL )
  310.         prevP->pe_nextP = peventP;
  311.     else
  312.         PendingL = peventP;
  313. }
  314. /*
  315.  
  316. *//* unschedule( peventP )
  317.  
  318.         Unschedule an event
  319.  
  320. Accepts :
  321.  
  322.         peventP         Ptr to pending event structure
  323.  
  324. Returns :
  325.  
  326.  
  327. Notes :
  328.  
  329.         This routine removes an event from the time-ordered list
  330.  
  331.         No check is made to ensure that the event is actually on the
  332.          list; it's assumed that the program calls this routine without
  333.          error.
  334.  
  335. */
  336.  
  337. void
  338. unschedule(
  339.         PEVENT          *peventP        /* Ptr to pending event */
  340. ) {
  341.  
  342.         PEVENT          *prevP;         /* Ptr to previous */
  343.         PEVENT          *nextP;         /* Ptr to next */
  344.  
  345.     /* Unlink it */
  346.     prevP = peventP->pe_prevP;
  347.     nextP = peventP->pe_nextP;
  348.  
  349.     if ( prevP != NULL )
  350.         prevP->pe_nextP = nextP;
  351.     else
  352.         PendingL = nextP;
  353.  
  354.     if ( nextP != NULL )
  355.         nextP->pe_prevP = prevP;
  356.  
  357. }
  358. /*
  359.  
  360. *//* event_find( nameP )
  361.  
  362.         Find an event by name
  363.  
  364. Accepts :
  365.  
  366.         nameP           Name to find
  367.  
  368. Returns :
  369.  
  370.         < value >       ptr to PEVENT, or NULL if no event found.
  371.  
  372. */
  373.  
  374. PEVENT *
  375. event_find(
  376.         char            *nameP          /* Name of the event */
  377.     ) {
  378.  
  379.         PEVENT          *peventP;       /* Ptr to event... */
  380.  
  381.     /* Look through the list */
  382.     for( peventP = PendingL; peventP != NULL; peventP = peventP->pe_nextP )
  383.         if ( stricmp( nameP, peventP->pe_eventP->ev_nameP ) == 0 )
  384.             break;
  385.  
  386.     /* Return what we got */
  387.     return ( peventP );
  388. }
  389. /*
  390.  
  391. *//* calcntime( peventP )
  392.  
  393.         Calculate next "fire" time for an event
  394.  
  395. Accepts :
  396.  
  397.         peventP         Ptr to pending event struct
  398.  
  399. Returns :
  400.  
  401.         peventP         modified to include next fire time
  402.  
  403. Notes :
  404.  
  405.         Next fire time is calculated relative to the current time.  For
  406.         events that fire rapidly, this could result in missed triggers.
  407.         I don't see that as a problem: events that fire that quickly
  408.         should not be allowed to get behind the clock, and most events
  409.         won't be in that category anyway.  If there's a requirement for
  410.         the other mode, it could be added as a special run category.
  411.         But it also probably wouldn't work: if an event gets behind and
  412.         is not allowed to catch up, it will likely get further and
  413.         further behind anyway.
  414.  
  415.         The calculation of the next fire time is done via a suite of
  416.         routines that either advance or minimize the particular
  417.         component, according to the constraints in the event
  418.         description.  When a component is advanced beyond its
  419.         maximum, and wraps around, a carry is given to the next
  420.         higher compoent, and a minimize is done on the next lower
  421.         component.  When a minimize operation is carried out,
  422.         a minimize is also performed on the next lower component.
  423.         Thus this is like a series of digit wheels, causing right-and-
  424.         left cascading.
  425.  
  426.         Some rigamarole has to be done to maintain proper day of week.
  427.         This is reflected in the fiddling with day-of-week and
  428.         day-of-year in places where the day is adjusted (year, month,
  429.         and day).
  430.  
  431.         I'm open to suggestions for a better way to do this...
  432.  
  433. */
  434.  
  435. void
  436. calcntime(
  437.         PEVENT          *peventP        /* Ptr to pending event */
  438.          ) {
  439.  
  440.         time_t          curtime;        /* Current time */
  441.         time_t          ntime;          /* New time */
  442.         struct tm       curtm;          /* Current time component */
  443.         struct tm       ntm;            /* New time components */
  444.  
  445.     curtime = time( (time_t *)NULL );
  446.     memcpy( &curtm, xlocaltime( &curtime ), sizeof( struct tm ) );
  447.     memcpy( &ntm, &curtm, sizeof( struct tm ) );
  448.  
  449.     /* Start the digit ticker by advancing the seconds */
  450.     calc_sec( peventP->pe_eventP, &ntm, 1 );
  451.  
  452.     /* Result is in ntm.  Year has been set to -1 if there's no
  453.         possible next fire time. */
  454.     if ( ntm.tm_year == -1 )
  455.         ntime = 0x7fffffff;
  456.     else {
  457.         ntime = tmclock( &ntm );
  458.         if ( ntime == -1 )
  459.             ntime = 0x7fffffff;
  460.     }
  461.  
  462.     /* Store the new value */
  463.     peventP->pe_ntime = ntime;
  464. }
  465. /*
  466.  
  467. *//* calc_xxx( eventP, tmP, op )
  468.  
  469.         Calculate-component routines follow.  These routines perform
  470.         a minimize or maximize operation on a particular time component
  471.         as constrained by an event description, as described in the
  472.         notes for calcntime().
  473.  
  474. Accepts :
  475.  
  476.         eventP          Ptr to event struct
  477.         tmP             Ptr to time value to manipulate
  478.         op              Operation: -1 = validate, 0 = minimize, 1 = maximize
  479.  
  480. Returns :
  481.  
  482.  
  483. Notes :
  484.  
  485.         Note that both min and max operations cause a right-minimize.
  486.  
  487. */
  488.  
  489.  
  490. static void
  491. calc_dayof( EVENT *eventP, struct tm *tmP, int op ) {
  492.  
  493.         int             dommax;         /* Maximum day of month */
  494.  
  495.     /* Get max days in this month */
  496.     dommax = Mdays[tmP->tm_mon];
  497.     if ( tmP->tm_mon == 1 )             /* Check Feb */
  498.         if ( tmP->tm_year % 4 == 0 )
  499.             ++dommax;
  500.  
  501.  
  502.     /* Proceed according to day-of class */
  503.     if ( eventP->ev_dayof == DAYOF_MONTH ) {
  504.         if ( op == -1 )                 /* Validate */
  505.             /* Decrement current value and go through the increment. */
  506.             --tmP->tm_mday;
  507.  
  508.         else if ( op == 0 )
  509.             /* We'll set day of month to zero and then advance it to
  510.                 a constrained value */
  511.             tmP->tm_mday = 0;
  512.  
  513.         /* Advance day of month until it meets constraints */
  514.         for( ; ; ) {
  515.             if ( ++tmP->tm_mday > dommax ) {
  516.                 /* Exceeded month; carry into next month. */
  517.                 calc_month( eventP, tmP, 1 );
  518.                 return;
  519.             }
  520.  
  521.             /* Check constraints */
  522.             if ( ( eventP->ev_dayofs == 0 ) ||
  523.                  ( ( eventP->ev_dayofs & ( 1L << ( tmP->tm_mday -1 ) ) )
  524.                         != 0 )
  525.                )
  526.                 /* OK. */
  527.                 break;
  528.  
  529.             op = 1;                     /* No longer just validating. */
  530.         }
  531.     }
  532.     else {                              /* Day-of week */
  533.         if ( op == -1 ) {               /* Validate */
  534.             /* Back up one, and go through the increment process. */
  535.             --tmP->tm_wday;
  536.             --tmP->tm_yday;
  537.             --tmP->tm_mday;
  538.         }
  539.         else if ( op == 0 ) {           /* Minimize */
  540.             /* As with dayofmonth, we'll reset to a point were we can
  541.                advance to a valid value. */
  542.             tmP->tm_wday -= (tmP->tm_mday % 7);
  543.             tmP->tm_yday -= tmP->tm_yday;
  544.             tmP->tm_mday = 0;
  545.         }
  546.  
  547.         /* Advance until constraint is met */
  548.         for ( ; ; ) {
  549.             ++tmP->tm_mday;
  550.             ++tmP->tm_yday;
  551.             tmP->tm_wday = (tmP->tm_wday +1 ) % 7;
  552.  
  553.             if ( tmP->tm_mday > dommax ) {
  554.                 /* Went over month; carry into next one. */
  555.                 calc_month( eventP, tmP, 1 );
  556.                 return;
  557.             }
  558.  
  559.             /* Check day-of-week constraints.  Note that TIMEX operates
  560.                 on a monday=0 basis, whereas the tm struct has sunday=0.
  561.                 Adjustment has to be made for that.
  562.             */
  563.             if ( ( eventP->ev_dayofs == 0 ) ||
  564.                  ( ( eventP->ev_dayofs &
  565.                          ( 1L << ( ( tmP->tm_wday + 6 ) % 7 ) ) ) != 0 )
  566.                )
  567.                 break;
  568.  
  569.             op = 1;                     /* Not just validating now. */
  570.         }
  571.     }
  572.  
  573.     if ( op != 0 )                      /* Validate left if not minimizing */
  574.         calc_month( eventP, tmP, -1 );
  575.     if ( op != -1 )                     /* Minimize right if not validating */
  576.         calc_hour( eventP, tmP, 0 );
  577.  
  578. }
  579.  
  580. static void
  581. calc_hour( EVENT *eventP, struct tm *tmP, int op ) {
  582.  
  583.     if ( op == -1 )                     /* Validate */
  584.         /* Back off an hour, and work forward. */
  585.         --tmP->tm_hour;
  586.  
  587.     else if ( op == 0 )                 /* Minimize */
  588.         tmP->tm_hour = -1;
  589.  
  590.     /* Advance hour until it is OK. */
  591.     for( ; ; ) {
  592.         ++tmP->tm_hour;
  593.         if ( tmP->tm_hour == 24 ) {
  594.             /* Went too far; carry into dayofs. */
  595.             calc_dayof( eventP, tmP, 1 );
  596.             return;
  597.         }
  598.  
  599.         if ( ( eventP->ev_hours == 0 ) ||
  600.              ( ( eventP->ev_hours & ( 1L << tmP->tm_hour ) ) != 0 )
  601.            )
  602.             break;
  603.  
  604.         op = 1;                         /* Clear validation. */
  605.     }
  606.  
  607.     if ( op != 0 )                      /* Validate left if not minimizing */
  608.         calc_dayof( eventP, tmP, -1 );
  609.     if ( op != -1 )                     /* Minimize right if not validating */
  610.         calc_min( eventP, tmP, 0 );
  611. }
  612.  
  613. static void
  614. calc_min( EVENT *eventP, struct tm *tmP, int op ) {
  615.  
  616.     if ( op == -1 )                     /* Validate */
  617.         --tmP->tm_min;
  618.  
  619.     else if ( op == 0 )                  /* Minimize */
  620.         tmP->tm_min = -1;
  621.  
  622.     /* Advance minutes until OK */
  623.     for( ; ; ) {
  624.         ++tmP->tm_min;
  625.         if ( tmP->tm_min == 60 ) {
  626.             /* Carry into the next hour. */
  627.             calc_hour( eventP, tmP, 1 );
  628.             return;
  629.         }
  630.  
  631.         if ( ( eventP->ev_mins[0] == 0 ) &&
  632.              ( eventP->ev_mins[1] == 0 )
  633.            )
  634.             break;
  635.  
  636.         if ( ( eventP->ev_mins[tmP->tm_min/32] &
  637.                 ( 1L << ( tmP->tm_min % 32 ) ) ) != 0 )
  638.             break;
  639.  
  640.         op = 1;                         /* Clear validation */
  641.     }
  642.  
  643.  
  644.     if ( op != 0 )                      /* Validate left if not minimizing */
  645.         calc_hour( eventP, tmP, -1 );
  646.     if ( op != -1 )                     /* Minimize right if not validating */
  647.         calc_sec( eventP, tmP, 0 );
  648. }
  649.  
  650. static void
  651. calc_month( EVENT *eventP, struct tm *tmP, int op ) {
  652.  
  653.         int             mdays;          /* Days in month */
  654.  
  655.     if ( op == -1 )                     /* Validation */
  656.         /* Back up one and then find a valid month by incrementing. */
  657.         --tmP->tm_mon;
  658.  
  659.     else if ( op == 0 ) {               /* Minimize */
  660.         /* Set to minus month in preparation for advancing */
  661.         tmP->tm_mon = -1;
  662.         tmP->tm_wday =- ( tmP->tm_yday % 7 );
  663.         tmP->tm_yday = -1;
  664.     }
  665.  
  666.     /* Advance until properly constrained */
  667.     for( ; ; ) {
  668.  
  669.         if ( tmP->tm_mon == 11 ) {
  670.             /* Carry into the next year */
  671.             calc_year( eventP, tmP, 1 );
  672.             return;
  673.         }
  674.  
  675.         if ( op == 1 ) {
  676.             mdays = Mdays[tmP->tm_mon];
  677.             if ( ( tmP->tm_mon == 1 ) &&
  678.                  ( ( tmP->tm_year % 4 ) == 0 )
  679.                )
  680.                 ++mdays;
  681.             tmP->tm_wday += (mdays % 7);
  682.             tmP->tm_yday += mdays;
  683.         }
  684.  
  685.         ++tmP->tm_mon;
  686.  
  687.         if ( ( eventP->ev_months == 0 ) ||
  688.              ( ( eventP->ev_months & ( 1 << tmP->tm_mon ) ) != 0 )
  689.            )
  690.             break;
  691.  
  692.         /* No longer validating... in increment mode. */
  693.         op = 1;
  694.     }
  695.  
  696.     if ( op != 0 )                      /* Validate left if not minimizing */
  697.         calc_year( eventP, tmP, -1 );
  698.     if ( op != -1 )                     /* Minimize right if not validating */
  699.         calc_dayof( eventP, tmP, 0 );
  700.  
  701. }
  702.  
  703. static void
  704. calc_sec( EVENT *eventP, struct tm *tmP, int op ) {
  705.  
  706.     /* TIMEX doesn't keep track of seconds, but this routine is
  707.        here just to keep them zero. */
  708.  
  709.     tmP->tm_sec = 0;
  710.  
  711.     /* If it's not a minimize, pass the operation leftwards. */
  712.     if ( op != 0 )
  713.         calc_min( eventP, tmP, op );
  714. }
  715.  
  716. static void
  717. calc_year( EVENT *eventP, struct tm *tmP, int op ) {
  718.  
  719.         int             year;           /* Actual year. */
  720.         int             yX;             /* Year index */
  721.         int             ydays;          /* Days in the year */
  722.  
  723.     if ( op == -1 )                     /* Validate */
  724.         /* Decrement and then walk forward as if incrementing. */
  725.         --tmP->tm_year;
  726.  
  727.     else if ( op == 0 )                  /* Minimize */
  728.         /* There is no year minimize -- just return. */
  729.         return;
  730.  
  731.     /* Must keep advancing by single years to keep the weekdays and
  732.         yeardays in line. */
  733.     for( ; ; ) {
  734.         if ( op == 1 ) {
  735.  
  736.             /* Adjust day of week.  Note that this (as well as the other
  737.                tm components such as month day) won't be exactly right
  738.                if we just left it like this -- it's only used by other
  739.                functions (month, day of month) when they do a minimize.
  740.             */
  741.  
  742.             ydays = 365;
  743.             if ( ( tmP->tm_year % 4 ) == 0 )
  744.                 ++ydays;
  745.             tmP->tm_wday += (ydays % 7);
  746.         }
  747.  
  748.         /* Bump year and see if it matches constraints. */
  749.         ++tmP->tm_year;
  750.  
  751.         if ( eventP->ev_yearC == 0 )
  752.             break;                      /* No year constraints. */
  753.  
  754.         /* See if we're in the table anywhere. */
  755.         year = tmP->tm_year + 1900;
  756.         for( yX = 0; yX < eventP->ev_yearC; ++yX )
  757.             if ( ( year >= eventP->ev_yearP[yX].y_first ) &&
  758.                  ( year <= eventP->ev_yearP[yX].y_last )
  759.                )
  760.             break;
  761.  
  762.         if ( yX == eventP->ev_yearC ) {
  763.             /* No matching year -- can't schedule this event.  We set
  764.                the year to -1 as a signal to the caller, and return.
  765.             */
  766.             tmP->tm_year = -1;
  767.             return;
  768.         }
  769.  
  770.         /* No longer validating.. */
  771.         op = 1;
  772.     }
  773.  
  774.  
  775.     if ( op != -1 )                     /* Minimize right if not validating */
  776.         calc_month( eventP, tmP, 0 );
  777. }
  778.  
  779.  
  780.  
  781.  
  782.  
  783.  
  784.