"Fossies" - the Fresh Open Source Software Archive

Member "ical-tcl/time/parse.C" (15 Apr 2019, 16747 Bytes) of package /linux/privat/ical-3.0.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* Copyright (c) 1994 by Sanjay Ghemawat */
    2 
    3 #include <ctype.h>
    4 #include <string.h>
    5 
    6 #include "../types/Array.h"
    7 
    8 #include "Date.h"
    9 #include "Month.h"
   10 #include "WeekDay.h"
   11 #include "Year.h"
   12 #include "config.h"
   13 
   14 // Token types
   15 enum Token_Type {
   16     INT_TOKEN,
   17     MONTH_TOKEN,
   18     WEEKDAY_TOKEN,
   19 
   20     TODAY_TOKEN,
   21     TOMORROW_TOKEN,
   22     YESTERDAY_TOKEN,
   23 
   24     AMPM_TOKEN,
   25     MIDNIGHT_TOKEN,
   26     NOON_TOKEN,
   27     ZONE_TOKEN,
   28 
   29     SLASH_TOKEN,
   30     COLON_TOKEN,
   31 
   32     TO_TOKEN,
   33 
   34     OTHER_TOKEN,
   35     LAST_TOKEN
   36 }; 
   37 
   38 // Part of string being parsed.
   39 struct Token {
   40     char const* ptr;            // Pointer into string
   41     int         len;            // Length of this token
   42     Token_Type  ttype;          // The type of this token
   43     long        tvalue;         // Any value associated with this token
   44 };
   45 
   46 declareArray(Tokens,Token)
   47 implementArray(Tokens,Token)
   48 
   49 static void parse_tokens(char const*, Tokens&);
   50 static void parse_token(char const*, Token&);
   51 static int  parse_word(char const*, int, Token&);
   52 
   53 static void parse_tokens(char const* str, Tokens& list) {
   54     while (str[0] != '\0') {
   55         Token t;
   56         parse_token(str, t);
   57         str += t.len;
   58 
   59         // Merge trailing whitespace etc. into token
   60         while (str[0] != '\0') {
   61             char c = str[0];
   62             if ((c != '.') && (c != ',') && !isspace(c))
   63                 break;
   64 
   65             t.len++;
   66             str++;
   67         }
   68 
   69         list.append(t);
   70     }
   71 
   72     // Append guard token
   73     Token t;
   74     t.ptr = str;
   75     t.len = 0;
   76     t.ttype = LAST_TOKEN;
   77     list.append(t);
   78 }
   79 
   80 // effects      Parse token from string and stuff it in "t".
   81 static void parse_token(char const* string, Token& t) {
   82     char c = string[0];
   83 
   84     // Parse number
   85     if (isdigit(c)) {
   86         char* endptr;
   87         long v = strtol(string, &endptr, 10);
   88         if (endptr > string) {
   89             t.ptr = string;
   90             t.len = endptr - string;
   91             t.ttype = INT_TOKEN;
   92             t.tvalue = v;
   93             
   94             return;
   95         }
   96     }
   97 
   98     // Special handling for "am/pm".
   99     if (isalpha(c)) {
  100         // Skip periods and alphabetic characters.
  101         int end = 1;
  102         while ((string[end] != '\0') &&
  103                (isalpha(string[end]) || (string[end] == '.')))
  104             end++;
  105 
  106         if (parse_word(string, end, t))
  107             return;
  108     }
  109     
  110     // Parse word
  111     if (isalpha(c)) {
  112         int end = 1;
  113         while ((string[end] != '\0') && isalpha(string[end]))
  114             end++;
  115 
  116         if (parse_word(string, end, t))
  117             return;
  118 
  119         // If word has exactly three uppercase letters, consider it a
  120         // time zone.
  121         if ((end == 3) &&
  122             isupper(string[0]) &&
  123             isupper(string[1]) &&
  124             isupper(string[2])) {
  125             t.ptr = string;
  126             t.len = end;
  127             t.ttype = ZONE_TOKEN;
  128             return;
  129         }
  130     }
  131 
  132     // Parse zone
  133     if ((c == '+') || (c == '-')) {
  134         Token rest;
  135         parse_token(string+1, rest);
  136         if ((rest.ttype == INT_TOKEN) && (rest.len >= 3)) {
  137             // Found a time zone
  138             t.ptr = string;
  139             t.len = rest.len+1;
  140             t.ttype = ZONE_TOKEN;
  141             return;
  142         }
  143     }
  144 
  145     // Parse range separator
  146     for (int seplen = 1; seplen <= 3; seplen++) {
  147         if (strncasecmp(string, "---", seplen) == 0) {
  148             t.ptr = string;
  149             t.len = seplen;
  150             t.ttype = TO_TOKEN;
  151             return;
  152         }
  153     }
  154 
  155     t.ptr = string;
  156     t.len = 1;
  157 
  158     switch (c) {
  159       case '/':
  160         t.ttype = SLASH_TOKEN;
  161         break;
  162       case ':':
  163         t.ttype = COLON_TOKEN;
  164         break;
  165       default:
  166         t.ttype = OTHER_TOKEN;
  167         break;
  168     }
  169 }
  170 
  171 // The lengths of the following tokens are the minimum lengths
  172 // that are required for a match.
  173 static Token words[] = {
  174     { "today",          5,      TODAY_TOKEN,    0       },
  175     { "tomorrow",       5,      TOMORROW_TOKEN, 0       },
  176     { "yesterday",      5,      YESTERDAY_TOKEN,0       },
  177 
  178     { "january",        3,      MONTH_TOKEN,    1       },
  179     { "february",       3,      MONTH_TOKEN,    2       },
  180     { "march",          3,      MONTH_TOKEN,    3       },
  181     { "april",          3,      MONTH_TOKEN,    4       },
  182     { "may",            3,      MONTH_TOKEN,    5       },
  183     { "june",           3,      MONTH_TOKEN,    6       },
  184     { "july",           3,      MONTH_TOKEN,    7       },
  185     { "august",         3,      MONTH_TOKEN,    8       },
  186     { "september",      3,      MONTH_TOKEN,    9       },
  187     { "october",        3,      MONTH_TOKEN,    10      },
  188     { "november",       3,      MONTH_TOKEN,    11      },
  189     { "december",       3,      MONTH_TOKEN,    12      },
  190 
  191     { "sunday",         3,      WEEKDAY_TOKEN,  1       },
  192     { "monday",         3,      WEEKDAY_TOKEN,  2       },
  193     { "tuesday",        3,      WEEKDAY_TOKEN,  3       },
  194     { "wednesday",      3,      WEEKDAY_TOKEN,  4       },
  195     { "thursday",       3,      WEEKDAY_TOKEN,  5       },
  196     { "friday",         3,      WEEKDAY_TOKEN,  6       },
  197     { "saturday",       3,      WEEKDAY_TOKEN,  7       },
  198 
  199     { "a.m.",           4,      AMPM_TOKEN,     0       },
  200     { "am.",            3,      AMPM_TOKEN,     0       },
  201     { "am",             2,      AMPM_TOKEN,     0       },
  202     { "p.m.",           4,      AMPM_TOKEN,     1       },
  203     { "pm.",            3,      AMPM_TOKEN,     1       },
  204     { "pm",             2,      AMPM_TOKEN,     1       },
  205 
  206     { "midnight",       8,      MIDNIGHT_TOKEN, 0       },
  207     { "noon",           4,      NOON_TOKEN,     0       },
  208 
  209     { "to",             2,      TO_TOKEN,       0       },
  210 
  211     { 0,                0,      OTHER_TOKEN,    0       }
  212 };
  213 
  214 static int build_date(int m, int d, int y, Date& result) {
  215     // Fix year
  216     if (y < 100) y += (Date::Today().GetYear() / 100) * 100;
  217 
  218     // Check range
  219     if ((y < Year::First()) || (y > Year::Last())) return 0;
  220     if ((m < 1) || (m > 12)) return 0;
  221 
  222     Month month = Month::First() + (m - 1);
  223     if ((d < 1) || (d > month.Size(y))) return 0;
  224 
  225     result = Date(d, month, y);
  226     return 1;
  227 }
  228 
  229 static int parse_word(char const* str, int len, Token& t) {
  230     for (int i = 0; words[i].ptr != 0; i++) {
  231         if (len < words[i].len) continue;
  232         if (len > strlen(words[i].ptr)) continue;
  233         if (strncasecmp(str, words[i].ptr, len) != 0) continue;
  234 
  235         // Found it
  236         t.ptr = str;
  237         t.len = len;
  238         t.ttype = words[i].ttype;
  239 
  240         t.tvalue = words[i].tvalue;
  241         return 1;
  242     }
  243 
  244     return 0;
  245 }
  246 
  247 // Used to ignore "am/pm" indicator from "match_timeofday"
  248 static int junk_ampm;
  249 
  250 // modifies     "result", "had_ampm"
  251 // effects      If "list" starting at "index" contains a time specification,
  252 //              parse the time into "result" and return the number of
  253 //              tokens used in the parse.  Otherwise return zero.
  254 //
  255 //              "result" is set to the number of seconds after midnight.
  256 //              "had_ampm" is set to true if an am/pm indication was
  257 //              found, otherwise it is set to false.
  258 
  259 static int match_timeofday(Tokens& list, int index, int& result,
  260                            int& had_ampm = junk_ampm) {
  261     if (index >= list.size()) return 0;
  262     Token t = list[index];
  263 
  264     if (t.ttype == MIDNIGHT_TOKEN) {
  265         result = 0;
  266         had_ampm = 1;
  267         return 1;
  268     }
  269 
  270     if (t.ttype == NOON_TOKEN) {
  271         result = 12 * 60 * 60;
  272         had_ampm = 1;
  273         return 1;
  274     }
  275 
  276     // "<hour>:<minute>[:<second>] [am|pm] [<time zone>]"
  277     if ((list[index+0].ttype == INT_TOKEN) &&
  278         (list[index+1].ttype == COLON_TOKEN) &&
  279         (list[index+2].ttype == INT_TOKEN)) {
  280         int h = list[index+0].tvalue;
  281         int m = list[index+2].tvalue;
  282         int s = 0;
  283         int c = 3;
  284 
  285         // See if seconds are specified
  286         if ((list[index+c+0].ttype == COLON_TOKEN) &&
  287             (list[index+c+1].ttype == INT_TOKEN)) {
  288             s = list[index+c+1].tvalue;
  289             c += 2;
  290         }
  291 
  292         // See if am/pm is specified
  293         int have_ampm = 0;
  294         int ispm = 0;
  295         if (list[index+c].ttype == AMPM_TOKEN) {
  296             have_ampm = 1;
  297             ispm = list[index+c].tvalue;
  298             c++;
  299         }
  300 
  301         // See if time zone is specified
  302         if (list[index+c].ttype == ZONE_TOKEN) {
  303             // XXX - Handle zone
  304             c++;
  305         }
  306 
  307         if (have_ampm) {
  308             // Hours > 12 not allowed with am/pm
  309             if (h > 12) return 0;
  310 
  311             if (h == 12) h = 0;
  312             if (ispm) h += 12;
  313         }
  314 
  315         // Range checking
  316         if (h >= 24) return 0;
  317         if ((m < 0) || (m > 60)) return 0;
  318         if ((s < 0) || (s > 60)) return 0;
  319 
  320         result = ((h * 60) + m) * 60 + s;
  321         had_ampm = (have_ampm || (h >= 12));
  322         return c;
  323     }
  324 
  325     // "<hour> [am|pm]"
  326     if ((list[index+0].ttype == INT_TOKEN) &&
  327         (list[index+1].ttype == AMPM_TOKEN)) {
  328         int h = list[index].tvalue;
  329         if (h > 12) return 0;
  330         if (h == 12) h = 0;
  331         if (list[index+1].tvalue) h += 12;
  332 
  333         result = (h * 60 * 60);
  334         had_ampm = 1;
  335         return 2;
  336     }
  337 
  338     return 0;
  339 }
  340 
  341 // effects      If "list" starting at "index" contains a range specification,
  342 //              parse the range into and return the number of
  343 //              tokens used in the parse.  Otherwise return zero.
  344 
  345 static int match_timerange(Tokens& list, int index, int& start, int& finish) {
  346     if (index >= list.size()) return 0;
  347     Token t = list[index];
  348     int noon = 12 * 60 * 60;
  349 
  350     // "<timeofday> <to> <timeofday>"
  351     int c1, r1, c2, r2, ampm1, ampm2;
  352     if (((c1 = match_timeofday(list, index, r1, ampm1)) != 0) &&
  353         (list[index+c1].ttype == TO_TOKEN) &&
  354         ((c2 = match_timeofday(list, index+c1+1, r2, ampm2)) != 0)) {
  355 
  356         if (!ampm1 && !ampm2 && (r1 > r2) && (r1 < noon) && (r2 < noon)) {
  357             // r1 must be am and r2 must be pm
  358             r2 += noon;
  359         }
  360         if (ampm1 && !ampm2 && (r1 < noon) && (r2 < r1)) {
  361             // r2 must be pm
  362             r2 += noon;
  363         }
  364         if (!ampm1 && ampm2 && (r1 < noon) && (r2 >= noon) &&
  365             ((r1 + noon) <= r2)) {
  366             // Make r1 match r2
  367             r1 += noon;
  368         }
  369 
  370         if (r1 <= r2) {
  371             start = r1;
  372             finish = r2;
  373             return c1 + c2 + 1;
  374         }
  375     }
  376 
  377     // "<hour> <to> <timeofday>"
  378     if ((list[index+0].ttype == INT_TOKEN) &&
  379         (list[index+1].ttype == TO_TOKEN) &&
  380         (list[index+0].tvalue > 0) &&
  381         (list[index+0].tvalue <= 12) &&
  382         ((c2 = match_timeofday(list, index+2, r2, ampm2)) != 0)) {
  383         int h = list[index+0].tvalue;
  384         if (h == 12) h = 0;
  385         r1 = h * 60 * 60;
  386 
  387         if (!ampm2 && (r1 > r2) && (r2 < noon)) {
  388             // r2 must be after noon
  389             r2 += noon;
  390         }
  391 
  392         // Move "r1" into second half of day if range > 12 hrs
  393         if ((r2 - r1) > (12*60*60))
  394             r1 += 12*60*60;
  395 
  396         if (r1 <= r2) {
  397             start = r1;
  398             finish = r2;
  399             return c2 + 2;
  400         }
  401     }
  402 
  403     return 0;
  404 }
  405 
  406 // effects      If "list" starting at "index" contains a date specification,
  407 //              parse the date into "date" and return the number of tokens used
  408 //              in the parse.  Otherwise return zero.
  409 
  410 static int match_date(Tokens& list, int index, Date& result) {
  411     if (index >= list.size()) return 0;
  412 
  413     Token t = list[index];
  414 
  415     // "<weekday> <date>"
  416     if (t.ttype == WEEKDAY_TOKEN) {
  417         // Try to find date starting at next token
  418         Date r;
  419         int count = match_date(list, index+1, r);
  420         if ((count > 0) && (r.GetWDay().Index() == t.tvalue)) {
  421             // Weekday matches the date
  422             result = r;
  423             return count + 1;
  424         }
  425 
  426         // Weekday does not match
  427         return 0;
  428     }
  429 
  430     // "today"
  431     if (t.ttype == TODAY_TOKEN) {
  432         result = Date::Today();
  433         return 1;
  434     }
  435 
  436     // "tomorrow"
  437     if (t.ttype == TOMORROW_TOKEN) {
  438         result = Date::Today() + 1;
  439         return 1;
  440     }
  441 
  442     // "yesterday"
  443     if (t.ttype == YESTERDAY_TOKEN) {
  444         result = Date::Today() - 1;
  445         return 1;
  446     }
  447 
  448     // "<int>/<int>[/<int>]"
  449     if ((list[index+0].ttype == INT_TOKEN) &&
  450         (list[index+1].ttype == SLASH_TOKEN) &&
  451         (list[index+2].ttype == INT_TOKEN)) {
  452 
  453         int m = list[index+0].tvalue;
  454         int d = list[index+2].tvalue;
  455         if (m > 12) {
  456             // European format
  457             int tmp = m;
  458             m = d;
  459             d = tmp;
  460         }
  461 
  462         int count;
  463         int y;
  464         if ((list[index+3].ttype == SLASH_TOKEN) &&
  465             (list[index+4].ttype == INT_TOKEN)) {
  466             // Found year as well
  467             y = list[index+4].tvalue;
  468             count = 5;
  469         }
  470         else {
  471             // Only month and year
  472             y = Date::Today().GetYear();
  473             count = 3;
  474         }
  475 
  476         if (build_date(m, d, y, result))
  477             return count;
  478     }
  479 
  480     // "<month> <int> [<time>] [<int>]"
  481     if ((list[index+0].ttype == MONTH_TOKEN) &&
  482         (list[index+1].ttype == INT_TOKEN)) {
  483 
  484         int m = list[index+0].tvalue;
  485         int d = list[index+1].tvalue;
  486         int y = Date::Today().GetYear();
  487         int c = 2;
  488 
  489         // Skip time specification (if any)
  490         int seconds;
  491         c += match_timeofday(list, index+c, seconds);
  492 
  493         // Handle year specification (if any)
  494         if (list[index+c].ttype == INT_TOKEN) {
  495             y = list[index+c].tvalue;
  496             c++;
  497         }
  498 
  499         if (build_date(m, d, y, result))
  500             return c;
  501     }
  502 
  503     // "<int> <month> [<time>] [<int>]"
  504     if ((list[index+0].ttype == INT_TOKEN) &&
  505         (list[index+1].ttype == MONTH_TOKEN)) {
  506 
  507         int m = list[index+1].tvalue;
  508         int d = list[index+0].tvalue;
  509         int y = Date::Today().GetYear();
  510         int c = 2;
  511 
  512         // Skip time specification (if any)
  513         int seconds;
  514         c += match_timeofday(list, index+c, seconds);
  515 
  516         // Handle year specification (if any)
  517         if (list[index+c].ttype == INT_TOKEN) {
  518             y = list[index+c].tvalue;
  519             c++;
  520         }
  521 
  522         if (build_date(m, d, y, result))
  523             return c;
  524     }
  525 
  526     return 0;
  527 }
  528 
  529 // Exported routines
  530 
  531 int find_date(char const* string, Date& result, int& start, int& length) {
  532     Tokens list;
  533     parse_tokens(string, list);
  534 
  535     int index = 0;              // Index into "list"
  536     int pos = 0;                // Number of chars in "list[0..index-1]".
  537     while (index < list.size()) {
  538         Date d;
  539         int count = match_date(list, index, d);
  540         if (count == 0) {
  541             // No date here, keep searching
  542             pos += list[index].len;
  543             index++;
  544             continue;
  545         }
  546 
  547         // Found date at "index"
  548         result = d;
  549         start = pos;
  550         length = 0;
  551         for (int i = index; i < index + count; i++)
  552             length += list[i].len;
  553 
  554         return 1;
  555     }
  556 
  557     return 0;
  558 }
  559 
  560 int find_timeofday(char const* string, int& result, int& start, int& length) {
  561     Tokens list;
  562     parse_tokens(string, list);
  563 
  564     int index = 0;              // Index into "list"
  565     int pos = 0;                // Number of chars in "list[0..index-1]".
  566     while (index < list.size()) {
  567         int seconds;
  568         int count = match_timeofday(list, index, seconds);
  569         if (count == 0) {
  570             // No time here, keep searching
  571             pos += list[index].len;
  572             index++;
  573             continue;
  574         }
  575 
  576         // Found time at "index"
  577         result = seconds;
  578         start = pos;
  579         length = 0;
  580         for (int i = index; i < index + count; i++)
  581             length += list[i].len;
  582 
  583         return 1;
  584     }
  585 
  586     return 0;
  587 }
  588 
  589 int find_timerange(char const* string,
  590                    int& startresult, int& finishresult,
  591                    int& start, int& length)
  592 {
  593     Tokens list;
  594     parse_tokens(string, list);
  595 
  596     int index = 0;              // Index into "list"
  597     int pos = 0;                // Number of chars in "list[0..index-1]".
  598     while (index < list.size()) {
  599         int stseconds, fiseconds;
  600         int count = match_timerange(list, index, stseconds, fiseconds);
  601         if (count == 0) {
  602             // No time here, keep searching
  603             pos += list[index].len;
  604             index++;
  605             continue;
  606         }
  607 
  608         // Found time at "index"
  609         startresult = stseconds;
  610         finishresult = fiseconds;
  611         start = pos;
  612         length = 0;
  613         for (int i = index; i < index + count; i++)
  614             length += list[i].len;
  615 
  616         return 1;
  617     }
  618 
  619     return 0;
  620 }