home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / TOP / USR / SRC / vcron.t.Z / vcron.t / VCRON / entry.c < prev    next >
Text File  |  1988-11-15  |  10KB  |  513 lines

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