1097 lines
26 KiB
Text
Vendored
1097 lines
26 KiB
Text
Vendored
/*
|
||
* tclGetDate.y --
|
||
*
|
||
* Contains yacc grammar for parsing date and time strings. The output of
|
||
* this file should be the file tclDate.c which is used directly in the
|
||
* Tcl sources. Note that this file is largely obsolete in Tcl 8.5; it is
|
||
* only used when doing free-form date parsing, an ill-defined process
|
||
* anyway.
|
||
*
|
||
* Copyright © 1992-1995 Karl Lehenbauer & Mark Diekhans.
|
||
* Copyright © 1995-1997 Sun Microsystems, Inc.
|
||
* Copyright © 2015 Sergey G. Brester aka sebres.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution of
|
||
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*/
|
||
|
||
%parse-param {DateInfo* info}
|
||
%lex-param {DateInfo* info}
|
||
%define api.pure
|
||
/* %error-verbose would be nice, but our token names are meaningless */
|
||
%locations
|
||
|
||
%{
|
||
/*
|
||
* tclDate.c --
|
||
*
|
||
* This file is generated from a yacc grammar defined in the file
|
||
* tclGetDate.y. It should not be edited directly.
|
||
*
|
||
* Copyright © 1992-1995 Karl Lehenbauer & Mark Diekhans.
|
||
* Copyright © 1995-1997 Sun Microsystems, Inc.
|
||
* Copyright © 2015 Sergey G. Brester aka sebres.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution of
|
||
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*
|
||
*/
|
||
#include "tclInt.h"
|
||
|
||
/*
|
||
* Bison generates several labels that happen to be unused. Several compilers
|
||
* don't like that, and complain. Simply disable the warning to silence them.
|
||
*/
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma warning( disable : 4102 )
|
||
#elif defined (__clang__) && (__clang_major__ > 14)
|
||
#pragma clang diagnostic ignored "-Wunused-but-set-variable"
|
||
#elif (__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))
|
||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||
#endif
|
||
|
||
#if 0
|
||
#define YYDEBUG 1
|
||
#endif
|
||
|
||
/*
|
||
* yyparse will accept a 'struct DateInfo' as its parameter; that's where the
|
||
* parsed fields will be returned.
|
||
*/
|
||
|
||
#include "tclDate.h"
|
||
|
||
#define YYMALLOC Tcl_Alloc
|
||
#define YYFREE(x) (Tcl_Free((void*) (x)))
|
||
|
||
#define EPOCH 1970
|
||
#define START_OF_TIME 1902
|
||
#define END_OF_TIME 2037
|
||
|
||
/*
|
||
* The offset of tm_year of struct tm returned by localtime, gmtime, etc.
|
||
* Posix requires 1900.
|
||
*/
|
||
|
||
#define TM_YEAR_BASE 1900
|
||
|
||
#define HOUR(x) ((60 * (int)(x)))
|
||
#define IsLeapYear(x) (((x) % 4 == 0) && ((x) % 100 != 0 || (x) % 400 == 0))
|
||
|
||
#define yyIncrFlags(f) \
|
||
do { \
|
||
info->errFlags |= (info->flags & (f)); \
|
||
if (info->errFlags) { YYABORT; } \
|
||
info->flags |= (f); \
|
||
} while (0);
|
||
|
||
/*
|
||
* An entry in the lexical lookup table.
|
||
*/
|
||
|
||
typedef struct {
|
||
const char *name;
|
||
int type;
|
||
int value;
|
||
} TABLE;
|
||
|
||
/*
|
||
* Daylight-savings mode: on, off, or not yet known.
|
||
*/
|
||
|
||
typedef enum _DSTMODE {
|
||
DSTon, DSToff, DSTmaybe
|
||
} DSTMODE;
|
||
|
||
%}
|
||
|
||
%union {
|
||
Tcl_WideInt Number;
|
||
MERIDIAN Meridian;
|
||
}
|
||
|
||
%{
|
||
|
||
/*
|
||
* Prototypes of internal functions.
|
||
*/
|
||
|
||
static int LookupWord(YYSTYPE* yylvalPtr, char *buff);
|
||
static void TclDateerror(YYLTYPE* location,
|
||
DateInfo* info, const char *s);
|
||
static int TclDatelex(YYSTYPE* yylvalPtr, YYLTYPE* location,
|
||
DateInfo* info);
|
||
MODULE_SCOPE int yyparse(DateInfo*);
|
||
|
||
%}
|
||
|
||
%token tAGO
|
||
%token tDAY
|
||
%token tDAYZONE
|
||
%token tID
|
||
%token tMERIDIAN
|
||
%token tMONTH
|
||
%token tRMONTH_UNIT
|
||
%token tSTARDATE
|
||
%token tSEC_UNIT
|
||
%token tRSEC_UNIT
|
||
%token tUNUMBER
|
||
%token tZONE
|
||
%token tZONEwO4
|
||
%token tZONEwO2
|
||
%token tEPOCH
|
||
%token tDST
|
||
%token tISOBAS8
|
||
%token tISOBAS6
|
||
%token tISOBASL
|
||
%token tDAY_UNIT
|
||
%token tRDAY_UNIT
|
||
%token tNEXT
|
||
%token SP
|
||
|
||
%type <Number> tDAY
|
||
%type <Number> tDAYZONE
|
||
%type <Number> tMONTH
|
||
%type <Number> tRMONTH_UNIT
|
||
%type <Number> tDST
|
||
%type <Number> tSEC_UNIT
|
||
%type <Number> tRSEC_UNIT
|
||
%type <Number> tUNUMBER
|
||
%type <Number> INTNUM
|
||
%type <Number> tZONE
|
||
%type <Number> tZONEwO4
|
||
%type <Number> tZONEwO2
|
||
%type <Number> tISOBAS8
|
||
%type <Number> tISOBAS6
|
||
%type <Number> tISOBASL
|
||
%type <Number> tDAY_UNIT
|
||
%type <Number> tRDAY_UNIT
|
||
%type <Number> runit
|
||
%type <Number> unit
|
||
%type <Number> sign
|
||
%type <Number> tNEXT
|
||
%type <Number> tSTARDATE
|
||
%type <Meridian> tMERIDIAN
|
||
%type <Meridian> o_merid
|
||
|
||
%%
|
||
|
||
spec : /* NULL */
|
||
| spec item
|
||
/* | spec SP item */
|
||
;
|
||
|
||
item : time {
|
||
yyIncrFlags(CLF_TIME);
|
||
}
|
||
| zone {
|
||
yyIncrFlags(CLF_ZONE);
|
||
}
|
||
| date {
|
||
yyIncrFlags(CLF_HAVEDATE);
|
||
}
|
||
| ordMonth {
|
||
yyIncrFlags(CLF_ORDINALMONTH);
|
||
info->flags |= CLF_RELCONV;
|
||
}
|
||
| day {
|
||
yyIncrFlags(CLF_DAYOFWEEK);
|
||
info->flags |= CLF_RELCONV;
|
||
}
|
||
| relspec {
|
||
info->flags |= CLF_RELCONV;
|
||
}
|
||
| nmzone {
|
||
yyIncrFlags(CLF_ZONE);
|
||
}
|
||
| iso {
|
||
yyIncrFlags(CLF_TIME|CLF_HAVEDATE);
|
||
}
|
||
| trek {
|
||
yyIncrFlags(CLF_TIME|CLF_HAVEDATE);
|
||
info->flags |= CLF_TREK;
|
||
}
|
||
| numitem
|
||
;
|
||
|
||
iextime : tUNUMBER ':' tUNUMBER ':' tUNUMBER {
|
||
yyHour = $1;
|
||
yyMinutes = $3;
|
||
yySeconds = $5;
|
||
}
|
||
| tUNUMBER ':' tUNUMBER {
|
||
yyHour = $1;
|
||
yyMinutes = $3;
|
||
yySeconds = 0;
|
||
}
|
||
;
|
||
time : tUNUMBER tMERIDIAN {
|
||
yyHour = $1;
|
||
yyMinutes = 0;
|
||
yySeconds = 0;
|
||
yyMeridian = $2;
|
||
}
|
||
| iextime o_merid {
|
||
yyMeridian = $2;
|
||
}
|
||
;
|
||
|
||
zone : tZONE tDST {
|
||
yyTimezone = $1;
|
||
yyDSTmode = DSTon;
|
||
}
|
||
| tZONE {
|
||
yyTimezone = $1;
|
||
yyDSTmode = DSToff;
|
||
}
|
||
| tDAYZONE {
|
||
yyTimezone = $1;
|
||
yyDSTmode = DSTon;
|
||
}
|
||
| tZONEwO4 sign INTNUM { /* GMT+0100, GMT-1000, etc. */
|
||
yyTimezone = $1 - $2*($3 % 100 + ($3 / 100) * 60);
|
||
yyDSTmode = DSToff;
|
||
}
|
||
| tZONEwO2 sign INTNUM { /* GMT+1, GMT-10, etc. */
|
||
yyTimezone = $1 - $2*($3 * 60);
|
||
yyDSTmode = DSToff;
|
||
}
|
||
;
|
||
nmzone : sign tUNUMBER %?{ yyDigitCount == 4 || yyDigitCount <= 2 } {
|
||
if (yyDigitCount == 4) { /* +0100, -0100 */
|
||
yyTimezone = -$1*($2 % 100 + ($2 / 100) * 60);
|
||
} else { /* +01, -01, +1, -1 */
|
||
yyTimezone = -$1*($2 * 60);
|
||
}
|
||
yyDSTmode = DSToff;
|
||
}
|
||
;
|
||
|
||
comma : ','
|
||
| ',' SP
|
||
;
|
||
|
||
day : tDAY {
|
||
yyDayOrdinal = 1;
|
||
yyDayOfWeek = $1;
|
||
}
|
||
| tDAY comma {
|
||
yyDayOrdinal = 1;
|
||
yyDayOfWeek = $1;
|
||
}
|
||
| tUNUMBER tDAY {
|
||
yyDayOrdinal = $1;
|
||
yyDayOfWeek = $2;
|
||
}
|
||
| sign SP tUNUMBER tDAY {
|
||
yyDayOrdinal = $1 * $3;
|
||
yyDayOfWeek = $4;
|
||
}
|
||
| sign tUNUMBER tDAY {
|
||
yyDayOrdinal = $1 * $2;
|
||
yyDayOfWeek = $3;
|
||
}
|
||
| tNEXT tDAY {
|
||
yyDayOrdinal = 2;
|
||
yyDayOfWeek = $2;
|
||
}
|
||
;
|
||
|
||
iexdate : tUNUMBER '-' tUNUMBER '-' tUNUMBER {
|
||
yyMonth = $3;
|
||
yyDay = $5;
|
||
yyYear = $1;
|
||
}
|
||
;
|
||
date : tUNUMBER '/' tUNUMBER {
|
||
yyMonth = $1;
|
||
yyDay = $3;
|
||
}
|
||
| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
|
||
yyMonth = $1;
|
||
yyDay = $3;
|
||
yyYear = $5;
|
||
}
|
||
| isodate
|
||
| tUNUMBER '-' tMONTH '-' tUNUMBER {
|
||
yyDay = $1;
|
||
yyMonth = $3;
|
||
yyYear = $5;
|
||
}
|
||
| tMONTH tUNUMBER {
|
||
yyMonth = $1;
|
||
yyDay = $2;
|
||
}
|
||
| tMONTH tUNUMBER comma tUNUMBER {
|
||
yyMonth = $1;
|
||
yyDay = $2;
|
||
yyYear = $4;
|
||
}
|
||
| tUNUMBER tMONTH {
|
||
yyMonth = $2;
|
||
yyDay = $1;
|
||
}
|
||
| tEPOCH {
|
||
yyMonth = 1;
|
||
yyDay = 1;
|
||
yyYear = EPOCH;
|
||
}
|
||
| tUNUMBER tMONTH tUNUMBER {
|
||
yyMonth = $2;
|
||
yyDay = $1;
|
||
yyYear = $3;
|
||
}
|
||
;
|
||
|
||
ordMonth: tNEXT tMONTH {
|
||
yyMonthOrdinalIncr = 1;
|
||
yyMonthOrdinal = $2;
|
||
}
|
||
| tNEXT tUNUMBER tMONTH {
|
||
yyMonthOrdinalIncr = $2;
|
||
yyMonthOrdinal = $3;
|
||
}
|
||
;
|
||
|
||
isosep : 'T'|SP
|
||
;
|
||
isodate : tISOBAS8 { /* YYYYMMDD */
|
||
yyYear = $1 / 10000;
|
||
yyMonth = ($1 % 10000)/100;
|
||
yyDay = $1 % 100;
|
||
}
|
||
| tISOBAS6 { /* YYMMDD */
|
||
yyYear = $1 / 10000;
|
||
yyMonth = ($1 % 10000)/100;
|
||
yyDay = $1 % 100;
|
||
}
|
||
| iexdate
|
||
;
|
||
isotime : tISOBAS6 {
|
||
yyHour = $1 / 10000;
|
||
yyMinutes = ($1 % 10000)/100;
|
||
yySeconds = $1 % 100;
|
||
}
|
||
| iextime
|
||
;
|
||
iso : isodate isosep isotime
|
||
| tISOBASL tISOBAS6 { /* YYYYMMDDhhmmss */
|
||
yyYear = $1 / 10000;
|
||
yyMonth = ($1 % 10000)/100;
|
||
yyDay = $1 % 100;
|
||
yyHour = $2 / 10000;
|
||
yyMinutes = ($2 % 10000)/100;
|
||
yySeconds = $2 % 100;
|
||
}
|
||
| tISOBASL tUNUMBER { /* YYYYMMDDhhmm */
|
||
if (yyDigitCount != 4) YYABORT; /* normally unreached */
|
||
yyYear = $1 / 10000;
|
||
yyMonth = ($1 % 10000)/100;
|
||
yyDay = $1 % 100;
|
||
yyHour = $2 / 100;
|
||
yyMinutes = ($2 % 100);
|
||
yySeconds = 0;
|
||
}
|
||
;
|
||
|
||
trek : tSTARDATE INTNUM '.' tUNUMBER {
|
||
/*
|
||
* Offset computed year by -377 so that the returned years will be
|
||
* in a range accessible with a 32 bit clock seconds value.
|
||
*/
|
||
|
||
yyYear = $2/1000 + 2323 - 377;
|
||
yyDay = 1;
|
||
yyMonth = 1;
|
||
yyRelDay += (($2%1000)*(365 + IsLeapYear(yyYear)))/1000;
|
||
yyRelSeconds += $4 * (144LL * 60LL);
|
||
info->flags |= CLF_RELCONV;
|
||
}
|
||
;
|
||
|
||
relspec : relunits tAGO {
|
||
yyRelSeconds *= -1;
|
||
yyRelMonth *= -1;
|
||
yyRelDay *= -1;
|
||
}
|
||
| relunits
|
||
;
|
||
|
||
relunits : sign SP INTNUM runit {
|
||
*yyRelPointer += $1 * $3 * $4;
|
||
}
|
||
| sign INTNUM runit {
|
||
*yyRelPointer += $1 * $2 * $3;
|
||
}
|
||
| INTNUM runit {
|
||
*yyRelPointer += $1 * $2;
|
||
}
|
||
| tNEXT runit {
|
||
*yyRelPointer += $2;
|
||
}
|
||
| tNEXT INTNUM runit {
|
||
*yyRelPointer += $2 * $3;
|
||
}
|
||
| unit {
|
||
*yyRelPointer += $1;
|
||
}
|
||
;
|
||
|
||
sign : '-' {
|
||
$$ = -1;
|
||
}
|
||
| '+' {
|
||
$$ = 1;
|
||
}
|
||
;
|
||
|
||
runit : tRSEC_UNIT {
|
||
$$ = $1;
|
||
yyRelPointer = &yyRelSeconds;
|
||
/* no flag CLF_RELCONV needed by seconds */
|
||
}
|
||
| tRDAY_UNIT {
|
||
$$ = $1;
|
||
yyRelPointer = &yyRelDay;
|
||
info->flags |= CLF_RELCONV;
|
||
}
|
||
| tRMONTH_UNIT {
|
||
$$ = $1;
|
||
yyRelPointer = &yyRelMonth;
|
||
info->flags |= CLF_RELCONV;
|
||
}
|
||
;
|
||
|
||
unit : tSEC_UNIT {
|
||
$$ = $1;
|
||
yyRelPointer = &yyRelSeconds;
|
||
/* no flag CLF_RELCONV needed by seconds */
|
||
}
|
||
| tDAY_UNIT {
|
||
$$ = $1;
|
||
yyRelPointer = &yyRelDay;
|
||
info->flags |= CLF_RELCONV;
|
||
}
|
||
;
|
||
|
||
INTNUM : tUNUMBER {
|
||
$$ = $1;
|
||
}
|
||
| tISOBAS6 {
|
||
$$ = $1;
|
||
}
|
||
| tISOBAS8 {
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
numitem : tUNUMBER {
|
||
if ((info->flags & (CLF_TIME|CLF_HAVEDATE|CLF_TREK)) == (CLF_TIME|CLF_HAVEDATE)) {
|
||
yyYear = $1;
|
||
} else {
|
||
yyIncrFlags(CLF_TIME);
|
||
if (yyDigitCount <= 2) {
|
||
yyHour = $1;
|
||
yyMinutes = 0;
|
||
} else {
|
||
yyHour = $1 / 100;
|
||
yyMinutes = $1 % 100;
|
||
}
|
||
yySeconds = 0;
|
||
yyMeridian = MER24;
|
||
}
|
||
}
|
||
;
|
||
|
||
o_merid : /* NULL */ {
|
||
$$ = MER24;
|
||
}
|
||
| tMERIDIAN {
|
||
$$ = $1;
|
||
}
|
||
;
|
||
|
||
%%
|
||
/*
|
||
* Month and day table.
|
||
*/
|
||
|
||
static const TABLE MonthDayTable[] = {
|
||
{ "january", tMONTH, 1 },
|
||
{ "february", tMONTH, 2 },
|
||
{ "march", tMONTH, 3 },
|
||
{ "april", tMONTH, 4 },
|
||
{ "may", tMONTH, 5 },
|
||
{ "june", tMONTH, 6 },
|
||
{ "july", tMONTH, 7 },
|
||
{ "august", tMONTH, 8 },
|
||
{ "september", tMONTH, 9 },
|
||
{ "sept", tMONTH, 9 },
|
||
{ "october", tMONTH, 10 },
|
||
{ "november", tMONTH, 11 },
|
||
{ "december", tMONTH, 12 },
|
||
{ "sunday", tDAY, 7 },
|
||
{ "monday", tDAY, 1 },
|
||
{ "tuesday", tDAY, 2 },
|
||
{ "tues", tDAY, 2 },
|
||
{ "wednesday", tDAY, 3 },
|
||
{ "wednes", tDAY, 3 },
|
||
{ "thursday", tDAY, 4 },
|
||
{ "thur", tDAY, 4 },
|
||
{ "thurs", tDAY, 4 },
|
||
{ "friday", tDAY, 5 },
|
||
{ "saturday", tDAY, 6 },
|
||
{ NULL, 0, 0 }
|
||
};
|
||
|
||
/*
|
||
* Time units table.
|
||
*/
|
||
|
||
static const TABLE UnitsTable[] = {
|
||
{ "year", tRMONTH_UNIT, 12 },
|
||
{ "month", tRMONTH_UNIT, 1 },
|
||
{ "fortnight", tRDAY_UNIT, 14 },
|
||
{ "week", tRDAY_UNIT, 7 },
|
||
{ "day", tRDAY_UNIT, 1 },
|
||
{ "hour", tRSEC_UNIT, 60 * 60 },
|
||
{ "minute", tRSEC_UNIT, 60 },
|
||
{ "min", tRSEC_UNIT, 60 },
|
||
{ "second", tRSEC_UNIT, 1 },
|
||
{ "sec", tRSEC_UNIT, 1 },
|
||
{ NULL, 0, 0 }
|
||
};
|
||
|
||
/*
|
||
* Assorted relative-time words.
|
||
*/
|
||
|
||
static const TABLE OtherTable[] = {
|
||
{ "tomorrow", tDAY_UNIT, 1 },
|
||
{ "yesterday", tDAY_UNIT, -1 },
|
||
{ "today", tDAY_UNIT, 0 },
|
||
{ "now", tSEC_UNIT, 0 },
|
||
{ "last", tUNUMBER, -1 },
|
||
{ "this", tSEC_UNIT, 0 },
|
||
{ "next", tNEXT, 1 },
|
||
{ "ago", tAGO, 1 },
|
||
{ "epoch", tEPOCH, 0 },
|
||
{ "stardate", tSTARDATE, 0 },
|
||
{ NULL, 0, 0 }
|
||
};
|
||
|
||
/*
|
||
* The timezone table. (Note: This table was modified to not use any floating
|
||
* point constants to work around an SGI compiler bug).
|
||
*/
|
||
|
||
static const TABLE TimezoneTable[] = {
|
||
{ "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
|
||
{ "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
|
||
{ "utc", tZONE, HOUR( 0) },
|
||
{ "uct", tZONE, HOUR( 0) }, /* Universal Coordinated Time */
|
||
{ "wet", tZONE, HOUR( 0) }, /* Western European */
|
||
{ "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
|
||
{ "wat", tZONE, HOUR( 1) }, /* West Africa */
|
||
{ "at", tZONE, HOUR( 2) }, /* Azores */
|
||
#if 0
|
||
/* For completeness. BST is also British Summer, and GST is
|
||
* also Guam Standard. */
|
||
{ "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
|
||
{ "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
|
||
#endif
|
||
{ "nft", tZONE, HOUR( 7/2) }, /* Newfoundland */
|
||
{ "nst", tZONE, HOUR( 7/2) }, /* Newfoundland Standard */
|
||
{ "ndt", tDAYZONE, HOUR( 7/2) }, /* Newfoundland Daylight */
|
||
{ "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
|
||
{ "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
|
||
{ "est", tZONE, HOUR( 5) }, /* Eastern Standard */
|
||
{ "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
|
||
{ "cst", tZONE, HOUR( 6) }, /* Central Standard */
|
||
{ "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
|
||
{ "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
|
||
{ "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
|
||
{ "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
|
||
{ "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
|
||
{ "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
|
||
{ "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
|
||
{ "akst", tZONE, HOUR( 9) }, /* Alaska Standard */
|
||
{ "akdt", tDAYZONE, HOUR( 9) }, /* Alaska Daylight */
|
||
{ "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
|
||
{ "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
|
||
{ "cat", tZONE, HOUR(10) }, /* Central Alaska */
|
||
{ "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
|
||
{ "nt", tZONE, HOUR(11) }, /* Nome */
|
||
{ "idlw", tZONE, HOUR(12) }, /* International Date Line West */
|
||
{ "cet", tZONE, -HOUR( 1) }, /* Central European */
|
||
{ "cest", tDAYZONE, -HOUR( 1) }, /* Central European Summer */
|
||
{ "met", tZONE, -HOUR( 1) }, /* Middle European */
|
||
{ "mewt", tZONE, -HOUR( 1) }, /* Middle European Winter */
|
||
{ "mest", tDAYZONE, -HOUR( 1) }, /* Middle European Summer */
|
||
{ "swt", tZONE, -HOUR( 1) }, /* Swedish Winter */
|
||
{ "sst", tDAYZONE, -HOUR( 1) }, /* Swedish Summer */
|
||
{ "fwt", tZONE, -HOUR( 1) }, /* French Winter */
|
||
{ "fst", tDAYZONE, -HOUR( 1) }, /* French Summer */
|
||
{ "eet", tZONE, -HOUR( 2) }, /* Eastern Europe, USSR Zone 1 */
|
||
{ "bt", tZONE, -HOUR( 3) }, /* Baghdad, USSR Zone 2 */
|
||
{ "it", tZONE, -HOUR( 7/2) }, /* Iran */
|
||
{ "zp4", tZONE, -HOUR( 4) }, /* USSR Zone 3 */
|
||
{ "zp5", tZONE, -HOUR( 5) }, /* USSR Zone 4 */
|
||
{ "ist", tZONE, -HOUR(11/2) }, /* Indian Standard */
|
||
{ "zp6", tZONE, -HOUR( 6) }, /* USSR Zone 5 */
|
||
#if 0
|
||
/* For completeness. NST is also Newfoundland Standard, and SST is
|
||
* also Swedish Summer. */
|
||
{ "nst", tZONE, -HOUR(13/2) }, /* North Sumatra */
|
||
{ "sst", tZONE, -HOUR( 7) }, /* South Sumatra, USSR Zone 6 */
|
||
#endif /* 0 */
|
||
{ "wast", tZONE, -HOUR( 7) }, /* West Australian Standard */
|
||
{ "wadt", tDAYZONE, -HOUR( 7) }, /* West Australian Daylight */
|
||
{ "jt", tZONE, -HOUR(15/2) }, /* Java (3pm in Cronusland!) */
|
||
{ "cct", tZONE, -HOUR( 8) }, /* China Coast, USSR Zone 7 */
|
||
{ "jst", tZONE, -HOUR( 9) }, /* Japan Standard, USSR Zone 8 */
|
||
{ "jdt", tDAYZONE, -HOUR( 9) }, /* Japan Daylight */
|
||
{ "kst", tZONE, -HOUR( 9) }, /* Korea Standard */
|
||
{ "kdt", tDAYZONE, -HOUR( 9) }, /* Korea Daylight */
|
||
{ "cast", tZONE, -HOUR(19/2) }, /* Central Australian Standard */
|
||
{ "cadt", tDAYZONE, -HOUR(19/2) }, /* Central Australian Daylight */
|
||
{ "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
|
||
{ "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
|
||
{ "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
|
||
{ "nzt", tZONE, -HOUR(12) }, /* New Zealand */
|
||
{ "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
|
||
{ "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
|
||
{ "idle", tZONE, -HOUR(12) }, /* International Date Line East */
|
||
/* ADDED BY Marco Nijdam */
|
||
{ "dst", tDST, HOUR( 0) }, /* DST on (hour is ignored) */
|
||
/* End ADDED */
|
||
{ NULL, 0, 0 }
|
||
};
|
||
|
||
/*
|
||
* Military timezone table.
|
||
*/
|
||
|
||
static const TABLE MilitaryTable[] = {
|
||
{ "a", tZONE, -HOUR( 1) },
|
||
{ "b", tZONE, -HOUR( 2) },
|
||
{ "c", tZONE, -HOUR( 3) },
|
||
{ "d", tZONE, -HOUR( 4) },
|
||
{ "e", tZONE, -HOUR( 5) },
|
||
{ "f", tZONE, -HOUR( 6) },
|
||
{ "g", tZONE, -HOUR( 7) },
|
||
{ "h", tZONE, -HOUR( 8) },
|
||
{ "i", tZONE, -HOUR( 9) },
|
||
{ "k", tZONE, -HOUR(10) },
|
||
{ "l", tZONE, -HOUR(11) },
|
||
{ "m", tZONE, -HOUR(12) },
|
||
{ "n", tZONE, HOUR( 1) },
|
||
{ "o", tZONE, HOUR( 2) },
|
||
{ "p", tZONE, HOUR( 3) },
|
||
{ "q", tZONE, HOUR( 4) },
|
||
{ "r", tZONE, HOUR( 5) },
|
||
{ "s", tZONE, HOUR( 6) },
|
||
{ "t", tZONE, HOUR( 7) },
|
||
{ "u", tZONE, HOUR( 8) },
|
||
{ "v", tZONE, HOUR( 9) },
|
||
{ "w", tZONE, HOUR( 10) },
|
||
{ "x", tZONE, HOUR( 11) },
|
||
{ "y", tZONE, HOUR( 12) },
|
||
{ "z", tZONE, HOUR( 0) },
|
||
{ NULL, 0, 0 }
|
||
};
|
||
|
||
static inline const char *
|
||
bypassSpaces(
|
||
const char *s)
|
||
{
|
||
while (TclIsSpaceProc(*s)) {
|
||
s++;
|
||
}
|
||
return s;
|
||
}
|
||
|
||
/*
|
||
* Dump error messages in the bit bucket.
|
||
*/
|
||
|
||
static void
|
||
TclDateerror(
|
||
YYLTYPE* location,
|
||
DateInfo* infoPtr,
|
||
const char *s)
|
||
{
|
||
Tcl_Obj* t;
|
||
if (!infoPtr->messages) {
|
||
TclNewObj(infoPtr->messages);
|
||
}
|
||
Tcl_AppendToObj(infoPtr->messages, infoPtr->separatrix, -1);
|
||
Tcl_AppendToObj(infoPtr->messages, s, -1);
|
||
Tcl_AppendToObj(infoPtr->messages, " (characters ", -1);
|
||
TclNewIntObj(t, location->first_column);
|
||
Tcl_IncrRefCount(t);
|
||
Tcl_AppendObjToObj(infoPtr->messages, t);
|
||
Tcl_DecrRefCount(t);
|
||
Tcl_AppendToObj(infoPtr->messages, "-", -1);
|
||
TclNewIntObj(t, location->last_column);
|
||
Tcl_IncrRefCount(t);
|
||
Tcl_AppendObjToObj(infoPtr->messages, t);
|
||
Tcl_DecrRefCount(t);
|
||
Tcl_AppendToObj(infoPtr->messages, ")", -1);
|
||
infoPtr->separatrix = "\n";
|
||
}
|
||
|
||
int
|
||
TclToSeconds(
|
||
int Hours,
|
||
int Minutes,
|
||
int Seconds,
|
||
MERIDIAN Meridian)
|
||
{
|
||
switch (Meridian) {
|
||
case MER24:
|
||
return (Hours * 60 + Minutes) * 60 + Seconds;
|
||
case MERam:
|
||
return (((Hours / 24) * 24 + (Hours % 12)) * 60 + Minutes) * 60 + Seconds;
|
||
case MERpm:
|
||
return (((Hours / 24) * 24 + (Hours % 12) + 12) * 60 + Minutes) * 60 + Seconds;
|
||
}
|
||
return -1; /* Should never be reached */
|
||
}
|
||
|
||
static int
|
||
LookupWord(
|
||
YYSTYPE* yylvalPtr,
|
||
char *buff)
|
||
{
|
||
char *p;
|
||
char *q;
|
||
const TABLE *tp;
|
||
int i, abbrev;
|
||
|
||
/*
|
||
* Make it lowercase.
|
||
*/
|
||
|
||
Tcl_UtfToLower(buff);
|
||
|
||
if (*buff == 'a' && (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0)) {
|
||
yylvalPtr->Meridian = MERam;
|
||
return tMERIDIAN;
|
||
}
|
||
if (*buff == 'p' && (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0)) {
|
||
yylvalPtr->Meridian = MERpm;
|
||
return tMERIDIAN;
|
||
}
|
||
|
||
/*
|
||
* See if we have an abbreviation for a month.
|
||
*/
|
||
|
||
if (strlen(buff) == 3) {
|
||
abbrev = 1;
|
||
} else if (strlen(buff) == 4 && buff[3] == '.') {
|
||
abbrev = 1;
|
||
buff[3] = '\0';
|
||
} else {
|
||
abbrev = 0;
|
||
}
|
||
|
||
for (tp = MonthDayTable; tp->name; tp++) {
|
||
if (abbrev) {
|
||
if (strncmp(buff, tp->name, 3) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
} else if (strcmp(buff, tp->name) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
}
|
||
|
||
for (tp = TimezoneTable; tp->name; tp++) {
|
||
if (strcmp(buff, tp->name) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
}
|
||
|
||
for (tp = UnitsTable; tp->name; tp++) {
|
||
if (strcmp(buff, tp->name) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Strip off any plural and try the units table again.
|
||
*/
|
||
|
||
i = strlen(buff) - 1;
|
||
if (i > 0 && buff[i] == 's') {
|
||
buff[i] = '\0';
|
||
for (tp = UnitsTable; tp->name; tp++) {
|
||
if (strcmp(buff, tp->name) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (tp = OtherTable; tp->name; tp++) {
|
||
if (strcmp(buff, tp->name) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Military timezones.
|
||
*/
|
||
|
||
if (buff[1] == '\0' && !(*buff & 0x80)
|
||
&& isalpha(UCHAR(*buff))) { /* INTL: ISO only */
|
||
for (tp = MilitaryTable; tp->name; tp++) {
|
||
if (strcmp(buff, tp->name) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Drop out any periods and try the timezone table again.
|
||
*/
|
||
|
||
for (i = 0, p = q = buff; *q; q++) {
|
||
if (*q != '.') {
|
||
*p++ = *q;
|
||
} else {
|
||
i++;
|
||
}
|
||
}
|
||
*p = '\0';
|
||
if (i) {
|
||
for (tp = TimezoneTable; tp->name; tp++) {
|
||
if (strcmp(buff, tp->name) == 0) {
|
||
yylvalPtr->Number = tp->value;
|
||
return tp->type;
|
||
}
|
||
}
|
||
}
|
||
|
||
return tID;
|
||
}
|
||
|
||
static int
|
||
TclDatelex(
|
||
YYSTYPE* yylvalPtr,
|
||
YYLTYPE* location,
|
||
DateInfo *info)
|
||
{
|
||
char c;
|
||
char *p;
|
||
char buff[20];
|
||
int Count;
|
||
const char *tokStart;
|
||
|
||
location->first_column = yyInput - info->dateStart;
|
||
for ( ; ; ) {
|
||
|
||
if (isspace(UCHAR(*yyInput))) {
|
||
yyInput = bypassSpaces(yyInput);
|
||
/* ignore space at end of text and before some words */
|
||
c = *yyInput;
|
||
if (c != '\0' && !isalpha(UCHAR(c))) {
|
||
return SP;
|
||
}
|
||
}
|
||
tokStart = yyInput;
|
||
|
||
if (isdigit(UCHAR(c = *yyInput))) { /* INTL: digit */
|
||
|
||
/*
|
||
* Count the number of digits.
|
||
*/
|
||
p = (char *)yyInput;
|
||
while (isdigit(UCHAR(*++p))) {};
|
||
yyDigitCount = p - yyInput;
|
||
/*
|
||
* A number with 12 or 14 digits is considered an ISO 8601 date.
|
||
*/
|
||
if (yyDigitCount == 14 || yyDigitCount == 12) {
|
||
/* long form of ISO 8601 (without separator), either
|
||
* YYYYMMDDhhmmss or YYYYMMDDhhmm, so reduce to date
|
||
* (8 chars is isodate) */
|
||
p = (char *)yyInput+8;
|
||
if (TclAtoWIe(&yylvalPtr->Number, yyInput, p, 1) != TCL_OK) {
|
||
return tID; /* overflow*/
|
||
}
|
||
yyDigitCount = 8;
|
||
yyInput = p;
|
||
location->last_column = yyInput - info->dateStart - 1;
|
||
return tISOBASL;
|
||
}
|
||
/*
|
||
* Convert the string into a number
|
||
*/
|
||
if (TclAtoWIe(&yylvalPtr->Number, yyInput, p, 1) != TCL_OK) {
|
||
return tID; /* overflow*/
|
||
}
|
||
yyInput = p;
|
||
/*
|
||
* A number with 6 or more digits is considered an ISO 8601 base.
|
||
*/
|
||
location->last_column = yyInput - info->dateStart - 1;
|
||
if (yyDigitCount >= 6) {
|
||
if (yyDigitCount == 8) {
|
||
return tISOBAS8;
|
||
}
|
||
if (yyDigitCount == 6) {
|
||
return tISOBAS6;
|
||
}
|
||
}
|
||
/* ignore spaces after digits (optional) */
|
||
yyInput = bypassSpaces(yyInput);
|
||
return tUNUMBER;
|
||
}
|
||
if (!(c & 0x80) && isalpha(UCHAR(c))) { /* INTL: ISO only. */
|
||
int ret;
|
||
for (p = buff; isalpha(UCHAR(c = *yyInput++)) /* INTL: ISO only. */
|
||
|| c == '.'; ) {
|
||
if (p < &buff[sizeof(buff) - 1]) {
|
||
*p++ = c;
|
||
}
|
||
}
|
||
*p = '\0';
|
||
yyInput--;
|
||
location->last_column = yyInput - info->dateStart - 1;
|
||
ret = LookupWord(yylvalPtr, buff);
|
||
/*
|
||
* lookahead:
|
||
* for spaces to consider word boundaries (for instance
|
||
* literal T in isodateTisotimeZ is not a TZ, but Z is UTC);
|
||
* for +/- digit, to differentiate between "GMT+1000 day" and "GMT +1000 day";
|
||
* bypass spaces after token (but ignore by TZ+OFFS), because should
|
||
* recognize next SP token, if TZ only.
|
||
*/
|
||
if (ret == tZONE || ret == tDAYZONE) {
|
||
c = *yyInput;
|
||
if (isdigit(UCHAR(c))) { /* literal not a TZ */
|
||
yyInput = tokStart;
|
||
return *yyInput++;
|
||
}
|
||
if ((c == '+' || c == '-') && isdigit(UCHAR(*(yyInput+1)))) {
|
||
if ( !isdigit(UCHAR(*(yyInput+2)))
|
||
|| !isdigit(UCHAR(*(yyInput+3)))) {
|
||
/* GMT+1, GMT-10, etc. */
|
||
return tZONEwO2;
|
||
}
|
||
if ( isdigit(UCHAR(*(yyInput+4)))
|
||
&& !isdigit(UCHAR(*(yyInput+5)))) {
|
||
/* GMT+1000, etc. */
|
||
return tZONEwO4;
|
||
}
|
||
}
|
||
}
|
||
yyInput = bypassSpaces(yyInput);
|
||
return ret;
|
||
|
||
}
|
||
if (c != '(') {
|
||
location->last_column = yyInput - info->dateStart;
|
||
return *yyInput++;
|
||
}
|
||
Count = 0;
|
||
do {
|
||
c = *yyInput++;
|
||
if (c == '\0') {
|
||
location->last_column = yyInput - info->dateStart - 1;
|
||
return c;
|
||
} else if (c == '(') {
|
||
Count++;
|
||
} else if (c == ')') {
|
||
Count--;
|
||
}
|
||
} while (Count > 0);
|
||
}
|
||
}
|
||
|
||
int
|
||
TclClockFreeScan(
|
||
Tcl_Interp *interp, /* Tcl interpreter */
|
||
DateInfo *info) /* Input and result parameters */
|
||
{
|
||
int status;
|
||
|
||
#if YYDEBUG
|
||
/* enable debugging if compiled with YYDEBUG */
|
||
yydebug = 1;
|
||
#endif
|
||
|
||
/*
|
||
* yyInput = stringToParse;
|
||
*
|
||
* ClockInitDateInfo(info) should be executed to pre-init info;
|
||
*/
|
||
|
||
yyDSTmode = DSTmaybe;
|
||
|
||
info->separatrix = "";
|
||
|
||
info->dateStart = yyInput;
|
||
|
||
/* ignore spaces at begin */
|
||
yyInput = bypassSpaces(yyInput);
|
||
|
||
/* parse */
|
||
status = yyparse(info);
|
||
if (status == 1) {
|
||
const char *msg = NULL;
|
||
if (info->errFlags & CLF_HAVEDATE) {
|
||
msg = "more than one date in string";
|
||
} else if (info->errFlags & CLF_TIME) {
|
||
msg = "more than one time of day in string";
|
||
} else if (info->errFlags & CLF_ZONE) {
|
||
msg = "more than one time zone in string";
|
||
} else if (info->errFlags & CLF_DAYOFWEEK) {
|
||
msg = "more than one weekday in string";
|
||
} else if (info->errFlags & CLF_ORDINALMONTH) {
|
||
msg = "more than one ordinal month in string";
|
||
}
|
||
if (msg) {
|
||
Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1));
|
||
Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "MULTIPLE", (char *)NULL);
|
||
} else {
|
||
Tcl_SetObjResult(interp,
|
||
info->messages ? info->messages : Tcl_NewObj());
|
||
info->messages = NULL;
|
||
Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "PARSE", (char *)NULL);
|
||
}
|
||
status = TCL_ERROR;
|
||
} else if (status == 2) {
|
||
Tcl_SetObjResult(interp, Tcl_NewStringObj("memory exhausted", -1));
|
||
Tcl_SetErrorCode(interp, "TCL", "MEMORY", (char *)NULL);
|
||
status = TCL_ERROR;
|
||
} else if (status != 0) {
|
||
Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown status returned "
|
||
"from date parser. Please "
|
||
"report this error as a "
|
||
"bug in Tcl.", -1));
|
||
Tcl_SetErrorCode(interp, "TCL", "BUG", (char *)NULL);
|
||
status = TCL_ERROR;
|
||
}
|
||
if (info->messages) {
|
||
Tcl_DecrRefCount(info->messages);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
/*
|
||
* Local Variables:
|
||
* mode: c
|
||
* c-basic-offset: 4
|
||
* fill-column: 78
|
||
* End:
|
||
*/
|