/* Copyright (c) 1994 by Sanjay Ghemawat */ #include #include #include "../types/Array.h" #include "Date.h" #include "Month.h" #include "WeekDay.h" #include "Year.h" #include "config.h" #ifndef HAVE_STRNCASECMP_PROTO extern "C" { extern int strncasecmp(char const*, char const*, unsigned long); } #endif // Token types enum Token_Type { INT_TOKEN, MONTH_TOKEN, WEEKDAY_TOKEN, TODAY_TOKEN, TOMORROW_TOKEN, YESTERDAY_TOKEN, AMPM_TOKEN, MIDNIGHT_TOKEN, NOON_TOKEN, ZONE_TOKEN, SLASH_TOKEN, COLON_TOKEN, TO_TOKEN, OTHER_TOKEN, LAST_TOKEN }; // Part of string being parsed. struct Token { char const* ptr; // Pointer into string int len; // Length of this token Token_Type ttype; // The type of this token long tvalue; // Any value associated with this token }; declareArray(Tokens,Token) implementArray(Tokens,Token) static void parse_tokens(char const*, Tokens&); static void parse_token(char const*, Token&); static int parse_word(char const*, int, Token&); static void parse_tokens(char const* str, Tokens& list) { while (str[0] != '\0') { Token t; parse_token(str, t); str += t.len; // Merge trailing whitespace etc. into token while (str[0] != '\0') { char c = str[0]; if ((c != '.') && (c != ',') && !isspace(c)) break; t.len++; str++; } list.append(t); } // Append guard token Token t; t.ptr = str; t.len = 0; t.ttype = LAST_TOKEN; list.append(t); } // effects Parse token from string and stuff it in "t". static void parse_token(char const* string, Token& t) { char c = string[0]; // Parse number if (isdigit(c)) { char* endptr; long v = strtol(string, &endptr, 10); if (endptr > string) { t.ptr = string; t.len = endptr - string; t.ttype = INT_TOKEN; t.tvalue = v; return; } } // Special handling for "am/pm". if (isalpha(c)) { // Skip periods and alphabetic characters. int end = 1; while ((string[end] != '\0') && (isalpha(string[end]) || (string[end] == '.'))) end++; if (parse_word(string, end, t)) return; } // Parse word if (isalpha(c)) { int end = 1; while ((string[end] != '\0') && isalpha(string[end])) end++; if (parse_word(string, end, t)) return; // If word has exactly three uppercase letters, consider it a // time zone. if ((end == 3) && isupper(string[0]) && isupper(string[1]) && isupper(string[2])) { t.ptr = string; t.len = end; t.ttype = ZONE_TOKEN; return; } } // Parse zone if ((c == '+') || (c == '-')) { Token rest; parse_token(string+1, rest); if ((rest.ttype == INT_TOKEN) && (rest.len >= 3)) { // Found a time zone t.ptr = string; t.len = rest.len+1; t.ttype = ZONE_TOKEN; return; } } // Parse range separator for (int seplen = 1; seplen <= 3; seplen++) { if (strncasecmp(string, "---", seplen) == 0) { t.ptr = string; t.len = seplen; t.ttype = TO_TOKEN; return; } } t.ptr = string; t.len = 1; switch (c) { case '/': t.ttype = SLASH_TOKEN; break; case ':': t.ttype = COLON_TOKEN; break; default: t.ttype = OTHER_TOKEN; break; } } // The lengths of the following tokens are the minimum lengths // that are required for a match. static Token words[] = { { "today", 5, TODAY_TOKEN, 0 }, { "tomorrow", 5, TOMORROW_TOKEN, 0 }, { "yesterday", 5, YESTERDAY_TOKEN,0 }, { "january", 3, MONTH_TOKEN, 1 }, { "february", 3, MONTH_TOKEN, 2 }, { "march", 3, MONTH_TOKEN, 3 }, { "april", 3, MONTH_TOKEN, 4 }, { "may", 3, MONTH_TOKEN, 5 }, { "june", 3, MONTH_TOKEN, 6 }, { "july", 3, MONTH_TOKEN, 7 }, { "august", 3, MONTH_TOKEN, 8 }, { "september", 3, MONTH_TOKEN, 9 }, { "october", 3, MONTH_TOKEN, 10 }, { "november", 3, MONTH_TOKEN, 11 }, { "december", 3, MONTH_TOKEN, 12 }, { "sunday", 3, WEEKDAY_TOKEN, 1 }, { "monday", 3, WEEKDAY_TOKEN, 2 }, { "tuesday", 3, WEEKDAY_TOKEN, 3 }, { "wednesday", 3, WEEKDAY_TOKEN, 4 }, { "thursday", 3, WEEKDAY_TOKEN, 5 }, { "friday", 3, WEEKDAY_TOKEN, 6 }, { "saturday", 3, WEEKDAY_TOKEN, 7 }, { "a.m.", 4, AMPM_TOKEN, 0 }, { "am.", 3, AMPM_TOKEN, 0 }, { "am", 2, AMPM_TOKEN, 0 }, { "p.m.", 4, AMPM_TOKEN, 1 }, { "pm.", 3, AMPM_TOKEN, 1 }, { "pm", 2, AMPM_TOKEN, 1 }, { "midnight", 8, MIDNIGHT_TOKEN, 0 }, { "noon", 4, NOON_TOKEN, 0 }, { "to", 2, TO_TOKEN, 0 }, { 0, 0, OTHER_TOKEN, 0 } }; static int build_date(int m, int d, int y, Date& result) { // Fix year if (y < 100) y += (Date::Today().GetYear() / 100) * 100; // Check range if ((y < Year::First()) || (y > Year::Last())) return 0; if ((m < 1) || (m > 12)) return 0; Month month = Month::First() + (m - 1); if ((d < 1) || (d > month.Size(y))) return 0; result = Date(d, month, y); return 1; } static int parse_word(char const* str, int len, Token& t) { for (int i = 0; words[i].ptr != 0; i++) { if (len < words[i].len) continue; if (len > strlen(words[i].ptr)) continue; if (strncasecmp(str, words[i].ptr, len) != 0) continue; // Found it t.ptr = str; t.len = len; t.ttype = words[i].ttype; t.tvalue = words[i].tvalue; return 1; } return 0; } // Used to ignore "am/pm" indicator from "match_timeofday" static int junk_ampm; // modifies "result", "had_ampm" // effects If "list" starting at "index" contains a time specification, // parse the time into "result" and return the number of // tokens used in the parse. Otherwise return zero. // // "result" is set to the number of seconds after midnight. // "had_ampm" is set to true if an am/pm indication was // found, otherwise it is set to false. static int match_timeofday(Tokens& list, int index, int& result, int& had_ampm = junk_ampm) { if (index >= list.size()) return 0; Token t = list[index]; if (t.ttype == MIDNIGHT_TOKEN) { result = 0; had_ampm = 1; return 1; } if (t.ttype == NOON_TOKEN) { result = 12 * 60 * 60; had_ampm = 1; return 1; } // ":[:] [am|pm] [