|  | /* | 
|  | * Copyright (C) Tildeslash Ltd. All rights reserved. | 
|  | * | 
|  | * This program is free software: you can redistribute it and/or modify | 
|  | * it under the terms of the GNU Affero General Public License version 3. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU Affero General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Affero General Public License | 
|  | * along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
|  | * | 
|  | * In addition, as a special exception, the copyright holders give | 
|  | * permission to link the code of portions of this program with the | 
|  | * OpenSSL library under certain conditions as described in each | 
|  | * individual source file, and distribute linked combinations | 
|  | * including the two. | 
|  | * | 
|  | * You must obey the GNU Affero General Public License in all respects | 
|  | * for all of the code used other than OpenSSL. | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include "Config.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <sys/time.h> | 
|  | #include <time.h> | 
|  | #include <ctype.h> | 
|  | #include <stdarg.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include "Str.h" | 
|  | #include "system/System.h" | 
|  | #include "system/Time.h" | 
|  |  | 
|  |  | 
|  | /** | 
|  | * Implementation of the Time interface | 
|  | * | 
|  | * @see http://www.mmonit.com/ | 
|  | * @file | 
|  | */ | 
|  |  | 
|  |  | 
|  | /* ----------------------------------------------------------- Definitions */ | 
|  |  | 
|  |  | 
|  | #define CTIME       "%a %b %d %H:%M:%S %z %Y" | 
|  | #define RFC822      "%a, %d %b %Y %H:%M:%S %z" | 
|  | #define RFC1123     "%a, %d %b %Y %H:%M:%S GMT" | 
|  |  | 
|  | #define TEST_RANGE(v, f, t) \ | 
|  | do { \ | 
|  | if (v < f || v > t) \ | 
|  | THROW(AssertException, "#v is outside the range (%d..%d)", f, t); \ | 
|  | } while (0) | 
|  |  | 
|  | static const char days[] = "SunMonTueWedThuFriSat"; | 
|  | static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; | 
|  | #define MONTHS_LEN 36 | 
|  |  | 
|  |  | 
|  | /* --------------------------------------------------------------- Private */ | 
|  |  | 
|  |  | 
|  | static time_t parseDate(const char *date) { | 
|  | #define YYCTYPE     char | 
|  | #define YYCURSOR    date | 
|  | #define YYLIMIT     end | 
|  | #define YYMARKER    m | 
|  | #define YYFILL(n)   ((void)0) | 
|  | const char *t; | 
|  | const char *m; | 
|  | struct tm time = {0}; | 
|  | const char *end = date + strlen(date); | 
|  | time.tm_mon   = -1; | 
|  | time.tm_year  = -1; | 
|  | time.tm_mday  = -1; | 
|  | time.tm_isdst = -1; | 
|  | for (;;) { | 
|  | if (YYCURSOR >= YYLIMIT) { | 
|  | if (time.tm_mon== -1 || time.tm_year== -1 || time.tm_mday== -1) | 
|  | return -1; | 
|  | return mktime(&time); | 
|  | } | 
|  | t = YYCURSOR; | 
|  | { | 
|  | YYCTYPE yych; | 
|  | unsigned int yyaccept = 0; | 
|  |  | 
|  | if ((YYLIMIT - YYCURSOR) < 8) YYFILL(8); | 
|  | yych = *YYCURSOR; | 
|  | switch (yych) { | 
|  | case '0': | 
|  | case '1': | 
|  | case '2': | 
|  | case '3': | 
|  | case '4': | 
|  | case '5': | 
|  | case '6': | 
|  | case '7': | 
|  | case '8': | 
|  | case '9':	goto yy11; | 
|  | case 'A': | 
|  | case 'a':	goto yy6; | 
|  | case 'D': | 
|  | case 'd':	goto yy10; | 
|  | case 'F': | 
|  | case 'f':	goto yy4; | 
|  | case 'J': | 
|  | case 'j':	goto yy2; | 
|  | case 'M': | 
|  | case 'm':	goto yy5; | 
|  | case 'N': | 
|  | case 'n':	goto yy9; | 
|  | case 'O': | 
|  | case 'o':	goto yy8; | 
|  | case 'S': | 
|  | case 's':	goto yy7; | 
|  | default:	goto yy12; | 
|  | } | 
|  | yy2: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych <= 'U') { | 
|  | if (yych == 'A') goto yy53; | 
|  | if (yych >= 'U') goto yy52; | 
|  | } else { | 
|  | if (yych <= 'a') { | 
|  | if (yych >= 'a') goto yy53; | 
|  | } else { | 
|  | if (yych == 'u') goto yy52; | 
|  | } | 
|  | } | 
|  | yy3: | 
|  | { | 
|  | continue; | 
|  | } | 
|  | yy4: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych == 'E') goto yy49; | 
|  | if (yych == 'e') goto yy49; | 
|  | goto yy3; | 
|  | yy5: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych == 'A') goto yy44; | 
|  | if (yych == 'a') goto yy44; | 
|  | goto yy3; | 
|  | yy6: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych <= 'U') { | 
|  | if (yych == 'P') goto yy39; | 
|  | if (yych <= 'T') goto yy3; | 
|  | goto yy38; | 
|  | } else { | 
|  | if (yych <= 'p') { | 
|  | if (yych <= 'o') goto yy3; | 
|  | goto yy39; | 
|  | } else { | 
|  | if (yych == 'u') goto yy38; | 
|  | goto yy3; | 
|  | } | 
|  | } | 
|  | yy7: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych == 'E') goto yy35; | 
|  | if (yych == 'e') goto yy35; | 
|  | goto yy3; | 
|  | yy8: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych == 'C') goto yy32; | 
|  | if (yych == 'c') goto yy32; | 
|  | goto yy3; | 
|  | yy9: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych == 'O') goto yy29; | 
|  | if (yych == 'o') goto yy29; | 
|  | goto yy3; | 
|  | yy10: | 
|  | yyaccept = 0; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych == 'E') goto yy26; | 
|  | if (yych == 'e') goto yy26; | 
|  | goto yy3; | 
|  | yy11: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= '/') goto yy3; | 
|  | if (yych <= '9') goto yy13; | 
|  | goto yy3; | 
|  | yy12: | 
|  | yych = *++YYCURSOR; | 
|  | goto yy3; | 
|  | yy13: | 
|  | yyaccept = 1; | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | if (yych <= '/') goto yy14; | 
|  | if (yych <= '9') goto yy15; | 
|  | if (yych <= ':') goto yy17; | 
|  | yy14: | 
|  | { | 
|  | if (sscanf(t, "%d", &time.tm_mday) != 1) | 
|  | time.tm_mday = -1; | 
|  | continue; | 
|  | } | 
|  | yy15: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= '/') goto yy16; | 
|  | if (yych <= '9') goto yy24; | 
|  | yy16: | 
|  | YYCURSOR = YYMARKER; | 
|  | if (yyaccept <= 0) { | 
|  | goto yy3; | 
|  | } else { | 
|  | goto yy14; | 
|  | } | 
|  | yy17: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= '/') goto yy16; | 
|  | if (yych >= ':') goto yy16; | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= '/') goto yy16; | 
|  | if (yych >= ':') goto yy16; | 
|  | yych = *++YYCURSOR; | 
|  | if (yych != ':') goto yy16; | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= '/') goto yy16; | 
|  | if (yych >= ':') goto yy16; | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= '/') goto yy16; | 
|  | if (yych >= ':') goto yy16; | 
|  | ++YYCURSOR; | 
|  | { | 
|  | sscanf(t, "%d:%d:%d", &time.tm_hour, &time.tm_min, &time.tm_sec); | 
|  | continue; | 
|  | } | 
|  | yy24: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | if (sscanf(t, "%d", &time.tm_year) == 1) | 
|  | time.tm_year -=1900; | 
|  | else | 
|  | time.tm_year = -1; | 
|  | continue; | 
|  | } | 
|  | yy26: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'C') goto yy27; | 
|  | if (yych != 'c') goto yy16; | 
|  | yy27: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 11; | 
|  | continue; | 
|  | } | 
|  | yy29: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'V') goto yy30; | 
|  | if (yych != 'v') goto yy16; | 
|  | yy30: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 10; | 
|  | continue; | 
|  | } | 
|  | yy32: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'T') goto yy33; | 
|  | if (yych != 't') goto yy16; | 
|  | yy33: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 9; | 
|  | continue; | 
|  | } | 
|  | yy35: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'P') goto yy36; | 
|  | if (yych != 'p') goto yy16; | 
|  | yy36: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 8; | 
|  | continue; | 
|  | } | 
|  | yy38: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'G') goto yy42; | 
|  | if (yych == 'g') goto yy42; | 
|  | goto yy16; | 
|  | yy39: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'R') goto yy40; | 
|  | if (yych != 'r') goto yy16; | 
|  | yy40: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 3; | 
|  | continue; | 
|  | } | 
|  | yy42: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 7; | 
|  | continue; | 
|  | } | 
|  | yy44: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= 'Y') { | 
|  | if (yych == 'R') goto yy45; | 
|  | if (yych <= 'X') goto yy16; | 
|  | goto yy47; | 
|  | } else { | 
|  | if (yych <= 'r') { | 
|  | if (yych <= 'q') goto yy16; | 
|  | } else { | 
|  | if (yych == 'y') goto yy47; | 
|  | goto yy16; | 
|  | } | 
|  | } | 
|  | yy45: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 2; | 
|  | continue; | 
|  | } | 
|  | yy47: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 4; | 
|  | continue; | 
|  | } | 
|  | yy49: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'B') goto yy50; | 
|  | if (yych != 'b') goto yy16; | 
|  | yy50: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 1; | 
|  | continue; | 
|  | } | 
|  | yy52: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= 'N') { | 
|  | if (yych == 'L') goto yy58; | 
|  | if (yych <= 'M') goto yy16; | 
|  | goto yy56; | 
|  | } else { | 
|  | if (yych <= 'l') { | 
|  | if (yych <= 'k') goto yy16; | 
|  | goto yy58; | 
|  | } else { | 
|  | if (yych == 'n') goto yy56; | 
|  | goto yy16; | 
|  | } | 
|  | } | 
|  | yy53: | 
|  | yych = *++YYCURSOR; | 
|  | if (yych == 'N') goto yy54; | 
|  | if (yych != 'n') goto yy16; | 
|  | yy54: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 0; | 
|  | continue; | 
|  | } | 
|  | yy56: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 5; | 
|  | continue; | 
|  | } | 
|  | yy58: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | time.tm_mon = 6; | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* ----------------------------------------------------------------- Class */ | 
|  |  | 
|  |  | 
|  | time_t Time_build(int year, int month, int day, int hour, int min, int sec) { | 
|  | struct tm tm = {.tm_isdst = -1}; | 
|  | TEST_RANGE(year, 1970, 2037); | 
|  | TEST_RANGE(month, 1, 12); | 
|  | TEST_RANGE(day, 1, 31); | 
|  | TEST_RANGE(hour, 0, 23); | 
|  | TEST_RANGE(min, 0, 59); | 
|  | TEST_RANGE(sec, 0, 61); | 
|  | tm.tm_year = (year - 1900); | 
|  | tm.tm_mon  = (month - 1); | 
|  | tm.tm_mday = day; | 
|  | tm.tm_hour = hour; | 
|  | tm.tm_min  = min; | 
|  | tm.tm_sec  = sec; | 
|  | return mktime(&tm); | 
|  | } | 
|  |  | 
|  |  | 
|  | time_t Time_parse(const char *date) { | 
|  | if (STR_DEF(date)) | 
|  | return parseDate(date); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  |  | 
|  | time_t Time_now(void) { | 
|  | struct timeval t; | 
|  | if (gettimeofday(&t, NULL) != 0) | 
|  | THROW(AssertException, "%s", System_getLastError()); | 
|  | return t.tv_sec; | 
|  | } | 
|  |  | 
|  |  | 
|  | long long int Time_milli(void) { | 
|  | struct timeval t; | 
|  | if (gettimeofday(&t, NULL) != 0) | 
|  | THROW(AssertException, "%s", System_getLastError()); | 
|  | return (long long int)t.tv_sec * 1000  +  (long long int)t.tv_usec / 1000; | 
|  | } | 
|  |  | 
|  |  | 
|  | time_t Time_gmt(time_t localtime) { | 
|  | struct tm r; | 
|  | gmtime_r(&localtime, &r); | 
|  | return mktime(&r); | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_seconds(time_t time) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | return tm.tm_sec; | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_minutes(time_t time) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | return tm.tm_min; | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_hour(time_t time) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | return tm.tm_hour; | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_weekday(time_t time) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | return tm.tm_wday; | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_day(time_t time) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | return tm.tm_mday; | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_month(time_t time) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | return (tm.tm_mon + 1); | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_year(time_t time) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | return (tm.tm_year + 1900); | 
|  | } | 
|  |  | 
|  |  | 
|  | time_t Time_add(time_t time, int years, int months, int days) { | 
|  | struct tm tm; | 
|  | localtime_r(&time, &tm); | 
|  | tm.tm_year += years; | 
|  | tm.tm_mon += months; | 
|  | tm.tm_mday += days; | 
|  | tm.tm_isdst = -1; | 
|  | return mktime(&tm); | 
|  | } | 
|  |  | 
|  |  | 
|  | int Time_daysBetween(time_t to, time_t from) { | 
|  | double t = difftime(to, from); | 
|  | if (t < 0) t *= -1; | 
|  | return ((t + (86400L/2))/86400L); | 
|  | } | 
|  |  | 
|  |  | 
|  | char *Time_string(time_t time, char *result) { | 
|  | #define i2a(i) (x[0]=(i/10)+'0', x[1]=(i%10)+'0') | 
|  | if (result) { | 
|  | char x[2]; | 
|  | struct tm ts; | 
|  | /* This implementation needs to be fast and is around 50% | 
|  | faster than strftime */ | 
|  | localtime_r((const time_t *)&time, &ts); | 
|  | memcpy(result, "aaa, xx aaa xxxx xx:xx:xx\0", 26); | 
|  | /*              0    5  8   1214 17 20 2326 */ | 
|  | memcpy(result, days+3*ts.tm_wday, 3); | 
|  | i2a(ts.tm_mday); | 
|  | result[5] = x[0]; | 
|  | result[6] = x[1]; | 
|  | memcpy(result + 8, months+3*ts.tm_mon, 3); | 
|  | i2a((ts.tm_year+1900)/100); | 
|  | result[12] = x[0]; | 
|  | result[13] = x[1]; | 
|  | i2a((ts.tm_year+1900)%100); | 
|  | result[14] = x[0]; | 
|  | result[15] = x[1]; | 
|  | i2a(ts.tm_hour); | 
|  | result[17] = x[0]; | 
|  | result[18] = x[1]; | 
|  | i2a(ts.tm_min); | 
|  | result[20] = x[0]; | 
|  | result[21] = x[1]; | 
|  | i2a(ts.tm_sec); | 
|  | result[23] = x[0]; | 
|  | result[24] = x[1]; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | char *Time_gmtstring(time_t time, char *result) { | 
|  | if (result) { | 
|  | char x[2]; | 
|  | struct tm ts; | 
|  | /* This implementation needs to be fast and is around 50% | 
|  | faster than strftime */ | 
|  | gmtime_r(&time, &ts); | 
|  | memcpy(result, "aaa, xx aaa xxxx xx:xx:xx GMT\0", 30); | 
|  | /*              0    5  8   1214 17 20 23    29 */ | 
|  | memcpy(result, days+3*ts.tm_wday, 3); | 
|  | i2a(ts.tm_mday); | 
|  | result[5] = x[0]; | 
|  | result[6] = x[1]; | 
|  | memcpy(result + 8, months+3*ts.tm_mon, 3); | 
|  | i2a((ts.tm_year+1900)/100); | 
|  | result[12] = x[0]; | 
|  | result[13] = x[1]; | 
|  | i2a((ts.tm_year+1900)%100); | 
|  | result[14] = x[0]; | 
|  | result[15] = x[1]; | 
|  | i2a(ts.tm_hour); | 
|  | result[17] = x[0]; | 
|  | result[18] = x[1]; | 
|  | i2a(ts.tm_min); | 
|  | result[20] = x[0]; | 
|  | result[21] = x[1]; | 
|  | i2a(ts.tm_sec); | 
|  | result[23] = x[0]; | 
|  | result[24] = x[1]; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | char *Time_fmt(char *result, int size, const char *format, time_t time) { | 
|  | struct tm tm; | 
|  | assert(result); | 
|  | assert(format); | 
|  | localtime_r((const time_t *)&time, &tm); | 
|  | if (strftime(result, size, format, &tm) == 0) | 
|  | *result = 0; | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | char *Time_uptime(time_t sec, char *result) { | 
|  | // Write max 24 bytes to result | 
|  | if (result) { | 
|  | int n = 0; | 
|  | time_t r = 0; | 
|  | result[0] = 0; | 
|  | if (sec > 0) { | 
|  | if ((r = sec/86400) > 0) { | 
|  | n = snprintf(result, 24, "%ldd", r); | 
|  | sec -= r * 86400; | 
|  | } | 
|  | if((r = sec/3600) > 0) { | 
|  | n += snprintf(result + n, (24 - n), "%s%ldh", n ? ", " : "", r); | 
|  | sec -= r * 3600; | 
|  | } | 
|  | r = sec/60; | 
|  | snprintf(result + n, (24 - n), "%s%ldm", n ? ", " : "", r); | 
|  | } | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | cron string is on format "minute hour day month wday" | 
|  | where fields may have a numeric type, an asterix, a | 
|  | sequence of numbers or a range | 
|  | */ | 
|  | int Time_incron(const char *cron, time_t time) { | 
|  | assert(cron); | 
|  | #undef YYCURSOR | 
|  | #undef YYLIMIT | 
|  | #undef YYMARKER | 
|  | #define YYCURSOR cron | 
|  | #define YYLIMIT  end | 
|  | #define YYMARKER m | 
|  | #define YYTOKEN  t | 
|  | const char *m; | 
|  | const char *t; | 
|  | const char *end = cron + strlen(cron); | 
|  | int n = 0; | 
|  | int found = 0; | 
|  | int fields[] = {Time_minutes(time), Time_hour(time), Time_day(time), Time_month(time), Time_weekday(time)}; | 
|  | parse: | 
|  | if (YYCURSOR >= YYLIMIT) | 
|  | return found == 5; | 
|  | YYTOKEN = YYCURSOR; | 
|  |  | 
|  | { | 
|  | YYCTYPE yych; | 
|  | static const unsigned char yybm[] = { | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 128, 128, 128, 128, 128, 128, 128, 128, | 
|  | 128, 128,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | 0,   0,   0,   0,   0,   0,   0,   0, | 
|  | }; | 
|  | if ((YYLIMIT - YYCURSOR) < 3) YYFILL(3); | 
|  | yych = *YYCURSOR; | 
|  | if (yych <= ' ') { | 
|  | if (yych <= '\f') { | 
|  | if (yych <= 0x08) goto yy70; | 
|  | if (yych >= '\v') goto yy70; | 
|  | } else { | 
|  | if (yych <= '\r') goto yy62; | 
|  | if (yych <= 0x1F) goto yy70; | 
|  | } | 
|  | } else { | 
|  | if (yych <= '+') { | 
|  | if (yych == '*') goto yy64; | 
|  | goto yy70; | 
|  | } else { | 
|  | if (yych <= ',') goto yy66; | 
|  | if (yych <= '/') goto yy70; | 
|  | if (yych <= '9') goto yy68; | 
|  | goto yy70; | 
|  | } | 
|  | } | 
|  | yy62: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | goto parse; | 
|  | } | 
|  | yy64: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | n++; | 
|  | found++; | 
|  | goto parse; | 
|  | } | 
|  | yy66: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | n--; // backtrack on field advance | 
|  | assert(n < 5 && n >= 0); | 
|  | goto parse; | 
|  | } | 
|  | yy68: | 
|  | yych = *(YYMARKER = ++YYCURSOR); | 
|  | goto yy73; | 
|  | yy69: | 
|  | { | 
|  | int v = Str_parseInt(YYTOKEN); | 
|  | if (fields[n] == v) | 
|  | found++; | 
|  | n++; | 
|  | goto parse; | 
|  | } | 
|  | yy70: | 
|  | ++YYCURSOR; | 
|  | { | 
|  | return false; | 
|  | } | 
|  | yy72: | 
|  | YYMARKER = ++YYCURSOR; | 
|  | if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); | 
|  | yych = *YYCURSOR; | 
|  | yy73: | 
|  | if (yybm[0+yych] & 128) { | 
|  | goto yy72; | 
|  | } | 
|  | if (yych != '-') goto yy69; | 
|  | yych = *++YYCURSOR; | 
|  | if (yych <= '/') goto yy75; | 
|  | if (yych <= '9') goto yy76; | 
|  | yy75: | 
|  | YYCURSOR = YYMARKER; | 
|  | goto yy69; | 
|  | yy76: | 
|  | ++YYCURSOR; | 
|  | if (YYLIMIT <= YYCURSOR) YYFILL(1); | 
|  | yych = *YYCURSOR; | 
|  | if (yych <= '/') goto yy78; | 
|  | if (yych <= '9') goto yy76; | 
|  | yy78: | 
|  | { | 
|  | int from = Str_parseInt(YYTOKEN); | 
|  | int to = Str_parseInt(strchr(YYTOKEN, '-') + 1); | 
|  | if ((fields[n] <= to) && (fields[n] >= from)) | 
|  | found++; | 
|  | n++; | 
|  | goto parse; | 
|  | } | 
|  | } | 
|  | return found == 5; | 
|  | } | 
|  |  | 
|  |  | 
|  | void Time_usleep(long u) { | 
|  | #ifdef NETBSD | 
|  | // usleep is broken on NetBSD (at least in version 5.1) | 
|  | struct timespec t = {u / 1000000, (u % 1000000) * 1000}; | 
|  | nanosleep(&t, NULL); | 
|  | #else | 
|  | usleep((useconds_t)u); | 
|  | #endif | 
|  | } | 
|  |  |