home *** CD-ROM | disk | FTP | other *** search
/ Nebula / nebula.bin / SourceCode / CronVixie2.1 / entry.c < prev    next >
C/C++ Source or Header  |  1992-01-14  |  12KB  |  487 lines

  1. #if !defined(lint) && !defined(LINT)
  2. static char rcsid[] = "$Header: entry.c,v 2.1 90/07/18 00:23:41 vixie Exp $";
  3. #endif
  4.  
  5. /* vix 26jan87 [RCS'd; rest of log is in RCS file]
  6.  * vix 01jan87 [added line-level error recovery]
  7.  * vix 31dec86 [added /step to the from-to range, per bob@acornrc]
  8.  * vix 30dec86 [written]
  9.  */
  10.  
  11.  
  12. /* Copyright 1988,1990 by Paul Vixie
  13.  * All rights reserved
  14.  *
  15.  * Distribute freely, except: don't remove my name from the source or
  16.  * documentation (don't take credit for my work), mark your changes (don't
  17.  * get me blamed for your possible bugs), don't alter or remove this
  18.  * notice.  May be sold if buildable source is provided to buyer.  No
  19.  * warrantee of any kind, express or implied, is included with this
  20.  * software; use at your own risk, responsibility for damages (if any) to
  21.  * anyone resulting from the use of this software rests entirely with the
  22.  * user.
  23.  *
  24.  * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
  25.  * I'll try to keep a version up to date.  I can be reached as follows:
  26.  * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013,
  27.  * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul
  28.  */
  29.  
  30.  
  31. #include "cron.h"
  32.  
  33. typedef    enum
  34.     {e_none, e_minute, e_hour, e_dom, e_month, e_dow, e_cmd, e_timespec}
  35.     ecode_e;
  36. static char *ecodes[] =
  37.     {
  38.         "no error",
  39.         "bad minute",
  40.         "bad hour",
  41.         "bad day-of-month",
  42.         "bad month",
  43.         "bad day-of-week",
  44.         "bad command",
  45.         "bad time specifier"
  46.     };
  47.  
  48. void
  49. free_entry(e)
  50.     entry    *e;
  51. {
  52.     int    free();
  53.  
  54.     (void) free(e->cmd);
  55.     (void) free(e);
  56. }
  57.  
  58.  
  59. entry *
  60. load_entry(file, error_func)
  61.     FILE    *file;
  62.     void    (*error_func)();
  63. {
  64.     /* this function reads one crontab entry -- the next -- from a file.
  65.      * it skips any leading blank lines, ignores comments, and returns
  66.      * EOF if for any reason the entry can't be read and parsed.
  67.      *
  68.      * the entry IS parsed here, btw.
  69.      *
  70.      * syntax:
  71.      *    minutes hours doms months dows cmd\n
  72.      */
  73.  
  74.     extern int    free();
  75.     extern char    *malloc(), *savestr();
  76.     extern void    unget_char();
  77.     static char    get_list();
  78.  
  79.     ecode_e    ecode = e_none;
  80.     entry    *e;
  81.     int    ch;
  82.     void    skip_comments();
  83.     char    cmd[MAX_COMMAND];
  84.  
  85.     e = (entry *) calloc(sizeof(entry), sizeof(char));
  86.  
  87.     Debug(DPARS, ("load_entry()...about to eat comments\n"))
  88.  
  89.     skip_comments(file);
  90.  
  91.     ch = get_char(file);
  92.  
  93.     /* ch is now the first useful character of a useful line.
  94.      * it may be an @special or it may be the first character
  95.      * of a list of minutes.
  96.      */
  97.  
  98.     if (ch == '@')
  99.     {
  100.         /* all of these should be flagged and load-limited; i.e.,
  101.          * instead of @hourly meaning "0 * * * *" it should mean
  102.          * "close to the front of every hour but not 'til the
  103.          * system load is low".  Problems are: how do you know
  104.          * what "low" means? (save me from /etc/crond.conf!) and:
  105.          * how to guarantee low variance (how low is low?), which
  106.          * means how to we run roughly every hour -- seems like
  107.          * we need to keep a history or let the first hour set
  108.          * the schedule, which means we aren't load-limited
  109.          * anymore.  too much for my overloaded brain. (vix, jan90)
  110.          * HINT
  111.          */
  112.         ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
  113.         if (!strcmp("reboot", cmd)) {
  114.             e->flags |= WHEN_REBOOT;
  115.         } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
  116.             bit_set(e->minute, 0);
  117.             bit_set(e->hour, 0);
  118.             bit_set(e->dom, 0);
  119.             bit_set(e->month, 0);
  120.             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
  121.         } else if (!strcmp("monthly", cmd)) {
  122.             bit_set(e->minute, 0);
  123.             bit_set(e->hour, 0);
  124.             bit_set(e->dom, 0);
  125.             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
  126.             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
  127.         } else if (!strcmp("weekly", cmd)) {
  128.             bit_set(e->minute, 0);
  129.             bit_set(e->hour, 0);
  130.             bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
  131.             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
  132.             bit_set(e->dow, 0);
  133.         } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
  134.             bit_set(e->minute, 0);
  135.             bit_set(e->hour, 0);
  136.             bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
  137.             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
  138.             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
  139.         } else if (!strcmp("hourly", cmd)) {
  140.             bit_set(e->minute, 0);
  141.             bit_set(e->hour, (LAST_HOUR-FIRST_HOUR+1));
  142.             bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
  143.             bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
  144.             bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
  145.         } else {
  146.             ecode = e_timespec;
  147.             goto eof;
  148.         }
  149.     }
  150.     else
  151.     {
  152.         Debug(DPARS, ("load_entry()...about to parse numerics\n"))
  153.  
  154.         ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
  155.                 PPC_NULL, ch, file);
  156.         if (ch == EOF)
  157.         {
  158.             ecode = e_minute;
  159.             goto eof;
  160.         }
  161.  
  162.         /* hours
  163.          */
  164.  
  165.         ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
  166.                 PPC_NULL, ch, file);
  167.         if (ch == EOF)
  168.         {
  169.             ecode = e_hour;
  170.             goto eof;
  171.         }
  172.  
  173.         /* DOM (days of month)
  174.          */
  175.  
  176.         if (ch == '*') e->flags |= DOM_STAR;
  177.         ch = get_list(e->dom, FIRST_DOM, LAST_DOM, PPC_NULL, ch, file);
  178.         if (ch == EOF)
  179.         {
  180.             ecode = e_dom;
  181.             goto eof;
  182.         }
  183.  
  184.         /* month
  185.          */
  186.  
  187.         ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
  188.                 MonthNames, ch, file);
  189.         if (ch == EOF)
  190.         {
  191.             ecode = e_month;
  192.             goto eof;
  193.         }
  194.  
  195.         /* DOW (days of week)
  196.          */
  197.  
  198.         if (ch == '*') e->flags |= DOW_STAR;
  199.         ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
  200.                 DowNames, ch, file);
  201.         if (ch == EOF)
  202.         {
  203.             ecode = e_dow;
  204.             goto eof;
  205.         }
  206.     }
  207.  
  208.     /* make sundays equivilent */
  209.     if (bit_test(e->dow, 0) || bit_test(e->dow, 7))
  210.     {
  211.         bit_set(e->dow, 0);
  212.         bit_set(e->dow, 7);
  213.     }
  214.  
  215.     Debug(DPARS, ("load_entry()...about to parse command\n"))
  216.  
  217.     /* ch is first character of a command.  everything up to the next
  218.      * \n or EOF is part of the command... too bad we don't know in
  219.      * advance how long it will be, since we need to malloc a string
  220.      * for it... so, we limit it to MAX_COMMAND
  221.      */ 
  222.     unget_char(ch, file);
  223.     ch = get_string(cmd, MAX_COMMAND, file, "\n");
  224.  
  225.     /* a file without a \n before the EOF is rude, so we'll complain...
  226.      */
  227.     if (ch == EOF)
  228.     {
  229.         ecode = e_cmd;
  230.         goto eof;
  231.     }
  232.  
  233.     /* got the command in the 'cmd' string; save it in *e.
  234.      */
  235.     e->cmd = savestr(cmd);
  236.  
  237.     Debug(DPARS, ("load_entry()...returning successfully\n"))
  238.  
  239.     /* success, fini, return pointer to the entry we just created...
  240.      */
  241.     return e;
  242.  
  243. eof:    /* if we want to return EOF, we have to jump down here and
  244.      * free the entry we've been building.
  245.      *
  246.      * now, in some cases, a parse routine will have returned EOF to
  247.      * indicate an error, but the file is not actually done.  since, in
  248.      * that case, we only want to skip the line with the error on it,
  249.      * we'll do that here.
  250.      *
  251.      * many, including the author, see what's below as evil programming
  252.      * practice: since I didn't want to change the structure of this
  253.      * whole function to support this error recovery, I recurse.  Cursed!
  254.      * (At least it's tail-recursion, as if it matters in C - vix/8feb88)
  255.      * I'm seriously considering using (another) GOTO...   argh!
  256.      * (this does not get less disgusting over time.  vix/15nov88)
  257.      */
  258.  
  259.     (void) free(e);
  260.  
  261.     if (feof(file))
  262.         return NULL;
  263.  
  264.     if (error_func)
  265.         (*error_func)(ecodes[(int)ecode]);
  266.     do  {ch = get_char(file);}
  267.     while (ch != EOF && ch != '\n');
  268.     if (ch == EOF)
  269.         return NULL;
  270.     return load_entry(file, error_func);
  271. }
  272.  
  273.  
  274. static char
  275. get_list(bits, low, high, names, ch, file)
  276.     bitstr_t    *bits;        /* one bit per flag, default=FALSE */
  277.     int        low, high;    /* bounds, impl. offset for bitstr */
  278.     char        *names[];    /* NULL or *[] of names for these elements */
  279.     int        ch;        /* current character being processed */
  280.     FILE        *file;        /* file being read */
  281. {
  282.     static char    get_range();
  283.     register int    done;
  284.  
  285.     /* we know that we point to a non-blank character here;
  286.      * must do a Skip_Blanks before we exit, so that the
  287.      * next call (or the code that picks up the cmd) can
  288.      * assume the same thing.
  289.      */
  290.  
  291.     Debug(DPARS|DEXT, ("get_list()...entered\n"))
  292.  
  293.     /* list = "*" | range {"," range}
  294.      */
  295.     
  296.     if (ch == '*')
  297.     {
  298.         /* '*' means 'all elements'.
  299.          */
  300.         bit_nset(bits, 0, (high-low+1));
  301.         goto exit;
  302.     }
  303.  
  304.     /* clear the bit string, since the default is 'off'.
  305.      */
  306.     bit_nclear(bits, 0, (high-low+1));
  307.  
  308.     /* process all ranges
  309.      */
  310.     done = FALSE;
  311.     while (!done)
  312.     {
  313.         ch = get_range(bits, low, high, names, ch, file);
  314.         if (ch == ',')
  315.             ch = get_char(file);
  316.         else
  317.             done = TRUE;
  318.     }
  319.  
  320. exit:    /* exiting.  skip to some blanks, then skip over the blanks.
  321.      */
  322.     Skip_Nonblanks(ch, file)
  323.     Skip_Blanks(ch, file)
  324.  
  325.     Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
  326.  
  327.     return ch;
  328. }
  329.  
  330.  
  331. static char
  332. get_range(bits, low, high, names, ch, file)
  333.     bitstr_t    *bits;        /* one bit per flag, default=FALSE */
  334.     int        low, high;    /* bounds, impl. offset for bitstr */
  335.     char        *names[];    /* NULL or names of elements */
  336.     int        ch;        /* current character being processed */
  337.     FILE        *file;        /* file being read */
  338. {
  339.     /* range = number | number "-" number [ "/" number ]
  340.      */
  341.  
  342.     static int    set_element();
  343.     static char    get_number();
  344.     register int    i;
  345.     auto int    num1, num2, num3;
  346.  
  347.     Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
  348.  
  349.     if (EOF == (ch = get_number(&num1, low, names, ch, file)))
  350.         return EOF;
  351.  
  352.     if (ch != '-')
  353.     {
  354.         /* not a range, it's a single number.
  355.          */
  356.         if (EOF == set_element(bits, low, high, num1))
  357.             return EOF;
  358.     }
  359.     else
  360.     {
  361.         /* eat the dash
  362.          */
  363.         ch = get_char(file);
  364.         if (ch == EOF)
  365.             return EOF;
  366.  
  367.         /* get the number following the dash
  368.          */
  369.         ch = get_number(&num2, low, names, ch, file);
  370.         if (ch == EOF)
  371.             return EOF;
  372.  
  373.         /* check for step size
  374.          */
  375.         if (ch == '/')
  376.         {
  377.             /* eat the slash
  378.              */
  379.             ch = get_char(file);
  380.             if (ch == EOF)
  381.                 return EOF;
  382.  
  383.             /* get the step size -- note: we don't pass the
  384.              * names here, because the number is not an
  385.              * element id, it's a step size.  'low' is
  386.              * sent as a 0 since there is no offset either.
  387.              */
  388.             ch = get_number(&num3, 0, PPC_NULL, ch, file);
  389.             if (ch == EOF)
  390.                 return EOF;
  391.         }
  392.         else
  393.         {
  394.             /* no step.  default==1.
  395.              */
  396.             num3 = 1;
  397.         }
  398.  
  399.         /* range. set all elements from num1 to num2, stepping
  400.          * by num3.  (the step is a downward-compatible extension
  401.          * proposed conceptually by bob@acornrc, syntactically
  402.          * designed then implmented by paul vixie).
  403.          */
  404.         for (i = num1;  i <= num2;  i += num3)
  405.             if (EOF == set_element(bits, low, high, i))
  406.                 return EOF;
  407.     }
  408.     return ch;
  409. }
  410.  
  411.  
  412. static char
  413. get_number(numptr, low, names, ch, file)
  414.     int    *numptr;
  415.     int    low;
  416.     char    *names[];
  417.     char    ch;
  418.     FILE    *file;
  419. {
  420.     char    temp[MAX_TEMPSTR], *pc;
  421.     int    len, i, all_digits;
  422.  
  423.     /* collect alphanumerics into our fixed-size temp array
  424.      */
  425.     pc = temp;
  426.     len = 0;
  427.     all_digits = TRUE;
  428.     while (isalnum(ch))
  429.     {
  430.         if (++len >= MAX_TEMPSTR)
  431.             return EOF;
  432.  
  433.         *pc++ = ch;
  434.  
  435.         if (!isdigit(ch))
  436.             all_digits = FALSE;
  437.  
  438.         ch = get_char(file);
  439.     }
  440.     *pc = '\0';
  441.  
  442.     /* try to find the name in the name list
  443.      */
  444.     if (names)
  445.         for (i = 0;  names[i] != NULL;  i++)
  446.         {
  447.             Debug(DPARS|DEXT,
  448.                 ("get_num, compare(%s,%s)\n", names[i], temp))
  449.             if (!nocase_strcmp(names[i], temp))
  450.             {
  451.                 *numptr = i+low;
  452.                 return ch;
  453.             }
  454.         }
  455.  
  456.     /* no name list specified, or there is one and our string isn't
  457.      * in it.  either way: if it's all digits, use its magnitude.
  458.      * otherwise, it's an error.
  459.      */
  460.     if (all_digits)
  461.     {
  462.         *numptr = atoi(temp);
  463.         return ch;
  464.     }
  465.  
  466.     return EOF;
  467. }
  468.  
  469.  
  470. static int
  471. set_element(bits, low, high, number)
  472.     bitstr_t    *bits;         /* one bit per flag, default=FALSE */
  473.     int        low;
  474.     int        high;
  475.     int        number;
  476. {
  477.     Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
  478.  
  479.     if (number < low || number > high)
  480.         return EOF;
  481.  
  482.     Debug(DPARS|DEXT, ("bit_set(%x,%d)\n",bits,(number-low)))
  483.     bit_set(bits, (number-low));
  484.     Debug(DPARS|DEXT, ("bit_set succeeded\n"))
  485.     return OK;
  486. }
  487.