home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / libc / getindate.c < prev    next >
C/C++ Source or Header  |  1991-12-10  |  6KB  |  236 lines

  1. /*
  2.  * getindate - parse the common Internet date case (rfc 822 & 1123) *fast*
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include <sys/types.h>
  10. #include <sys/timeb.h>
  11. #include "dateconv.h"
  12. #include "datetok.h"
  13.  
  14. /* STREQ is an optimised strcmp(a,b)==0 */
  15. #define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
  16.  
  17. #define    PACK_TWO_CHARS(c1, c2)    (((c1)<<8)|(c2))
  18. #define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
  19. #define SKIPTOSPC(s) \
  20.     while ((ch = *(s)++), (!ISSPACE(ch) && ch != '\0')) \
  21.         ; \
  22.     (s)--            /* N.B.: no semi-colon */
  23. #define SKIPSPC(s) \
  24.     while ((ch = *(s)++), ISSPACE(ch)) \
  25.         ; \
  26.     (s)--            /* N.B.: no semi-colon */
  27. #define SKIPOVER(s) \
  28.     SKIPTOSPC(s); \
  29.     SKIPSPC(s)        /* N.B.: no semi-colon */
  30.  
  31. /* this is fast but dirty.  note the return's in the middle. */
  32. #define GOBBLE_NUM(cp, c, x, ip) \
  33.     (c) = *(cp)++; \
  34.     if ((c) < '0' || (c) > '9') \
  35.         return -1;        /* missing digit */ \
  36.     (x) = (c) - '0'; \
  37.     (c) = *(cp)++; \
  38.     if ((c) >= '0' && (c) <= '9') { \
  39.         (x) = 10*(x) + (c) - '0'; \
  40.         (c) = *(cp)++; \
  41.     } \
  42.     if ((c) != ':' && (c) != '\0' && !ISSPACE(c)) \
  43.         return -1;        /* missing colon */ \
  44.     *(ip) = (x)            /* N.B.: no semi-colon here */
  45.  
  46. /*
  47.  * If the date is in the form
  48.  *    [Weekday,] dd Mmm [19]yy hh:mm[:ss] Timezone
  49.  * as most dates in news articles are, then we can parse it much quicker than
  50.  * getdate and quite a bit faster than getabsdate.
  51.  *
  52.  * parse and convert Internet date in timestr (the normal interface)
  53.  */
  54. /* ARGSUSED */
  55. time_t
  56. getindate(line, now)
  57. register char *line;            /* can be modified */
  58. struct timeb *now;            /* unused; for getdate compatibility */
  59. {
  60.     int tz = 0;
  61.     struct tm date;
  62.  
  63.     return prsindate(line, &date, &tz) < 0? -1: dateconv(&date, tz);
  64. }
  65.  
  66. /*
  67.  * just parse the Internet date in timestr and get back a broken-out date.
  68.  */
  69. int
  70. prsindate(line, tm, tzp)
  71. register char *line;            /* can be modified */
  72. register struct tm *tm;
  73. int *tzp;
  74. {
  75.     register int c;
  76.     register char ch;        /* used by SKIPTOSPC */
  77.     register char *cp;
  78.     register char c2;
  79.  
  80.     tm->tm_isdst = 0;
  81.     SKIPSPC(line);
  82.     if ((ch = *line) < '0' || ch > '9') {
  83.         cp = line;
  84.         while ((ch = *cp++), (!ISSPACE(ch) && ch != ',' && ch != '\0'))
  85.             ;
  86.         cp--;
  87.         if (ch == ',') {
  88.             line = cp;
  89.             SKIPOVER(line);        /* skip weekday */
  90.         } else
  91.             return -1;        /* missing comma after weekday */
  92.     }
  93.  
  94.     GOBBLE_NUM(line, ch, c, &tm->tm_mday);
  95.  
  96.     /*
  97.      * we have to map to canonical case because RFC 822 requires
  98.      * case independence, so we pay a performance penalty for the sake
  99.      * of 0.1% of dates actually seen in Date: headers in news.
  100.      * Way to go, IETF.
  101.      */
  102.     ch = *line++;
  103.     if (ch == '\0')
  104.         return -1;        /* no month */
  105.     if (isascii(ch) && islower(ch))
  106.         ch = toupper(ch);
  107.     c2 = *line++;
  108.     if (c2 == '\0')
  109.         return -1;        /* month too short */
  110.     if (isascii(c2) && isupper(c2))
  111.         c2 = tolower(c2);
  112.     switch (PACK_TWO_CHARS(ch, c2)) {
  113.     case PACK_TWO_CHARS('J', 'a'):
  114.         tm->tm_mon = 1;
  115.         break;
  116.     case PACK_TWO_CHARS('F', 'e'):
  117.         tm->tm_mon = 2;
  118.         break;
  119.     case PACK_TWO_CHARS('M', 'a'):    /* March, May */
  120.         tm->tm_mon = ((ch = *line) == 'r' || ch == 'R'? 3: 5);
  121.         break;
  122.     case PACK_TWO_CHARS('A', 'p'):
  123.         tm->tm_mon = 4;
  124.         break;
  125.     case PACK_TWO_CHARS('J', 'u'):
  126.         tm->tm_mon = 6;
  127.         if ((ch = *line) == 'l' || ch == 'L')
  128.             tm->tm_mon++;        /* July */
  129.         break;
  130.     case PACK_TWO_CHARS('A', 'u'):
  131.         tm->tm_mon = 8;
  132.         break;
  133.     case PACK_TWO_CHARS('S', 'e'):
  134.         tm->tm_mon = 9;
  135.         break;
  136.     case PACK_TWO_CHARS('O', 'c'):
  137.         tm->tm_mon = 10;
  138.         break;
  139.     case PACK_TWO_CHARS('N', 'o'):
  140.         tm->tm_mon = 11;
  141.         break;
  142.     case PACK_TWO_CHARS('D', 'e'):
  143.         tm->tm_mon = 12;
  144.         break;
  145.     default:
  146.         return -1;        /* bad month name */
  147.     }
  148.     tm->tm_mon--;            /* convert month to zero-origin */
  149.     SKIPOVER(line);            /* skip month */
  150.  
  151.     tm->tm_year = atoi(line);
  152.     if (tm->tm_year <= 0)
  153.         return -1;        /* year is non-positive or missing */
  154.     if (tm->tm_year >= 1900)    /* convert year to 1900 origin, */
  155.         tm->tm_year -= 1900;    /* but 2-digit years need no work */
  156.     SKIPOVER(line);            /* skip year */
  157.  
  158.     if (parsetime(line, tm) < 0)
  159.         return -1;
  160.     SKIPOVER(line);            /* skip time */
  161.  
  162.     cp = line;
  163.     if (*cp++ == 'G' && *cp++ == 'M' && *cp++ == 'T' &&
  164.         (*cp == '\n' || *cp == '\0'))
  165.         *tzp = 0;
  166.     else {                /* weirdo time zone */
  167.         register datetkn *tp;
  168.  
  169.         cp = line;        /* time zone start */
  170.         SKIPTOSPC(line);
  171.         c = *line;        /* save old delimiter */
  172.         *line = '\0';        /* terminate time zone */
  173.  
  174.         tp = datetoktype(cp, (int *)NULL);
  175.         switch (tp->type) {
  176.         case DTZ:
  177. #if 0
  178.             tm->tm_isdst++;
  179. #endif
  180.             /* FALLTHROUGH */
  181.         case TZ:
  182.             *tzp = FROMVAL(tp);
  183.             /* FALLTHROUGH */
  184.         case IGNORE:
  185.             break;
  186.         default:
  187.             return -1;    /* bad token type */
  188.         }
  189.  
  190.         *line = c;        /* restore old delimiter */
  191.         SKIPSPC(line);
  192.         if (*line != '\0') {    /* garbage after the date? */
  193.             if (*line != '(')    /* not even an 822 comment? */
  194.                 return -1;
  195.             /*
  196.              * a full 822 parse of the comment would
  197.              * be ridiculously complicated, so nested
  198.              * comments and quotes are not honoured.
  199.              * just look for a closing paren; it's only
  200.              * a time zone name.
  201.              */
  202.             while ((c = *++line) != ')' && c != '\0')
  203.                 ;
  204.             if (c == ')')
  205.                 ++line;
  206.             else
  207.                 return -1;    /* comment not terminated */
  208.             SKIPSPC(line);
  209.             if (*line != '\0')    /* trash left? */
  210.                 return -1;
  211.         }
  212.     }
  213.     return 0;
  214. }
  215.  
  216. /* return -1 on failure */
  217. int
  218. parsetime(time, tm)
  219. register char *time;
  220. register struct tm *tm;
  221. {
  222.     register char c;
  223.     register int x;
  224.  
  225.     tm->tm_sec = 0;
  226.     GOBBLE_NUM(time, c, x, &tm->tm_hour);
  227.     if (c != ':')
  228.         return -1;        /* only hour; too short */
  229.     GOBBLE_NUM(time, c, x, &tm->tm_min);
  230.     if (c != ':')
  231.         return 0;        /* no seconds; okay */
  232.     GOBBLE_NUM(time, c, x, &tm->tm_sec);
  233.     /* this may be considered too strict.  garbage at end of time? */
  234.     return (c == '\0' || ISSPACE(c)? 0: -1);
  235. }
  236.