diff options
author | jan.nijtmans <nijtmans@users.sourceforge.net> | 2017-06-02 08:12:38 (GMT) |
---|---|---|
committer | jan.nijtmans <nijtmans@users.sourceforge.net> | 2017-06-02 08:12:38 (GMT) |
commit | f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5 (patch) | |
tree | 1601cdbe0f43c015bfcb743565108c36f488e67b /generic | |
parent | 1a543aa367940f7b7f4f8c6a8e83f673e2715611 (diff) | |
parent | 3ae95af52ca24414d723b827fc99cc1a2b94f778 (diff) | |
download | tcl-f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5.zip tcl-f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5.tar.gz tcl-f00c9c9e4aa0c923528903a88e4bf2ef9aa6c2d5.tar.bz2 |
Merge core-8-6-branch. This removes the work currently being done in "sebres-8-6-clock-speedup-cr1" branch, but that will be merged again as soon as the work is done.
All other changes in "trunk" since then (e.g. the INST_STR_CONCAT1 performance improvement, and the removal of SunOS-4) are retained.
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tclBasic.c | 1 | ||||
-rw-r--r-- | generic/tclClock.c | 2596 | ||||
-rw-r--r-- | generic/tclClockFmt.c | 3137 | ||||
-rw-r--r-- | generic/tclCmdMZ.c | 345 | ||||
-rw-r--r-- | generic/tclDate.c | 548 | ||||
-rw-r--r-- | generic/tclDate.h | 512 | ||||
-rw-r--r-- | generic/tclDictObj.c | 117 | ||||
-rw-r--r-- | generic/tclEnsemble.c | 20 | ||||
-rw-r--r-- | generic/tclEnv.c | 9 | ||||
-rw-r--r-- | generic/tclGetDate.y | 196 | ||||
-rw-r--r-- | generic/tclInt.h | 23 | ||||
-rw-r--r-- | generic/tclStrIdxTree.c | 527 | ||||
-rw-r--r-- | generic/tclStrIdxTree.h | 169 | ||||
-rw-r--r-- | generic/tclUtf.c | 12 |
14 files changed, 740 insertions, 7472 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 4093614..154c555 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -285,7 +285,6 @@ static const CmdInfo builtInCmds[] = { {"source", Tcl_SourceObjCmd, NULL, TclNRSourceObjCmd, 0}, {"tell", Tcl_TellObjCmd, NULL, NULL, CMD_IS_SAFE}, {"time", Tcl_TimeObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"timerate", Tcl_TimeRateObjCmd, NULL, NULL, CMD_IS_SAFE}, {"unload", Tcl_UnloadObjCmd, NULL, NULL, 0}, {"update", Tcl_UpdateObjCmd, NULL, NULL, CMD_IS_SAFE}, {"vwait", Tcl_VwaitObjCmd, NULL, NULL, CMD_IS_SAFE}, diff --git a/generic/tclClock.c b/generic/tclClock.c index c38af6b..d44e9dc 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -8,16 +8,12 @@ * Copyright 1991-1995 Karl Lehenbauer and Mark Diekhans. * Copyright (c) 1995 Sun Microsystems, Inc. * Copyright (c) 2004 by Kevin B. Kenny. All rights reserved. - * Copyright (c) 2015 by Sergey G. Brester aka sebres. All rights reserved. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "tclStrIdxTree.h" -#include "tclDate.h" -#include "tclCompile.h" /* * Windows has mktime. The configurators do not check. @@ -28,6 +24,21 @@ #endif /* + * Constants + */ + +#define JULIAN_DAY_POSIX_EPOCH 2440588 +#define SECONDS_PER_DAY 86400 +#define JULIAN_SEC_POSIX_EPOCH (((Tcl_WideInt) JULIAN_DAY_POSIX_EPOCH) \ + * SECONDS_PER_DAY) +#define FOUR_CENTURIES 146097 /* days */ +#define JDAY_1_JAN_1_CE_JULIAN 1721424 +#define JDAY_1_JAN_1_CE_GREGORIAN 1721426 +#define ONE_CENTURY_GREGORIAN 36524 /* days */ +#define FOUR_YEARS 1461 /* days */ +#define ONE_YEAR 365 /* days */ + +/* * Table of the days in each month, leap and common years */ @@ -44,13 +55,70 @@ static const int daysInPriorMonths[2][13] = { * Enumeration of the string literals used in [clock] */ -CLOCK_LITERAL_ARRAY(Literals); +typedef enum ClockLiteral { + LIT__NIL, + LIT__DEFAULT_FORMAT, + LIT_BCE, LIT_C, + LIT_CANNOT_USE_GMT_AND_TIMEZONE, + LIT_CE, + LIT_DAYOFMONTH, LIT_DAYOFWEEK, LIT_DAYOFYEAR, + LIT_ERA, LIT_GMT, LIT_GREGORIAN, + LIT_INTEGER_VALUE_TOO_LARGE, + LIT_ISO8601WEEK, LIT_ISO8601YEAR, + LIT_JULIANDAY, LIT_LOCALSECONDS, + LIT_MONTH, + LIT_SECONDS, LIT_TZNAME, LIT_TZOFFSET, + LIT_YEAR, + LIT__END +} ClockLiteral; +static const char *const literals[] = { + "", + "%a %b %d %H:%M:%S %Z %Y", + "BCE", "C", + "cannot use -gmt and -timezone in same call", + "CE", + "dayOfMonth", "dayOfWeek", "dayOfYear", + "era", ":GMT", "gregorian", + "integer value too large to represent", + "iso8601Week", "iso8601Year", + "julianDay", "localSeconds", + "month", + "seconds", "tzName", "tzOffset", + "year" +}; + +/* + * Structure containing the client data for [clock] + */ + +typedef struct ClockClientData { + size_t refCount; /* Number of live references. */ + Tcl_Obj **literals; /* Pool of object literals. */ +} ClockClientData; -/* Msgcat literals for exact match (mcKey) */ -CLOCK_LOCALE_LITERAL_ARRAY(MsgCtLiterals, ""); -/* Msgcat index literals prefixed with _IDX_, used for quick dictionary search */ -CLOCK_LOCALE_LITERAL_ARRAY(MsgCtLitIdxs, "_IDX_"); +/* + * Structure containing the fields used in [clock format] and [clock scan] + */ +typedef struct TclDateFields { + Tcl_WideInt seconds; /* Time expressed in seconds from the Posix + * epoch */ + Tcl_WideInt localSeconds; /* Local time expressed in nominal seconds + * from the Posix epoch */ + int tzOffset; /* Time zone offset in seconds east of + * Greenwich */ + Tcl_Obj *tzName; /* Time zone name */ + int julianDay; /* Julian Day Number in local time zone */ + enum {BCE=1, CE=0} era; /* Era */ + int gregorian; /* Flag == 1 if the date is Gregorian */ + int year; /* Year of the era */ + int dayOfYear; /* Day of the year (1 January == 1) */ + int month; /* Month number */ + int dayOfMonth; /* Day of the month */ + int iso8601Year; /* ISO8601 week-based year */ + int iso8601Week; /* ISO8601 week number */ + int dayOfWeek; /* Day of the week */ +} TclDateFields; static const char *const eras[] = { "CE", "BCE", NULL }; /* @@ -71,23 +139,26 @@ TCL_DECLARE_MUTEX(clockMutex) * Function prototypes for local procedures in this file: */ +static int ConvertUTCToLocal(Tcl_Interp *, + TclDateFields *, Tcl_Obj *, int); static int ConvertUTCToLocalUsingTable(Tcl_Interp *, - TclDateFields *, int, Tcl_Obj *const[], - Tcl_WideInt rangesVal[2]); + TclDateFields *, int, Tcl_Obj *const[]); static int ConvertUTCToLocalUsingC(Tcl_Interp *, TclDateFields *, int); -static int ConvertLocalToUTC(ClientData clientData, Tcl_Interp *, - TclDateFields *, Tcl_Obj *timezoneObj, int); +static int ConvertLocalToUTC(Tcl_Interp *, + TclDateFields *, Tcl_Obj *, int); static int ConvertLocalToUTCUsingTable(Tcl_Interp *, - TclDateFields *, int, Tcl_Obj *const[], - Tcl_WideInt rangesVal[2]); + TclDateFields *, int, Tcl_Obj *const[]); static int ConvertLocalToUTCUsingC(Tcl_Interp *, TclDateFields *, int); -static int ClockConfigureObjCmd(ClientData clientData, - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +static Tcl_Obj * LookupLastTransition(Tcl_Interp *, Tcl_WideInt, + int, Tcl_Obj *const *); static void GetYearWeekDay(TclDateFields *, int); static void GetGregorianEraYearDay(TclDateFields *, int); static void GetMonthDay(TclDateFields *); +static void GetJulianDayFromEraYearWeekDay(TclDateFields *, int); +static void GetJulianDayFromEraYearMonthDay(TclDateFields *, int); +static int IsGregorianLeapYear(TclDateFields *); static int WeekdayOnOrBefore(int, int); static int ClockClicksObjCmd( ClientData clientData, Tcl_Interp *interp, @@ -95,10 +166,6 @@ static int ClockClicksObjCmd( static int ClockConvertlocaltoutcObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); - -static int ClockGetDateFields(ClientData clientData, - Tcl_Interp *interp, TclDateFields *fields, - Tcl_Obj *timezoneObj, int changeover); static int ClockGetdatefieldsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -117,28 +184,13 @@ static int ClockMicrosecondsObjCmd( static int ClockMillisecondsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -static int ClockSecondsObjCmd( - ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int ClockFormatObjCmd( - ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int ClockScanObjCmd( +static int ClockParseformatargsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -static int ClockScanCommit( - ClientData clientData, register DateInfo *info, - register ClockFmtScnCmdArgs *opts); -static int ClockFreeScan( - register DateInfo *info, - Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts); -static int ClockCalcRelTime( - register DateInfo *info, ClockFmtScnCmdArgs *opts); -static int ClockAddObjCmd( +static int ClockSecondsObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); static struct tm * ThreadSafeLocalTime(const time_t *); -static unsigned long TzsetGetEpoch(void); static void TzsetIfNecessary(void); static void ClockDeleteCmdProc(ClientData); @@ -150,32 +202,22 @@ struct ClockCommand { const char *name; /* The tail of the command name. The full name * is "::tcl::clock::<name>". When NULL marks * the end of the table. */ - Tcl_ObjCmdProc *objCmdProc; /* Function that implements the command. This + Tcl_ObjCmdProc *objCmdProc; /* Function that implements the command. This * will always have the ClockClientData sent * to it, but may well ignore this data. */ - CompileProc *compileProc; /* The compiler for the command. */ - ClientData clientData; /* Any clientData to give the command (if NULL - * a reference to ClockClientData will be sent) */ }; static const struct ClockCommand clockCommands[] = { - {"add", ClockAddObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"clicks", ClockClicksObjCmd, TclCompileClockClicksCmd, NULL}, - {"format", ClockFormatObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"getenv", ClockGetenvObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"microseconds", ClockMicrosecondsObjCmd,TclCompileClockReadingCmd, INT2PTR(1)}, - {"milliseconds", ClockMillisecondsObjCmd,TclCompileClockReadingCmd, INT2PTR(2)}, - {"scan", ClockScanObjCmd, TclCompileBasicMin1ArgCmd, NULL}, - {"seconds", ClockSecondsObjCmd, TclCompileClockReadingCmd, INT2PTR(3)}, - {"configure", ClockConfigureObjCmd, NULL, NULL}, - {"Oldscan", TclClockOldscanObjCmd, NULL, NULL}, - {"ConvertLocalToUTC", ClockConvertlocaltoutcObjCmd, NULL, NULL}, - {"GetDateFields", ClockGetdatefieldsObjCmd, NULL, NULL}, - {"GetJulianDayFromEraYearMonthDay", - ClockGetjuliandayfromerayearmonthdayObjCmd, NULL, NULL}, - {"GetJulianDayFromEraYearWeekDay", - ClockGetjuliandayfromerayearweekdayObjCmd, NULL, NULL}, - {NULL, NULL, NULL, NULL} + { "getenv", ClockGetenvObjCmd }, + { "Oldscan", TclClockOldscanObjCmd }, + { "ConvertLocalToUTC", ClockConvertlocaltoutcObjCmd }, + { "GetDateFields", ClockGetdatefieldsObjCmd }, + { "GetJulianDayFromEraYearMonthDay", + ClockGetjuliandayfromerayearmonthdayObjCmd }, + { "GetJulianDayFromEraYearWeekDay", + ClockGetjuliandayfromerayearweekdayObjCmd }, + { "ParseFormatArgs", ClockParseformatargsObjCmd }, + { NULL, NULL } }; /* @@ -204,10 +246,22 @@ TclClockInit( char cmdName[50]; /* Buffer large enough to hold the string *::tcl::clock::GetJulianDayFromEraYearMonthDay * plus a terminating NUL. */ - Command *cmdPtr; ClockClientData *data; int i; + /* Structure of the 'clock' ensemble */ + + static const EnsembleImplMap clockImplMap[] = { + {"add", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0}, + {"clicks", ClockClicksObjCmd, TclCompileClockClicksCmd, NULL, NULL, 0}, + {"format", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL, 0}, + {"microseconds", ClockMicrosecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(1), 0}, + {"milliseconds", ClockMillisecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(2), 0}, + {"scan", NULL, TclCompileBasicMin1ArgCmd, NULL, NULL , 0}, + {"seconds", ClockSecondsObjCmd, TclCompileClockReadingCmd, NULL, INT2PTR(3), 0}, + {NULL, NULL, NULL, NULL, NULL, 0} + }; + /* * Safe interps get [::clock] as alias to a master, so do not need their * own copies of the support routines. @@ -225,918 +279,27 @@ TclClockInit( data->refCount = 0; data->literals = ckalloc(LIT__END * sizeof(Tcl_Obj*)); for (i = 0; i < LIT__END; ++i) { - Tcl_InitObjRef(data->literals[i], Tcl_NewStringObj(Literals[i], -1)); + data->literals[i] = Tcl_NewStringObj(literals[i], -1); + Tcl_IncrRefCount(data->literals[i]); } - data->mcLiterals = NULL; - data->mcLitIdxs = NULL; - data->LastTZEpoch = 0; - data->currentYearCentury = ClockDefaultYearCentury; - data->yearOfCenturySwitch = ClockDefaultCenturySwitch; - data->SystemTimeZone = NULL; - data->SystemSetupTZData = NULL; - data->GMTSetupTimeZone = NULL; - data->GMTSetupTZData = NULL; - data->AnySetupTimeZone = NULL; - data->AnySetupTZData = NULL; - data->LastUnnormSetupTimeZone = NULL; - data->LastSetupTimeZone = NULL; - data->LastSetupTZData = NULL; - - data->CurrentLocale = NULL; - data->CurrentLocaleDict = NULL; - data->LastUnnormUsedLocale = NULL; - data->LastUsedLocale = NULL; - data->LastUsedLocaleDict = NULL; - - data->lastBase.timezoneObj = NULL; - data->UTC2Local.timezoneObj = NULL; - data->UTC2Local.tzName = NULL; - data->Local2UTC.timezoneObj = NULL; /* * Install the commands. + * TODO - Let Tcl_MakeEnsemble do this? */ #define TCL_CLOCK_PREFIX_LEN 14 /* == strlen("::tcl::clock::") */ memcpy(cmdName, "::tcl::clock::", TCL_CLOCK_PREFIX_LEN); for (clockCmdPtr=clockCommands ; clockCmdPtr->name!=NULL ; clockCmdPtr++) { - ClientData clientData; - strcpy(cmdName + TCL_CLOCK_PREFIX_LEN, clockCmdPtr->name); - if (!(clientData = clockCmdPtr->clientData)) { - clientData = data; - data->refCount++; - } - cmdPtr = (Command *)Tcl_CreateObjCommand(interp, cmdName, - clockCmdPtr->objCmdProc, clientData, - clockCmdPtr->clientData ? NULL : ClockDeleteCmdProc); - cmdPtr->compileProc = clockCmdPtr->compileProc ? - clockCmdPtr->compileProc : TclCompileBasicMin0ArgCmd; - } -} - -/* - *---------------------------------------------------------------------- - * - * ClockConfigureClear -- - * - * Clean up cached resp. run-time storages used in clock commands. - * - * Shared usage for clean-up (ClockDeleteCmdProc) and "configure -clear". - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -ClockConfigureClear( - ClockClientData *data) -{ - ClockFrmScnClearCaches(); - - data->LastTZEpoch = 0; - Tcl_UnsetObjRef(data->SystemTimeZone); - Tcl_UnsetObjRef(data->SystemSetupTZData); - Tcl_UnsetObjRef(data->GMTSetupTimeZone); - Tcl_UnsetObjRef(data->GMTSetupTZData); - Tcl_UnsetObjRef(data->AnySetupTimeZone); - Tcl_UnsetObjRef(data->AnySetupTZData); - Tcl_UnsetObjRef(data->LastUnnormSetupTimeZone); - Tcl_UnsetObjRef(data->LastSetupTimeZone); - Tcl_UnsetObjRef(data->LastSetupTZData); - - Tcl_UnsetObjRef(data->CurrentLocale); - Tcl_UnsetObjRef(data->CurrentLocaleDict); - Tcl_UnsetObjRef(data->LastUnnormUsedLocale); - Tcl_UnsetObjRef(data->LastUsedLocale); - Tcl_UnsetObjRef(data->LastUsedLocaleDict); - - Tcl_UnsetObjRef(data->lastBase.timezoneObj); - Tcl_UnsetObjRef(data->UTC2Local.timezoneObj); - Tcl_UnsetObjRef(data->UTC2Local.tzName); - Tcl_UnsetObjRef(data->Local2UTC.timezoneObj); -} - -/* - *---------------------------------------------------------------------- - * - * ClockDeleteCmdProc -- - * - * Remove a reference to the clock client data, and clean up memory - * when it's all gone. - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ -static void -ClockDeleteCmdProc( - ClientData clientData) /* Opaque pointer to the client data */ -{ - ClockClientData *data = clientData; - int i; - - if (data->refCount-- <= 1) { - for (i = 0; i < LIT__END; ++i) { - Tcl_DecrRefCount(data->literals[i]); - } - if (data->mcLiterals != NULL) { - for (i = 0; i < MCLIT__END; ++i) { - Tcl_DecrRefCount(data->mcLiterals[i]); - } - data->mcLiterals = NULL; - } - if (data->mcLitIdxs != NULL) { - for (i = 0; i < MCLIT__END; ++i) { - Tcl_DecrRefCount(data->mcLitIdxs[i]); - } - data->mcLitIdxs = NULL; - } - - ClockConfigureClear(data); - - ckfree(data->literals); - ckfree(data); - } -} - -/* - *---------------------------------------------------------------------- - * - * NormTimezoneObj -- - * - * Normalizes the timezone object (used for caching puposes). - * - * If already cached time zone could be found, returns this - * object (last setup or last used, system (current) or gmt). - * - * Results: - * Normalized tcl object pointer. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -NormTimezoneObj( - ClockClientData *dataPtr, /* Client data containing literal pool */ - Tcl_Obj *timezoneObj) -{ - const char *tz; - if ( timezoneObj == dataPtr->LastUnnormSetupTimeZone - && dataPtr->LastSetupTimeZone != NULL - ) { - return dataPtr->LastSetupTimeZone; - } - if ( timezoneObj == dataPtr->LastSetupTimeZone - || timezoneObj == dataPtr->literals[LIT_GMT] - || timezoneObj == dataPtr->SystemTimeZone - || timezoneObj == dataPtr->AnySetupTimeZone - ) { - return timezoneObj; - } - - tz = TclGetString(timezoneObj); - if (dataPtr->AnySetupTimeZone != NULL && - (timezoneObj == dataPtr->AnySetupTimeZone - || strcmp(tz, TclGetString(dataPtr->AnySetupTimeZone)) == 0 - ) - ) { - timezoneObj = dataPtr->AnySetupTimeZone; - } - else - if (dataPtr->SystemTimeZone != NULL && - (timezoneObj == dataPtr->SystemTimeZone - || strcmp(tz, TclGetString(dataPtr->SystemTimeZone)) == 0 - ) - ) { - timezoneObj = dataPtr->SystemTimeZone; - } - else - if ( - strcmp(tz, Literals[LIT_GMT]) == 0 - ) { - timezoneObj = dataPtr->literals[LIT_GMT]; - } - return timezoneObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockGetSystemLocale -- - * - * Returns system locale. - * - * Executes ::tcl::clock::GetSystemLocale in given interpreter. - * - * Results: - * Returns system locale tcl object. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -ClockGetSystemLocale( - ClockClientData *dataPtr, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp) /* Tcl interpreter */ -{ - if (Tcl_EvalObjv(interp, 1, &dataPtr->literals[LIT_GETSYSTEMLOCALE], 0) != TCL_OK) { - return NULL; - } - - return Tcl_GetObjResult(interp); -} -/* - *---------------------------------------------------------------------- - * - * ClockGetCurrentLocale -- - * - * Returns current locale. - * - * Executes ::tcl::clock::mclocale in given interpreter. - * - * Results: - * Returns current locale tcl object. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -ClockGetCurrentLocale( - ClockClientData *dataPtr, /* Client data containing literal pool */ - Tcl_Interp *interp) /* Tcl interpreter */ -{ - if (Tcl_EvalObjv(interp, 1, &dataPtr->literals[LIT_GETCURRENTLOCALE], 0) != TCL_OK) { - return NULL; + data->refCount++; + Tcl_CreateObjCommand(interp, cmdName, clockCmdPtr->objCmdProc, data, + ClockDeleteCmdProc); } - Tcl_SetObjRef(dataPtr->CurrentLocale, Tcl_GetObjResult(interp)); - Tcl_UnsetObjRef(dataPtr->CurrentLocaleDict); + /* Make the clock ensemble */ - return dataPtr->CurrentLocale; -} - -/* - *---------------------------------------------------------------------- - * - * NormLocaleObj -- - * - * Normalizes the locale object (used for caching puposes). - * - * If already cached locale could be found, returns this - * object (current, system (OS) or last used locales). - * - * Results: - * Normalized tcl object pointer. - * - *---------------------------------------------------------------------- - */ - -static Tcl_Obj * -NormLocaleObj( - ClockClientData *dataPtr, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *localeObj, - Tcl_Obj **mcDictObj) -{ - const char *loc; - if ( localeObj == NULL || localeObj == dataPtr->CurrentLocale - || localeObj == dataPtr->literals[LIT_C] - || localeObj == dataPtr->literals[LIT_CURRENT] - ) { - if (dataPtr->CurrentLocale == NULL) { - ClockGetCurrentLocale(dataPtr, interp); - } - *mcDictObj = dataPtr->CurrentLocaleDict; - return dataPtr->CurrentLocale; - } - if ( localeObj == dataPtr->LastUsedLocale - || localeObj == dataPtr->LastUnnormUsedLocale - ) { - *mcDictObj = dataPtr->LastUsedLocaleDict; - return dataPtr->LastUsedLocale; - } - - loc = TclGetString(localeObj); - if ( dataPtr->CurrentLocale != NULL - && ( localeObj == dataPtr->CurrentLocale - || (localeObj->length == dataPtr->CurrentLocale->length - && strcmp(loc, TclGetString(dataPtr->CurrentLocale)) == 0 - ) - ) - ) { - *mcDictObj = dataPtr->CurrentLocaleDict; - localeObj = dataPtr->CurrentLocale; - } - else - if ( dataPtr->LastUsedLocale != NULL - && ( localeObj == dataPtr->LastUsedLocale - || (localeObj->length == dataPtr->LastUsedLocale->length - && strcmp(loc, TclGetString(dataPtr->LastUsedLocale)) == 0 - ) - ) - ) { - *mcDictObj = dataPtr->LastUsedLocaleDict; - Tcl_SetObjRef(dataPtr->LastUnnormUsedLocale, localeObj); - localeObj = dataPtr->LastUsedLocale; - } - else - if ( - (localeObj->length == 1 /* C */ - && strncasecmp(loc, Literals[LIT_C], localeObj->length) == 0) - || (localeObj->length == 7 /* current */ - && strncasecmp(loc, Literals[LIT_CURRENT], localeObj->length) == 0) - ) { - if (dataPtr->CurrentLocale == NULL) { - ClockGetCurrentLocale(dataPtr, interp); - } - *mcDictObj = dataPtr->CurrentLocaleDict; - localeObj = dataPtr->CurrentLocale; - } - else - if ( - (localeObj->length == 6 /* system */ - && strncasecmp(loc, Literals[LIT_SYSTEM], localeObj->length) == 0) - ) { - Tcl_SetObjRef(dataPtr->LastUnnormUsedLocale, localeObj); - localeObj = ClockGetSystemLocale(dataPtr, interp); - Tcl_SetObjRef(dataPtr->LastUsedLocale, localeObj); - *mcDictObj = NULL; - } - else - { - *mcDictObj = NULL; - } - return localeObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCDict -- - * - * Retrieves a localized storage dictionary object for the given - * locale object. - * - * This corresponds with call `::tcl::clock::mcget locale`. - * Cached representation stored in options (for further access). - * - * Results: - * Tcl-object contains smart reference to msgcat dictionary. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockMCDict(ClockFmtScnCmdArgs *opts) -{ - ClockClientData *dataPtr = opts->clientData; - - /* if dict not yet retrieved */ - if (opts->mcDictObj == NULL) { - - /* if locale was not yet used */ - if ( !(opts->flags & CLF_LOCALE_USED) ) { - - opts->localeObj = NormLocaleObj(opts->clientData, opts->interp, - opts->localeObj, &opts->mcDictObj); - - if (opts->localeObj == NULL) { - Tcl_SetObjResult(opts->interp, - Tcl_NewStringObj("locale not specified and no default locale set", -1)); - Tcl_SetErrorCode(opts->interp, "CLOCK", "badOption", NULL); - return NULL; - } - opts->flags |= CLF_LOCALE_USED; - - /* check locale literals already available (on demand creation) */ - if (dataPtr->mcLiterals == NULL) { - int i; - dataPtr->mcLiterals = ckalloc(MCLIT__END * sizeof(Tcl_Obj*)); - for (i = 0; i < MCLIT__END; ++i) { - Tcl_InitObjRef(dataPtr->mcLiterals[i], - Tcl_NewStringObj(MsgCtLiterals[i], -1)); - } - } - } - - if (opts->mcDictObj == NULL) { - Tcl_Obj *callargs[2]; - /* get msgcat dictionary - ::tcl::clock::mcget locale */ - callargs[0] = dataPtr->literals[LIT_MCGET]; - callargs[1] = opts->localeObj; - - if (Tcl_EvalObjv(opts->interp, 2, callargs, 0) != TCL_OK) { - return NULL; - } - - opts->mcDictObj = Tcl_GetObjResult(opts->interp); - /* be sure that object reference not increases (dict changeable) */ - if (opts->mcDictObj->refCount > 0) { - /* smart reference (shared dict as object with no ref-counter) */ - opts->mcDictObj = Tcl_DictObjSmartRef(opts->interp, opts->mcDictObj); - } - if ( opts->localeObj == dataPtr->CurrentLocale ) { - Tcl_SetObjRef(dataPtr->CurrentLocaleDict, opts->mcDictObj); - } else if ( opts->localeObj == dataPtr->LastUsedLocale ) { - Tcl_SetObjRef(dataPtr->LastUsedLocaleDict, opts->mcDictObj); - } else { - Tcl_SetObjRef(dataPtr->LastUsedLocale, opts->localeObj); - Tcl_UnsetObjRef(dataPtr->LastUnnormUsedLocale); - Tcl_SetObjRef(dataPtr->LastUsedLocaleDict, opts->mcDictObj); - } - Tcl_ResetResult(opts->interp); - } - } - - return opts->mcDictObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCGet -- - * - * Retrieves a msgcat value for the given literal integer mcKey - * from localized storage (corresponding given locale object) - * by mcLiterals[mcKey] (e. g. MONTHS_FULL). - * - * Results: - * Tcl-object contains localized value. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockMCGet( - ClockFmtScnCmdArgs *opts, - int mcKey) -{ - ClockClientData *dataPtr = opts->clientData; - - Tcl_Obj *valObj = NULL; - - if (opts->mcDictObj == NULL) { - ClockMCDict(opts); - if (opts->mcDictObj == NULL) - return NULL; - } - - Tcl_DictObjGet(opts->interp, opts->mcDictObj, - dataPtr->mcLiterals[mcKey], &valObj); - - return valObj; /* or NULL in obscure case if Tcl_DictObjGet failed */ -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCGetIdx -- - * - * Retrieves an indexed msgcat value for the given literal integer mcKey - * from localized storage (corresponding given locale object) - * by mcLitIdxs[mcKey] (e. g. _IDX_MONTHS_FULL). - * - * Results: - * Tcl-object contains localized indexed value. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockMCGetIdx( - ClockFmtScnCmdArgs *opts, - int mcKey) -{ - ClockClientData *dataPtr = opts->clientData; - - Tcl_Obj *valObj = NULL; - - if (opts->mcDictObj == NULL) { - ClockMCDict(opts); - if (opts->mcDictObj == NULL) - return NULL; - } - - /* try to get indices object */ - if (dataPtr->mcLitIdxs == NULL) { - return NULL; - } - - if (Tcl_DictObjGet(NULL, opts->mcDictObj, - dataPtr->mcLitIdxs[mcKey], &valObj) != TCL_OK - ) { - return NULL; - } - - return valObj; -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCSetIdx -- - * - * Sets an indexed msgcat value for the given literal integer mcKey - * in localized storage (corresponding given locale object) - * by mcLitIdxs[mcKey] (e. g. _IDX_MONTHS_FULL). - * - * Results: - * Returns a standard Tcl result. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE int -ClockMCSetIdx( - ClockFmtScnCmdArgs *opts, - int mcKey, Tcl_Obj *valObj) -{ - ClockClientData *dataPtr = opts->clientData; - - if (opts->mcDictObj == NULL) { - ClockMCDict(opts); - if (opts->mcDictObj == NULL) - return TCL_ERROR; - } - - /* if literal storage for indices not yet created */ - if (dataPtr->mcLitIdxs == NULL) { - int i; - dataPtr->mcLitIdxs = ckalloc(MCLIT__END * sizeof(Tcl_Obj*)); - for (i = 0; i < MCLIT__END; ++i) { - Tcl_InitObjRef(dataPtr->mcLitIdxs[i], - Tcl_NewStringObj(MsgCtLitIdxs[i], -1)); - } - } - - return Tcl_DictObjPut(opts->interp, opts->mcDictObj, - dataPtr->mcLitIdxs[mcKey], valObj); -} - -/* - *---------------------------------------------------------------------- - * - * ClockConfigureObjCmd -- - * - * This function is invoked to process the Tcl "clock configure" command. - * - * Usage: - * ::tcl::clock::configure ?-option ?value?? - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -ClockConfigureObjCmd( - ClientData clientData, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[]) /* Parameter vector */ -{ - ClockClientData *dataPtr = clientData; - - static const char *const options[] = { - "-system-tz", "-setup-tz", "-default-locale", - "-clear", - "-year-century", "-century-switch", - NULL - }; - enum optionInd { - CLOCK_SYSTEM_TZ, CLOCK_SETUP_TZ, CLOCK_CURRENT_LOCALE, - CLOCK_CLEAR_CACHE, - CLOCK_YEAR_CENTURY, CLOCK_CENTURY_SWITCH, - CLOCK_SETUP_GMT, CLOCK_SETUP_NOP - }; - int optionIndex; /* Index of an option. */ - int i; - - for (i = 1; i < objc; i++) { - if (Tcl_GetIndexFromObj(interp, objv[i++], options, - "option", 0, &optionIndex) != TCL_OK) { - Tcl_SetErrorCode(interp, "CLOCK", "badOption", - Tcl_GetString(objv[i-1]), NULL); - return TCL_ERROR; - } - switch (optionIndex) { - case CLOCK_SYSTEM_TZ: - if (1) { - /* validate current tz-epoch */ - unsigned long lastTZEpoch = TzsetGetEpoch(); - if (i < objc) { - if (dataPtr->SystemTimeZone != objv[i]) { - Tcl_SetObjRef(dataPtr->SystemTimeZone, objv[i]); - Tcl_UnsetObjRef(dataPtr->SystemSetupTZData); - } - dataPtr->LastTZEpoch = lastTZEpoch; - } - if (i+1 >= objc && dataPtr->SystemTimeZone != NULL - && dataPtr->LastTZEpoch == lastTZEpoch) { - Tcl_SetObjResult(interp, dataPtr->SystemTimeZone); - } - } - break; - case CLOCK_SETUP_TZ: - if (i < objc) { - /* differentiate GMT and system zones, because used often */ - Tcl_Obj *timezoneObj = NormTimezoneObj(dataPtr, objv[i]); - Tcl_SetObjRef(dataPtr->LastUnnormSetupTimeZone, objv[i]); - if (dataPtr->LastSetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->LastSetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->LastSetupTZData); - } - if (timezoneObj == dataPtr->literals[LIT_GMT]) { - optionIndex = CLOCK_SETUP_GMT; - } else if (timezoneObj == dataPtr->SystemTimeZone) { - optionIndex = CLOCK_SETUP_NOP; - } - switch (optionIndex) { - case CLOCK_SETUP_GMT: - if (i < objc) { - if (dataPtr->GMTSetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->GMTSetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->GMTSetupTZData); - } - } - break; - case CLOCK_SETUP_TZ: - if (i < objc) { - if (dataPtr->AnySetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->AnySetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->AnySetupTZData); - } - } - break; - } - } - if (i+1 >= objc && dataPtr->LastSetupTimeZone != NULL) { - Tcl_SetObjResult(interp, dataPtr->LastSetupTimeZone); - } - break; - case CLOCK_CURRENT_LOCALE: - if (i < objc) { - if (dataPtr->CurrentLocale != objv[i]) { - Tcl_SetObjRef(dataPtr->CurrentLocale, objv[i]); - Tcl_UnsetObjRef(dataPtr->CurrentLocaleDict); - } - } - if (i+1 >= objc && dataPtr->CurrentLocale != NULL) { - Tcl_SetObjResult(interp, dataPtr->CurrentLocale); - } - break; - case CLOCK_YEAR_CENTURY: - if (i < objc) { - int year; - if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) { - return TCL_ERROR; - } - dataPtr->currentYearCentury = year; - if (i+1 >= objc) { - Tcl_SetObjResult(interp, objv[i]); - } - continue; - } - if (i+1 >= objc) { - Tcl_SetObjResult(interp, - Tcl_NewIntObj(dataPtr->currentYearCentury)); - } - break; - case CLOCK_CENTURY_SWITCH: - if (i < objc) { - int year; - if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) { - return TCL_ERROR; - } - dataPtr->yearOfCenturySwitch = year; - Tcl_SetObjResult(interp, objv[i]); - continue; - } - if (i+1 >= objc) { - Tcl_SetObjResult(interp, - Tcl_NewIntObj(dataPtr->yearOfCenturySwitch)); - } - break; - case CLOCK_CLEAR_CACHE: - ClockConfigureClear(dataPtr); - break; - } - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * ClockGetTZData -- - * - * Retrieves tzdata table for given normalized timezone. - * - * Results: - * Returns a tcl object with tzdata. - * - * Side effects: - * The tzdata can be cached in ClockClientData structure. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -ClockGetTZData( - ClientData clientData, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *timezoneObj) /* Name of the timezone */ -{ - ClockClientData *dataPtr = clientData; - Tcl_Obj **literals = dataPtr->literals; - Tcl_Obj *ret, **out = NULL; - - /* if cached (if already setup this one) */ - if ( dataPtr->LastSetupTZData != NULL - && ( timezoneObj == dataPtr->LastSetupTimeZone - || timezoneObj == dataPtr->LastUnnormSetupTimeZone - ) - ) { - return dataPtr->LastSetupTZData; - } - - /* differentiate GMT and system zones, because used often */ - /* simple caching, because almost used the tz-data of last timezone - */ - if (timezoneObj == dataPtr->SystemTimeZone) { - if (dataPtr->SystemSetupTZData != NULL) { - return dataPtr->SystemSetupTZData; - } - out = &dataPtr->SystemSetupTZData; - } - else - if (timezoneObj == dataPtr->GMTSetupTimeZone) { - if (dataPtr->GMTSetupTZData != NULL) { - return dataPtr->GMTSetupTZData; - } - out = &dataPtr->GMTSetupTZData; - } - else - if (timezoneObj == dataPtr->AnySetupTimeZone) { - if (dataPtr->AnySetupTZData != NULL) { - return dataPtr->AnySetupTZData; - } - out = &dataPtr->AnySetupTZData; - } - - ret = Tcl_ObjGetVar2(interp, literals[LIT_TZDATA], - timezoneObj, TCL_LEAVE_ERR_MSG); - - /* cache using corresponding slot and as last used */ - if (out != NULL) { - Tcl_SetObjRef(*out, ret); - } - Tcl_SetObjRef(dataPtr->LastSetupTZData, ret); - if (dataPtr->LastSetupTimeZone != timezoneObj) { - Tcl_SetObjRef(dataPtr->LastSetupTimeZone, timezoneObj); - Tcl_UnsetObjRef(dataPtr->LastUnnormSetupTimeZone); - } - return ret; -} - -/* - *---------------------------------------------------------------------- - * - * ClockGetSystemTimeZone -- - * - * Returns system (current) timezone. - * - * If system zone not yet cached, it executes ::tcl::clock::GetSystemTimeZone - * in given interpreter and caches its result. - * - * Results: - * Returns normalized timezone object. - * - *---------------------------------------------------------------------- - */ - -static Tcl_Obj * -ClockGetSystemTimeZone( - ClientData clientData, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp) /* Tcl interpreter */ -{ - ClockClientData *dataPtr = clientData; - Tcl_Obj **literals; - - /* if known (cached and same epoch) - return now */ - if (dataPtr->SystemTimeZone != NULL - && dataPtr->LastTZEpoch == TzsetGetEpoch()) { - return dataPtr->SystemTimeZone; - } - - Tcl_UnsetObjRef(dataPtr->SystemTimeZone); - Tcl_UnsetObjRef(dataPtr->SystemSetupTZData); - - literals = dataPtr->literals; - - if (Tcl_EvalObjv(interp, 1, &literals[LIT_GETSYSTEMTIMEZONE], 0) != TCL_OK) { - return NULL; - } - if (dataPtr->SystemTimeZone == NULL) { - Tcl_SetObjRef(dataPtr->SystemTimeZone, Tcl_GetObjResult(interp)); - } - return dataPtr->SystemTimeZone; -} - -/* - *---------------------------------------------------------------------- - * - * ClockSetupTimeZone -- - * - * Sets up the timezone. Loads tzdata, etc. - * - * Results: - * Returns normalized timezone object. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockSetupTimeZone( - ClientData clientData, /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *timezoneObj) -{ - ClockClientData *dataPtr = clientData; - Tcl_Obj **literals = dataPtr->literals; - Tcl_Obj *callargs[2]; - - /* if cached (if already setup this one) */ - if ( dataPtr->LastSetupTimeZone != NULL - && ( timezoneObj == dataPtr->LastSetupTimeZone - || timezoneObj == dataPtr->LastUnnormSetupTimeZone - ) - ) { - return dataPtr->LastSetupTimeZone; - } - - /* differentiate GMT and system zones, because used often and already set */ - timezoneObj = NormTimezoneObj(dataPtr, timezoneObj); - if ( timezoneObj == dataPtr->GMTSetupTimeZone - || timezoneObj == dataPtr->SystemTimeZone - || timezoneObj == dataPtr->AnySetupTimeZone - ) { - return timezoneObj; - } - - callargs[0] = literals[LIT_SETUPTIMEZONE]; - callargs[1] = timezoneObj; - - if (Tcl_EvalObjv(interp, 2, callargs, 0) == TCL_OK) { - return dataPtr->LastSetupTimeZone; - } - return NULL; -} - -/* - *---------------------------------------------------------------------- - * - * ClockFormatNumericTimeZone -- - * - * Formats a time zone as +hhmmss - * - * Parameters: - * z - Time zone in seconds east of Greenwich - * - * Results: - * Returns the time zone object (formatted in a numeric form) - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -ClockFormatNumericTimeZone(int z) { - char sign = '+'; - int h, m; - if ( z < 0 ) { - z = -z; - sign = '-'; - } - h = z / 3600; - z %= 3600; - m = z / 60; - z %= 60; - if (z != 0) { - return Tcl_ObjPrintf("%c%02d%02d%02d", sign, h, m, z); - } - return Tcl_ObjPrintf("%c%02d%02d", sign, h, m); + TclMakeEnsemble(interp, "clock", clockImplMap); } /* @@ -1148,11 +311,11 @@ ClockFormatNumericTimeZone(int z) { * is available. * * Usage: - * ::tcl::clock::ConvertUTCToLocal dictionary timezone changeover + * ::tcl::clock::ConvertUTCToLocal dictionary tzdata changeover * * Parameters: * dict - Dictionary containing a 'localSeconds' entry. - * timezone - Time zone + * tzdata - Time zone data * changeover - Julian Day of the adoption of the Gregorian calendar. * * Results: @@ -1182,13 +345,12 @@ ClockConvertlocaltoutcObjCmd( int created = 0; int status; - fields.tzName = NULL; /* * Check params and convert time. */ if (objc != 4) { - Tcl_WrongNumArgs(interp, 1, objv, "dict timezone changeover"); + Tcl_WrongNumArgs(interp, 1, objv, "dict tzdata changeover"); return TCL_ERROR; } dict = objv[1]; @@ -1201,10 +363,10 @@ ClockConvertlocaltoutcObjCmd( "found in dictionary", -1)); return TCL_ERROR; } - if ((TclGetWideIntFromObj(interp, secondsObj, + if ((Tcl_GetWideIntFromObj(interp, secondsObj, &fields.localSeconds) != TCL_OK) || (TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK) - || ConvertLocalToUTC(clientData, interp, &fields, objv[2], changeover)) { + || ConvertLocalToUTC(interp, &fields, objv[2], changeover)) { return TCL_ERROR; } @@ -1238,11 +400,12 @@ ClockConvertlocaltoutcObjCmd( * formatting a date, and populates a dictionary with them. * * Usage: - * ::tcl::clock::GetDateFields seconds timezone changeover + * ::tcl::clock::GetDateFields seconds tzdata changeover * * Parameters: * seconds - Time expressed in seconds from the Posix epoch. - * timezone - Time zone in which time is to be expressed. + * tzdata - Time zone data of the time zone in which time is to be + * expressed. * changeover - Julian Day Number at which the current locale adopted * the Gregorian calendar * @@ -1271,17 +434,15 @@ ClockGetdatefieldsObjCmd( Tcl_Obj *const *literals = data->literals; int changeover; - fields.tzName = NULL; - /* * Check params. */ if (objc != 4) { - Tcl_WrongNumArgs(interp, 1, objv, "seconds timezone changeover"); + Tcl_WrongNumArgs(interp, 1, objv, "seconds tzdata changeover"); return TCL_ERROR; } - if (TclGetWideIntFromObj(interp, objv[1], &fields.seconds) != TCL_OK + if (Tcl_GetWideIntFromObj(interp, objv[1], &fields.seconds) != TCL_OK || TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK) { return TCL_ERROR; } @@ -1296,14 +457,28 @@ ClockGetdatefieldsObjCmd( return TCL_ERROR; } - /* Extract fields */ + /* + * Convert UTC time to local. + */ - if (ClockGetDateFields(clientData, interp, &fields, objv[2], - changeover) != TCL_OK) { + if (ConvertUTCToLocal(interp, &fields, objv[2], changeover) != TCL_OK) { return TCL_ERROR; } - /* Make dict of fields */ + /* + * Extract Julian day. + */ + + fields.julianDay = (int) ((fields.localSeconds + JULIAN_SEC_POSIX_EPOCH) + / SECONDS_PER_DAY); + + /* + * Convert to Julian or Gregorian calendar. + */ + + GetGregorianEraYearDay(&fields, changeover); + GetMonthDay(&fields); + GetYearWeekDay(&fields, changeover); dict = Tcl_NewDictObj(); Tcl_DictObjPut(NULL, dict, literals[LIT_LOCALSECONDS], @@ -1342,58 +517,6 @@ ClockGetdatefieldsObjCmd( /* *---------------------------------------------------------------------- * - * ClockGetDateFields -- - * - * Converts given UTC time (seconds in a TclDateFields structure) - * to local time and determines the values that clock routines will - * use in scanning or formatting a date. - * - * Results: - * Date-time values are stored in structure "fields". - * Returns a standard Tcl result. - * - *---------------------------------------------------------------------- - */ - -int -ClockGetDateFields( - ClientData clientData, /* Client data of the interpreter */ - Tcl_Interp *interp, /* Tcl interpreter */ - TclDateFields *fields, /* Pointer to result fields, where - * fields->seconds contains date to extract */ - Tcl_Obj *timezoneObj, /* Time zone object or NULL for gmt */ - int changeover) /* Julian Day Number */ -{ - /* - * Convert UTC time to local. - */ - - if (ConvertUTCToLocal(clientData, interp, fields, timezoneObj, - changeover) != TCL_OK) { - return TCL_ERROR; - } - - /* - * Extract Julian day. - */ - - fields->julianDay = (int) ((fields->localSeconds + JULIAN_SEC_POSIX_EPOCH) - / SECONDS_PER_DAY); - - /* - * Convert to Julian or Gregorian calendar. - */ - - GetGregorianEraYearDay(fields, changeover); - GetMonthDay(fields); - GetYearWeekDay(fields, changeover); - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * * ClockGetjuliandayfromerayearmonthdayObjCmd -- * * Tcl command that converts a time from era-year-month-day to a Julian @@ -1468,8 +591,6 @@ ClockGetjuliandayfromerayearmonthdayObjCmd( int status; int era = 0; - fields.tzName = NULL; - /* * Check params. */ @@ -1554,8 +675,6 @@ ClockGetjuliandayfromerayearweekdayObjCmd( int status; int era = 0; - fields.tzName = NULL; - /* * Check params. */ @@ -1623,70 +742,18 @@ ClockGetjuliandayfromerayearweekdayObjCmd( static int ConvertLocalToUTC( - ClientData clientData, /* Client data of the interpreter */ Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the time */ - Tcl_Obj *timezoneObj, /* Time zone */ + Tcl_Obj *tzdata, /* Time zone data */ int changeover) /* Julian Day of the Gregorian transition */ { - ClockClientData *dataPtr = clientData; - Tcl_Obj *tzdata; /* Time zone data */ int rowc; /* Number of rows in tzdata */ Tcl_Obj **rowv; /* Pointers to the rows */ - Tcl_WideInt seconds; - - /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */ - if (timezoneObj == dataPtr->GMTSetupTimeZone && dataPtr->GMTSetupTimeZone != NULL) { - fields->seconds = fields->localSeconds; - fields->tzOffset = 0; - return TCL_OK; - } - - /* - * Check cacheable conversion could be used - * (last-period Local2UTC cache within the same TZ) - */ - seconds = fields->localSeconds - dataPtr->Local2UTC.tzOffset; - if ( timezoneObj == dataPtr->Local2UTC.timezoneObj - && ( fields->localSeconds == dataPtr->Local2UTC.localSeconds - || ( seconds >= dataPtr->Local2UTC.rangesVal[0] - && seconds < dataPtr->Local2UTC.rangesVal[1]) - ) - && changeover == dataPtr->Local2UTC.changeover - ) { - /* the same time zone and offset (UTC time inside the last minute) */ - fields->tzOffset = dataPtr->Local2UTC.tzOffset; - fields->seconds = seconds; - return TCL_OK; - } - - /* - * Check cacheable back-conversion could be used - * (last-period UTC2Local cache within the same TZ) - */ - seconds = fields->localSeconds - dataPtr->UTC2Local.tzOffset; - if ( timezoneObj == dataPtr->UTC2Local.timezoneObj - && ( seconds == dataPtr->UTC2Local.seconds - || ( seconds >= dataPtr->UTC2Local.rangesVal[0] - && seconds < dataPtr->UTC2Local.rangesVal[1]) - ) - && changeover == dataPtr->UTC2Local.changeover - ) { - /* the same time zone and offset (UTC time inside the last minute) */ - fields->tzOffset = dataPtr->UTC2Local.tzOffset; - fields->seconds = seconds; - return TCL_OK; - } /* * Unpack the tz data. */ - tzdata = ClockGetTZData(clientData, interp, timezoneObj); - if (tzdata == NULL) { - return TCL_ERROR; - } - if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) { return TCL_ERROR; } @@ -1697,26 +764,10 @@ ConvertLocalToUTC( */ if (rowc == 0) { - dataPtr->Local2UTC.rangesVal[0] = 0; - dataPtr->Local2UTC.rangesVal[1] = 0; - - if (ConvertLocalToUTCUsingC(interp, fields, changeover) != TCL_OK) { - return TCL_ERROR; - }; + return ConvertLocalToUTCUsingC(interp, fields, changeover); } else { - if (ConvertLocalToUTCUsingTable(interp, fields, rowc, rowv, - dataPtr->Local2UTC.rangesVal) != TCL_OK) { - return TCL_ERROR; - }; + return ConvertLocalToUTCUsingTable(interp, fields, rowc, rowv); } - - /* Cache the last conversion */ - Tcl_SetObjRef(dataPtr->Local2UTC.timezoneObj, timezoneObj); - dataPtr->Local2UTC.localSeconds = fields->localSeconds; - dataPtr->Local2UTC.changeover = changeover; - dataPtr->Local2UTC.tzOffset = fields->tzOffset; - - return TCL_OK; } /* @@ -1742,8 +793,7 @@ ConvertLocalToUTCUsingTable( Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Time to convert, with 'seconds' filled in */ int rowc, /* Number of points at which time changes */ - Tcl_Obj *const rowv[], /* Points at which time changes */ - Tcl_WideInt rangesVal[2]) /* Return bounds for time period */ + Tcl_Obj *const rowv[]) /* Points at which time changes */ { Tcl_Obj *row; int cellc; @@ -1767,8 +817,7 @@ ConvertLocalToUTCUsingTable( fields->tzOffset = 0; fields->seconds = fields->localSeconds; while (!found) { - row = LookupLastTransition(interp, fields->seconds, rowc, rowv, - rangesVal); + row = LookupLastTransition(interp, fields->seconds, rowc, rowv); if ((row == NULL) || TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK @@ -1793,41 +842,6 @@ ConvertLocalToUTCUsingTable( } fields->tzOffset = have[i]; fields->seconds = fields->localSeconds - fields->tzOffset; - -#if 0 - /* currently unused, test purposes only */ - /* - * Convert back from UTC, if local times are different - wrong local time - * (local time seems to be in between DST-hole). - */ - if (fields->tzOffset) { - - int corrOffset; - Tcl_WideInt backCompVal; - /* check DST-hole interval contains UTC time */ - TclGetWideIntFromObj(NULL, cellv[0], &backCompVal); - if ( fields->seconds >= backCompVal - fields->tzOffset - && fields->seconds <= backCompVal + fields->tzOffset - ) { - row = LookupLastTransition(interp, fields->seconds, rowc, rowv); - if (row == NULL || - TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK || - TclGetIntFromObj(interp, cellv[1], &corrOffset) != TCL_OK) { - return TCL_ERROR; - } - if (fields->localSeconds != fields->seconds + corrOffset) { - Tcl_Panic("wrong local time %ld by LocalToUTC conversion," - " local time seems to be in between DST-hole", - fields->localSeconds); - /* correcting offset * / - fields->tzOffset -= corrOffset; - fields->seconds += fields->tzOffset; - */ - } - } - } -#endif - return TCL_OK; } @@ -1929,61 +943,20 @@ ConvertLocalToUTCUsingC( *---------------------------------------------------------------------- */ -MODULE_SCOPE int +static int ConvertUTCToLocal( - ClientData clientData, /* Client data of the interpreter */ Tcl_Interp *interp, /* Tcl interpreter */ TclDateFields *fields, /* Fields of the time */ - Tcl_Obj *timezoneObj, /* Time zone */ + Tcl_Obj *tzdata, /* Time zone data */ int changeover) /* Julian Day of the Gregorian transition */ { - ClockClientData *dataPtr = clientData; - Tcl_Obj *tzdata; /* Time zone data */ int rowc; /* Number of rows in tzdata */ Tcl_Obj **rowv; /* Pointers to the rows */ - /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */ - if (timezoneObj == dataPtr->GMTSetupTimeZone - && dataPtr->GMTSetupTimeZone != NULL - && dataPtr->GMTSetupTZData != NULL - ) { - fields->localSeconds = fields->seconds; - fields->tzOffset = 0; - if ( TclListObjGetElements(interp, dataPtr->GMTSetupTZData, &rowc, &rowv) != TCL_OK - || Tcl_ListObjIndex(interp, rowv[0], 3, &fields->tzName) != TCL_OK) { - return TCL_ERROR; - } - Tcl_IncrRefCount(fields->tzName); - return TCL_OK; - } - - /* - * Check cacheable conversion could be used - * (last-period UTC2Local cache within the same TZ) - */ - if ( timezoneObj == dataPtr->UTC2Local.timezoneObj - && ( fields->seconds == dataPtr->UTC2Local.seconds - || ( fields->seconds >= dataPtr->UTC2Local.rangesVal[0] - && fields->seconds < dataPtr->UTC2Local.rangesVal[1]) - ) - && changeover == dataPtr->UTC2Local.changeover - ) { - /* the same time zone and offset (UTC time inside the last minute) */ - Tcl_SetObjRef(fields->tzName, dataPtr->UTC2Local.tzName); - fields->tzOffset = dataPtr->UTC2Local.tzOffset; - fields->localSeconds = fields->seconds + fields->tzOffset; - return TCL_OK; - } - /* * Unpack the tz data. */ - tzdata = ClockGetTZData(clientData, interp, timezoneObj); - if (tzdata == NULL) { - return TCL_ERROR; - } - if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) { return TCL_ERROR; } @@ -1994,26 +967,10 @@ ConvertUTCToLocal( */ if (rowc == 0) { - dataPtr->UTC2Local.rangesVal[0] = 0; - dataPtr->UTC2Local.rangesVal[1] = 0; - - if (ConvertUTCToLocalUsingC(interp, fields, changeover) != TCL_OK) { - return TCL_ERROR; - } + return ConvertUTCToLocalUsingC(interp, fields, changeover); } else { - if (ConvertUTCToLocalUsingTable(interp, fields, rowc, rowv, - dataPtr->UTC2Local.rangesVal) != TCL_OK) { - return TCL_ERROR; - } + return ConvertUTCToLocalUsingTable(interp, fields, rowc, rowv); } - - /* Cache the last conversion */ - Tcl_SetObjRef(dataPtr->UTC2Local.timezoneObj, timezoneObj); - dataPtr->UTC2Local.seconds = fields->seconds; - dataPtr->UTC2Local.changeover = changeover; - dataPtr->UTC2Local.tzOffset = fields->tzOffset; - Tcl_SetObjRef(dataPtr->UTC2Local.tzName, fields->tzName); - return TCL_OK; } /* @@ -2040,8 +997,7 @@ ConvertUTCToLocalUsingTable( TclDateFields *fields, /* Fields of the date */ int rowc, /* Number of rows in the conversion table * (>= 1) */ - Tcl_Obj *const rowv[], /* Rows of the conversion table */ - Tcl_WideInt rangesVal[2]) /* Return bounds for time period */ + Tcl_Obj *const rowv[]) /* Rows of the conversion table */ { Tcl_Obj *row; /* Row containing the current information */ int cellc; /* Count of cells in the row (must be 4) */ @@ -2051,7 +1007,7 @@ ConvertUTCToLocalUsingTable( * Look up the nearest transition time. */ - row = LookupLastTransition(interp, fields->seconds, rowc, rowv, rangesVal); + row = LookupLastTransition(interp, fields->seconds, rowc, rowv); if (row == NULL || TclListObjGetElements(interp, row, &cellc, &cellv) != TCL_OK || TclGetIntFromObj(interp, cellv[1], &fields->tzOffset) != TCL_OK) { @@ -2062,7 +1018,8 @@ ConvertUTCToLocalUsingTable( * Convert the time. */ - Tcl_SetObjRef(fields->tzName, cellv[3]); + fields->tzName = cellv[3]; + Tcl_IncrRefCount(fields->tzName); fields->localSeconds = fields->seconds + fields->tzOffset; return TCL_OK; } @@ -2155,7 +1112,8 @@ ConvertUTCToLocalUsingC( if (diff > 0) { sprintf(buffer+5, "%02d", diff); } - Tcl_SetObjRef(fields->tzName, Tcl_NewStringObj(buffer, -1)); + fields->tzName = Tcl_NewStringObj(buffer, -1); + Tcl_IncrRefCount(fields->tzName); return TCL_OK; } @@ -2173,25 +1131,24 @@ ConvertUTCToLocalUsingC( *---------------------------------------------------------------------- */ -MODULE_SCOPE Tcl_Obj * +static Tcl_Obj * LookupLastTransition( Tcl_Interp *interp, /* Interpreter for error messages */ Tcl_WideInt tick, /* Time from the epoch */ int rowc, /* Number of rows of tzdata */ - Tcl_Obj *const *rowv, /* Rows in tzdata */ - Tcl_WideInt rangesVal[2]) /* Return bounds for time period */ + Tcl_Obj *const *rowv) /* Rows in tzdata */ { - int l = 0; + int l; int u; Tcl_Obj *compObj; - Tcl_WideInt compVal, fromVal = tick, toVal = tick; + Tcl_WideInt compVal; /* * Examine the first row to make sure we're in bounds. */ if (Tcl_ListObjIndex(interp, rowv[0], 0, &compObj) != TCL_OK - || TclGetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { + || Tcl_GetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { return NULL; } @@ -2201,36 +1158,28 @@ LookupLastTransition( */ if (tick < compVal) { - goto done; + return rowv[0]; } /* * Binary-search to find the transition. */ + l = 0; u = rowc-1; while (l < u) { int m = (l + u + 1) / 2; if (Tcl_ListObjIndex(interp, rowv[m], 0, &compObj) != TCL_OK || - TclGetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { + Tcl_GetWideIntFromObj(interp, compObj, &compVal) != TCL_OK) { return NULL; } if (tick >= compVal) { l = m; - fromVal = compVal; } else { u = m-1; - toVal = compVal; } } - -done: - - if (rangesVal) { - rangesVal[0] = fromVal; - rangesVal[1] = toVal; - } return rowv[l]; } @@ -2261,8 +1210,6 @@ GetYearWeekDay( TclDateFields temp; int dayOfFiscalYear; - temp.tzName = NULL; - /* * Find the given date, minus three days, plus one year. That date's * iso8601 year is an upper bound on the ISO8601 year of the given date. @@ -2469,7 +1416,7 @@ GetMonthDay( *---------------------------------------------------------------------- */ -MODULE_SCOPE void +static void GetJulianDayFromEraYearWeekDay( TclDateFields *fields, /* Date to convert */ int changeover) /* Julian Day Number of the Gregorian @@ -2479,8 +1426,6 @@ GetJulianDayFromEraYearWeekDay( * given year */ TclDateFields firstWeek; - firstWeek.tzName = NULL; - /* * Find January 4 in the ISO8601 year, which will always be in week 1. */ @@ -2522,7 +1467,7 @@ GetJulianDayFromEraYearWeekDay( *---------------------------------------------------------------------- */ -MODULE_SCOPE void +static void GetJulianDayFromEraYearMonthDay( TclDateFields *fields, /* Date to convert */ int changeover) /* Gregorian transition date as a Julian Day */ @@ -2576,9 +1521,9 @@ GetJulianDayFromEraYearMonthDay( * See above bug for details. The casts are necessary. */ if (ym1 >= 0) - ym1o4 = ym1 / 4; + ym1o4 = ym1 / 4; else { - ym1o4 = - (int) (((unsigned int) -ym1) / 4); + ym1o4 = - (int) (((unsigned int) -ym1) / 4); } #endif if (ym1 % 4 < 0) { @@ -2618,61 +1563,6 @@ GetJulianDayFromEraYearMonthDay( /* *---------------------------------------------------------------------- * - * GetJulianDayFromEraYearDay -- - * - * Given era, year, and dayOfYear (in TclDateFields), and the - * Gregorian transition date, computes the Julian Day Number. - * - * Results: - * None. - * - * Side effects: - * Stores day number in 'julianDay' - * - *---------------------------------------------------------------------- - */ - - -MODULE_SCOPE void -GetJulianDayFromEraYearDay( - TclDateFields *fields, /* Date to convert */ - int changeover) /* Gregorian transition date as a Julian Day */ -{ - int year, ym1; - - /* Get absolute year number from the civil year */ - if (fields->era == BCE) { - year = 1 - fields->year; - } else { - year = fields->year; - } - - ym1 = year - 1; - - /* Try the Gregorian calendar first. */ - fields->gregorian = 1; - fields->julianDay = - 1721425 - + fields->dayOfYear - + ( 365 * ym1 ) - + ( ym1 / 4 ) - - ( ym1 / 100 ) - + ( ym1 / 400 ); - - /* If the date is before the Gregorian change, use the Julian calendar. */ - - if ( fields->julianDay < changeover ) { - fields->gregorian = 0; - fields->julianDay = - 1721423 - + fields->dayOfYear - + ( 365 * ym1 ) - + ( ym1 / 4 ); - } -} -/* - *---------------------------------------------------------------------- - * * IsGregorianLeapYear -- * * Tests whether a given year is a leap year, in either Julian or @@ -2684,14 +1574,16 @@ GetJulianDayFromEraYearDay( *---------------------------------------------------------------------- */ -MODULE_SCOPE int +static int IsGregorianLeapYear( TclDateFields *fields) /* Date to test */ { - int year = fields->year; + int year; if (fields->era == BCE) { - year = 1 - year; + year = 1 - fields->year; + } else { + year = fields->year; } if (year%4 != 0) { return 0; @@ -2865,7 +1757,7 @@ ClockClicksObjCmd( } break; default: - Tcl_WrongNumArgs(interp, 0, NULL, "clock clicks ?-switch?"); + Tcl_WrongNumArgs(interp, 1, objv, "?-switch?"); return TCL_ERROR; } @@ -2882,7 +1774,8 @@ ClockClicksObjCmd( #endif break; case CLICKS_MICROS: - clicks = TclpGetMicroseconds(); + Tcl_GetTime(&now); + clicks = ((Tcl_WideInt) now.sec * 1000000) + now.usec; break; } @@ -2918,7 +1811,7 @@ ClockMillisecondsObjCmd( Tcl_Time now; if (objc != 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock milliseconds"); + Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } Tcl_GetTime(&now); @@ -2952,1043 +1845,134 @@ ClockMicrosecondsObjCmd( int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { + Tcl_Time now; + if (objc != 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock microseconds"); + Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclpGetMicroseconds())); + Tcl_GetTime(&now); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj( + ((Tcl_WideInt) now.sec * 1000000) + now.usec)); return TCL_OK; } -static inline void -ClockInitFmtScnArgs( - ClientData clientData, - Tcl_Interp *interp, - ClockFmtScnCmdArgs *opts) -{ - memset(opts, 0, sizeof(*opts)); - opts->clientData = clientData; - opts->interp = interp; -} - /* *----------------------------------------------------------------------------- * - * ClockParseFmtScnArgs -- + * ClockParseformatargsObjCmd -- * - * Parses the arguments for [clock scan] and [clock format]. + * Parses the arguments for [clock format]. * * Results: - * Returns a standard Tcl result, and stores parsed options - * (format, the locale, timezone and base) in structure "opts". + * Returns a standard Tcl result, whose value is a four-element list + * comprising the time format, the locale, and the timezone. + * + * This function exists because the loop that parses the [clock format] + * options is a known performance "hot spot", and is implemented in an effort + * to speed that particular code up. * *----------------------------------------------------------------------------- */ -#define CLC_FMT_ARGS (0) -#define CLC_SCN_ARGS (1 << 0) -#define CLC_ADD_ARGS (1 << 1) - static int -ClockParseFmtScnArgs( - register - ClockFmtScnCmdArgs *opts, /* Result vector: format, locale, timezone... */ - TclDateFields *date, /* Extracted date-time corresponding base - * (by scan or add) resp. clockval (by format) */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[], /* Parameter vector */ - int flags /* Flags, differentiates between format, scan, add */ -) { - Tcl_Interp *interp = opts->interp; - ClockClientData *dataPtr = opts->clientData; +ClockParseformatargsObjCmd( + ClientData clientData, /* Client data containing literal pool */ + Tcl_Interp *interp, /* Tcl interpreter */ + int objc, /* Parameter count */ + Tcl_Obj *const objv[]) /* Parameter vector */ +{ + ClockClientData *dataPtr = clientData; + Tcl_Obj **litPtr = dataPtr->literals; + Tcl_Obj *results[3]; /* Format, locale and timezone */ +#define formatObj results[0] +#define localeObj results[1] +#define timezoneObj results[2] int gmtFlag = 0; - static const char *const options[] = { + static const char *const options[] = { /* Command line options expected */ "-format", "-gmt", "-locale", - "-timezone", "-base", NULL - }; + "-timezone", NULL }; enum optionInd { - CLC_ARGS_FORMAT, CLC_ARGS_GMT, CLC_ARGS_LOCALE, - CLC_ARGS_TIMEZONE, CLC_ARGS_BASE + CLOCK_FORMAT_FORMAT, CLOCK_FORMAT_GMT, CLOCK_FORMAT_LOCALE, + CLOCK_FORMAT_TIMEZONE }; int optionIndex; /* Index of an option. */ int saw = 0; /* Flag == 1 if option was seen already. */ + Tcl_WideInt clockVal; /* Clock value - just used to parse. */ int i; - Tcl_WideInt baseVal; /* Base time, expressed in seconds from the Epoch */ - /* clock value (as current base) */ - if ( !(flags & (CLC_SCN_ARGS)) ) { - opts->baseObj = objv[1]; - saw |= (1 << CLC_ARGS_BASE); + /* + * Args consist of a time followed by keyword-value pairs. + */ + + if (objc < 2 || (objc % 2) != 0) { + Tcl_WrongNumArgs(interp, 0, objv, + "clock format clockval ?-format string? " + "?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?"); + Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); + return TCL_ERROR; } /* * Extract values for the keywords. */ + formatObj = litPtr[LIT__DEFAULT_FORMAT]; + localeObj = litPtr[LIT_C]; + timezoneObj = litPtr[LIT__NIL]; for (i = 2; i < objc; i+=2) { - /* bypass integers (offsets) by "clock add" */ - if (flags & CLC_ADD_ARGS) { - Tcl_WideInt num; - if (TclGetWideIntFromObj(NULL, objv[i], &num) == TCL_OK) { - continue; - } - } - /* get option */ - if (Tcl_GetIndexFromObj(interp, objv[i], options, - "option", 0, &optionIndex) != TCL_OK) { - goto badOption; - } - /* if already specified */ - if (saw & (1 << optionIndex)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": doubly present", - TclGetString(objv[i])) - ); - goto badOption; + if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, + &optionIndex) != TCL_OK) { + Tcl_SetErrorCode(interp, "CLOCK", "badOption", + Tcl_GetString(objv[i]), NULL); + return TCL_ERROR; } switch (optionIndex) { - case CLC_ARGS_FORMAT: - if (flags & CLC_ADD_ARGS) { - goto badOptionMsg; - } - opts->formatObj = objv[i+1]; + case CLOCK_FORMAT_FORMAT: + formatObj = objv[i+1]; break; - case CLC_ARGS_GMT: + case CLOCK_FORMAT_GMT: if (Tcl_GetBooleanFromObj(interp, objv[i+1], &gmtFlag) != TCL_OK){ return TCL_ERROR; } break; - case CLC_ARGS_LOCALE: - opts->localeObj = objv[i+1]; - break; - case CLC_ARGS_TIMEZONE: - opts->timezoneObj = objv[i+1]; + case CLOCK_FORMAT_LOCALE: + localeObj = objv[i+1]; break; - case CLC_ARGS_BASE: - if ( !(flags & (CLC_SCN_ARGS)) ) { - goto badOptionMsg; - } - opts->baseObj = objv[i+1]; + case CLOCK_FORMAT_TIMEZONE: + timezoneObj = objv[i+1]; break; } - saw |= (1 << optionIndex); + saw |= 1 << optionIndex; } /* * Check options. */ - if ((saw & (1 << CLC_ARGS_GMT)) - && (saw & (1 << CLC_ARGS_TIMEZONE))) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot use -gmt and -timezone in same call", -1)); - Tcl_SetErrorCode(interp, "CLOCK", "gmtWithTimezone", NULL); - return TCL_ERROR; - } - if (gmtFlag) { - opts->timezoneObj = dataPtr->literals[LIT_GMT]; - } - - /* If time zone not specified use system time zone */ - - if ( opts->timezoneObj == NULL - || TclGetString(opts->timezoneObj) == NULL - || opts->timezoneObj->length == 0 - ) { - opts->timezoneObj = ClockGetSystemTimeZone(opts->clientData, interp); - if (opts->timezoneObj == NULL) { - return TCL_ERROR; - } - } - - /* Setup timezone (normalize object if needed and load TZ on demand) */ - - opts->timezoneObj = ClockSetupTimeZone(opts->clientData, interp, opts->timezoneObj); - if (opts->timezoneObj == NULL) { - return TCL_ERROR; - } - - /* Base (by scan or add) or clock value (by format) */ - - if (opts->baseObj != NULL) { - register Tcl_Obj *baseObj = opts->baseObj; - /* bypass integer recognition if looks like option "-now" */ - if ( - (baseObj->length == 4 && baseObj->bytes && *(baseObj->bytes+1) == 'n') || - TclGetWideIntFromObj(NULL, baseObj, &baseVal) != TCL_OK - ) { - - /* we accept "-now" as current date-time */ - static const char *const nowOpts[] = { - "-now", NULL - }; - int idx; - if (Tcl_GetIndexFromObj(NULL, baseObj, nowOpts, "seconds or -now", - TCL_EXACT, &idx) == TCL_OK - ) { - goto baseNow; - } - - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "expected integer but got \"%s\"", - Tcl_GetString(baseObj))); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "INTEGER", NULL); - i = 1; - goto badOption; - } - /* - * seconds could be an unsigned number that overflowed. Make sure - * that it isn't. - */ - - if (baseObj->typePtr == &tclBignumType) { - Tcl_SetObjResult(interp, dataPtr->literals[LIT_INTEGER_VALUE_TOO_LARGE]); - return TCL_ERROR; - } - - } else { - -baseNow: - { - Tcl_Time now; - Tcl_GetTime(&now); - baseVal = (Tcl_WideInt) now.sec; - } - } - - /* - * Extract year, month and day from the base time for the parser to use as - * defaults - */ - - /* check base fields already cached (by TZ, last-second cache) */ - if ( dataPtr->lastBase.timezoneObj == opts->timezoneObj - && dataPtr->lastBase.Date.seconds == baseVal) { - memcpy(date, &dataPtr->lastBase.Date, ClockCacheableDateFieldsSize); - } else { - /* extact fields from base */ - date->seconds = baseVal; - if (ClockGetDateFields(opts->clientData, interp, date, opts->timezoneObj, - GREGORIAN_CHANGE_DATE) != TCL_OK) { /* TODO - GREGORIAN_CHANGE_DATE should be locale-dependent */ - return TCL_ERROR; - } - /* cache last base */ - memcpy(&dataPtr->lastBase.Date, date, ClockCacheableDateFieldsSize); - Tcl_SetObjRef(dataPtr->lastBase.timezoneObj, opts->timezoneObj); - } - - return TCL_OK; - -badOptionMsg: - - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": unexpected for command \"%s\"", - TclGetString(objv[i]), TclGetString(objv[0])) - ); - -badOption: - - Tcl_SetErrorCode(interp, "CLOCK", "badOption", - i < objc ? Tcl_GetString(objv[i]) : NULL, NULL); - - return TCL_ERROR; -} - -/*---------------------------------------------------------------------- - * - * ClockFormatObjCmd -- , clock format -- - * - * This function is invoked to process the Tcl "clock format" command. - * - * Formats a count of seconds since the Posix Epoch as a time of day. - * - * The 'clock format' command formats times of day for output. Refer - * to the user documentation to see what it does. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -ClockFormatObjCmd( - ClientData clientData, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[]) /* Parameter values */ -{ - ClockClientData *dataPtr = clientData; - - int ret; - ClockFmtScnCmdArgs opts; /* Format, locale, timezone and base */ - DateFormat dateFmt; /* Common structure used for formatting */ - - /* even number of arguments */ - if ((objc & 1) == 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock format clockval|-now " - "?-format string? " - "?-gmt boolean? " - "?-locale LOCALE? ?-timezone ZONE?"); - Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); + if (Tcl_GetWideIntFromObj(interp, objv[1], &clockVal) != TCL_OK) { return TCL_ERROR; } - - memset(&dateFmt, 0, sizeof(dateFmt)); - - /* - * Extract values for the keywords. - */ - - ClockInitFmtScnArgs(clientData, interp, &opts); - ret = ClockParseFmtScnArgs(&opts, &dateFmt.date, objc, objv, - CLC_FMT_ARGS); - if (ret != TCL_OK) { - goto done; - } - - /* Default format */ - if (opts.formatObj == NULL) { - opts.formatObj = dataPtr->literals[LIT__DEFAULT_FORMAT]; - } - - /* Use compiled version of Format - */ - - ret = ClockFormat(&dateFmt, &opts); - -done: - - Tcl_UnsetObjRef(dateFmt.date.tzName); - - if (ret != TCL_OK) { - return ret; - } - - return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockScanObjCmd -- , clock scan -- - * - * This function is invoked to process the Tcl "clock scan" command. - * - * Inputs a count of seconds since the Posix Epoch as a time of day. - * - * The 'clock scan' command scans times of day on input. Refer to the - * user documentation to see what it does. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -ClockScanObjCmd( - ClientData clientData, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[]) /* Parameter values */ -{ - int ret; - ClockFmtScnCmdArgs opts; /* Format, locale, timezone and base */ - DateInfo yy; /* Common structure used for parsing */ - DateInfo *info = &yy; - - /* even number of arguments */ - if ((objc & 1) == 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock scan string " - "?-base seconds? " - "?-format string? " - "?-gmt boolean? " - "?-locale LOCALE? ?-timezone ZONE?"); - Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); + if ((saw & (1 << CLOCK_FORMAT_GMT)) + && (saw & (1 << CLOCK_FORMAT_TIMEZONE))) { + Tcl_SetObjResult(interp, litPtr[LIT_CANNOT_USE_GMT_AND_TIMEZONE]); + Tcl_SetErrorCode(interp, "CLOCK", "gmtWithTimezone", NULL); return TCL_ERROR; } - - ClockInitDateInfo(&yy); - - /* - * Extract values for the keywords. - */ - - ClockInitFmtScnArgs(clientData, interp, &opts); - ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv, - CLC_SCN_ARGS); - if (ret != TCL_OK) { - goto done; - } - - /* seconds are in localSeconds (relative base date), so reset time here */ - yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; - - /* If free scan */ - if (opts.formatObj == NULL) { - /* Use compiled version of FreeScan - */ - - /* [SB] TODO: Perhaps someday we'll localize the legacy code. Right now, it's not localized. */ - if (opts.localeObj != NULL) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("legacy [clock scan] does not support -locale", -1)); - Tcl_SetErrorCode(interp, "CLOCK", "flagWithLegacyFormat", NULL); - return TCL_ERROR; - } - ret = ClockFreeScan(&yy, objv[1], &opts); - } - else { - /* Use compiled version of Scan - */ - - ret = ClockScan(&yy, objv[1], &opts); - } - - /* Convert date info structure into UTC seconds */ - - if (ret == TCL_OK) { - ret = ClockScanCommit(clientData, &yy, &opts); - } - -done: - - Tcl_UnsetObjRef(yy.date.tzName); - - if (ret != TCL_OK) { - return ret; - } - - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(yy.date.seconds)); - return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockScanCommit -- - * - * Converts date info structure into UTC seconds. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -ClockScanCommit( - ClientData clientData, /* Client data containing literal pool */ - register DateInfo *info, /* Clock scan info structure */ - register - ClockFmtScnCmdArgs *opts) /* Format, locale, timezone and base */ -{ - /* If needed assemble julianDay using year, month, etc. */ - if (info->flags & CLF_ASSEMBLE_JULIANDAY) { - if ((info->flags & CLF_ISO8601)) { - GetJulianDayFromEraYearWeekDay(&yydate, GREGORIAN_CHANGE_DATE); - } - else - if (!(info->flags & CLF_DAYOFYEAR)) { - GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); - } else { - GetJulianDayFromEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); - } - } - - /* some overflow checks, if not extended */ - if (!(opts->flags & CLF_EXTENDED)) { - if (yydate.julianDay > 5373484) { - Tcl_SetObjResult(opts->interp, Tcl_NewStringObj( - "requested date too large to represent", -1)); - Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", NULL); - return TCL_ERROR; - } - } - - /* Local seconds to UTC (stored in yydate.seconds) */ - - if (info->flags & (CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY)) { - yydate.localSeconds = - -210866803200L - + ( SECONDS_PER_DAY * (Tcl_WideInt)yydate.julianDay ) - + ( yySeconds % SECONDS_PER_DAY ); - } - - if (info->flags & (CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY|CLF_LOCALSEC)) { - if (ConvertLocalToUTC(clientData, opts->interp, &yydate, opts->timezoneObj, - GREGORIAN_CHANGE_DATE) != TCL_OK) { - return TCL_ERROR; - } - } - - /* Increment UTC seconds with relative time */ - - yydate.seconds += yyRelSeconds; - - return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockFreeScan -- - * - * Used by ClockScanObjCmd for free scanning without format. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -ClockFreeScan( - register - DateInfo *info, /* Date fields used for parsing & converting - * simultaneously a yy-parse structure of the - * TclClockFreeScan */ - Tcl_Obj *strObj, /* String containing the time to scan */ - ClockFmtScnCmdArgs *opts) /* Command options */ -{ - Tcl_Interp *interp = opts->interp; - ClockClientData *dataPtr = opts->clientData; - - int ret = TCL_ERROR; - - /* - * Parse the date. The parser will fill a structure "info" with date, - * time, time zone, relative month/day/seconds, relative weekday, ordinal - * month. - * Notice that many yy-defines point to values in the "info" or "date" - * structure, e. g. yySeconds -> info->date.secondOfDay or - * yySeconds -> info->date.month (same as yydate.month) - */ - yyInput = Tcl_GetString(strObj); - - if (TclClockFreeScan(interp, info) != TCL_OK) { - Tcl_Obj *msg = Tcl_NewObj(); - Tcl_AppendPrintfToObj(msg, "unable to convert date-time string \"%s\": %s", - Tcl_GetString(strObj), TclGetString(Tcl_GetObjResult(interp))); - Tcl_SetObjResult(interp, msg); - goto done; - } - - /* - * If the caller supplied a date in the string, update the date with - * the value. If the caller didn't specify a time with the date, default to - * midnight. - */ - - if (yyHaveDate) { - if (yyYear < 100) { - if (yyYear >= dataPtr->yearOfCenturySwitch) { - yyYear -= 100; - } - yyYear += dataPtr->currentYearCentury; - } - yydate.era = CE; - if (yyHaveTime == 0) { - yyHaveTime = -1; - } - info->flags |= CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS; - } - - /* - * If the caller supplied a time zone in the string, make it into a time - * zone indicator of +-hhmm and setup this time zone. - */ - - if (yyHaveZone) { - Tcl_Obj *tzObjStor = NULL; - int minEast = -yyTimezone; - int dstFlag = 1 - yyDSTmode; - tzObjStor = ClockFormatNumericTimeZone( - 60 * minEast + 3600 * dstFlag); - Tcl_IncrRefCount(tzObjStor); - - opts->timezoneObj = ClockSetupTimeZone(dataPtr, interp, tzObjStor); - - Tcl_DecrRefCount(tzObjStor); - if (opts->timezoneObj == NULL) { - goto done; - } - - // Tcl_SetObjRef(yydate.tzName, opts->timezoneObj); - - info->flags |= CLF_ASSEMBLE_SECONDS; - } - - /* - * Assemble date, time, zone into seconds-from-epoch - */ - - if (yyHaveTime == -1) { - yySeconds = 0; - info->flags |= CLF_ASSEMBLE_SECONDS; - } - else - if (yyHaveTime) { - yySeconds = ToSeconds(yyHour, yyMinutes, - yySeconds, yyMeridian); - info->flags |= CLF_ASSEMBLE_SECONDS; - } - else - if ( (yyHaveDay && !yyHaveDate) - || yyHaveOrdinalMonth - || ( yyHaveRel - && ( yyRelMonth != 0 - || yyRelDay != 0 ) ) - ) { - yySeconds = 0; - info->flags |= CLF_ASSEMBLE_SECONDS; - } - else { - yySeconds = yydate.localSeconds % SECONDS_PER_DAY; - } - - /* - * Do relative times - */ - - ret = ClockCalcRelTime(info, opts); - - /* Free scanning completed - date ready */ - -done: - - return ret; -} - -/*---------------------------------------------------------------------- - * - * ClockCalcRelTime -- - * - * Used for calculating of relative times. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ -int -ClockCalcRelTime( - register - DateInfo *info, /* Date fields used for converting */ - ClockFmtScnCmdArgs *opts) /* Command options */ -{ - /* - * Because some calculations require in-between conversion of the - * julian day, we can repeat this processing multiple times - */ -repeat_rel: - - if (yyHaveRel) { - - /* - * Relative conversion normally possible in UTC time only, because - * of possible wrong local time increment if ignores in-between DST-hole. - * (see test-cases clock-34.53, clock-34.54). - * So increment date in julianDay, but time inside day in UTC (seconds). - */ - - /* add months (or years in months) */ - - if (yyRelMonth != 0) { - int m, h; - - /* if needed extract year, month, etc. again */ - if (info->flags & CLF_ASSEMBLE_DATE) { - GetGregorianEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); - GetMonthDay(&yydate); - GetYearWeekDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_DATE; - } - - /* add the requisite number of months */ - yyMonth += yyRelMonth - 1; - yyYear += yyMonth / 12; - m = yyMonth % 12; - yyMonth = m + 1; - - /* if the day doesn't exist in the current month, repair it */ - h = hath[IsGregorianLeapYear(&yydate)][m]; - if (yyDay > h) { - yyDay = h; - } - - /* on demand (lazy) assemble julianDay using new year, month, etc. */ - info->flags |= CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS; - - yyRelMonth = 0; - } - - /* add days (or other parts aligned to days) */ - if (yyRelDay) { - - /* assemble julianDay using new year, month, etc. */ - if (info->flags & CLF_ASSEMBLE_JULIANDAY) { - GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_JULIANDAY; - } - yydate.julianDay += yyRelDay; - - /* julianDay was changed, on demand (lazy) extract year, month, etc. again */ - info->flags |= CLF_ASSEMBLE_DATE|CLF_ASSEMBLE_SECONDS; - - yyRelDay = 0; - } - - /* relative time (seconds), if exceeds current date, do the day conversion and - * leave rest of the increment in yyRelSeconds to add it hereafter in UTC seconds */ - if (yyRelSeconds) { - int newSecs = yySeconds + yyRelSeconds; - - /* if seconds increment outside of current date, increment day */ - if (newSecs / SECONDS_PER_DAY != yySeconds / SECONDS_PER_DAY) { - - yyRelDay += newSecs / SECONDS_PER_DAY; - yySeconds = 0; - yyRelSeconds = newSecs % SECONDS_PER_DAY; - - goto repeat_rel; - } - } - - yyHaveRel = 0; - } - - /* - * Do relative (ordinal) month - */ - - if (yyHaveOrdinalMonth) { - int monthDiff; - - /* if needed extract year, month, etc. again */ - if (info->flags & CLF_ASSEMBLE_DATE) { - GetGregorianEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); - GetMonthDay(&yydate); - GetYearWeekDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_DATE; - } - - if (yyMonthOrdinalIncr > 0) { - monthDiff = yyMonthOrdinal - yyMonth; - if (monthDiff <= 0) { - monthDiff += 12; - } - yyMonthOrdinalIncr--; - } else { - monthDiff = yyMonth - yyMonthOrdinal; - if (monthDiff >= 0) { - monthDiff -= 12; - } - yyMonthOrdinalIncr++; - } - - /* process it further via relative times */ - yyHaveRel++; - yyYear += yyMonthOrdinalIncr; - yyRelMonth += monthDiff; - yyHaveOrdinalMonth = 0; - - info->flags |= CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS; - - goto repeat_rel; + if (gmtFlag) { + timezoneObj = litPtr[LIT_GMT]; } /* - * Do relative weekday + * Return options as a list. */ - if (yyHaveDay && !yyHaveDate) { - - /* if needed assemble julianDay now */ - if (info->flags & CLF_ASSEMBLE_JULIANDAY) { - GetJulianDayFromEraYearMonthDay(&yydate, GREGORIAN_CHANGE_DATE); - info->flags &= ~CLF_ASSEMBLE_JULIANDAY; - } - - yydate.era = CE; - yydate.julianDay = WeekdayOnOrBefore(yyDayNumber, yydate.julianDay + 6) - + 7 * yyDayOrdinal; - if (yyDayOrdinal > 0) { - yydate.julianDay -= 7; - } - info->flags |= CLF_ASSEMBLE_DATE|CLF_ASSEMBLE_SECONDS; - } - + Tcl_SetObjResult(interp, Tcl_NewListObj(3, results)); return TCL_OK; -} - -/*---------------------------------------------------------------------- - * - * ClockWeekdaysOffs -- - * - * Get offset in days for the number of week days corresponding the - * given day of week (skipping Saturdays and Sundays). - * - * - * Results: - * Returns a day increment adjusted the given weekdays - * - *---------------------------------------------------------------------- - */ - -static inline int -ClockWeekdaysOffs( - register int dayOfWeek, - register int offs) -{ - register int weeks, resDayOfWeek; - - /* offset in days */ - weeks = offs / 5; - offs = offs % 5; - /* compiler fix for negative offs - wrap (0, -1) -> (-1, 4) */ - if (offs < 0) { - weeks--; - offs = 5 + offs; - } - offs += 7 * weeks; - - /* resulting day of week */ - { - register int day = (offs % 7); - /* compiler fix for negative offs - wrap (0, -1) -> (-1, 6) */ - if (day < 0) { - day = 7 + day; - } - resDayOfWeek = dayOfWeek + day; - } - - /* adjust if we start from a weekend */ - if (dayOfWeek > 5) { - int adj = 5 - dayOfWeek; - offs += adj; - resDayOfWeek += adj; - } - - /* adjust if we end up on a weekend */ - if (resDayOfWeek > 5) { - offs += 2; - } - - return offs; -} - - - -/*---------------------------------------------------------------------- - * - * ClockAddObjCmd -- , clock add -- - * - * Adds an offset to a given time. - * - * Refer to the user documentation to see what it exactly does. - * - * Syntax: - * clock add clockval ?count unit?... ?-option value? - * - * Parameters: - * clockval -- Starting time value - * count -- Amount of a unit of time to add - * unit -- Unit of time to add, must be one of: - * years year months month weeks week - * days day hours hour minutes minute - * seconds second - * - * Options: - * -gmt BOOLEAN - * Flag synonymous with '-timezone :GMT' - * -timezone ZONE - * Name of the time zone in which calculations are to be done. - * -locale NAME - * Name of the locale in which calculations are to be done. - * Used to determine the Gregorian change date. - * - * Results: - * Returns a standard Tcl result with the given time adjusted - * by the given offset(s) in order. - * - * Notes: - * It is possible that adding a number of months or years will adjust the - * day of the month as well. For instance, the time at one month after - * 31 January is either 28 or 29 February, because February has fewer - * than 31 days. - * - *---------------------------------------------------------------------- - */ - -int -ClockAddObjCmd( - ClientData clientData, /* Client data containing literal pool */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Parameter count */ - Tcl_Obj *const objv[]) /* Parameter values */ -{ - ClockClientData *dataPtr = clientData; - int ret; - ClockFmtScnCmdArgs opts; /* Format, locale, timezone and base */ - DateInfo yy; /* Common structure used for parsing */ - DateInfo *info = &yy; - - /* add "week" to units also (because otherwise ambiguous) */ - static const char *const units[] = { - "years", "months", "week", "weeks", - "days", "weekdays", - "hours", "minutes", "seconds", - NULL - }; - enum unitInd { - CLC_ADD_YEARS, CLC_ADD_MONTHS, CLC_ADD_WEEK, CLC_ADD_WEEKS, - CLC_ADD_DAYS, CLC_ADD_WEEKDAYS, - CLC_ADD_HOURS, CLC_ADD_MINUTES, CLC_ADD_SECONDS - }; - int unitIndex; /* Index of an option. */ - int i; - Tcl_WideInt offs; - - /* even number of arguments */ - if ((objc & 1) == 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock add clockval|-now ?number units?..." - "?-gmt boolean? " - "?-locale LOCALE? ?-timezone ZONE?"); - Tcl_SetErrorCode(interp, "CLOCK", "wrongNumArgs", NULL); - return TCL_ERROR; - } - - ClockInitDateInfo(&yy); - - /* - * Extract values for the keywords. - */ - - ClockInitFmtScnArgs(clientData, interp, &opts); - ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv, - CLC_ADD_ARGS); - if (ret != TCL_OK) { - goto done; - } - - /* time together as seconds of the day */ - yySeconds = yydate.localSeconds % SECONDS_PER_DAY; - /* seconds are in localSeconds (relative base date), so reset time here */ - yyHour = 0; yyMinutes = 0; yyMeridian = MER24; - - ret = TCL_ERROR; - - /* - * Find each offset and process date increment - */ - - for (i = 2; i < objc; i+=2) { - /* bypass not integers (options, allready processed above) */ - if (TclGetWideIntFromObj(NULL, objv[i], &offs) != TCL_OK) { - continue; - } - if (objv[i]->typePtr == &tclBignumType) { - Tcl_SetObjResult(interp, dataPtr->literals[LIT_INTEGER_VALUE_TOO_LARGE]); - goto done; - } - /* get unit */ - if (Tcl_GetIndexFromObj(interp, objv[i+1], units, "unit", 0, - &unitIndex) != TCL_OK) { - goto done; - } - - /* nothing to do if zero quantity */ - if (!offs) { - continue; - } - - /* if in-between conversion needed (already have relative date/time), - * correct date info, because the date may be changed, - * so refresh it now */ - - if ( yyHaveRel - && ( unitIndex == CLC_ADD_WEEKDAYS - /* some months can be shorter as another */ - || yyRelMonth || yyRelDay - /* day changed */ - || yySeconds + yyRelSeconds > SECONDS_PER_DAY - || yySeconds + yyRelSeconds < 0 - ) - ) { - if (ClockCalcRelTime(info, &opts) != TCL_OK) { - goto done; - } - } - - /* process increment by offset + unit */ - yyHaveRel++; - switch (unitIndex) { - case CLC_ADD_YEARS: - yyRelMonth += offs * 12; - break; - case CLC_ADD_MONTHS: - yyRelMonth += offs; - break; - case CLC_ADD_WEEK: - case CLC_ADD_WEEKS: - yyRelDay += offs * 7; - break; - case CLC_ADD_DAYS: - yyRelDay += offs; - break; - case CLC_ADD_WEEKDAYS: - /* add number of week days (skipping Saturdays and Sundays) - * to a relative days value. */ - offs = ClockWeekdaysOffs(yy.date.dayOfWeek, offs); - yyRelDay += offs; - break; - case CLC_ADD_HOURS: - yyRelSeconds += offs * 60 * 60; - break; - case CLC_ADD_MINUTES: - yyRelSeconds += offs * 60; - break; - case CLC_ADD_SECONDS: - yyRelSeconds += offs; - break; - } - } - - /* - * Do relative times (if not yet already processed interim): - */ - - if (yyHaveRel) { - if (ClockCalcRelTime(info, &opts) != TCL_OK) { - goto done; - } - } - - /* Convert date info structure into UTC seconds */ - - ret = ClockScanCommit(clientData, &yy, &opts); - -done: - - Tcl_UnsetObjRef(yy.date.tzName); - - if (ret != TCL_OK) { - return ret; - } - - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(yy.date.seconds)); - return TCL_OK; +#undef timezoneObj +#undef localeObj +#undef formatObj } /*---------------------------------------------------------------------- @@ -4019,7 +2003,7 @@ ClockSecondsObjCmd( Tcl_Time now; if (objc != 1) { - Tcl_WrongNumArgs(interp, 0, NULL, "clock seconds"); + Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } Tcl_GetTime(&now); @@ -4030,7 +2014,7 @@ ClockSecondsObjCmd( /* *---------------------------------------------------------------------- * - * TzsetGetEpoch --, TzsetIfNecessary -- + * TzsetIfNecessary -- * * Calls the tzset() library function if the contents of the TZ * environment variable has changed. @@ -4044,37 +2028,15 @@ ClockSecondsObjCmd( *---------------------------------------------------------------------- */ -static unsigned long -TzsetGetEpoch(void) +static void +TzsetIfNecessary(void) { - static char* tzWas = INT2PTR(-1); /* Previous value of TZ, protected by - * clockMutex. */ - static long tzLastRefresh = 0; /* Used for latency before next refresh */ - static size_t tzWasEpoch = 0; /* Epoch, signals that TZ changed */ - static size_t tzEnvEpoch = 0; /* Last env epoch, for faster signaling, - that TZ changed via TCL */ - - const char *tzIsNow; /* Current value of TZ */ + static char* tzWas = INT2PTR(-1); /* Previous value of TZ, protected by + * clockMutex. */ + const char *tzIsNow; /* Current value of TZ */ - /* - * Prevent performance regression on some platforms by resolving of system time zone: - * small latency for check whether environment was changed (once per second) - * no latency if environment was chaned with tcl-env (compare both epoch values) - */ - Tcl_Time now; - Tcl_GetTime(&now); - if (now.sec == tzLastRefresh && tzEnvEpoch == TclEnvEpoch) { - return tzWasEpoch; - } - tzEnvEpoch = TclEnvEpoch; - tzLastRefresh = now.sec; - - /* check in lock */ Tcl_MutexLock(&clockMutex); - tzIsNow = getenv("TCL_TZ"); - if (tzIsNow == NULL) { - tzIsNow = getenv("TZ"); - } + tzIsNow = getenv("TZ"); if (tzIsNow != NULL && (tzWas == NULL || tzWas == INT2PTR(-1) || strcmp(tzIsNow, tzWas) != 0)) { tzset(); @@ -4083,22 +2045,42 @@ TzsetGetEpoch(void) } tzWas = ckalloc(strlen(tzIsNow) + 1); strcpy(tzWas, tzIsNow); - tzWasEpoch++; } else if (tzIsNow == NULL && tzWas != NULL) { tzset(); if (tzWas != INT2PTR(-1)) ckfree(tzWas); tzWas = NULL; - tzWasEpoch++; } Tcl_MutexUnlock(&clockMutex); - - return tzWasEpoch; } + +/* + *---------------------------------------------------------------------- + * + * ClockDeleteCmdProc -- + * + * Remove a reference to the clock client data, and clean up memory + * when it's all gone. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ static void -TzsetIfNecessary(void) +ClockDeleteCmdProc( + ClientData clientData) /* Opaque pointer to the client data */ { - TzsetGetEpoch(); + ClockClientData *data = clientData; + int i; + + if (data->refCount-- <= 1) { + for (i = 0; i < LIT__END; ++i) { + Tcl_DecrRefCount(data->literals[i]); + } + ckfree(data->literals); + ckfree(data); + } } /* diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c deleted file mode 100644 index 5de05d0..0000000 --- a/generic/tclClockFmt.c +++ /dev/null @@ -1,3137 +0,0 @@ -/* - * tclClockFmt.c -- - * - * Contains the date format (and scan) routines. This code is back-ported - * from the time and date facilities of tclSE engine, by Serg G. Brester. - * - * Copyright (c) 2015 by Sergey G. Brester aka sebres. All rights reserved. - * - * See the file "license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. - */ - -#include "tclInt.h" -#include "tclStrIdxTree.h" -#include "tclDate.h" - -/* - * Miscellaneous forward declarations and functions used within this file - */ - -static void -ClockFmtObj_DupInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); -static void -ClockFmtObj_FreeInternalRep(Tcl_Obj *objPtr); -static int -ClockFmtObj_SetFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); -static void -ClockFmtObj_UpdateString(Tcl_Obj *objPtr); - - -TCL_DECLARE_MUTEX(ClockFmtMutex); /* Serializes access to common format list. */ - -static void ClockFmtScnStorageDelete(ClockFmtScnStorage *fss); - -static void ClockFrmScnFinalize(ClientData clientData); - -/* - * Clock scan and format facilities. - */ - -/* - *---------------------------------------------------------------------- - * - * _str2int -- , _str2wideInt -- - * - * Fast inline-convertion of string to signed int or wide int by given - * start/end. - * - * The given string should contain numbers chars only (because already - * pre-validated within parsing routines) - * - * Results: - * Returns a standard Tcl result. - * TCL_OK - by successful conversion, TCL_ERROR by (wide) int overflow - * - *---------------------------------------------------------------------- - */ - -static inline int -_str2int( - int *out, - register - const char *p, - const char *e, - int sign) -{ - register int val = 0, prev = 0; - if (sign >= 0) { - while (p < e) { - val = val * 10 + (*p++ - '0'); - if (val < prev) { - return TCL_ERROR; - } - prev = val; - } - } else { - while (p < e) { - val = val * 10 - (*p++ - '0'); - if (val > prev) { - return TCL_ERROR; - } - prev = val; - } - } - *out = val; - return TCL_OK; -} - -static inline int -_str2wideInt( - Tcl_WideInt *out, - register - const char *p, - const char *e, - int sign) -{ - register Tcl_WideInt val = 0, prev = 0; - if (sign >= 0) { - while (p < e) { - val = val * 10 + (*p++ - '0'); - if (val < prev) { - return TCL_ERROR; - } - prev = val; - } - } else { - while (p < e) { - val = val * 10 - (*p++ - '0'); - if (val > prev) { - return TCL_ERROR; - } - prev = val; - } - } - *out = val; - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * _itoaw -- , _witoaw -- - * - * Fast inline-convertion of signed int or wide int to string, using - * given padding with specified padchar and width (or without padding). - * - * This is a very fast replacement for sprintf("%02d"). - * - * Results: - * Returns position in buffer after end of conversion result. - * - *---------------------------------------------------------------------- - */ - -static inline char * -_itoaw( - char *buf, - register int val, - char padchar, - unsigned short int width) -{ - register char *p; - static int wrange[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; - - /* positive integer */ - - if (val >= 0) - { - /* check resp. recalculate width */ - while (width <= 9 && val >= wrange[width]) { - width++; - } - /* number to string backwards */ - p = buf + width; - *p-- = '\0'; - do { - register char c = (val % 10); val /= 10; - *p-- = '0' + c; - } while (val > 0); - /* fulling with pad-char */ - while (p >= buf) { - *p-- = padchar; - } - - return buf + width; - } - /* negative integer */ - - if (!width) width++; - /* check resp. recalculate width (regarding sign) */ - width--; - while (width <= 9 && val <= -wrange[width]) { - width++; - } - width++; - /* number to string backwards */ - p = buf + width; - *p-- = '\0'; - /* differentiate platforms with -1 % 10 == 1 and -1 % 10 == -1 */ - if (-1 % 10 == -1) { - do { - register char c = (val % 10); val /= 10; - *p-- = '0' - c; - } while (val < 0); - } else { - do { - register char c = (val % 10); val /= 10; - *p-- = '0' + c; - } while (val < 0); - } - /* sign by 0 padding */ - if (padchar != '0') { *p-- = '-'; } - /* fulling with pad-char */ - while (p >= buf + 1) { - *p-- = padchar; - } - /* sign by non 0 padding */ - if (padchar == '0') { *p = '-'; } - - return buf + width; -} - -static inline char * -_witoaw( - char *buf, - register Tcl_WideInt val, - char padchar, - unsigned short int width) -{ - register char *p; - static int wrange[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; - - /* positive integer */ - - if (val >= 0) - { - /* check resp. recalculate width */ - if (val >= 10000000000L) { - Tcl_WideInt val2; - val2 = val / 10000000000L; - while (width <= 9 && val2 >= wrange[width]) { - width++; - } - width += 10; - } else { - while (width <= 9 && val >= wrange[width]) { - width++; - } - } - /* number to string backwards */ - p = buf + width; - *p-- = '\0'; - do { - register char c = (val % 10); val /= 10; - *p-- = '0' + c; - } while (val > 0); - /* fulling with pad-char */ - while (p >= buf) { - *p-- = padchar; - } - - return buf + width; - } - - /* negative integer */ - - if (!width) width++; - /* check resp. recalculate width (regarding sign) */ - width--; - if (val <= 10000000000L) { - Tcl_WideInt val2; - val2 = val / 10000000000L; - while (width <= 9 && val2 <= -wrange[width]) { - width++; - } - width += 10; - } else { - while (width <= 9 && val <= -wrange[width]) { - width++; - } - } - width++; - /* number to string backwards */ - p = buf + width; - *p-- = '\0'; - /* differentiate platforms with -1 % 10 == 1 and -1 % 10 == -1 */ - if (-1 % 10 == -1) { - do { - register char c = (val % 10); val /= 10; - *p-- = '0' - c; - } while (val < 0); - } else { - do { - register char c = (val % 10); val /= 10; - *p-- = '0' + c; - } while (val < 0); - } - /* sign by 0 padding */ - if (padchar != '0') { *p-- = '-'; } - /* fulling with pad-char */ - while (p >= buf + 1) { - *p-- = padchar; - } - /* sign by non 0 padding */ - if (padchar == '0') { *p = '-'; } - - return buf + width; -} - -/* - * Global GC as LIFO for released scan/format object storages. - * - * Used to holds last released CLOCK_FMT_SCN_STORAGE_GC_SIZE formats - * (after last reference from Tcl-object will be removed). This is helpful - * to avoid continuous (re)creation and compiling by some dynamically resp. - * variable format objects, that could be often reused. - * - * As long as format storage is used resp. belongs to GC, it takes place in - * FmtScnHashTable also. - */ - -#if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 - -static struct { - ClockFmtScnStorage *stackPtr; - ClockFmtScnStorage *stackBound; - unsigned int count; -} ClockFmtScnStorage_GC = {NULL, NULL, 0}; - -/* - *---------------------------------------------------------------------- - * - * ClockFmtScnStorageGC_In -- - * - * Adds an format storage object to GC. - * - * If current GC is full (size larger as CLOCK_FMT_SCN_STORAGE_GC_SIZE) - * this removes last unused storage at begin of GC stack (LIFO). - * - * Assumes caller holds the ClockFmtMutex. - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ - -static inline void -ClockFmtScnStorageGC_In(ClockFmtScnStorage *entry) -{ - /* add new entry */ - TclSpliceIn(entry, ClockFmtScnStorage_GC.stackPtr); - if (ClockFmtScnStorage_GC.stackBound == NULL) { - ClockFmtScnStorage_GC.stackBound = entry; - } - ClockFmtScnStorage_GC.count++; - - /* if GC ist full */ - if (ClockFmtScnStorage_GC.count > CLOCK_FMT_SCN_STORAGE_GC_SIZE) { - - /* GC stack is LIFO: delete first inserted entry */ - ClockFmtScnStorage *delEnt = ClockFmtScnStorage_GC.stackBound; - ClockFmtScnStorage_GC.stackBound = delEnt->prevPtr; - TclSpliceOut(delEnt, ClockFmtScnStorage_GC.stackPtr); - ClockFmtScnStorage_GC.count--; - delEnt->prevPtr = delEnt->nextPtr = NULL; - /* remove it now */ - ClockFmtScnStorageDelete(delEnt); - } -} - -/* - *---------------------------------------------------------------------- - * - * ClockFmtScnStorage_GC_Out -- - * - * Restores (for reusing) given format storage object from GC. - * - * Assumes caller holds the ClockFmtMutex. - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ - -static inline void -ClockFmtScnStorage_GC_Out(ClockFmtScnStorage *entry) -{ - TclSpliceOut(entry, ClockFmtScnStorage_GC.stackPtr); - ClockFmtScnStorage_GC.count--; - if (ClockFmtScnStorage_GC.stackBound == entry) { - ClockFmtScnStorage_GC.stackBound = entry->prevPtr; - } - entry->prevPtr = entry->nextPtr = NULL; -} - -#endif - - -/* - * Global format storage hash table of type ClockFmtScnStorageHashKeyType - * (contains list of scan/format object storages, shared across all threads). - * - * Used for fast searching by format string. - */ -static Tcl_HashTable FmtScnHashTable; -static int initialized = 0; - -/* - * Wrappers between pointers to hash entry and format storage object - */ -static inline Tcl_HashEntry * -HashEntry4FmtScn(ClockFmtScnStorage *fss) { - return (Tcl_HashEntry*)(fss + 1); -}; -static inline ClockFmtScnStorage * -FmtScn4HashEntry(Tcl_HashEntry *hKeyPtr) { - return (ClockFmtScnStorage*)(((char*)hKeyPtr) - sizeof(ClockFmtScnStorage)); -}; - -/* - *---------------------------------------------------------------------- - * - * ClockFmtScnStorageAllocProc -- - * - * Allocate space for a hash entry containing format storage together - * with the string key. - * - * Results: - * The return value is a pointer to the created entry. - * - *---------------------------------------------------------------------- - */ - -static Tcl_HashEntry * -ClockFmtScnStorageAllocProc( - Tcl_HashTable *tablePtr, /* Hash table. */ - void *keyPtr) /* Key to store in the hash table entry. */ -{ - ClockFmtScnStorage *fss; - - const char *string = (const char *) keyPtr; - Tcl_HashEntry *hPtr; - unsigned int size, - allocsize = sizeof(ClockFmtScnStorage) + sizeof(Tcl_HashEntry); - - allocsize += (size = strlen(string) + 1); - if (size > sizeof(hPtr->key)) { - allocsize -= sizeof(hPtr->key); - } - - fss = ckalloc(allocsize); - - /* initialize */ - memset(fss, 0, sizeof(*fss)); - - hPtr = HashEntry4FmtScn(fss); - memcpy(&hPtr->key.string, string, size); - hPtr->clientData = 0; /* currently unused */ - - return hPtr; -} - -/* - *---------------------------------------------------------------------- - * - * ClockFmtScnStorageFreeProc -- - * - * Free format storage object and space of given hash entry. - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -ClockFmtScnStorageFreeProc( - Tcl_HashEntry *hPtr) -{ - ClockFmtScnStorage *fss = FmtScn4HashEntry(hPtr); - - if (fss->scnTok != NULL) { - ckfree(fss->scnTok); - fss->scnTok = NULL; - fss->scnTokC = 0; - } - if (fss->fmtTok != NULL) { - ckfree(fss->fmtTok); - fss->fmtTok = NULL; - fss->fmtTokC = 0; - } - - ckfree(fss); -} - -/* - *---------------------------------------------------------------------- - * - * ClockFmtScnStorageDelete -- - * - * Delete format storage object. - * - * Results: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -ClockFmtScnStorageDelete(ClockFmtScnStorage *fss) { - Tcl_HashEntry *hPtr = HashEntry4FmtScn(fss); - /* - * This will delete a hash entry and call "ckfree" for storage self, if - * some additionally handling required, freeEntryProc can be used instead - */ - Tcl_DeleteHashEntry(hPtr); -} - - -/* - * Derivation of tclStringHashKeyType with another allocEntryProc - */ - -static Tcl_HashKeyType ClockFmtScnStorageHashKeyType; - - -/* - * Type definition of clock-format tcl object type. - */ - -Tcl_ObjType ClockFmtObjType = { - "clock-format", /* name */ - ClockFmtObj_FreeInternalRep, /* freeIntRepProc */ - ClockFmtObj_DupInternalRep, /* dupIntRepProc */ - ClockFmtObj_UpdateString, /* updateStringProc */ - ClockFmtObj_SetFromAny /* setFromAnyProc */ -}; - -#define ObjClockFmtScn(objPtr) \ - (*((ClockFmtScnStorage **)&(objPtr)->internalRep.twoPtrValue.ptr1)) - -#define ObjLocFmtKey(objPtr) \ - (*((Tcl_Obj **)&(objPtr)->internalRep.twoPtrValue.ptr2)) - -static void -ClockFmtObj_DupInternalRep(srcPtr, copyPtr) - Tcl_Obj *srcPtr; - Tcl_Obj *copyPtr; -{ - ClockFmtScnStorage *fss = ObjClockFmtScn(srcPtr); - - if (fss != NULL) { - Tcl_MutexLock(&ClockFmtMutex); - fss->objRefCount++; - Tcl_MutexUnlock(&ClockFmtMutex); - } - - ObjClockFmtScn(copyPtr) = fss; - /* regards special case - format not localizable */ - if (ObjLocFmtKey(srcPtr) != srcPtr) { - Tcl_InitObjRef(ObjLocFmtKey(copyPtr), ObjLocFmtKey(srcPtr)); - } else { - ObjLocFmtKey(copyPtr) = copyPtr; - } - copyPtr->typePtr = &ClockFmtObjType; - - - /* if no format representation, dup string representation */ - if (fss == NULL) { - copyPtr->bytes = ckalloc(srcPtr->length + 1); - memcpy(copyPtr->bytes, srcPtr->bytes, srcPtr->length + 1); - copyPtr->length = srcPtr->length; - } -} - -static void -ClockFmtObj_FreeInternalRep(objPtr) - Tcl_Obj *objPtr; -{ - ClockFmtScnStorage *fss = ObjClockFmtScn(objPtr); - if (fss != NULL) { - Tcl_MutexLock(&ClockFmtMutex); - /* decrement object reference count of format/scan storage */ - if (--fss->objRefCount <= 0) { - #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 - /* don't remove it right now (may be reusable), just add to GC */ - ClockFmtScnStorageGC_In(fss); - #else - /* remove storage (format representation) */ - ClockFmtScnStorageDelete(fss); - #endif - } - Tcl_MutexUnlock(&ClockFmtMutex); - } - ObjClockFmtScn(objPtr) = NULL; - if (ObjLocFmtKey(objPtr) != objPtr) { - Tcl_UnsetObjRef(ObjLocFmtKey(objPtr)); - } else { - ObjLocFmtKey(objPtr) = NULL; - } - objPtr->typePtr = NULL; -}; - -static int -ClockFmtObj_SetFromAny(interp, objPtr) - Tcl_Interp *interp; - Tcl_Obj *objPtr; -{ - /* validate string representation before free old internal represenation */ - (void)TclGetString(objPtr); - - /* free old internal represenation */ - if (objPtr->typePtr && objPtr->typePtr->freeIntRepProc) - objPtr->typePtr->freeIntRepProc(objPtr); - - /* initial state of format object */ - ObjClockFmtScn(objPtr) = NULL; - ObjLocFmtKey(objPtr) = NULL; - objPtr->typePtr = &ClockFmtObjType; - - return TCL_OK; -}; - -static void -ClockFmtObj_UpdateString(objPtr) - Tcl_Obj *objPtr; -{ - const char *name = "UNKNOWN"; - int len; - ClockFmtScnStorage *fss = ObjClockFmtScn(objPtr); - - if (fss != NULL) { - Tcl_HashEntry *hPtr = HashEntry4FmtScn(fss); - name = hPtr->key.string; - } - len = strlen(name); - objPtr->length = len, - objPtr->bytes = ckalloc((size_t)++len); - if (objPtr->bytes) - memcpy(objPtr->bytes, name, len); -} - -/* - *---------------------------------------------------------------------- - * - * ClockFrmObjGetLocFmtKey -- - * - * Retrieves format key object used to search localized format. - * - * This is normally stored in second pointer of internal representation. - * If format object is not localizable, it is equal the given format - * pointer (special case to fast fallback by not-localizable formats). - * - * Results: - * Returns tcl object with key or format object if not localizable. - * - * Side effects: - * Converts given format object to ClockFmtObjType on demand for caching - * the key inside its internal representation. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj* -ClockFrmObjGetLocFmtKey( - Tcl_Interp *interp, - Tcl_Obj *objPtr) -{ - Tcl_Obj *keyObj; - - if (objPtr->typePtr != &ClockFmtObjType) { - if (ClockFmtObj_SetFromAny(interp, objPtr) != TCL_OK) { - return NULL; - } - } - - keyObj = ObjLocFmtKey(objPtr); - if (keyObj) { - return keyObj; - } - - keyObj = Tcl_ObjPrintf("FMT_%s", TclGetString(objPtr)); - Tcl_InitObjRef(ObjLocFmtKey(objPtr), keyObj); - - return keyObj; -} - -/* - *---------------------------------------------------------------------- - * - * FindOrCreateFmtScnStorage -- - * - * Retrieves format storage for given string format. - * - * This will find the given format in the global storage hash table - * or create a format storage object on demaind and save the - * reference in the first pointer of internal representation of given - * object. - * - * Results: - * Returns scan/format storage pointer to ClockFmtScnStorage. - * - * Side effects: - * Converts given format object to ClockFmtObjType on demand for caching - * the format storage reference inside its internal representation. - * Increments objRefCount of the ClockFmtScnStorage reference. - * - *---------------------------------------------------------------------- - */ - -static ClockFmtScnStorage * -FindOrCreateFmtScnStorage( - Tcl_Interp *interp, - Tcl_Obj *objPtr) -{ - const char *strFmt = TclGetString(objPtr); - ClockFmtScnStorage *fss = NULL; - int new; - Tcl_HashEntry *hPtr; - - Tcl_MutexLock(&ClockFmtMutex); - - /* if not yet initialized */ - if (!initialized) { - /* initialize type */ - memcpy(&ClockFmtScnStorageHashKeyType, &tclStringHashKeyType, sizeof(tclStringHashKeyType)); - ClockFmtScnStorageHashKeyType.allocEntryProc = ClockFmtScnStorageAllocProc; - ClockFmtScnStorageHashKeyType.freeEntryProc = ClockFmtScnStorageFreeProc; - - /* initialize hash table */ - Tcl_InitCustomHashTable(&FmtScnHashTable, TCL_CUSTOM_TYPE_KEYS, - &ClockFmtScnStorageHashKeyType); - - initialized = 1; - Tcl_CreateExitHandler(ClockFrmScnFinalize, NULL); - } - - /* get or create entry (and alocate storage) */ - hPtr = Tcl_CreateHashEntry(&FmtScnHashTable, strFmt, &new); - if (hPtr != NULL) { - - fss = FmtScn4HashEntry(hPtr); - - #if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 - /* unlink if it is currently in GC */ - if (new == 0 && fss->objRefCount == 0) { - ClockFmtScnStorage_GC_Out(fss); - } - #endif - - /* new reference, so increment in lock right now */ - fss->objRefCount++; - - ObjClockFmtScn(objPtr) = fss; - } - - Tcl_MutexUnlock(&ClockFmtMutex); - - if (fss == NULL && interp != NULL) { - Tcl_AppendResult(interp, "retrieve clock format failed \"", - strFmt ? strFmt : "", "\"", NULL); - Tcl_SetErrorCode(interp, "TCL", "EINVAL", NULL); - } - - return fss; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_GetClockFrmScnFromObj -- - * - * Returns a clock format/scan representation of (*objPtr), if possible. - * If something goes wrong, NULL is returned, and if interp is non-NULL, - * an error message is written there. - * - * Results: - * Valid representation of type ClockFmtScnStorage. - * - * Side effects: - * Caches the ClockFmtScnStorage reference as the internal rep of (*objPtr) - * and in global hash table, shared across all threads. - * - *---------------------------------------------------------------------- - */ - -ClockFmtScnStorage * -Tcl_GetClockFrmScnFromObj( - Tcl_Interp *interp, - Tcl_Obj *objPtr) -{ - ClockFmtScnStorage *fss; - - if (objPtr->typePtr != &ClockFmtObjType) { - if (ClockFmtObj_SetFromAny(interp, objPtr) != TCL_OK) { - return NULL; - } - } - - fss = ObjClockFmtScn(objPtr); - - if (fss == NULL) { - fss = FindOrCreateFmtScnStorage(interp, objPtr); - } - - return fss; -} -/* - *---------------------------------------------------------------------- - * - * ClockLocalizeFormat -- - * - * Wrap the format object in options to the localized format, - * corresponding given locale. - * - * This searches localized format in locale catalog, and if not yet - * exists, it executes ::tcl::clock::LocalizeFormat in given interpreter - * and caches its result in the locale catalog. - * - * Results: - * Localized format object. - * - * Side effects: - * Caches the localized format inside locale catalog. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE Tcl_Obj * -ClockLocalizeFormat( - ClockFmtScnCmdArgs *opts) -{ - ClockClientData *dataPtr = opts->clientData; - Tcl_Obj *valObj = NULL, *keyObj; - - keyObj = ClockFrmObjGetLocFmtKey(opts->interp, opts->formatObj); - - /* special case - format object is not localizable */ - if (keyObj == opts->formatObj) { - return opts->formatObj; - } - - /* prevents loss of key object if the format object (where key stored) - * becomes changed (loses its internal representation during evals) */ - Tcl_IncrRefCount(keyObj); - - if (opts->mcDictObj == NULL) { - ClockMCDict(opts); - if (opts->mcDictObj == NULL) - goto done; - } - - /* try to find in cache within locale mc-catalog */ - if (Tcl_DictObjGet(NULL, opts->mcDictObj, - keyObj, &valObj) != TCL_OK) { - goto done; - } - - /* call LocalizeFormat locale format fmtkey */ - if (valObj == NULL) { - Tcl_Obj *callargs[4]; - callargs[0] = dataPtr->literals[LIT_LOCALIZE_FORMAT]; - callargs[1] = opts->localeObj; - callargs[2] = opts->formatObj; - callargs[3] = keyObj; - if (Tcl_EvalObjv(opts->interp, 4, callargs, 0) != TCL_OK - ) { - goto done; - } - - valObj = Tcl_GetObjResult(opts->interp); - - /* cache it inside mc-dictionary (this incr. ref count of keyObj/valObj) */ - if (Tcl_DictObjPut(opts->interp, opts->mcDictObj, - keyObj, valObj) != TCL_OK - ) { - valObj = NULL; - goto done; - } - - Tcl_ResetResult(opts->interp); - - /* check special case - format object is not localizable */ - if (valObj == opts->formatObj) { - /* mark it as unlocalizable, by setting self as key (without refcount incr) */ - if (opts->formatObj->typePtr == &ClockFmtObjType) { - Tcl_UnsetObjRef(ObjLocFmtKey(opts->formatObj)); - ObjLocFmtKey(opts->formatObj) = opts->formatObj; - } - } - } - -done: - - Tcl_UnsetObjRef(keyObj); - return (opts->formatObj = valObj); -} - -/* - *---------------------------------------------------------------------- - * - * FindTokenBegin -- - * - * Find begin of given scan token in string, corresponding token type. - * - * Results: - * Position of token inside string if found. Otherwise - end of string. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static const char * -FindTokenBegin( - register const char *p, - register const char *end, - ClockScanToken *tok) -{ - char c; - if (p < end) { - /* next token a known token type */ - switch (tok->map->type) { - case CTOKT_DIGIT: - /* should match at least one digit */ - while (!isdigit(UCHAR(*p)) && (p = TclUtfNext(p)) < end) {}; - return p; - break; - case CTOKT_WORD: - c = *(tok->tokWord.start); - /* should match at least to the first char of this word */ - while (*p != c && (p = TclUtfNext(p)) < end) {}; - return p; - break; - case CTOKT_SPACE: - while (!isspace(UCHAR(*p)) && (p = TclUtfNext(p)) < end) {}; - return p; - break; - case CTOKT_CHAR: - c = *((char *)tok->map->data); - while (*p != c && (p = TclUtfNext(p)) < end) {}; - return p; - break; - } - } - return p; -} - -/* - *---------------------------------------------------------------------- - * - * DetermineGreedySearchLen -- - * - * Determine min/max lengths as exact as possible (speed, greedy match). - * - * Results: - * None. Lengths are stored in *minLenPtr, *maxLenPtr. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static void -DetermineGreedySearchLen(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok, - int *minLenPtr, int *maxLenPtr) -{ - register int minLen = tok->map->minSize; - register int maxLen; - register const char *p = yyInput + minLen, - *end = info->dateEnd; - - /* if still tokens available, try to correct minimum length */ - if ((tok+1)->map) { - end -= tok->endDistance + yySpaceCount; - /* find position of next known token */ - p = FindTokenBegin(p, end, tok+1); - if (p < end) { - minLen = p - yyInput; - } - } - - /* max length to the end regarding distance to end (min-width of following tokens) */ - maxLen = end - yyInput; - /* several amendments */ - if (maxLen > tok->map->maxSize) { - maxLen = tok->map->maxSize; - }; - if (minLen < tok->map->minSize) { - minLen = tok->map->minSize; - } - if (minLen > maxLen) { - maxLen = minLen; - } - if (maxLen > info->dateEnd - yyInput) { - maxLen = info->dateEnd - yyInput; - } - - /* check digits rigth now */ - if (tok->map->type == CTOKT_DIGIT) { - p = yyInput; - end = p + maxLen; - if (end > info->dateEnd) { end = info->dateEnd; }; - while (isdigit(UCHAR(*p)) && p < end) { p++; }; - maxLen = p - yyInput; - } - - /* try to get max length more precise for greedy match, - * check the next ahead token available there */ - if (minLen < maxLen && tok->lookAhTok) { - ClockScanToken *laTok = tok + tok->lookAhTok + 1; - p = yyInput + maxLen; - /* regards all possible spaces here (because they are optional) */ - end = p + tok->lookAhMax + yySpaceCount + 1; - if (end > info->dateEnd) { - end = info->dateEnd; - } - p += tok->lookAhMin; - if (laTok->map && p < end) { - const char *f; - /* try to find laTok between [lookAhMin, lookAhMax] */ - while (minLen < maxLen) { - f = FindTokenBegin(p, end, laTok); - /* if found (not below lookAhMax) */ - if (f < end) { - break; - } - /* try again with fewer length */ - maxLen--; - p--; - end--; - } - } else if (p > end) { - maxLen -= (p - end); - if (maxLen < minLen) { - maxLen = minLen; - } - } - } - - *minLenPtr = minLen; - *maxLenPtr = maxLen; -} - -/* - *---------------------------------------------------------------------- - * - * ObjListSearch -- - * - * Find largest part of the input string from start regarding min and - * max lengths in the given list (utf-8, case sensitive). - * - * Results: - * TCL_OK - match found, TCL_RETURN - not matched, TCL_ERROR in error case. - * - * Side effects: - * Input points to end of the found token in string. - * - *---------------------------------------------------------------------- - */ - -static inline int -ObjListSearch(ClockFmtScnCmdArgs *opts, - DateInfo *info, int *val, - Tcl_Obj **lstv, int lstc, - int minLen, int maxLen) -{ - int i, l, lf = -1; - const char *s, *f, *sf; - /* search in list */ - for (i = 0; i < lstc; i++) { - s = TclGetString(lstv[i]); - l = lstv[i]->length; - - if ( l >= minLen - && (f = TclUtfFindEqualNC(yyInput, yyInput + maxLen, s, s + l, &sf)) > yyInput - ) { - l = f - yyInput; - if (l < minLen) { - continue; - } - /* found, try to find longest value (greedy search) */ - if (l < maxLen && minLen != maxLen) { - lf = i; - minLen = l + 1; - continue; - } - /* max possible - end of search */ - *val = i; - yyInput += l; - break; - } - } - - /* if found */ - if (i < lstc) { - return TCL_OK; - } - if (lf >= 0) { - *val = lf; - yyInput += minLen - 1; - return TCL_OK; - } - return TCL_RETURN; -} -#if 0 -/* currently unused */ - -static int -LocaleListSearch(ClockFmtScnCmdArgs *opts, - DateInfo *info, int mcKey, int *val, - int minLen, int maxLen) -{ - Tcl_Obj **lstv; - int lstc; - Tcl_Obj *valObj; - - /* get msgcat value */ - valObj = ClockMCGet(opts, mcKey); - if (valObj == NULL) { - return TCL_ERROR; - } - - /* is a list */ - if (TclListObjGetElements(opts->interp, valObj, &lstc, &lstv) != TCL_OK) { - return TCL_ERROR; - } - - /* search in list */ - return ObjListSearch(opts, info, val, lstv, lstc, - minLen, maxLen); -} -#endif - -/* - *---------------------------------------------------------------------- - * - * ClockMCGetListIdxTree -- - * - * Retrieves localized string indexed tree in the locale catalog for - * given literal index mcKey (and builds it on demand). - * - * Searches localized index in locale catalog, and if not yet exists, - * creates string indexed tree and stores it in the locale catalog. - * - * Results: - * Localized string index tree. - * - * Side effects: - * Caches the localized string index tree inside locale catalog. - * - *---------------------------------------------------------------------- - */ - -static TclStrIdxTree * -ClockMCGetListIdxTree( - ClockFmtScnCmdArgs *opts, - int mcKey) -{ - TclStrIdxTree * idxTree; - Tcl_Obj *objPtr = ClockMCGetIdx(opts, mcKey); - if ( objPtr != NULL - && (idxTree = TclStrIdxTreeGetFromObj(objPtr)) != NULL - ) { - return idxTree; - - } else { - /* build new index */ - - Tcl_Obj **lstv; - int lstc; - Tcl_Obj *valObj; - - objPtr = TclStrIdxTreeNewObj(); - if ((idxTree = TclStrIdxTreeGetFromObj(objPtr)) == NULL) { - goto done; /* unexpected, but ...*/ - } - - valObj = ClockMCGet(opts, mcKey); - if (valObj == NULL) { - goto done; - } - - if (TclListObjGetElements(opts->interp, valObj, - &lstc, &lstv) != TCL_OK) { - goto done; - }; - - if (TclStrIdxTreeBuildFromList(idxTree, lstc, lstv, NULL) != TCL_OK) { - goto done; - } - - ClockMCSetIdx(opts, mcKey, objPtr); - objPtr = NULL; - }; - -done: - if (objPtr) { - Tcl_DecrRefCount(objPtr); - idxTree = NULL; - } - - return idxTree; -} - -/* - *---------------------------------------------------------------------- - * - * ClockMCGetMultiListIdxTree -- - * - * Retrieves localized string indexed tree in the locale catalog for - * multiple lists by literal indices mcKeys (and builds it on demand). - * - * Searches localized index in locale catalog for mcKey, and if not - * yet exists, creates string indexed tree and stores it in the - * locale catalog. - * - * Results: - * Localized string index tree. - * - * Side effects: - * Caches the localized string index tree inside locale catalog. - * - *---------------------------------------------------------------------- - */ - -static TclStrIdxTree * -ClockMCGetMultiListIdxTree( - ClockFmtScnCmdArgs *opts, - int mcKey, - int *mcKeys) -{ - TclStrIdxTree * idxTree; - Tcl_Obj *objPtr = ClockMCGetIdx(opts, mcKey); - if ( objPtr != NULL - && (idxTree = TclStrIdxTreeGetFromObj(objPtr)) != NULL - ) { - return idxTree; - - } else { - /* build new index */ - - Tcl_Obj **lstv; - int lstc; - Tcl_Obj *valObj; - - objPtr = TclStrIdxTreeNewObj(); - if ((idxTree = TclStrIdxTreeGetFromObj(objPtr)) == NULL) { - goto done; /* unexpected, but ...*/ - } - - while (*mcKeys) { - - valObj = ClockMCGet(opts, *mcKeys); - if (valObj == NULL) { - goto done; - } - - if (TclListObjGetElements(opts->interp, valObj, - &lstc, &lstv) != TCL_OK) { - goto done; - }; - - if (TclStrIdxTreeBuildFromList(idxTree, lstc, lstv, NULL) != TCL_OK) { - goto done; - } - mcKeys++; - } - - ClockMCSetIdx(opts, mcKey, objPtr); - objPtr = NULL; - }; - -done: - if (objPtr) { - Tcl_DecrRefCount(objPtr); - idxTree = NULL; - } - - return idxTree; -} - -/* - *---------------------------------------------------------------------- - * - * ClockStrIdxTreeSearch -- - * - * Find largest part of the input string from start regarding lengths - * in the given localized string indexed tree (utf-8, case sensitive). - * - * Results: - * TCL_OK - match found and the index stored in *val, - * TCL_RETURN - not matched or ambigous, - * TCL_ERROR - in error case. - * - * Side effects: - * Input points to end of the found token in string. - * - *---------------------------------------------------------------------- - */ - -static inline int -ClockStrIdxTreeSearch(ClockFmtScnCmdArgs *opts, - DateInfo *info, TclStrIdxTree *idxTree, int *val, - int minLen, int maxLen) -{ - const char *f; - TclStrIdx *foundItem; - f = TclStrIdxTreeSearch(NULL, &foundItem, idxTree, - yyInput, yyInput + maxLen); - - if (f <= yyInput || (f - yyInput) < minLen) { - /* not found */ - return TCL_RETURN; - } - if (!foundItem->value) { - /* ambigous */ - return TCL_RETURN; - } - - *val = PTR2INT(foundItem->value); - - /* shift input pointer */ - yyInput = f; - - return TCL_OK; -} -#if 0 -/* currently unused */ - -static int -StaticListSearch(ClockFmtScnCmdArgs *opts, - DateInfo *info, const char **lst, int *val) -{ - int len; - const char **s = lst; - while (*s != NULL) { - len = strlen(*s); - if ( len <= info->dateEnd - yyInput - && strncasecmp(yyInput, *s, len) == 0 - ) { - *val = (s - lst); - yyInput += len; - break; - } - s++; - } - if (*s != NULL) { - return TCL_OK; - } - return TCL_RETURN; -} -#endif - -static inline const char * -FindWordEnd( - ClockScanToken *tok, - register const char * p, const char * end) -{ - register const char *x = tok->tokWord.start; - const char *pfnd = p; - if (x == tok->tokWord.end - 1) { /* fast phase-out for single char word */ - if (*p == *x) { - return ++p; - } - } - /* multi-char word */ - x = TclUtfFindEqualNC(x, tok->tokWord.end, p, end, &pfnd); - if (x < tok->tokWord.end) { - /* no match -> error */ - return NULL; - } - return pfnd; -} - -static int -ClockScnToken_Month_Proc(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok) -{ -#if 0 -/* currently unused, test purposes only */ - static const char * months[] = { - /* full */ - "January", "February", "March", - "April", "May", "June", - "July", "August", "September", - "October", "November", "December", - /* abbr */ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - NULL - }; - int val; - if (StaticListSearch(opts, info, months, &val) != TCL_OK) { - return TCL_RETURN; - } - yyMonth = (val % 12) + 1; - return TCL_OK; -#endif - - static int monthsKeys[] = {MCLIT_MONTHS_FULL, MCLIT_MONTHS_ABBREV, 0}; - - int ret, val; - int minLen, maxLen; - TclStrIdxTree *idxTree; - - DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); - - /* get or create tree in msgcat dict */ - - idxTree = ClockMCGetMultiListIdxTree(opts, MCLIT_MONTHS_COMB, monthsKeys); - if (idxTree == NULL) { - return TCL_ERROR; - } - - ret = ClockStrIdxTreeSearch(opts, info, idxTree, &val, minLen, maxLen); - if (ret != TCL_OK) { - return ret; - } - - yyMonth = val; - return TCL_OK; - -} - -static int -ClockScnToken_DayOfWeek_Proc(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok) -{ - static int dowKeys[] = {MCLIT_DAYS_OF_WEEK_ABBREV, MCLIT_DAYS_OF_WEEK_FULL, 0}; - - int ret, val; - int minLen, maxLen; - char curTok = *tok->tokWord.start; - TclStrIdxTree *idxTree; - - DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); - - /* %u %w %Ou %Ow */ - if ( curTok != 'a' && curTok != 'A' - && ((minLen <= 1 && maxLen >= 1) || PTR2INT(tok->map->data)) - ) { - - val = -1; - - if (PTR2INT(tok->map->data) == 0) { - if (*yyInput >= '0' && *yyInput <= '9') { - val = *yyInput - '0'; - } - } else { - idxTree = ClockMCGetListIdxTree(opts, PTR2INT(tok->map->data) /* mcKey */); - if (idxTree == NULL) { - return TCL_ERROR; - } - - ret = ClockStrIdxTreeSearch(opts, info, idxTree, &val, minLen, maxLen); - if (ret != TCL_OK) { - return ret; - } - --val; - } - - if (val != -1) { - if (val == 0) { - val = 7; - } - if (val > 7) { - Tcl_SetObjResult(opts->interp, Tcl_NewStringObj("day of week is greater than 7", -1)); - Tcl_SetErrorCode(opts->interp, "CLOCK", "badDayOfWeek", NULL); - return TCL_ERROR; - } - info->date.dayOfWeek = val; - yyInput++; - return TCL_OK; - } - - - return TCL_RETURN; - } - - /* %a %A */ - idxTree = ClockMCGetMultiListIdxTree(opts, MCLIT_DAYS_OF_WEEK_COMB, dowKeys); - if (idxTree == NULL) { - return TCL_ERROR; - } - - ret = ClockStrIdxTreeSearch(opts, info, idxTree, &val, minLen, maxLen); - if (ret != TCL_OK) { - return ret; - } - --val; - - if (val == 0) { - val = 7; - } - info->date.dayOfWeek = val; - return TCL_OK; - -} - -static int -ClockScnToken_amPmInd_Proc(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok) -{ - int ret, val; - int minLen, maxLen; - Tcl_Obj *amPmObj[2]; - - DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); - - amPmObj[0] = ClockMCGet(opts, MCLIT_AM); - amPmObj[1] = ClockMCGet(opts, MCLIT_PM); - - if (amPmObj[0] == NULL || amPmObj[1] == NULL) { - return TCL_ERROR; - } - - ret = ObjListSearch(opts, info, &val, amPmObj, 2, - minLen, maxLen); - if (ret != TCL_OK) { - return ret; - } - - if (val == 0) { - yyMeridian = MERam; - } else { - yyMeridian = MERpm; - } - - return TCL_OK; -} - -static int -ClockScnToken_LocaleERA_Proc(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok) -{ - ClockClientData *dataPtr = opts->clientData; - - int ret, val; - int minLen, maxLen; - Tcl_Obj *eraObj[6]; - - DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); - - eraObj[0] = ClockMCGet(opts, MCLIT_BCE); - eraObj[1] = ClockMCGet(opts, MCLIT_CE); - eraObj[2] = dataPtr->mcLiterals[MCLIT_BCE2]; - eraObj[3] = dataPtr->mcLiterals[MCLIT_CE2]; - eraObj[4] = dataPtr->mcLiterals[MCLIT_BCE3]; - eraObj[5] = dataPtr->mcLiterals[MCLIT_CE3]; - - if (eraObj[0] == NULL || eraObj[1] == NULL) { - return TCL_ERROR; - } - - ret = ObjListSearch(opts, info, &val, eraObj, 6, - minLen, maxLen); - if (ret != TCL_OK) { - return ret; - } - - if (val & 1) { - yydate.era = CE; - } else { - yydate.era = BCE; - } - - return TCL_OK; -} - -static int -ClockScnToken_LocaleListMatcher_Proc(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok) -{ - int ret, val; - int minLen, maxLen; - TclStrIdxTree *idxTree; - - DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); - - /* get or create tree in msgcat dict */ - - idxTree = ClockMCGetListIdxTree(opts, PTR2INT(tok->map->data) /* mcKey */); - if (idxTree == NULL) { - return TCL_ERROR; - } - - ret = ClockStrIdxTreeSearch(opts, info, idxTree, &val, minLen, maxLen); - if (ret != TCL_OK) { - return ret; - } - - if (tok->map->offs > 0) { - *(int *)(((char *)info) + tok->map->offs) = --val; - } - - return TCL_OK; -} - -static int -ClockScnToken_TimeZone_Proc(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok) -{ - int minLen, maxLen; - int len = 0; - register const char *p = yyInput; - Tcl_Obj *tzObjStor = NULL; - - DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); - - /* numeric timezone */ - if (*p == '+' || *p == '-') { - /* max chars in numeric zone = "+00:00:00" */ - #define MAX_ZONE_LEN 9 - char buf[MAX_ZONE_LEN + 1]; - char *bp = buf; - *bp++ = *p++; len++; - if (maxLen > MAX_ZONE_LEN) - maxLen = MAX_ZONE_LEN; - /* cumulate zone into buf without ':' */ - while (len + 1 < maxLen) { - if (!isdigit(UCHAR(*p))) break; - *bp++ = *p++; len++; - if (!isdigit(UCHAR(*p))) break; - *bp++ = *p++; len++; - if (len + 2 < maxLen) { - if (*p == ':') { - p++; len++; - } - } - } - *bp = '\0'; - - if (len < minLen) { - return TCL_RETURN; - } - #undef MAX_ZONE_LEN - - /* timezone */ - tzObjStor = Tcl_NewStringObj(buf, bp-buf); - } else { - /* legacy (alnum) timezone like CEST, etc. */ - if (maxLen > 4) - maxLen = 4; - while (len < maxLen) { - if ( (*p & 0x80) - || (!isalpha(UCHAR(*p)) && !isdigit(UCHAR(*p))) - ) { /* INTL: ISO only. */ - break; - } - p++; len++; - } - - if (len < minLen) { - return TCL_RETURN; - } - - /* timezone */ - tzObjStor = Tcl_NewStringObj(yyInput, p-yyInput); - - /* convert using dict */ - } - - /* try to apply new time zone */ - Tcl_IncrRefCount(tzObjStor); - - opts->timezoneObj = ClockSetupTimeZone(opts->clientData, opts->interp, - tzObjStor); - - Tcl_DecrRefCount(tzObjStor); - if (opts->timezoneObj == NULL) { - return TCL_ERROR; - } - - yyInput += len; - - return TCL_OK; -} - -static int -ClockScnToken_StarDate_Proc(ClockFmtScnCmdArgs *opts, - DateInfo *info, ClockScanToken *tok) -{ - int minLen, maxLen; - register const char *p = yyInput, *end; const char *s; - int year, fractYear, fractDayDiv, fractDay; - static const char *stardatePref = "stardate "; - - DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen); - - end = yyInput + maxLen; - - /* stardate string */ - p = TclUtfFindEqualNCInLwr(p, end, stardatePref, stardatePref + 9, &s); - if (p >= end || p - yyInput < 9) { - return TCL_RETURN; - } - /* bypass spaces */ - while (p < end && isspace(UCHAR(*p))) { - p++; - } - if (p >= end) { - return TCL_RETURN; - } - /* currently positive stardate only */ - if (*p == '+') { p++; }; - s = p; - while (p < end && isdigit(UCHAR(*p))) { - p++; - } - if (p >= end || p - s < 4) { - return TCL_RETURN; - } - if ( _str2int(&year, s, p-3, 1) != TCL_OK - || _str2int(&fractYear, p-3, p, 1) != TCL_OK) { - return TCL_RETURN; - }; - if (*p++ != '.') { - return TCL_RETURN; - } - s = p; - fractDayDiv = 1; - while (p < end && isdigit(UCHAR(*p))) { - fractDayDiv *= 10; - p++; - } - if ( _str2int(&fractDay, s, p, 1) != TCL_OK) { - return TCL_RETURN; - }; - yyInput = p; - - /* Build a date from year and fraction. */ - - yydate.year = year + RODDENBERRY; - yydate.era = CE; - yydate.gregorian = 1; - - if (IsGregorianLeapYear(&yydate)) { - fractYear *= 366; - } else { - fractYear *= 365; - } - yydate.dayOfYear = fractYear / 1000 + 1; - if (fractYear % 1000 >= 500) { - yydate.dayOfYear++; - } - - GetJulianDayFromEraYearDay(&yydate, GREGORIAN_CHANGE_DATE); - - yydate.localSeconds = - -210866803200L - + ( SECONDS_PER_DAY * (Tcl_WideInt)yydate.julianDay ) - + ( SECONDS_PER_DAY * fractDay / fractDayDiv ); - - return TCL_OK; -} - -static const char *ScnSTokenMapIndex = - "dmbyYHMSpJjCgGVazUsntQ"; -static ClockScanTokenMap ScnSTokenMap[] = { - /* %d %e */ - {CTOKT_DIGIT, CLF_DAYOFMONTH, 0, 1, 2, TclOffset(DateInfo, date.dayOfMonth), - NULL}, - /* %m %N */ - {CTOKT_DIGIT, CLF_MONTH, 0, 1, 2, TclOffset(DateInfo, date.month), - NULL}, - /* %b %B %h */ - {CTOKT_PARSER, CLF_MONTH, 0, 0, 0xffff, 0, - ClockScnToken_Month_Proc}, - /* %y */ - {CTOKT_DIGIT, CLF_YEAR, 0, 1, 2, TclOffset(DateInfo, date.year), - NULL}, - /* %Y */ - {CTOKT_DIGIT, CLF_YEAR | CLF_CENTURY, 0, 4, 4, TclOffset(DateInfo, date.year), - NULL}, - /* %H %k %I %l */ - {CTOKT_DIGIT, CLF_TIME, 0, 1, 2, TclOffset(DateInfo, date.hour), - NULL}, - /* %M */ - {CTOKT_DIGIT, CLF_TIME, 0, 1, 2, TclOffset(DateInfo, date.minutes), - NULL}, - /* %S */ - {CTOKT_DIGIT, CLF_TIME, 0, 1, 2, TclOffset(DateInfo, date.secondOfDay), - NULL}, - /* %p %P */ - {CTOKT_PARSER, CLF_ISO8601, 0, 0, 0xffff, 0, - ClockScnToken_amPmInd_Proc, NULL}, - /* %J */ - {CTOKT_DIGIT, CLF_JULIANDAY, 0, 1, 0xffff, TclOffset(DateInfo, date.julianDay), - NULL}, - /* %j */ - {CTOKT_DIGIT, CLF_DAYOFYEAR, 0, 1, 3, TclOffset(DateInfo, date.dayOfYear), - NULL}, - /* %C */ - {CTOKT_DIGIT, CLF_CENTURY|CLF_ISO8601CENTURY, 0, 1, 2, TclOffset(DateInfo, dateCentury), - NULL}, - /* %g */ - {CTOKT_DIGIT, CLF_ISO8601YEAR | CLF_ISO8601, 0, 2, 2, TclOffset(DateInfo, date.iso8601Year), - NULL}, - /* %G */ - {CTOKT_DIGIT, CLF_ISO8601YEAR | CLF_ISO8601 | CLF_ISO8601CENTURY, 0, 4, 4, TclOffset(DateInfo, date.iso8601Year), - NULL}, - /* %V */ - {CTOKT_DIGIT, CLF_ISO8601, 0, 1, 2, TclOffset(DateInfo, date.iso8601Week), - NULL}, - /* %a %A %u %w */ - {CTOKT_PARSER, CLF_ISO8601, 0, 0, 0xffff, 0, - ClockScnToken_DayOfWeek_Proc, NULL}, - /* %z %Z */ - {CTOKT_PARSER, CLF_OPTIONAL, 0, 0, 0xffff, 0, - ClockScnToken_TimeZone_Proc, NULL}, - /* %U %W */ - {CTOKT_DIGIT, CLF_OPTIONAL, 0, 1, 2, 0, /* currently no capture, parse only token */ - NULL}, - /* %s */ - {CTOKT_DIGIT, CLF_POSIXSEC | CLF_SIGNED, 0, 1, 0xffff, TclOffset(DateInfo, date.seconds), - NULL}, - /* %n */ - {CTOKT_CHAR, 0, 0, 1, 1, 0, NULL, "\n"}, - /* %t */ - {CTOKT_CHAR, 0, 0, 1, 1, 0, NULL, "\t"}, - /* %Q */ - {CTOKT_PARSER, CLF_LOCALSEC, 0, 16, 30, 0, - ClockScnToken_StarDate_Proc, NULL}, -}; -static const char *ScnSTokenMapAliasIndex[2] = { - "eNBhkIlPAuwZW", - "dmbbHHHpaaazU" -}; - -static const char *ScnETokenMapIndex = - "Eys"; -static ClockScanTokenMap ScnETokenMap[] = { - /* %EE */ - {CTOKT_PARSER, 0, 0, 0, 0xffff, TclOffset(DateInfo, date.year), - ClockScnToken_LocaleERA_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Ey */ - {CTOKT_PARSER, 0, 0, 0, 0xffff, 0, /* currently no capture, parse only token */ - ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Es */ - {CTOKT_DIGIT, CLF_LOCALSEC | CLF_SIGNED, 0, 1, 0xffff, TclOffset(DateInfo, date.localSeconds), - NULL}, -}; -static const char *ScnETokenMapAliasIndex[2] = { - "", - "" -}; - -static const char *ScnOTokenMapIndex = - "dmyHMSu"; -static ClockScanTokenMap ScnOTokenMap[] = { - /* %Od %Oe */ - {CTOKT_PARSER, CLF_DAYOFMONTH, 0, 0, 0xffff, TclOffset(DateInfo, date.dayOfMonth), - ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Om */ - {CTOKT_PARSER, CLF_MONTH, 0, 0, 0xffff, TclOffset(DateInfo, date.month), - ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Oy */ - {CTOKT_PARSER, CLF_YEAR, 0, 0, 0xffff, TclOffset(DateInfo, date.year), - ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %OH %Ok %OI %Ol */ - {CTOKT_PARSER, CLF_TIME, 0, 0, 0xffff, TclOffset(DateInfo, date.hour), - ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %OM */ - {CTOKT_PARSER, CLF_TIME, 0, 0, 0xffff, TclOffset(DateInfo, date.minutes), - ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %OS */ - {CTOKT_PARSER, CLF_TIME, 0, 0, 0xffff, TclOffset(DateInfo, date.secondOfDay), - ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Ou Ow */ - {CTOKT_PARSER, CLF_ISO8601, 0, 0, 0xffff, 0, - ClockScnToken_DayOfWeek_Proc, (void *)MCLIT_LOCALE_NUMERALS}, -}; -static const char *ScnOTokenMapAliasIndex[2] = { - "ekIlw", - "dHHHu" -}; - -static const char *ScnSpecTokenMapIndex = - " "; -static ClockScanTokenMap ScnSpecTokenMap[] = { - {CTOKT_SPACE, 0, 0, 1, 1, 0, - NULL}, -}; - -static ClockScanTokenMap ScnWordTokenMap = { - CTOKT_WORD, 0, 0, 1, 1, 0, - NULL -}; - - -static inline unsigned int -EstimateTokenCount( - register const char *fmt, - register const char *end) -{ - register const char *p = fmt; - unsigned int tokcnt; - /* estimate token count by % char and format length */ - tokcnt = 0; - while (p <= end) { - if (*p++ == '%') { - tokcnt++; - p++; - } - } - p = fmt + tokcnt * 2; - if (p < end) { - if ((unsigned int)(end - p) < tokcnt) { - tokcnt += (end - p); - } else { - tokcnt += tokcnt; - } - } - return ++tokcnt; -} - -#define AllocTokenInChain(tok, chain, tokCnt) \ - if (++(tok) >= (chain) + (tokCnt)) { \ - chain = ckrealloc((char *)(chain), \ - (tokCnt + CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE) * sizeof(*(tok))); \ - if ((chain) == NULL) { goto done; }; \ - (tok) = (chain) + (tokCnt); \ - (tokCnt) += CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE; \ - } \ - memset(tok, 0, sizeof(*(tok))); - -/* - *---------------------------------------------------------------------- - */ -ClockFmtScnStorage * -ClockGetOrParseScanFormat( - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *formatObj) /* Format container */ -{ - ClockFmtScnStorage *fss; - ClockScanToken *tok; - - fss = Tcl_GetClockFrmScnFromObj(interp, formatObj); - if (fss == NULL) { - return NULL; - } - - /* if first time scanning - tokenize format */ - if (fss->scnTok == NULL) { - unsigned int tokCnt; - register const char *p, *e, *cp; - - e = p = HashEntry4FmtScn(fss)->key.string; - e += strlen(p); - - /* estimate token count by % char and format length */ - fss->scnTokC = EstimateTokenCount(p, e); - - fss->scnSpaceCount = 0; - - Tcl_MutexLock(&ClockFmtMutex); - - fss->scnTok = tok = ckalloc(sizeof(*tok) * fss->scnTokC); - memset(tok, 0, sizeof(*(tok))); - tokCnt = 1; - while (p < e) { - switch (*p) { - case '%': - if (1) { - ClockScanTokenMap * scnMap = ScnSTokenMap; - const char *mapIndex = ScnSTokenMapIndex, - **aliasIndex = ScnSTokenMapAliasIndex; - if (p+1 >= e) { - goto word_tok; - } - p++; - /* try to find modifier: */ - switch (*p) { - case '%': - /* begin new word token - don't join with previous word token, - * because current mapping should be "...%%..." -> "...%..." */ - tok->map = &ScnWordTokenMap; - tok->tokWord.start = p; - tok->tokWord.end = p+1; - AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); tokCnt++; - p++; - continue; - break; - case 'E': - scnMap = ScnETokenMap, - mapIndex = ScnETokenMapIndex, - aliasIndex = ScnETokenMapAliasIndex; - p++; - break; - case 'O': - scnMap = ScnOTokenMap, - mapIndex = ScnOTokenMapIndex, - aliasIndex = ScnOTokenMapAliasIndex; - p++; - break; - } - /* search direct index */ - cp = strchr(mapIndex, *p); - if (!cp || *cp == '\0') { - /* search wrapper index (multiple chars for same token) */ - cp = strchr(aliasIndex[0], *p); - if (!cp || *cp == '\0') { - p--; if (scnMap != ScnSTokenMap) p--; - goto word_tok; - } - cp = strchr(mapIndex, aliasIndex[1][cp - aliasIndex[0]]); - if (!cp || *cp == '\0') { /* unexpected, but ... */ - #ifdef DEBUG - Tcl_Panic("token \"%c\" has no map in wrapper resolver", *p); - #endif - p--; if (scnMap != ScnSTokenMap) p--; - goto word_tok; - } - } - tok->map = &scnMap[cp - mapIndex]; - tok->tokWord.start = p; - - /* calculate look ahead value by standing together tokens */ - if (tok > fss->scnTok) { - ClockScanToken *prevTok = tok - 1; - - while (prevTok >= fss->scnTok) { - if (prevTok->map->type != tok->map->type) { - break; - } - prevTok->lookAhMin += tok->map->minSize; - prevTok->lookAhMax += tok->map->maxSize; - prevTok->lookAhTok++; - prevTok--; - } - } - - /* increase space count used in format */ - if ( tok->map->type == CTOKT_CHAR - && isspace(UCHAR(*((char *)tok->map->data))) - ) { - fss->scnSpaceCount++; - } - - /* next token */ - AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); tokCnt++; - p++; - continue; - } - break; - case ' ': - cp = strchr(ScnSpecTokenMapIndex, *p); - if (!cp || *cp == '\0') { - p--; - goto word_tok; - } - tok->map = &ScnSpecTokenMap[cp - ScnSpecTokenMapIndex]; - /* increase space count used in format */ - fss->scnSpaceCount++; - /* next token */ - AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); tokCnt++; - p++; - continue; - break; - default: -word_tok: - if (1) { - ClockScanToken *wordTok = tok; - if (tok > fss->scnTok && (tok-1)->map == &ScnWordTokenMap) { - wordTok = tok-1; - } - /* new word token */ - if (wordTok == tok) { - wordTok->tokWord.start = p; - wordTok->map = &ScnWordTokenMap; - AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); tokCnt++; - } - if (isspace(UCHAR(*p))) { - fss->scnSpaceCount++; - } - p = TclUtfNext(p); - wordTok->tokWord.end = p; - } - break; - } - } - - /* calculate end distance value for each tokens */ - if (tok > fss->scnTok) { - unsigned int endDist = 0; - ClockScanToken *prevTok = tok-1; - - while (prevTok >= fss->scnTok) { - prevTok->endDistance = endDist; - if (prevTok->map->type != CTOKT_WORD) { - endDist += prevTok->map->minSize; - } else { - endDist += prevTok->tokWord.end - prevTok->tokWord.start; - } - prevTok--; - } - } - - /* correct count of real used tokens and free mem if desired - * (1 is acceptable delta to prevent memory fragmentation) */ - if (fss->scnTokC > tokCnt + (CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE / 2)) { - if ( (tok = ckrealloc(fss->scnTok, tokCnt * sizeof(*tok))) != NULL ) { - fss->scnTok = tok; - } - } - fss->scnTokC = tokCnt; - -done: - Tcl_MutexUnlock(&ClockFmtMutex); - } - - return fss; -} - -/* - *---------------------------------------------------------------------- - */ -int -ClockScan( - register DateInfo *info, /* Date fields used for parsing & converting */ - Tcl_Obj *strObj, /* String containing the time to scan */ - ClockFmtScnCmdArgs *opts) /* Command options */ -{ - ClockClientData *dataPtr = opts->clientData; - ClockFmtScnStorage *fss; - ClockScanToken *tok; - ClockScanTokenMap *map; - register const char *p, *x, *end; - unsigned short int flags = 0; - int ret = TCL_ERROR; - - /* get localized format */ - if (ClockLocalizeFormat(opts) == NULL) { - return TCL_ERROR; - } - - if ( !(fss = ClockGetOrParseScanFormat(opts->interp, opts->formatObj)) - || !(tok = fss->scnTok) - ) { - return TCL_ERROR; - } - - /* prepare parsing */ - - yyMeridian = MER24; - - p = TclGetString(strObj); - end = p + strObj->length; - /* in strict mode - bypass spaces at begin / end only (not between tokens) */ - if (opts->flags & CLF_STRICT) { - while (p < end && isspace(UCHAR(*p))) { - p++; - } - } - yyInput = p; - /* look ahead to count spaces (bypass it by count length and distances) */ - x = end; - while (p < end) { - if (isspace(UCHAR(*p))) { - x = p++; - yySpaceCount++; - continue; - } - x = end; - p++; - } - /* ignore spaces at end */ - yySpaceCount -= (end - x); - end = x; - /* ignore mandatory spaces used in format */ - yySpaceCount -= fss->scnSpaceCount; - if (yySpaceCount < 0) { - yySpaceCount = 0; - } - info->dateStart = p = yyInput; - info->dateEnd = end; - - /* parse string */ - for (; tok->map != NULL; tok++) { - map = tok->map; - /* bypass spaces at begin of input before parsing each token */ - if ( !(opts->flags & CLF_STRICT) - && ( map->type != CTOKT_SPACE - && map->type != CTOKT_WORD - && map->type != CTOKT_CHAR ) - ) { - while (p < end && isspace(UCHAR(*p))) { - yySpaceCount--; - p++; - } - } - yyInput = p; - /* end of input string */ - if (p >= end) { - break; - } - switch (map->type) - { - case CTOKT_DIGIT: - if (1) { - int minLen, size; - int sign = 1; - if (map->flags & CLF_SIGNED) { - if (*p == '+') { yyInput = ++p; } - else - if (*p == '-') { yyInput = ++p; sign = -1; }; - } - - DetermineGreedySearchLen(opts, info, tok, &minLen, &size); - - if (size < map->minSize) { - /* missing input -> error */ - if ((map->flags & CLF_OPTIONAL)) { - continue; - } - goto not_match; - } - /* string 2 number, put number into info structure by offset */ - if (map->offs) { - p = yyInput; x = p + size; - if (!(map->flags & (CLF_LOCALSEC|CLF_POSIXSEC))) { - if (_str2int((int *)(((char *)info) + map->offs), - p, x, sign) != TCL_OK) { - goto overflow; - } - p = x; - } else { - if (_str2wideInt((Tcl_WideInt *)(((char *)info) + map->offs), - p, x, sign) != TCL_OK) { - goto overflow; - } - p = x; - } - flags = (flags & ~map->clearFlags) | map->flags; - } - } - break; - case CTOKT_PARSER: - switch (map->parser(opts, info, tok)) { - case TCL_OK: - break; - case TCL_RETURN: - if ((map->flags & CLF_OPTIONAL)) { - yyInput = p; - continue; - } - goto not_match; - break; - default: - goto done; - break; - }; - /* decrement count for possible spaces in match */ - while (p < yyInput) { - if (isspace(UCHAR(*p++))) { - yySpaceCount--; - } - } - p = yyInput; - flags = (flags & ~map->clearFlags) | map->flags; - break; - case CTOKT_SPACE: - /* at least one space */ - if (!isspace(UCHAR(*p))) { - /* unmatched -> error */ - goto not_match; - } - yySpaceCount--; - p++; - while (p < end && isspace(UCHAR(*p))) { - yySpaceCount--; - p++; - } - break; - case CTOKT_WORD: - x = FindWordEnd(tok, p, end); - if (!x) { - /* no match -> error */ - goto not_match; - } - p = x; - break; - case CTOKT_CHAR: - x = (char *)map->data; - if (*x != *p) { - /* no match -> error */ - goto not_match; - } - if (isspace(UCHAR(*x))) { - yySpaceCount--; - } - p++; - break; - } - } - /* check end was reached */ - if (p < end) { - /* something after last token - wrong format */ - goto not_match; - } - /* end of string, check only optional tokens at end, otherwise - not match */ - while (tok->map != NULL) { - if (!(opts->flags & CLF_STRICT) && (tok->map->type == CTOKT_SPACE)) { - tok++; - if (tok->map == NULL) break; - } - if (!(tok->map->flags & CLF_OPTIONAL)) { - goto not_match; - } - tok++; - } - - /* - * Invalidate result - */ - - /* seconds token (%s) take precedence over all other tokens */ - if ((opts->flags & CLF_EXTENDED) || !(flags & CLF_POSIXSEC)) { - if (flags & CLF_DATE) { - - if (!(flags & CLF_JULIANDAY)) { - info->flags |= CLF_ASSEMBLE_SECONDS|CLF_ASSEMBLE_JULIANDAY; - - /* dd precedence below ddd */ - switch (flags & (CLF_MONTH|CLF_DAYOFYEAR|CLF_DAYOFMONTH)) { - case (CLF_DAYOFYEAR|CLF_DAYOFMONTH): - /* miss month: ddd over dd (without month) */ - flags &= ~CLF_DAYOFMONTH; - case (CLF_DAYOFYEAR): - /* ddd over naked weekday */ - if (!(flags & CLF_ISO8601YEAR)) { - flags &= ~CLF_ISO8601; - } - break; - case (CLF_MONTH|CLF_DAYOFYEAR|CLF_DAYOFMONTH): - /* both available: mmdd over ddd */ - flags &= ~CLF_DAYOFYEAR; - case (CLF_MONTH|CLF_DAYOFMONTH): - case (CLF_DAYOFMONTH): - /* mmdd / dd over naked weekday */ - if (!(flags & CLF_ISO8601YEAR)) { - flags &= ~CLF_ISO8601; - } - break; - } - - /* YearWeekDay below YearMonthDay */ - if ( (flags & CLF_ISO8601) - && ( (flags & (CLF_YEAR|CLF_DAYOFYEAR)) == (CLF_YEAR|CLF_DAYOFYEAR) - || (flags & (CLF_YEAR|CLF_DAYOFMONTH|CLF_MONTH)) == (CLF_YEAR|CLF_DAYOFMONTH|CLF_MONTH) - ) - ) { - /* yy precedence below yyyy */ - if (!(flags & CLF_ISO8601CENTURY) && (flags & CLF_CENTURY)) { - /* normally precedence of ISO is higher, but no century - so put it down */ - flags &= ~CLF_ISO8601; - } - else - /* yymmdd or yyddd over naked weekday */ - if (!(flags & CLF_ISO8601YEAR)) { - flags &= ~CLF_ISO8601; - } - } - - if (!(flags & CLF_ISO8601)) { - if (yyYear < 100) { - if (!(flags & CLF_CENTURY)) { - if (yyYear >= dataPtr->yearOfCenturySwitch) { - yyYear -= 100; - } - yyYear += dataPtr->currentYearCentury; - } else { - yyYear += info->dateCentury * 100; - } - } - } else { - if (info->date.iso8601Year < 100) { - if (!(flags & CLF_ISO8601CENTURY)) { - if (info->date.iso8601Year >= dataPtr->yearOfCenturySwitch) { - info->date.iso8601Year -= 100; - } - info->date.iso8601Year += dataPtr->currentYearCentury; - } else { - info->date.iso8601Year += info->dateCentury * 100; - } - } - } - } - } - - /* if no time - reset time */ - if (!(flags & (CLF_TIME|CLF_LOCALSEC|CLF_POSIXSEC))) { - info->flags |= CLF_ASSEMBLE_SECONDS; - yydate.localSeconds = 0; - } - - if (flags & CLF_TIME) { - info->flags |= CLF_ASSEMBLE_SECONDS; - yySeconds = ToSeconds(yyHour, yyMinutes, - yySeconds, yyMeridian); - } else - if (!(flags & (CLF_LOCALSEC|CLF_POSIXSEC))) { - info->flags |= CLF_ASSEMBLE_SECONDS; - yySeconds = yydate.localSeconds % SECONDS_PER_DAY; - } - } - - /* tell caller which flags were set */ - info->flags |= flags; - - ret = TCL_OK; - goto done; - -overflow: - - Tcl_SetObjResult(opts->interp, Tcl_NewStringObj("requested date too large to represent", - -1)); - Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", NULL); - goto done; - -not_match: - - Tcl_SetObjResult(opts->interp, Tcl_NewStringObj("input string does not match supplied format", - -1)); - Tcl_SetErrorCode(opts->interp, "CLOCK", "badInputString", NULL); - -done: - - return ret; -} - -static inline int -FrmResultAllocate( - register DateFormat *dateFmt, - int len) -{ - int needed = dateFmt->output + len - dateFmt->resEnd; - if (needed >= 0) { /* >= 0 - regards NTS zero */ - int newsize = dateFmt->resEnd - dateFmt->resMem - + needed + MIN_FMT_RESULT_BLOCK_ALLOC; - char *newRes = ckrealloc(dateFmt->resMem, newsize); - if (newRes == NULL) { - return TCL_ERROR; - } - dateFmt->output = newRes + (dateFmt->output - dateFmt->resMem); - dateFmt->resMem = newRes; - dateFmt->resEnd = newRes + newsize; - } - return TCL_OK; -} - -static int -ClockFmtToken_HourAMPM_Proc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val) -{ - *val = ( ( ( *val % SECONDS_PER_DAY ) + SECONDS_PER_DAY - 3600 ) / 3600 ) % 12 + 1; - return TCL_OK; -} - -static int -ClockFmtToken_AMPM_Proc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val) -{ - Tcl_Obj *mcObj; - const char *s; - int len; - - if ((*val % SECONDS_PER_DAY) < (SECONDS_PER_DAY / 2)) { - mcObj = ClockMCGet(opts, MCLIT_AM); - } else { - mcObj = ClockMCGet(opts, MCLIT_PM); - } - if (mcObj == NULL) { - return TCL_ERROR; - } - s = TclGetString(mcObj); len = mcObj->length; - if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; - memcpy(dateFmt->output, s, len + 1); - if (*tok->tokWord.start == 'p') { - len = Tcl_UtfToUpper(dateFmt->output); - } - dateFmt->output += len; - - return TCL_OK; -} - -static int -ClockFmtToken_StarDate_Proc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val) - { - int fractYear; - /* Get day of year, zero based */ - int v = dateFmt->date.dayOfYear - 1; - - /* Convert day of year to a fractional year */ - if (IsGregorianLeapYear(&dateFmt->date)) { - fractYear = 1000 * v / 366; - } else { - fractYear = 1000 * v / 365; - } - - /* Put together the StarDate as "Stardate %02d%03d.%1d" */ - if (FrmResultAllocate(dateFmt, 30) != TCL_OK) { return TCL_ERROR; }; - memcpy(dateFmt->output, "Stardate ", 9); - dateFmt->output += 9; - dateFmt->output = _itoaw(dateFmt->output, - dateFmt->date.year - RODDENBERRY, '0', 2); - dateFmt->output = _itoaw(dateFmt->output, - fractYear, '0', 3); - *dateFmt->output++ = '.'; - /* be sure positive after decimal point (note: clock-value can be negative) */ - v = dateFmt->date.localSeconds % SECONDS_PER_DAY / ( SECONDS_PER_DAY / 10 ); - if (v < 0) v = 10 + v; - dateFmt->output = _itoaw(dateFmt->output, v, '0', 1); - - return TCL_OK; -} -static int -ClockFmtToken_WeekOfYear_Proc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val) -{ - int dow = dateFmt->date.dayOfWeek; - if (*tok->tokWord.start == 'U') { - if (dow == 7) { - dow = 0; - } - dow++; - } - *val = ( dateFmt->date.dayOfYear - dow + 7 ) / 7; - return TCL_OK; -} -static int -ClockFmtToken_TimeZone_Proc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val) -{ - if (*tok->tokWord.start == 'z') { - int z = dateFmt->date.tzOffset; - char sign = '+'; - if ( z < 0 ) { - z = -z; - sign = '-'; - } - if (FrmResultAllocate(dateFmt, 7) != TCL_OK) { return TCL_ERROR; }; - *dateFmt->output++ = sign; - dateFmt->output = _itoaw(dateFmt->output, z / 3600, '0', 2); - z %= 3600; - dateFmt->output = _itoaw(dateFmt->output, z / 60, '0', 2); - z %= 60; - if (z != 0) { - dateFmt->output = _itoaw(dateFmt->output, z, '0', 2); - } - } else { - Tcl_Obj * objPtr; - const char *s; int len; - /* convert seconds to local seconds to obtain tzName object */ - if (ConvertUTCToLocal(opts->clientData, opts->interp, - &dateFmt->date, opts->timezoneObj, - GREGORIAN_CHANGE_DATE) != TCL_OK) { - return TCL_ERROR; - }; - objPtr = dateFmt->date.tzName; - s = TclGetString(objPtr); - len = objPtr->length; - if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; - memcpy(dateFmt->output, s, len + 1); - dateFmt->output += len; - } - return TCL_OK; -} - -static int -ClockFmtToken_LocaleERA_Proc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val) -{ - Tcl_Obj *mcObj; - const char *s; - int len; - - if (dateFmt->date.era == BCE) { - mcObj = ClockMCGet(opts, MCLIT_BCE); - } else { - mcObj = ClockMCGet(opts, MCLIT_CE); - } - if (mcObj == NULL) { - return TCL_ERROR; - } - s = TclGetString(mcObj); len = mcObj->length; - if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; - memcpy(dateFmt->output, s, len + 1); - dateFmt->output += len; - - return TCL_OK; -} - -static int -ClockFmtToken_LocaleERAYear_Proc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val) -{ - int rowc; - Tcl_Obj **rowv; - - if (dateFmt->localeEra == NULL) { - Tcl_Obj *mcObj = ClockMCGet(opts, MCLIT_LOCALE_ERAS); - if (mcObj == NULL) { - return TCL_ERROR; - } - if (TclListObjGetElements(opts->interp, mcObj, &rowc, &rowv) != TCL_OK) { - return TCL_ERROR; - } - if (rowc != 0) { - dateFmt->localeEra = LookupLastTransition(opts->interp, - dateFmt->date.localSeconds, rowc, rowv, NULL); - } - if (dateFmt->localeEra == NULL) { - dateFmt->localeEra = (Tcl_Obj*)1; - } - } - - /* if no LOCALE_ERAS in catalog or era not found */ - if (dateFmt->localeEra == (Tcl_Obj*)1) { - if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { return TCL_ERROR; }; - if (*tok->tokWord.start == 'C') { /* %EC */ - *val = dateFmt->date.year / 100; - dateFmt->output = _itoaw(dateFmt->output, - *val, '0', 2); - } else { /* %Ey */ - *val = dateFmt->date.year % 100; - dateFmt->output = _itoaw(dateFmt->output, - *val, '0', 2); - } - } else { - Tcl_Obj *objPtr; - const char *s; - int len; - if (*tok->tokWord.start == 'C') { /* %EC */ - if (Tcl_ListObjIndex(opts->interp, dateFmt->localeEra, 1, - &objPtr) != TCL_OK ) { - return TCL_ERROR; - } - } else { /* %Ey */ - if (Tcl_ListObjIndex(opts->interp, dateFmt->localeEra, 2, - &objPtr) != TCL_OK ) { - return TCL_ERROR; - } - if (Tcl_GetIntFromObj(opts->interp, objPtr, val) != TCL_OK) { - return TCL_ERROR; - } - *val = dateFmt->date.year - *val; - /* if year in locale numerals */ - if (*val >= 0 && *val < 100) { - /* year as integer */ - Tcl_Obj * mcObj = ClockMCGet(opts, MCLIT_LOCALE_NUMERALS); - if (mcObj == NULL) { - return TCL_ERROR; - } - if (Tcl_ListObjIndex(opts->interp, mcObj, *val, &objPtr) != TCL_OK) { - return TCL_ERROR; - } - } else { - /* year as integer */ - if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { return TCL_ERROR; }; - dateFmt->output = _itoaw(dateFmt->output, - *val, '0', 2); - return TCL_OK; - } - } - s = TclGetString(objPtr); - len = objPtr->length; - if (FrmResultAllocate(dateFmt, len) != TCL_OK) { return TCL_ERROR; }; - memcpy(dateFmt->output, s, len + 1); - dateFmt->output += len; - } - return TCL_OK; -} - - -static const char *FmtSTokenMapIndex = - "demNbByYCHMSIklpaAuwUVzgGjJsntQ"; -static ClockFormatTokenMap FmtSTokenMap[] = { - /* %d */ - {CFMTT_INT, "0", 2, 0, 0, 0, TclOffset(DateFormat, date.dayOfMonth), NULL}, - /* %e */ - {CFMTT_INT, " ", 2, 0, 0, 0, TclOffset(DateFormat, date.dayOfMonth), NULL}, - /* %m */ - {CFMTT_INT, "0", 2, 0, 0, 0, TclOffset(DateFormat, date.month), NULL}, - /* %N */ - {CFMTT_INT, " ", 2, 0, 0, 0, TclOffset(DateFormat, date.month), NULL}, - /* %b %h */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX | CLFMT_DECR, 0, 12, TclOffset(DateFormat, date.month), - NULL, (void *)MCLIT_MONTHS_ABBREV}, - /* %B */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX | CLFMT_DECR, 0, 12, TclOffset(DateFormat, date.month), - NULL, (void *)MCLIT_MONTHS_FULL}, - /* %y */ - {CFMTT_INT, "0", 2, 0, 0, 100, TclOffset(DateFormat, date.year), NULL}, - /* %Y */ - {CFMTT_INT, "0", 4, 0, 0, 0, TclOffset(DateFormat, date.year), NULL}, - /* %C */ - {CFMTT_INT, "0", 2, 0, 100, 0, TclOffset(DateFormat, date.year), NULL}, - /* %H */ - {CFMTT_INT, "0", 2, 0, 3600, 24, TclOffset(DateFormat, date.secondOfDay), NULL}, - /* %M */ - {CFMTT_INT, "0", 2, 0, 60, 60, TclOffset(DateFormat, date.secondOfDay), NULL}, - /* %S */ - {CFMTT_INT, "0", 2, 0, 0, 60, TclOffset(DateFormat, date.secondOfDay), NULL}, - /* %I */ - {CFMTT_INT, "0", 2, CLFMT_CALC, 0, 0, TclOffset(DateFormat, date.secondOfDay), - ClockFmtToken_HourAMPM_Proc, NULL}, - /* %k */ - {CFMTT_INT, " ", 2, 0, 3600, 24, TclOffset(DateFormat, date.secondOfDay), NULL}, - /* %l */ - {CFMTT_INT, " ", 2, CLFMT_CALC, 0, 0, TclOffset(DateFormat, date.secondOfDay), - ClockFmtToken_HourAMPM_Proc, NULL}, - /* %p %P */ - {CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.secondOfDay), - ClockFmtToken_AMPM_Proc, NULL}, - /* %a */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, TclOffset(DateFormat, date.dayOfWeek), - NULL, (void *)MCLIT_DAYS_OF_WEEK_ABBREV}, - /* %A */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, TclOffset(DateFormat, date.dayOfWeek), - NULL, (void *)MCLIT_DAYS_OF_WEEK_FULL}, - /* %u */ - {CFMTT_INT, " ", 1, 0, 0, 0, TclOffset(DateFormat, date.dayOfWeek), NULL}, - /* %w */ - {CFMTT_INT, " ", 1, 0, 0, 7, TclOffset(DateFormat, date.dayOfWeek), NULL}, - /* %U %W */ - {CFMTT_INT, "0", 2, CLFMT_CALC, 0, 0, TclOffset(DateFormat, date.dayOfYear), - ClockFmtToken_WeekOfYear_Proc, NULL}, - /* %V */ - {CFMTT_INT, "0", 2, 0, 0, 0, TclOffset(DateFormat, date.iso8601Week), NULL}, - /* %z %Z */ - {CFMTT_INT, NULL, 0, 0, 0, 0, 0, - ClockFmtToken_TimeZone_Proc, NULL}, - /* %g */ - {CFMTT_INT, "0", 2, 0, 0, 100, TclOffset(DateFormat, date.iso8601Year), NULL}, - /* %G */ - {CFMTT_INT, "0", 4, 0, 0, 0, TclOffset(DateFormat, date.iso8601Year), NULL}, - /* %j */ - {CFMTT_INT, "0", 3, 0, 0, 0, TclOffset(DateFormat, date.dayOfYear), NULL}, - /* %J */ - {CFMTT_INT, "0", 7, 0, 0, 0, TclOffset(DateFormat, date.julianDay), NULL}, - /* %s */ - {CFMTT_WIDE, "0", 1, 0, 0, 0, TclOffset(DateFormat, date.seconds), NULL}, - /* %n */ - {CTOKT_CHAR, "\n", 0, 0, 0, 0, 0, NULL}, - /* %t */ - {CTOKT_CHAR, "\t", 0, 0, 0, 0, 0, NULL}, - /* %Q */ - {CFMTT_INT, NULL, 0, 0, 0, 0, 0, - ClockFmtToken_StarDate_Proc, NULL}, -}; -static const char *FmtSTokenMapAliasIndex[2] = { - "hPWZ", - "bpUz" -}; - -static const char *FmtETokenMapIndex = - "Eys"; -static ClockFormatTokenMap FmtETokenMap[] = { - /* %EE */ - {CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.era), - ClockFmtToken_LocaleERA_Proc, NULL}, - /* %Ey %EC */ - {CFMTT_INT, NULL, 0, 0, 0, 0, TclOffset(DateFormat, date.year), - ClockFmtToken_LocaleERAYear_Proc, NULL}, - /* %Es */ - {CFMTT_WIDE, "0", 1, 0, 0, 0, TclOffset(DateFormat, date.localSeconds), NULL}, -}; -static const char *FmtETokenMapAliasIndex[2] = { - "C", - "y" -}; - -static const char *FmtOTokenMapIndex = - "dmyHIMSuw"; -static ClockFormatTokenMap FmtOTokenMap[] = { - /* %Od %Oe */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.dayOfMonth), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Om */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.month), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Oy */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.year), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, - /* %OH %Ok */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 3600, 24, TclOffset(DateFormat, date.secondOfDay), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, - /* %OI %Ol */ - {CFMTT_INT, NULL, 0, CLFMT_CALC | CLFMT_LOCALE_INDX, 0, 0, TclOffset(DateFormat, date.secondOfDay), - ClockFmtToken_HourAMPM_Proc, (void *)MCLIT_LOCALE_NUMERALS}, - /* %OM */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 60, 60, TclOffset(DateFormat, date.secondOfDay), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, - /* %OS */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 60, TclOffset(DateFormat, date.secondOfDay), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Ou */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 100, TclOffset(DateFormat, date.dayOfWeek), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, - /* %Ow */ - {CFMTT_INT, NULL, 0, CLFMT_LOCALE_INDX, 0, 7, TclOffset(DateFormat, date.dayOfWeek), - NULL, (void *)MCLIT_LOCALE_NUMERALS}, -}; -static const char *FmtOTokenMapAliasIndex[2] = { - "ekl", - "dHI" -}; - -static ClockFormatTokenMap FmtWordTokenMap = { - CTOKT_WORD, NULL, 0, 0, 0, 0, 0, NULL -}; - -/* - *---------------------------------------------------------------------- - */ -ClockFmtScnStorage * -ClockGetOrParseFmtFormat( - Tcl_Interp *interp, /* Tcl interpreter */ - Tcl_Obj *formatObj) /* Format container */ -{ - ClockFmtScnStorage *fss; - ClockFormatToken *tok; - - fss = Tcl_GetClockFrmScnFromObj(interp, formatObj); - if (fss == NULL) { - return NULL; - } - - /* if first time scanning - tokenize format */ - if (fss->fmtTok == NULL) { - unsigned int tokCnt; - register const char *p, *e, *cp; - - e = p = HashEntry4FmtScn(fss)->key.string; - e += strlen(p); - - /* estimate token count by % char and format length */ - fss->fmtTokC = EstimateTokenCount(p, e); - - Tcl_MutexLock(&ClockFmtMutex); - - fss->fmtTok = tok = ckalloc(sizeof(*tok) * fss->fmtTokC); - memset(tok, 0, sizeof(*(tok))); - tokCnt = 1; - while (p < e) { - switch (*p) { - case '%': - if (1) { - ClockFormatTokenMap * fmtMap = FmtSTokenMap; - const char *mapIndex = FmtSTokenMapIndex, - **aliasIndex = FmtSTokenMapAliasIndex; - if (p+1 >= e) { - goto word_tok; - } - p++; - /* try to find modifier: */ - switch (*p) { - case '%': - /* begin new word token - don't join with previous word token, - * because current mapping should be "...%%..." -> "...%..." */ - tok->map = &FmtWordTokenMap; - tok->tokWord.start = p; - tok->tokWord.end = p+1; - AllocTokenInChain(tok, fss->fmtTok, fss->fmtTokC); tokCnt++; - p++; - continue; - break; - case 'E': - fmtMap = FmtETokenMap, - mapIndex = FmtETokenMapIndex, - aliasIndex = FmtETokenMapAliasIndex; - p++; - break; - case 'O': - fmtMap = FmtOTokenMap, - mapIndex = FmtOTokenMapIndex, - aliasIndex = FmtOTokenMapAliasIndex; - p++; - break; - } - /* search direct index */ - cp = strchr(mapIndex, *p); - if (!cp || *cp == '\0') { - /* search wrapper index (multiple chars for same token) */ - cp = strchr(aliasIndex[0], *p); - if (!cp || *cp == '\0') { - p--; if (fmtMap != FmtSTokenMap) p--; - goto word_tok; - } - cp = strchr(mapIndex, aliasIndex[1][cp - aliasIndex[0]]); - if (!cp || *cp == '\0') { /* unexpected, but ... */ - #ifdef DEBUG - Tcl_Panic("token \"%c\" has no map in wrapper resolver", *p); - #endif - p--; if (fmtMap != FmtSTokenMap) p--; - goto word_tok; - } - } - tok->map = &fmtMap[cp - mapIndex]; - tok->tokWord.start = p; - /* next token */ - AllocTokenInChain(tok, fss->fmtTok, fss->fmtTokC); tokCnt++; - p++; - continue; - } - break; - default: -word_tok: - if (1) { - ClockFormatToken *wordTok = tok; - if (tok > fss->fmtTok && (tok-1)->map == &FmtWordTokenMap) { - wordTok = tok-1; - } - if (wordTok == tok) { - wordTok->tokWord.start = p; - wordTok->map = &FmtWordTokenMap; - AllocTokenInChain(tok, fss->fmtTok, fss->fmtTokC); tokCnt++; - } - p = TclUtfNext(p); - wordTok->tokWord.end = p; - } - break; - } - } - - /* correct count of real used tokens and free mem if desired - * (1 is acceptable delta to prevent memory fragmentation) */ - if (fss->fmtTokC > tokCnt + (CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE / 2)) { - if ( (tok = ckrealloc(fss->fmtTok, tokCnt * sizeof(*tok))) != NULL ) { - fss->fmtTok = tok; - } - } - fss->fmtTokC = tokCnt; - -done: - Tcl_MutexUnlock(&ClockFmtMutex); - } - - return fss; -} - -/* - *---------------------------------------------------------------------- - */ -int -ClockFormat( - register DateFormat *dateFmt, /* Date fields used for parsing & converting */ - ClockFmtScnCmdArgs *opts) /* Command options */ -{ - ClockFmtScnStorage *fss; - ClockFormatToken *tok; - ClockFormatTokenMap *map; - - /* get localized format */ - if (ClockLocalizeFormat(opts) == NULL) { - return TCL_ERROR; - } - - if ( !(fss = ClockGetOrParseFmtFormat(opts->interp, opts->formatObj)) - || !(tok = fss->fmtTok) - ) { - return TCL_ERROR; - } - - /* prepare formatting */ - dateFmt->date.secondOfDay = (int)(dateFmt->date.localSeconds % SECONDS_PER_DAY); - if (dateFmt->date.secondOfDay < 0) { - dateFmt->date.secondOfDay += SECONDS_PER_DAY; - } - - /* result container object */ - dateFmt->resMem = ckalloc(MIN_FMT_RESULT_BLOCK_ALLOC); - if (dateFmt->resMem == NULL) { - return TCL_ERROR; - } - dateFmt->output = dateFmt->resMem; - dateFmt->resEnd = dateFmt->resMem + MIN_FMT_RESULT_BLOCK_ALLOC; - *dateFmt->output = '\0'; - - /* do format each token */ - for (; tok->map != NULL; tok++) { - map = tok->map; - switch (map->type) - { - case CFMTT_INT: - if (1) { - int val = (int)*(int *)(((char *)dateFmt) + map->offs); - if (map->fmtproc == NULL) { - if (map->flags & CLFMT_DECR) { - val--; - } - if (map->flags & CLFMT_INCR) { - val++; - } - if (map->divider) { - val /= map->divider; - } - if (map->divmod) { - val %= map->divmod; - } - } else { - if (map->fmtproc(opts, dateFmt, tok, &val) != TCL_OK) { - goto done; - } - /* if not calculate only (output inside fmtproc) */ - if (!(map->flags & CLFMT_CALC)) { - continue; - } - } - if (!(map->flags & CLFMT_LOCALE_INDX)) { - if (FrmResultAllocate(dateFmt, 11) != TCL_OK) { goto error; }; - if (map->width) { - dateFmt->output = _itoaw(dateFmt->output, val, *map->tostr, map->width); - } else { - dateFmt->output += sprintf(dateFmt->output, map->tostr, val); - } - } else { - const char *s; - Tcl_Obj * mcObj = ClockMCGet(opts, PTR2INT(map->data) /* mcKey */); - if (mcObj == NULL) { - goto error; - } - if ( Tcl_ListObjIndex(opts->interp, mcObj, val, &mcObj) != TCL_OK - || mcObj == NULL - ) { - goto error; - } - s = TclGetString(mcObj); - if (FrmResultAllocate(dateFmt, mcObj->length) != TCL_OK) { goto error; }; - memcpy(dateFmt->output, s, mcObj->length + 1); - dateFmt->output += mcObj->length; - } - } - break; - case CFMTT_WIDE: - if (1) { - Tcl_WideInt val = *(Tcl_WideInt *)(((char *)dateFmt) + map->offs); - if (FrmResultAllocate(dateFmt, 21) != TCL_OK) { goto error; }; - if (map->width) { - dateFmt->output = _witoaw(dateFmt->output, val, *map->tostr, map->width); - } else { - dateFmt->output += sprintf(dateFmt->output, map->tostr, val); - } - } - break; - case CTOKT_CHAR: - if (FrmResultAllocate(dateFmt, 1) != TCL_OK) { goto error; }; - *dateFmt->output++ = *map->tostr; - break; - case CFMTT_PROC: - if (map->fmtproc(opts, dateFmt, tok, NULL) != TCL_OK) { - goto error; - }; - break; - case CTOKT_WORD: - if (1) { - int len = tok->tokWord.end - tok->tokWord.start; - if (FrmResultAllocate(dateFmt, len) != TCL_OK) { goto error; }; - if (len == 1) { - *dateFmt->output++ = *tok->tokWord.start; - } else { - memcpy(dateFmt->output, tok->tokWord.start, len); - dateFmt->output += len; - } - } - break; - } - } - - goto done; - -error: - - ckfree(dateFmt->resMem); - dateFmt->resMem = NULL; - -done: - - if (dateFmt->resMem) { - Tcl_Obj * result = Tcl_NewObj(); - result->length = dateFmt->output - dateFmt->resMem; - result->bytes = NULL; - result->bytes = ckrealloc(dateFmt->resMem, result->length+1); - if (result->bytes == NULL) { - result->bytes = dateFmt->resMem; - } - result->bytes[result->length] = '\0'; - Tcl_SetObjResult(opts->interp, result); - return TCL_OK; - } - - return TCL_ERROR; -} - - -MODULE_SCOPE void -ClockFrmScnClearCaches(void) -{ - Tcl_MutexLock(&ClockFmtMutex); - /* clear caches ... */ - Tcl_MutexUnlock(&ClockFmtMutex); -} - -static void -ClockFrmScnFinalize( - ClientData clientData) /* Not used. */ -{ - Tcl_MutexLock(&ClockFmtMutex); -#if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 - /* clear GC */ - ClockFmtScnStorage_GC.stackPtr = NULL; - ClockFmtScnStorage_GC.stackBound = NULL; - ClockFmtScnStorage_GC.count = 0; -#endif - if (initialized) { - Tcl_DeleteHashTable(&FmtScnHashTable); - initialized = 0; - } - Tcl_MutexUnlock(&ClockFmtMutex); - Tcl_MutexFinalize(&ClockFmtMutex); -} -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index e0929f0..668405c 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -4049,351 +4049,6 @@ Tcl_TimeObjCmd( /* *---------------------------------------------------------------------- * - * Tcl_TimeRateObjCmd -- - * - * This object-based procedure is invoked to process the "timerate" Tcl - * command. - * This is similar to command "time", except the execution limited by - * given time (in milliseconds) instead of repetition count. - * - * Example: - * timerate {after 5} 1000 ; # equivalent for `time {after 5} [expr 1000/5]` - * - * Results: - * A standard Tcl object result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_TimeRateObjCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - static - double measureOverhead = 0; /* global measure-overhead */ - double overhead = -1; /* given measure-overhead */ - register Tcl_Obj *objPtr; - register int result, i; - Tcl_Obj *calibrate = NULL, *direct = NULL; - Tcl_WideInt count = 0; /* Holds repetition count */ - Tcl_WideInt maxms = -0x7FFFFFFFFFFFFFFFL; - /* Maximal running time (in milliseconds) */ - Tcl_WideInt threshold = 1; /* Current threshold for check time (faster - * repeat count without time check) */ - Tcl_WideInt maxIterTm = 1; /* Max time of some iteration as max threshold - * additionally avoid divide to zero (never < 1) */ - register Tcl_WideInt start, middle, stop; -#ifndef TCL_WIDE_CLICKS - Tcl_Time now; -#endif - - static const char *const options[] = { - "-direct", "-overhead", "-calibrate", "--", NULL - }; - enum options { - TMRT_EV_DIRECT, TMRT_OVERHEAD, TMRT_CALIBRATE, TMRT_LAST - }; - - NRE_callback *rootPtr; - ByteCode *codePtr = NULL; - - for (i = 1; i < objc - 1; i++) { - int index; - if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", TCL_EXACT, - &index) != TCL_OK) { - break; - } - if (index == TMRT_LAST) { - i++; - break; - } - switch (index) { - case TMRT_EV_DIRECT: - direct = objv[i]; - break; - case TMRT_OVERHEAD: - if (++i >= objc - 1) { - goto usage; - } - if (Tcl_GetDoubleFromObj(interp, objv[i], &overhead) != TCL_OK) { - return TCL_ERROR; - } - break; - case TMRT_CALIBRATE: - calibrate = objv[i]; - break; - } - } - - if (i >= objc || i < objc-2) { -usage: - Tcl_WrongNumArgs(interp, 1, objv, "?-direct? ?-calibrate? ?-overhead double? command ?time?"); - return TCL_ERROR; - } - objPtr = objv[i++]; - if (i < objc) { - result = TclGetWideIntFromObj(interp, objv[i], &maxms); - if (result != TCL_OK) { - return result; - } - } - - /* if calibrate */ - if (calibrate) { - - /* if no time specified for the calibration */ - if (maxms == -0x7FFFFFFFFFFFFFFFL) { - Tcl_Obj *clobjv[6]; - Tcl_WideInt maxCalTime = 5000; - double lastMeasureOverhead = measureOverhead; - - clobjv[0] = objv[0]; - i = 1; - if (direct) { - clobjv[i++] = direct; - } - clobjv[i++] = objPtr; - - /* reset last measurement overhead */ - measureOverhead = (double)0; - - /* self-call with 100 milliseconds to warm-up, - * before entering the calibration cycle */ - TclNewLongObj(clobjv[i], 100); - Tcl_IncrRefCount(clobjv[i]); - result = Tcl_TimeRateObjCmd(dummy, interp, i+1, clobjv); - Tcl_DecrRefCount(clobjv[i]); - if (result != TCL_OK) { - return result; - } - - i--; - clobjv[i++] = calibrate; - clobjv[i++] = objPtr; - - /* set last measurement overhead to max */ - measureOverhead = (double)0x7FFFFFFFFFFFFFFFL; - - /* calibration cycle until it'll be preciser */ - maxms = -1000; - do { - lastMeasureOverhead = measureOverhead; - TclNewLongObj(clobjv[i], (int)maxms); - Tcl_IncrRefCount(clobjv[i]); - result = Tcl_TimeRateObjCmd(dummy, interp, i+1, clobjv); - Tcl_DecrRefCount(clobjv[i]); - if (result != TCL_OK) { - return result; - } - maxCalTime += maxms; - /* increase maxms for preciser calibration */ - maxms -= (-maxms / 4); - /* as long as new value more as 0.05% better */ - } while ( (measureOverhead >= lastMeasureOverhead - || measureOverhead / lastMeasureOverhead <= 0.9995) - && maxCalTime > 0 - ); - - return result; - } - if (maxms == 0) { - /* reset last measurement overhead */ - measureOverhead = 0; - Tcl_SetObjResult(interp, Tcl_NewLongObj(0)); - return TCL_OK; - } - - /* if time is negative - make current overhead more precise */ - if (maxms > 0) { - /* set last measurement overhead to max */ - measureOverhead = (double)0x7FFFFFFFFFFFFFFFL; - } else { - maxms = -maxms; - } - - } - - if (maxms == -0x7FFFFFFFFFFFFFFFL) { - maxms = 1000; - } - if (overhead == -1) { - overhead = measureOverhead; - } - - /* be sure that resetting of result will not smudge the further measurement */ - Tcl_ResetResult(interp); - - /* compile object */ - if (!direct) { - if (TclInterpReady(interp) != TCL_OK) { - return TCL_ERROR; - } - codePtr = TclCompileObj(interp, objPtr, NULL, 0); - TclPreserveByteCode(codePtr); - } - - /* get start and stop time */ -#ifdef TCL_WIDE_CLICKS - start = middle = TclpGetWideClicks(); - /* time to stop execution (in wide clicks) */ - stop = start + (maxms * 1000 / TclpWideClickInMicrosec()); -#else - Tcl_GetTime(&now); - start = now.sec; start *= 1000000; start += now.usec; - middle = start; - /* time to stop execution (in microsecs) */ - stop = start + maxms * 1000; -#endif - - /* start measurement */ - while (1) { - /* eval single iteration */ - count++; - - if (!direct) { - /* precompiled */ - rootPtr = TOP_CB(interp); - result = TclNRExecuteByteCode(interp, codePtr); - result = TclNRRunCallbacks(interp, result, rootPtr); - } else { - /* eval */ - result = TclEvalObjEx(interp, objPtr, 0, NULL, 0); - } - if (result != TCL_OK) { - goto done; - } - - /* don't check time up to threshold */ - if (--threshold > 0) continue; - - /* check stop time reached, estimate new threshold */ - #ifdef TCL_WIDE_CLICKS - middle = TclpGetWideClicks(); - #else - Tcl_GetTime(&now); - middle = now.sec; middle *= 1000000; middle += now.usec; - #endif - if (middle >= stop) { - break; - } - - /* don't calculate threshold by few iterations, because sometimes - * first iteration(s) can be too fast (cached, delayed clean up, etc) */ - if (count < 10) { - threshold = 1; continue; - } - - /* average iteration time in microsecs */ - threshold = (middle - start) / count; - if (threshold > maxIterTm) { - maxIterTm = threshold; - } - /* as relation between remaining time and time since last check */ - threshold = ((stop - middle) / maxIterTm) / 4; - if (threshold > 100000) { /* fix for too large threshold */ - threshold = 100000; - } - } - - { - Tcl_Obj *objarr[8], **objs = objarr; - Tcl_WideInt val; - const char *fmt; - - middle -= start; /* execution time in microsecs */ - - #ifdef TCL_WIDE_CLICKS - /* convert execution time in wide clicks to microsecs */ - middle *= TclpWideClickInMicrosec(); - #endif - - /* if not calibrate */ - if (!calibrate) { - /* minimize influence of measurement overhead */ - if (overhead > 0) { - /* estimate the time of overhead (microsecs) */ - Tcl_WideInt curOverhead = overhead * count; - if (middle > curOverhead) { - middle -= curOverhead; - } else { - middle = 1; - } - } - } else { - /* calibration - obtaining new measurement overhead */ - if (measureOverhead > (double)middle / count) { - measureOverhead = (double)middle / count; - } - objs[0] = Tcl_NewDoubleObj(measureOverhead); - TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#-overhead"); /* mics */ - objs += 2; - } - - val = middle / count; /* microsecs per iteration */ - if (val >= 1000000) { - objs[0] = Tcl_NewWideIntObj(val); - } else { - if (val < 10) { fmt = "%.6f"; } else - if (val < 100) { fmt = "%.4f"; } else - if (val < 1000) { fmt = "%.3f"; } else - if (val < 10000) { fmt = "%.2f"; } else - { fmt = "%.1f"; }; - objs[0] = Tcl_ObjPrintf(fmt, ((double)middle)/count); - } - - objs[2] = Tcl_NewWideIntObj(count); /* iterations */ - - /* calculate speed as rate (count) per sec */ - if (!middle) middle++; /* +1 ms, just to avoid divide by zero */ - if (count < (0x7FFFFFFFFFFFFFFFL / 1000000)) { - val = (count * 1000000) / middle; - if (val < 100000) { - if (val < 100) { fmt = "%.3f"; } else - if (val < 1000) { fmt = "%.2f"; } else - { fmt = "%.1f"; }; - objs[4] = Tcl_ObjPrintf(fmt, ((double)(count * 1000000)) / middle); - } else { - objs[4] = Tcl_NewWideIntObj(val); - } - } else { - objs[4] = Tcl_NewWideIntObj((count / middle) * 1000000); - } - - /* estimated net execution time (in millisecs) */ - if (!calibrate) { - objs[6] = Tcl_ObjPrintf("%.3f", (double)middle / 1000); - TclNewLiteralStringObj(objs[7], "nett-ms"); - } - - /* - * Construct the result as a list because many programs have always parsed - * as such (extracting the first element, typically). - */ - - TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#"); /* mics/# */ - TclNewLiteralStringObj(objs[3], "#"); - TclNewLiteralStringObj(objs[5], "#/sec"); - Tcl_SetObjResult(interp, Tcl_NewListObj(8, objarr)); - } - -done: - - if (codePtr != NULL) { - TclReleaseByteCode(codePtr); - } - - return result; -} - -/* - *---------------------------------------------------------------------- - * * Tcl_TryObjCmd, TclNRTryObjCmd -- * * This procedure is invoked to process the "try" Tcl command. See the diff --git a/generic/tclDate.c b/generic/tclDate.c index 934fe5f..e4dd000 100644 --- a/generic/tclDate.c +++ b/generic/tclDate.c @@ -1,14 +1,14 @@ -/* A Bison parser, made by GNU Bison 2.4.2. */ +/* A Bison parser, made by GNU Bison 2.3. */ /* Skeleton implementation for Bison's Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2006, 2009-2010 Free Software - Foundation, Inc. + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. - This program is free software: you can redistribute it and/or modify + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + the Free Software Foundation; either version 2, or (at your option) + any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -16,7 +16,9 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. */ + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -45,7 +47,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.4.2" +#define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -53,24 +55,65 @@ /* Pure parsers. */ #define YYPURE 1 -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - /* Using locations. */ #define YYLSP_NEEDED 1 /* Substitute the variable and function names. */ -#define yyparse TclDateparse -#define yylex TclDatelex -#define yyerror TclDateerror -#define yylval TclDatelval -#define yychar TclDatechar -#define yydebug TclDatedebug -#define yynerrs TclDatenerrs -#define yylloc TclDatelloc +#define yyparse TclDateparse +#define yylex TclDatelex +#define yyerror TclDateerror +#define yylval TclDatelval +#define yychar TclDatechar +#define yydebug TclDatedebug +#define yynerrs TclDatenerrs +#define yylloc TclDatelloc + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + tAGO = 258, + tDAY = 259, + tDAYZONE = 260, + tID = 261, + tMERIDIAN = 262, + tMONTH = 263, + tMONTH_UNIT = 264, + tSTARDATE = 265, + tSEC_UNIT = 266, + tSNUMBER = 267, + tUNUMBER = 268, + tZONE = 269, + tEPOCH = 270, + tDST = 271, + tISOBASE = 272, + tDAY_UNIT = 273, + tNEXT = 274 + }; +#endif +/* Tokens. */ +#define tAGO 258 +#define tDAY 259 +#define tDAYZONE 260 +#define tID 261 +#define tMERIDIAN 262 +#define tMONTH 263 +#define tMONTH_UNIT 264 +#define tSTARDATE 265 +#define tSEC_UNIT 266 +#define tSNUMBER 267 +#define tUNUMBER 268 +#define tZONE 269 +#define tEPOCH 270 +#define tDST 271 +#define tISOBASE 272 +#define tDAY_UNIT 273 +#define tNEXT 274 + + + /* Copy the first part of user declarations. */ @@ -86,7 +129,6 @@ * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * */ #include "tclInt.h" @@ -104,11 +146,73 @@ * parsed fields will be returned. */ -#include "tclDate.h" +typedef struct DateInfo { + + Tcl_Obj* messages; /* Error messages */ + const char* separatrix; /* String separating messages */ + + time_t dateYear; + time_t dateMonth; + time_t dateDay; + int dateHaveDate; + + time_t dateHour; + time_t dateMinutes; + time_t dateSeconds; + int dateMeridian; + int dateHaveTime; + + time_t dateTimezone; + int dateDSTmode; + int dateHaveZone; + + time_t dateRelMonth; + time_t dateRelDay; + time_t dateRelSeconds; + int dateHaveRel; + + time_t dateMonthOrdinal; + int dateHaveOrdinalMonth; + + time_t dateDayOrdinal; + time_t dateDayNumber; + int dateHaveDay; + + const char *dateStart; + const char *dateInput; + time_t *dateRelPointer; + + int dateDigitCount; +} DateInfo; #define YYMALLOC ckalloc #define YYFREE(x) (ckfree((void*) (x))) +#define yyDSTmode (info->dateDSTmode) +#define yyDayOrdinal (info->dateDayOrdinal) +#define yyDayNumber (info->dateDayNumber) +#define yyMonthOrdinal (info->dateMonthOrdinal) +#define yyHaveDate (info->dateHaveDate) +#define yyHaveDay (info->dateHaveDay) +#define yyHaveOrdinalMonth (info->dateHaveOrdinalMonth) +#define yyHaveRel (info->dateHaveRel) +#define yyHaveTime (info->dateHaveTime) +#define yyHaveZone (info->dateHaveZone) +#define yyTimezone (info->dateTimezone) +#define yyDay (info->dateDay) +#define yyMonth (info->dateMonth) +#define yyYear (info->dateYear) +#define yyHour (info->dateHour) +#define yyMinutes (info->dateMinutes) +#define yySeconds (info->dateSeconds) +#define yyMeridian (info->dateMeridian) +#define yyRelMonth (info->dateRelMonth) +#define yyRelDay (info->dateRelDay) +#define yyRelSeconds (info->dateRelSeconds) +#define yyRelPointer (info->dateRelPointer) +#define yyInput (info->dateInput) +#define yyDigitCount (info->dateDigitCount) + #define EPOCH 1970 #define START_OF_TIME 1902 #define END_OF_TIME 2037 @@ -142,6 +246,13 @@ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; +/* + * Meridian: am, pm, or 24-hour style. + */ + +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; @@ -163,49 +274,19 @@ typedef enum _DSTMODE { # define YYTOKEN_TABLE 0 #endif - -/* Tokens. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - /* Put the tokens into the symbol table, so that GDB and other debuggers - know about them. */ - enum yytokentype { - tAGO = 258, - tDAY = 259, - tDAYZONE = 260, - tID = 261, - tMERIDIAN = 262, - tMONTH = 263, - tMONTH_UNIT = 264, - tSTARDATE = 265, - tSEC_UNIT = 266, - tSNUMBER = 267, - tUNUMBER = 268, - tZONE = 269, - tEPOCH = 270, - tDST = 271, - tISOBASE = 272, - tDAY_UNIT = 273, - tNEXT = 274 - }; -#endif - - - #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -{ - +{ time_t Number; enum _MERIDIAN Meridian; +} +/* Line 187 of yacc.c. */ - - -} YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 + YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 #endif #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED @@ -235,10 +316,14 @@ static int LookupWord(YYSTYPE* yylvalPtr, char *buff); DateInfo* info, const char *s); static int TclDatelex(YYSTYPE* yylvalPtr, YYLTYPE* location, DateInfo* info); +static time_t ToSeconds(time_t Hours, time_t Minutes, + time_t Seconds, MERIDIAN Meridian); MODULE_SCOPE int yyparse(DateInfo*); +/* Line 216 of yacc.c. */ + #ifdef short # undef short @@ -274,21 +359,15 @@ typedef short int yytype_int16; #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ - || defined __cplusplus || defined _MSC_VER) -# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t # else -# define YYSIZE_T unsigned int +# define YYSIZE_T size_t # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ -# if defined YYENABLE_NLS && YYENABLE_NLS +# if YYENABLE_NLS # if ENABLE_NLS # include <libintl.h> /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) @@ -313,14 +392,14 @@ typedef short int yytype_int16; #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int yyi) +YYID (int i) #else static int -YYID (yyi) - int yyi; +YYID (i) + int i; #endif { - return yyi; + return i; } #endif @@ -402,9 +481,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss_alloc; - YYSTYPE yyvs_alloc; - YYLTYPE yyls_alloc; + yytype_int16 yyss; + YYSTYPE yyvs; + YYLTYPE yyls; }; /* The size of the maximum gap between one aligned stack and the next. */ @@ -439,12 +518,12 @@ union yyalloc elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ +# define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ @@ -545,12 +624,12 @@ static const yytype_int8 yyrhs[] = /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 152, 152, 153, 156, 159, 162, 165, 168, 171, - 174, 178, 183, 186, 192, 198, 206, 212, 223, 227, - 231, 237, 241, 245, 249, 253, 259, 263, 268, 273, - 278, 283, 287, 292, 296, 301, 308, 312, 318, 327, - 336, 346, 360, 365, 368, 371, 374, 377, 380, 385, - 388, 393, 397, 401, 407, 425, 428 + 0, 225, 225, 226, 229, 232, 235, 238, 241, 244, + 247, 251, 256, 259, 265, 271, 279, 285, 296, 300, + 304, 310, 314, 318, 322, 326, 332, 336, 341, 346, + 351, 356, 360, 365, 369, 374, 381, 385, 391, 400, + 409, 419, 433, 438, 441, 444, 447, 450, 453, 458, + 461, 466, 470, 474, 480, 498, 501 }; #endif @@ -704,18 +783,9 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. However, - YYFAIL appears to be in use. Nevertheless, it is formally deprecated - in Bison 2.4.2's NEWS entry, where a plan to phase it out is - discussed. */ + Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab -#if defined YYFAIL - /* This is here to suppress warnings from the GCC cpp's - -Wunused-macros. Normally we don't worry about that warning, but - some users do, and we want to make it easy for users to remove - YYFAIL uses, which will produce warnings from Bison 2.5. */ -#endif #define YYRECOVERING() (!!yyerrstatus) @@ -772,7 +842,7 @@ while (YYID (0)) we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT -# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ @@ -891,20 +961,17 @@ yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, info) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void -yy_stack_print (yybottom, yytop) - yytype_int16 *yybottom; - yytype_int16 *yytop; +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } @@ -940,11 +1007,11 @@ yy_reduce_print (yyvsp, yylsp, yyrule, info) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - YYFPRINTF (stderr, " $%d = ", yyi + 1); + fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) , &(yylsp[(yyi + 1) - (yynrhs)]) , info); - YYFPRINTF (stderr, "\n"); + fprintf (stderr, "\n"); } } @@ -1228,8 +1295,10 @@ yydestruct (yymsg, yytype, yyvaluep, yylocationp, info) break; } } + /* Prevent warnings from -Wmissing-prototypes. */ + #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); @@ -1248,9 +1317,10 @@ int yyparse (); -/*-------------------------. -| yyparse or yypush_parse. | -`-------------------------*/ + +/*----------. +| yyparse. | +`----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ @@ -1274,97 +1344,88 @@ yyparse (info) #endif #endif { -/* The lookahead symbol. */ + /* The look-ahead symbol. */ int yychar; -/* The semantic value of the lookahead symbol. */ +/* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; -/* Location data for the lookahead symbol. */ +/* Number of syntax errors so far. */ +int yynerrs; +/* Location data for the look-ahead symbol. */ YYLTYPE yylloc; - /* Number of syntax errors so far. */ - int yynerrs; - - int yystate; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif - /* The stacks and their tools: - `yyss': related to states. - `yyvs': related to semantic values. - `yyls': related to locations. + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss; - yytype_int16 *yyssp; + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs; - YYSTYPE *yyvsp; + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; - /* The location stack. */ - YYLTYPE yylsa[YYINITDEPTH]; - YYLTYPE *yyls; - YYLTYPE *yylsp; + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[2]; - /* The locations where the error started and ended. */ - YYLTYPE yyerror_range[2]; +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) - YYSIZE_T yystacksize; + YYSIZE_T yystacksize = YYINITDEPTH; - int yyn; - int yyresult; - /* Lookahead token as an internal (translated) token number. */ - int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; YYLTYPE yyloc; -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) - /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; - yyls = yylsa; - yystacksize = YYINITDEPTH; - YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ + yyssp = yyss; yyvsp = yyvs; yylsp = yyls; - -#if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +#if YYLTYPE_IS_TRIVIAL /* Initialize the default location before parsing starts. */ yylloc.first_line = yylloc.last_line = 1; - yylloc.first_column = yylloc.last_column = 1; + yylloc.first_column = yylloc.last_column = 0; #endif goto yysetstate; @@ -1403,7 +1464,6 @@ YYLTYPE yylloc; &yyvs1, yysize * sizeof (*yyvsp), &yyls1, yysize * sizeof (*yylsp), &yystacksize); - yyls = yyls1; yyss = yyss1; yyvs = yyvs1; @@ -1425,9 +1485,9 @@ YYLTYPE yylloc; (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); - YYSTACK_RELOCATE (yyls_alloc, yyls); + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + YYSTACK_RELOCATE (yyls); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -1448,9 +1508,6 @@ YYLTYPE yylloc; YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - if (yystate == YYFINAL) - YYACCEPT; - goto yybackup; /*-----------. @@ -1459,16 +1516,16 @@ YYLTYPE yylloc; yybackup: /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ + look-ahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to lookahead token. */ + /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; - /* Not known => get a lookahead token if don't already have one. */ + /* Not known => get a look-ahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -1500,16 +1557,20 @@ yybackup: goto yyreduce; } + if (yyn == YYFINAL) + YYACCEPT; + /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; - /* Shift the lookahead token. */ + /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token. */ - yychar = YYEMPTY; + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; @@ -1817,16 +1878,16 @@ yyreduce: case 36: { - yyMonthOrdinalIncr = 1; - yyMonthOrdinal = (yyvsp[(2) - (2)].Number); + yyMonthOrdinal = 1; + yyMonth = (yyvsp[(2) - (2)].Number); ;} break; case 37: { - yyMonthOrdinalIncr = (yyvsp[(2) - (3)].Number); - yyMonthOrdinal = (yyvsp[(3) - (3)].Number); + yyMonthOrdinal = (yyvsp[(2) - (3)].Number); + yyMonth = (yyvsp[(3) - (3)].Number); ;} break; @@ -2001,6 +2062,7 @@ yyreduce: break; +/* Line 1267 of yacc.c. */ default: break; } @@ -2077,7 +2139,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse lookahead token after an + /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -2094,7 +2156,7 @@ yyerrlab: } } - /* Else will try to reuse lookahead token after shifting the error + /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; @@ -2152,11 +2214,14 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } + if (yyn == YYFINAL) + YYACCEPT; + *++yyvsp = yylval; yyerror_range[1] = yylloc; /* Using YYLLOC is tempting, but would change the location of - the lookahead. YYLOC is available though. */ + the look-ahead. YYLOC is available though. */ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); *++yylsp = yyloc; @@ -2181,7 +2246,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -2192,7 +2257,7 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEMPTY) + if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, &yylloc, info); /* Do not reclaim the symbols of the rule which action triggered @@ -2448,11 +2513,11 @@ TclDateerror( infoPtr->separatrix = "\n"; } -MODULE_SCOPE int +static time_t ToSeconds( - int Hours, - int Minutes, - int Seconds, + time_t Hours, + time_t Minutes, + time_t Seconds, MERIDIAN Meridian) { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) { @@ -2615,7 +2680,7 @@ TclDatelex( location->first_column = yyInput - info->dateStart; for ( ; ; ) { - while (isspace(UCHAR(*yyInput))) { + while (TclIsSpaceProc(*yyInput)) { yyInput++; } @@ -2675,36 +2740,65 @@ TclDatelex( } while (Count > 0); } } - + int -TclClockFreeScan( +TclClockOldscanObjCmd( + ClientData clientData, /* Unused */ Tcl_Interp *interp, /* Tcl interpreter */ - DateInfo *info) /* Input and result parameters */ + int objc, /* Count of paraneters */ + Tcl_Obj *const *objv) /* Parameters */ { + Tcl_Obj *result, *resultElement; + int yr, mo, da; + DateInfo dateInfo; + DateInfo* info = &dateInfo; int status; - /* - * yyInput = stringToParse; - * - * ClockInitDateInfo(info) should be executed to pre-init info; - */ + if (objc != 5) { + Tcl_WrongNumArgs(interp, 1, objv, + "stringToParse baseYear baseMonth baseDay" ); + return TCL_ERROR; + } - yyDSTmode = DSTmaybe; + yyInput = Tcl_GetString( objv[1] ); + dateInfo.dateStart = yyInput; + + yyHaveDate = 0; + if (Tcl_GetIntFromObj(interp, objv[2], &yr) != TCL_OK + || Tcl_GetIntFromObj(interp, objv[3], &mo) != TCL_OK + || Tcl_GetIntFromObj(interp, objv[4], &da) != TCL_OK) { + return TCL_ERROR; + } + yyYear = yr; yyMonth = mo; yyDay = da; + + yyHaveTime = 0; + yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; + + yyHaveZone = 0; + yyTimezone = 0; yyDSTmode = DSTmaybe; + + yyHaveOrdinalMonth = 0; + yyMonthOrdinal = 0; + + yyHaveDay = 0; + yyDayOrdinal = 0; yyDayNumber = 0; - info->messages = Tcl_NewObj(); - info->separatrix = ""; - Tcl_IncrRefCount(info->messages); + yyHaveRel = 0; + yyRelMonth = 0; yyRelDay = 0; yyRelSeconds = 0; yyRelPointer = NULL; - info->dateStart = yyInput; - status = yyparse(info); + dateInfo.messages = Tcl_NewObj(); + dateInfo.separatrix = ""; + Tcl_IncrRefCount(dateInfo.messages); + + status = yyparse(&dateInfo); if (status == 1) { - Tcl_SetObjResult(interp, info->messages); - Tcl_DecrRefCount(info->messages); + Tcl_SetObjResult(interp, dateInfo.messages); + Tcl_DecrRefCount(dateInfo.messages); Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "PARSE", NULL); return TCL_ERROR; } else if (status == 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("memory exhausted", -1)); - Tcl_DecrRefCount(info->messages); + Tcl_DecrRefCount(dateInfo.messages); Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); return TCL_ERROR; } else if (status != 0) { @@ -2712,11 +2806,11 @@ TclClockFreeScan( "from date parser. Please " "report this error as a " "bug in Tcl.", -1)); - Tcl_DecrRefCount(info->messages); + Tcl_DecrRefCount(dateInfo.messages); Tcl_SetErrorCode(interp, "TCL", "BUG", NULL); return TCL_ERROR; } - Tcl_DecrRefCount(info->messages); + Tcl_DecrRefCount(dateInfo.messages); if (yyHaveDate > 1) { Tcl_SetObjResult(interp, @@ -2749,40 +2843,6 @@ TclClockFreeScan( return TCL_ERROR; } - return TCL_OK; -} - -int -TclClockOldscanObjCmd( - ClientData clientData, /* Unused */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Count of paraneters */ - Tcl_Obj *const *objv) /* Parameters */ -{ - Tcl_Obj *result, *resultElement; - int yr, mo, da; - DateInfo dateInfo; - DateInfo* info = &dateInfo; - - if (objc != 5) { - Tcl_WrongNumArgs(interp, 1, objv, - "stringToParse baseYear baseMonth baseDay" ); - return TCL_ERROR; - } - - yyInput = Tcl_GetString( objv[1] ); - - if (Tcl_GetIntFromObj(interp, objv[2], &yr) != TCL_OK - || Tcl_GetIntFromObj(interp, objv[3], &mo) != TCL_OK - || Tcl_GetIntFromObj(interp, objv[4], &da) != TCL_OK) { - return TCL_ERROR; - } - yyYear = yr; yyMonth = mo; yyDay = da; - - if (TclClockFreeScan(interp, info) != TCL_OK) { - return TCL_ERROR; - } - result = Tcl_NewObj(); resultElement = Tcl_NewObj(); if (yyHaveDate) { @@ -2834,9 +2894,9 @@ TclClockOldscanObjCmd( resultElement = Tcl_NewObj(); if (yyHaveOrdinalMonth) { Tcl_ListObjAppendElement(interp, resultElement, - Tcl_NewIntObj((int) yyMonthOrdinalIncr)); - Tcl_ListObjAppendElement(interp, resultElement, Tcl_NewIntObj((int) yyMonthOrdinal)); + Tcl_ListObjAppendElement(interp, resultElement, + Tcl_NewIntObj((int) yyMonth)); } Tcl_ListObjAppendElement(interp, result, resultElement); diff --git a/generic/tclDate.h b/generic/tclDate.h deleted file mode 100644 index 570a8e4..0000000 --- a/generic/tclDate.h +++ /dev/null @@ -1,512 +0,0 @@ -/* - * tclDate.h -- - * - * This header file handles common usage of clock primitives - * between tclDate.c (yacc), tclClock.c and tclClockFmt.c. - * - * Copyright (c) 2014 Serg 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. - */ - -#ifndef _TCLCLOCK_H -#define _TCLCLOCK_H - -/* - * Constants - */ - -#define JULIAN_DAY_POSIX_EPOCH 2440588 -#define GREGORIAN_CHANGE_DATE 2361222 -#define SECONDS_PER_DAY 86400 -#define JULIAN_SEC_POSIX_EPOCH (((Tcl_WideInt) JULIAN_DAY_POSIX_EPOCH) \ - * SECONDS_PER_DAY) -#define FOUR_CENTURIES 146097 /* days */ -#define JDAY_1_JAN_1_CE_JULIAN 1721424 -#define JDAY_1_JAN_1_CE_GREGORIAN 1721426 -#define ONE_CENTURY_GREGORIAN 36524 /* days */ -#define FOUR_YEARS 1461 /* days */ -#define ONE_YEAR 365 /* days */ - -#define RODDENBERRY 1946 /* Another epoch (Hi, Jeff!) */ - - -#define CLF_OPTIONAL (1 << 0) /* token is non mandatory */ -#define CLF_POSIXSEC (1 << 1) -#define CLF_LOCALSEC (1 << 2) -#define CLF_JULIANDAY (1 << 3) -#define CLF_TIME (1 << 4) -#define CLF_CENTURY (1 << 6) -#define CLF_DAYOFMONTH (1 << 7) -#define CLF_DAYOFYEAR (1 << 8) -#define CLF_MONTH (1 << 9) -#define CLF_YEAR (1 << 10) -#define CLF_ISO8601YEAR (1 << 12) -#define CLF_ISO8601 (1 << 13) -#define CLF_ISO8601CENTURY (1 << 14) -#define CLF_SIGNED (1 << 15) -/* On demand (lazy) assemble flags */ -#define CLF_ASSEMBLE_DATE (1 << 28) /* assemble year, month, etc. using julianDay */ -#define CLF_ASSEMBLE_JULIANDAY (1 << 29) /* assemble julianDay using year, month, etc. */ -#define CLF_ASSEMBLE_SECONDS (1 << 30) /* assemble localSeconds (and seconds at end) */ - -#define CLF_DATE (CLF_JULIANDAY | CLF_DAYOFMONTH | CLF_DAYOFYEAR | \ - CLF_MONTH | CLF_YEAR | CLF_ISO8601YEAR | CLF_ISO8601) - -/* - * Enumeration of the string literals used in [clock] - */ - -typedef enum ClockLiteral { - LIT__NIL, - LIT__DEFAULT_FORMAT, - LIT_SYSTEM, LIT_CURRENT, LIT_C, - LIT_BCE, LIT_CE, - LIT_DAYOFMONTH, LIT_DAYOFWEEK, LIT_DAYOFYEAR, - LIT_ERA, LIT_GMT, LIT_GREGORIAN, - LIT_INTEGER_VALUE_TOO_LARGE, - LIT_ISO8601WEEK, LIT_ISO8601YEAR, - LIT_JULIANDAY, LIT_LOCALSECONDS, - LIT_MONTH, - LIT_SECONDS, LIT_TZNAME, LIT_TZOFFSET, - LIT_YEAR, - LIT_TZDATA, - LIT_GETSYSTEMTIMEZONE, - LIT_SETUPTIMEZONE, - LIT_MCGET, - LIT_GETSYSTEMLOCALE, LIT_GETCURRENTLOCALE, - LIT_LOCALIZE_FORMAT, - LIT__END -} ClockLiteral; - -#define CLOCK_LITERAL_ARRAY(litarr) static const char *const litarr[] = { \ - "", \ - "%a %b %d %H:%M:%S %Z %Y", \ - "system", "current", "C", \ - "BCE", "CE", \ - "dayOfMonth", "dayOfWeek", "dayOfYear", \ - "era", ":GMT", "gregorian", \ - "integer value too large to represent", \ - "iso8601Week", "iso8601Year", \ - "julianDay", "localSeconds", \ - "month", \ - "seconds", "tzName", "tzOffset", \ - "year", \ - "::tcl::clock::TZData", \ - "::tcl::clock::GetSystemTimeZone", \ - "::tcl::clock::SetupTimeZone", \ - "::tcl::clock::mcget", \ - "::tcl::clock::GetSystemLocale", "::tcl::clock::mclocale", \ - "::tcl::clock::LocalizeFormat" \ -} - -/* - * Enumeration of the msgcat literals used in [clock] - */ - -typedef enum ClockMsgCtLiteral { - MCLIT__NIL, /* placeholder */ - MCLIT_MONTHS_FULL, MCLIT_MONTHS_ABBREV, MCLIT_MONTHS_COMB, - MCLIT_DAYS_OF_WEEK_FULL, MCLIT_DAYS_OF_WEEK_ABBREV, MCLIT_DAYS_OF_WEEK_COMB, - MCLIT_AM, MCLIT_PM, - MCLIT_LOCALE_ERAS, - MCLIT_BCE, MCLIT_CE, - MCLIT_BCE2, MCLIT_CE2, - MCLIT_BCE3, MCLIT_CE3, - MCLIT_LOCALE_NUMERALS, - MCLIT__END -} ClockMsgCtLiteral; - -#define CLOCK_LOCALE_LITERAL_ARRAY(litarr, pref) static const char *const litarr[] = { \ - pref "", \ - pref "MONTHS_FULL", pref "MONTHS_ABBREV", pref "MONTHS_COMB", \ - pref "DAYS_OF_WEEK_FULL", pref "DAYS_OF_WEEK_ABBREV", pref "DAYS_OF_WEEK_COMB", \ - pref "AM", pref "PM", \ - pref "LOCALE_ERAS", \ - pref "BCE", pref "CE", \ - pref "b.c.e.", pref "c.e.", \ - pref "b.c.", pref "a.d.", \ - pref "LOCALE_NUMERALS", \ -} - -/* - * Structure containing the fields used in [clock format] and [clock scan] - */ - -typedef struct TclDateFields { - - /* Cacheable fields: */ - - Tcl_WideInt seconds; /* Time expressed in seconds from the Posix - * epoch */ - Tcl_WideInt localSeconds; /* Local time expressed in nominal seconds - * from the Posix epoch */ - int tzOffset; /* Time zone offset in seconds east of - * Greenwich */ - int julianDay; /* Julian Day Number in local time zone */ - enum {BCE=1, CE=0} era; /* Era */ - int gregorian; /* Flag == 1 if the date is Gregorian */ - int year; /* Year of the era */ - int dayOfYear; /* Day of the year (1 January == 1) */ - int month; /* Month number */ - int dayOfMonth; /* Day of the month */ - int iso8601Year; /* ISO8601 week-based year */ - int iso8601Week; /* ISO8601 week number */ - int dayOfWeek; /* Day of the week */ - int hour; /* Hours of day (in-between time only calculation) */ - int minutes; /* Minutes of day (in-between time only calculation) */ - int secondOfDay; /* Seconds of day (in-between time only calculation) */ - - /* Non cacheable fields: */ - - Tcl_Obj *tzName; /* Name (or corresponding DST-abbreviation) of the - * time zone, if set the refCount is incremented */ -} TclDateFields; - -#define ClockCacheableDateFieldsSize \ - TclOffset(TclDateFields, tzName) - -/* - * Structure contains return parsed fields. - */ - -typedef struct DateInfo { - const char *dateStart; - const char *dateInput; - const char *dateEnd; - - TclDateFields date; - - int flags; - - int dateHaveDate; - - int dateMeridian; - int dateHaveTime; - - int dateTimezone; - int dateDSTmode; - int dateHaveZone; - - int dateRelMonth; - int dateRelDay; - int dateRelSeconds; - int dateHaveRel; - - int dateMonthOrdinalIncr; - int dateMonthOrdinal; - int dateHaveOrdinalMonth; - - int dateDayOrdinal; - int dateDayNumber; - int dateHaveDay; - - int *dateRelPointer; - - int dateSpaceCount; - int dateDigitCount; - - int dateCentury; - - Tcl_Obj* messages; /* Error messages */ - const char* separatrix; /* String separating messages */ -} DateInfo; - -#define yydate (info->date) /* Date fields used for converting */ - -#define yyDay (info->date.dayOfMonth) -#define yyMonth (info->date.month) -#define yyYear (info->date.year) - -#define yyHour (info->date.hour) -#define yyMinutes (info->date.minutes) -#define yySeconds (info->date.secondOfDay) - -#define yyDSTmode (info->dateDSTmode) -#define yyDayOrdinal (info->dateDayOrdinal) -#define yyDayNumber (info->dateDayNumber) -#define yyMonthOrdinalIncr (info->dateMonthOrdinalIncr) -#define yyMonthOrdinal (info->dateMonthOrdinal) -#define yyHaveDate (info->dateHaveDate) -#define yyHaveDay (info->dateHaveDay) -#define yyHaveOrdinalMonth (info->dateHaveOrdinalMonth) -#define yyHaveRel (info->dateHaveRel) -#define yyHaveTime (info->dateHaveTime) -#define yyHaveZone (info->dateHaveZone) -#define yyTimezone (info->dateTimezone) -#define yyMeridian (info->dateMeridian) -#define yyRelMonth (info->dateRelMonth) -#define yyRelDay (info->dateRelDay) -#define yyRelSeconds (info->dateRelSeconds) -#define yyRelPointer (info->dateRelPointer) -#define yyInput (info->dateInput) -#define yyDigitCount (info->dateDigitCount) -#define yySpaceCount (info->dateSpaceCount) - -static inline void -ClockInitDateInfo(DateInfo *info) { - memset(info, 0, sizeof(DateInfo)); -} - -/* - * Structure containing the command arguments supplied to [clock format] and [clock scan] - */ - -#define CLF_EXTENDED (1 << 4) -#define CLF_STRICT (1 << 8) -#define CLF_LOCALE_USED (1 << 15) - -typedef struct ClockFmtScnCmdArgs { - ClientData clientData; /* Opaque pointer to literal pool, etc. */ - Tcl_Interp *interp; /* Tcl interpreter */ - - Tcl_Obj *formatObj; /* Format */ - Tcl_Obj *localeObj; /* Name of the locale where the time will be expressed. */ - Tcl_Obj *timezoneObj; /* Default time zone in which the time will be expressed */ - Tcl_Obj *baseObj; /* Base (scan and add) or clockValue (format) */ - int flags; /* Flags control scanning */ - - Tcl_Obj *mcDictObj; /* Current dictionary of tcl::clock package for given localeObj*/ -} ClockFmtScnCmdArgs; - -/* - * Structure containing the client data for [clock] - */ - -typedef struct ClockClientData { - size_t refCount; /* Number of live references. */ - Tcl_Obj **literals; /* Pool of object literals (common, locale independent). */ - Tcl_Obj **mcLiterals; /* Msgcat object literals with mc-keys for search with locale. */ - Tcl_Obj **mcLitIdxs; /* Msgcat object indices prefixed with _IDX_, - * used for quick dictionary search */ - - /* Cache for current clock parameters, imparted via "configure" */ - unsigned long LastTZEpoch; - int currentYearCentury; - int yearOfCenturySwitch; - Tcl_Obj *SystemTimeZone; - Tcl_Obj *SystemSetupTZData; - Tcl_Obj *GMTSetupTimeZone; - Tcl_Obj *GMTSetupTZData; - Tcl_Obj *AnySetupTimeZone; - Tcl_Obj *AnySetupTZData; - Tcl_Obj *LastUnnormSetupTimeZone; - Tcl_Obj *LastSetupTimeZone; - Tcl_Obj *LastSetupTZData; - - Tcl_Obj *CurrentLocale; - Tcl_Obj *CurrentLocaleDict; - Tcl_Obj *LastUnnormUsedLocale; - Tcl_Obj *LastUsedLocale; - Tcl_Obj *LastUsedLocaleDict; - - /* Cache for last base (last-second fast convert if base/tz not changed) */ - struct { - Tcl_Obj *timezoneObj; - TclDateFields Date; - } lastBase; - /* Las-period cache for fast UTC2Local conversion */ - struct { - /* keys */ - Tcl_Obj *timezoneObj; - int changeover; - Tcl_WideInt seconds; - Tcl_WideInt rangesVal[2]; /* Bounds for cached time zone offset */ - /* values */ - int tzOffset; - Tcl_Obj *tzName; - } UTC2Local; - /* Las-period cache for fast Local2UTC conversion */ - struct { - /* keys */ - Tcl_Obj *timezoneObj; - int changeover; - Tcl_WideInt localSeconds; - Tcl_WideInt rangesVal[2]; /* Bounds for cached time zone offset */ - /* values */ - int tzOffset; - } Local2UTC; -} ClockClientData; - -#define ClockDefaultYearCentury 2000 -#define ClockDefaultCenturySwitch 38 - -/* - * Meridian: am, pm, or 24-hour style. - */ - -typedef enum _MERIDIAN { - MERam, MERpm, MER24 -} MERIDIAN; - -/* - * Clock scan and format facilities. - */ - -#define CLOCK_FMT_SCN_STORAGE_GC_SIZE 32 - -#define CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE 2 - -typedef struct ClockScanToken ClockScanToken; - - -typedef int ClockScanTokenProc( - ClockFmtScnCmdArgs *opts, - DateInfo *info, - ClockScanToken *tok); - - -typedef enum _CLCKTOK_TYPE { - CTOKT_DIGIT = 1, CTOKT_PARSER, CTOKT_SPACE, CTOKT_WORD, CTOKT_CHAR, - CFMTT_INT, CFMTT_WIDE, CFMTT_PROC -} CLCKTOK_TYPE; - -typedef struct ClockScanTokenMap { - unsigned short int type; - unsigned short int flags; - unsigned short int clearFlags; - unsigned short int minSize; - unsigned short int maxSize; - unsigned short int offs; - ClockScanTokenProc *parser; - const void *data; -} ClockScanTokenMap; - -typedef struct ClockScanToken { - ClockScanTokenMap *map; - struct { - const char *start; - const char *end; - } tokWord; - unsigned short int endDistance; - unsigned short int lookAhMin; - unsigned short int lookAhMax; - unsigned short int lookAhTok; -} ClockScanToken; - - -#define MIN_FMT_RESULT_BLOCK_ALLOC 200 - -typedef struct DateFormat { - char *resMem; - char *resEnd; - char *output; - - TclDateFields date; - - Tcl_Obj *localeEra; -} DateFormat; - -#define CLFMT_INCR (1 << 3) -#define CLFMT_DECR (1 << 4) -#define CLFMT_CALC (1 << 5) -#define CLFMT_LOCALE_INDX (1 << 8) - -typedef struct ClockFormatToken ClockFormatToken; - -typedef int ClockFormatTokenProc( - ClockFmtScnCmdArgs *opts, - DateFormat *dateFmt, - ClockFormatToken *tok, - int *val); - -typedef struct ClockFormatTokenMap { - unsigned short int type; - const char *tostr; - unsigned short int width; - unsigned short int flags; - unsigned short int divider; - unsigned short int divmod; - unsigned short int offs; - ClockFormatTokenProc *fmtproc; - void *data; -} ClockFormatTokenMap; -typedef struct ClockFormatToken { - ClockFormatTokenMap *map; - struct { - const char *start; - const char *end; - } tokWord; -} ClockFormatToken; - - -typedef struct ClockFmtScnStorage ClockFmtScnStorage; - -typedef struct ClockFmtScnStorage { - int objRefCount; /* Reference count shared across threads */ - ClockScanToken *scnTok; - unsigned int scnTokC; - unsigned int scnSpaceCount; /* Count of mandatory spaces used in format */ - ClockFormatToken *fmtTok; - unsigned int fmtTokC; -#if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0 - ClockFmtScnStorage *nextPtr; - ClockFmtScnStorage *prevPtr; -#endif -#if 0 - +Tcl_HashEntry hashEntry /* ClockFmtScnStorage is a derivate of Tcl_HashEntry, - * stored by offset +sizeof(self) */ -#endif -} ClockFmtScnStorage; - -/* - * Prototypes of module functions. - */ - -MODULE_SCOPE int ToSeconds(int Hours, int Minutes, - int Seconds, MERIDIAN Meridian); -MODULE_SCOPE int IsGregorianLeapYear(TclDateFields *); -MODULE_SCOPE void - GetJulianDayFromEraYearWeekDay( - TclDateFields *fields, int changeover); -MODULE_SCOPE void - GetJulianDayFromEraYearMonthDay( - TclDateFields *fields, int changeover); -MODULE_SCOPE void - GetJulianDayFromEraYearDay( - TclDateFields *fields, int changeover); -MODULE_SCOPE int ConvertUTCToLocal(ClientData clientData, Tcl_Interp *, - TclDateFields *, Tcl_Obj *timezoneObj, int); -MODULE_SCOPE Tcl_Obj * - LookupLastTransition(Tcl_Interp *, Tcl_WideInt, - int, Tcl_Obj *const *, Tcl_WideInt rangesVal[2]); - -MODULE_SCOPE int TclClockFreeScan(Tcl_Interp *interp, DateInfo *info); - -/* tclClock.c module declarations */ - -MODULE_SCOPE Tcl_Obj * - ClockSetupTimeZone(ClientData clientData, - Tcl_Interp *interp, Tcl_Obj *timezoneObj); - -MODULE_SCOPE Tcl_Obj * - ClockMCDict(ClockFmtScnCmdArgs *opts); -MODULE_SCOPE Tcl_Obj * - ClockMCGet(ClockFmtScnCmdArgs *opts, int mcKey); -MODULE_SCOPE Tcl_Obj * - ClockMCGetIdx(ClockFmtScnCmdArgs *opts, int mcKey); -MODULE_SCOPE int ClockMCSetIdx(ClockFmtScnCmdArgs *opts, int mcKey, - Tcl_Obj *valObj); - -/* tclClockFmt.c module declarations */ - -MODULE_SCOPE Tcl_Obj* - ClockFrmObjGetLocFmtKey(Tcl_Interp *interp, - Tcl_Obj *objPtr); - -MODULE_SCOPE ClockFmtScnStorage * - Tcl_GetClockFrmScnFromObj(Tcl_Interp *interp, - Tcl_Obj *objPtr); -MODULE_SCOPE Tcl_Obj * - ClockLocalizeFormat(ClockFmtScnCmdArgs *opts); - -MODULE_SCOPE int ClockScan(register DateInfo *info, - Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts); - -MODULE_SCOPE int ClockFormat(register DateFormat *dateFmt, - ClockFmtScnCmdArgs *opts); - -MODULE_SCOPE void ClockFrmScnClearCaches(void); - -#endif /* _TCLCLOCK_H */ diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index 44ab882..4009b80 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -51,8 +51,6 @@ static int DictSetCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); static int DictSizeCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); -static int DictSmartRefCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *const *objv); static int DictUnsetCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); static int DictUpdateCmd(ClientData dummy, Tcl_Interp *interp, @@ -100,7 +98,6 @@ static const EnsembleImplMap implementationMap[] = { {"replace", DictReplaceCmd, NULL, NULL, NULL, 0 }, {"set", DictSetCmd, TclCompileDictSetCmd, NULL, NULL, 0 }, {"size", DictSizeCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0 }, - {"smartref",DictSmartRefCmd,NULL, NULL, NULL, 0 }, {"unset", DictUnsetCmd, TclCompileDictUnsetCmd, NULL, NULL, 0 }, {"update", DictUpdateCmd, TclCompileDictUpdateCmd, NULL, NULL, 0 }, {"values", DictValuesCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, @@ -145,7 +142,7 @@ typedef struct Dict { * the entries in the order that they are * created. */ int epoch; /* Epoch counter */ - size_t refCount; /* Reference counter (see above) */ + int refcount; /* Reference counter (see above) */ Tcl_Obj *chain; /* Linked list used for invalidating the * string representations of updated nested * dictionaries. */ @@ -395,7 +392,7 @@ DupDictInternalRep( newDict->epoch = 0; newDict->chain = NULL; - newDict->refCount = 1; + newDict->refcount = 1; /* * Store in the object. @@ -430,7 +427,8 @@ FreeDictInternalRep( { Dict *dict = DICT(dictPtr); - if (dict->refCount-- <= 1) { + dict->refcount--; + if (dict->refcount <= 0) { DeleteDict(dict); } dictPtr->typePtr = NULL; @@ -715,7 +713,7 @@ SetDictFromAny( TclFreeIntRep(objPtr); dict->epoch = 0; dict->chain = NULL; - dict->refCount = 1; + dict->refcount = 1; DICT(objPtr) = dict; objPtr->internalRep.twoPtrValue.ptr2 = NULL; objPtr->typePtr = &tclDictType; @@ -1119,7 +1117,7 @@ Tcl_DictObjFirst( searchPtr->dictionaryPtr = (Tcl_Dict) dict; searchPtr->epoch = dict->epoch; searchPtr->next = cPtr->nextPtr; - dict->refCount++; + dict->refcount++; if (keyPtrPtr != NULL) { *keyPtrPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry); } @@ -1233,7 +1231,8 @@ Tcl_DictObjDone( if (searchPtr->epoch != -1) { searchPtr->epoch = -1; dict = (Dict *) searchPtr->dictionaryPtr; - if (dict->refCount-- <= 1) { + dict->refcount--; + if (dict->refcount <= 0) { DeleteDict(dict); } } @@ -1385,7 +1384,7 @@ Tcl_NewDictObj(void) InitChainTable(dict); dict->epoch = 0; dict->chain = NULL; - dict->refCount = 1; + dict->refcount = 1; DICT(dictPtr) = dict; dictPtr->internalRep.twoPtrValue.ptr2 = NULL; dictPtr->typePtr = &tclDictType; @@ -1435,7 +1434,7 @@ Tcl_DbNewDictObj( InitChainTable(dict); dict->epoch = 0; dict->chain = NULL; - dict->refCount = 1; + dict->refcount = 1; DICT(dictPtr) = dict; dictPtr->internalRep.twoPtrValue.ptr2 = NULL; dictPtr->typePtr = &tclDictType; @@ -1961,102 +1960,6 @@ DictSizeCmd( /* *---------------------------------------------------------------------- * - * Tcl_DictObjSmartRef -- - * - * This function returns new tcl-object with the smart reference to - * dictionary object. - * - * Object returned with this function is a smart reference (pointer), - * so new object of type tclDictType, that directly references given - * dictionary object (with internally increased refCount). - * - * The usage of such pointer objects allows to hold more as one - * reference to the same real dictionary object, allows to make a pointer - * to part of another dictionary, allows to change the dictionary without - * regarding of the "shared" state of the dictionary object. - * - * Prevents "called with shared object" exception if object is multiple - * referenced. - * - * Results: - * The newly create object (contains smart reference) is returned. - * The returned object has a ref count of 0. - * - * Side effects: - * Increases ref count of the referenced dictionary. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -Tcl_DictObjSmartRef( - Tcl_Interp *interp, - Tcl_Obj *dictPtr) -{ - Tcl_Obj *result; - Dict *dict; - - if (dictPtr->typePtr != &tclDictType - && SetDictFromAny(interp, dictPtr) != TCL_OK) { - return NULL; - } - - dict = DICT(dictPtr); - - result = Tcl_NewObj(); - DICT(result) = dict; - dict->refCount++; - result->internalRep.twoPtrValue.ptr2 = NULL; - result->typePtr = &tclDictType; - - return result; -} - -/* - *---------------------------------------------------------------------- - * - * DictSmartRefCmd -- - * - * This function implements the "dict smartref" Tcl command. - * - * See description of Tcl_DictObjSmartRef for details. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -static int -DictSmartRefCmd( - ClientData dummy, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const *objv) -{ - Tcl_Obj *result; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "dictionary"); - return TCL_ERROR; - } - - result = Tcl_DictObjSmartRef(interp, objv[1]); - if (result == NULL) { - return TCL_ERROR; - } - - Tcl_SetObjResult(interp, result); - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * * DictExistsCmd -- * * This function implements the "dict exists" Tcl command. See the user diff --git a/generic/tclEnsemble.c b/generic/tclEnsemble.c index 477aeee..f3e8187 100644 --- a/generic/tclEnsemble.c +++ b/generic/tclEnsemble.c @@ -55,12 +55,11 @@ enum EnsSubcmds { }; static const char *const ensembleCreateOptions[] = { - "-command", "-compile", "-map", "-parameters", "-prefixes", - "-subcommands", "-unknown", NULL + "-command", "-map", "-parameters", "-prefixes", "-subcommands", + "-unknown", NULL }; enum EnsCreateOpts { - CRT_CMD, CRT_COMPILE, CRT_MAP, CRT_PARAM, CRT_PREFIX, - CRT_SUBCMDS, CRT_UNKNOWN + CRT_CMD, CRT_MAP, CRT_PARAM, CRT_PREFIX, CRT_SUBCMDS, CRT_UNKNOWN }; static const char *const ensembleConfigOptions[] = { @@ -184,7 +183,6 @@ TclNamespaceEnsembleCmd( int permitPrefix = 1; Tcl_Obj *unknownObj = NULL; Tcl_Obj *paramObj = NULL; - int ensCompFlag = -1; /* * Check that we've got option-value pairs... [Bug 1558654] @@ -327,12 +325,6 @@ TclNamespaceEnsembleCmd( return TCL_ERROR; } continue; - case CRT_COMPILE: - if (Tcl_GetBooleanFromObj(interp, objv[1], - &ensCompFlag) != TCL_OK) { - return TCL_ERROR; - }; - continue; case CRT_UNKNOWN: if (TclListObjLength(interp, objv[1], &len) != TCL_OK) { if (allocatedMapFlag) { @@ -358,12 +350,6 @@ TclNamespaceEnsembleCmd( Tcl_SetEnsembleMappingDict(interp, token, mapObj); Tcl_SetEnsembleUnknownHandler(interp, token, unknownObj); Tcl_SetEnsembleParameterList(interp, token, paramObj); - /* - * Ensemble should be compiled if it has map (performance purposes) - */ - if (ensCompFlag > 0 && mapObj != NULL) { - Tcl_SetEnsembleFlags(interp, token, ENSEMBLE_COMPILE); - } /* * Tricky! Must ensure that the result is not shared (command delete diff --git a/generic/tclEnv.c b/generic/tclEnv.c index d05cc61..66ddb57 100644 --- a/generic/tclEnv.c +++ b/generic/tclEnv.c @@ -17,11 +17,6 @@ TCL_DECLARE_MUTEX(envMutex) /* To serialize access to environ. */ - -/* MODULE_SCOPE */ -size_t TclEnvEpoch = 0; /* Epoch of the tcl environment - * (if changed with tcl-env). */ - static struct { int cacheSize; /* Number of env strings in cache. */ char **cache; /* Array containing all of the environment @@ -376,7 +371,6 @@ Tcl_PutEnv( value[0] = '\0'; TclSetEnv(name, value+1); } - TclEnvEpoch++; Tcl_DStringFree(&nameString); return 0; @@ -585,7 +579,6 @@ EnvTraceProc( if (flags & TCL_TRACE_ARRAY) { TclSetupEnv(interp); - TclEnvEpoch++; return NULL; } @@ -606,7 +599,6 @@ EnvTraceProc( value = Tcl_GetVar2(interp, "env", name2, TCL_GLOBAL_ONLY); TclSetEnv(name2, value); - TclEnvEpoch++; } /* @@ -630,7 +622,6 @@ EnvTraceProc( if (flags & TCL_TRACE_UNSETS) { TclUnsetEnv(name2); - TclEnvEpoch++; } return NULL; } diff --git a/generic/tclGetDate.y b/generic/tclGetDate.y index b83644b..da4c3fd 100644 --- a/generic/tclGetDate.y +++ b/generic/tclGetDate.y @@ -9,7 +9,6 @@ * * Copyright (c) 1992-1995 Karl Lehenbauer and Mark Diekhans. * Copyright (c) 1995-1997 Sun Microsystems, Inc. - * Copyright (c) 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. @@ -51,11 +50,73 @@ * parsed fields will be returned. */ -#include "tclDate.h" +typedef struct DateInfo { + + Tcl_Obj* messages; /* Error messages */ + const char* separatrix; /* String separating messages */ + + time_t dateYear; + time_t dateMonth; + time_t dateDay; + int dateHaveDate; + + time_t dateHour; + time_t dateMinutes; + time_t dateSeconds; + int dateMeridian; + int dateHaveTime; + + time_t dateTimezone; + int dateDSTmode; + int dateHaveZone; + + time_t dateRelMonth; + time_t dateRelDay; + time_t dateRelSeconds; + int dateHaveRel; + + time_t dateMonthOrdinal; + int dateHaveOrdinalMonth; + + time_t dateDayOrdinal; + time_t dateDayNumber; + int dateHaveDay; + + const char *dateStart; + const char *dateInput; + time_t *dateRelPointer; + + int dateDigitCount; +} DateInfo; #define YYMALLOC ckalloc #define YYFREE(x) (ckfree((void*) (x))) +#define yyDSTmode (info->dateDSTmode) +#define yyDayOrdinal (info->dateDayOrdinal) +#define yyDayNumber (info->dateDayNumber) +#define yyMonthOrdinal (info->dateMonthOrdinal) +#define yyHaveDate (info->dateHaveDate) +#define yyHaveDay (info->dateHaveDay) +#define yyHaveOrdinalMonth (info->dateHaveOrdinalMonth) +#define yyHaveRel (info->dateHaveRel) +#define yyHaveTime (info->dateHaveTime) +#define yyHaveZone (info->dateHaveZone) +#define yyTimezone (info->dateTimezone) +#define yyDay (info->dateDay) +#define yyMonth (info->dateMonth) +#define yyYear (info->dateYear) +#define yyHour (info->dateHour) +#define yyMinutes (info->dateMinutes) +#define yySeconds (info->dateSeconds) +#define yyMeridian (info->dateMeridian) +#define yyRelMonth (info->dateRelMonth) +#define yyRelDay (info->dateRelDay) +#define yyRelSeconds (info->dateRelSeconds) +#define yyRelPointer (info->dateRelPointer) +#define yyInput (info->dateInput) +#define yyDigitCount (info->dateDigitCount) + #define EPOCH 1970 #define START_OF_TIME 1902 #define END_OF_TIME 2037 @@ -89,6 +150,14 @@ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; +/* + * Meridian: am, pm, or 24-hour style. + */ + +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + %} %union { @@ -107,6 +176,8 @@ static int LookupWord(YYSTYPE* yylvalPtr, char *buff); DateInfo* info, const char *s); static int TclDatelex(YYSTYPE* yylvalPtr, YYLTYPE* location, DateInfo* info); +static time_t ToSeconds(time_t Hours, time_t Minutes, + time_t Seconds, MERIDIAN Meridian); MODULE_SCOPE int yyparse(DateInfo*); %} @@ -306,12 +377,12 @@ date : tUNUMBER '/' tUNUMBER { ; ordMonth: tNEXT tMONTH { - yyMonthOrdinalIncr = 1; - yyMonthOrdinal = $2; + yyMonthOrdinal = 1; + yyMonth = $2; } | tNEXT tUNUMBER tMONTH { - yyMonthOrdinalIncr = $2; - yyMonthOrdinal = $3; + yyMonthOrdinal = $2; + yyMonth = $3; } ; @@ -659,11 +730,11 @@ TclDateerror( infoPtr->separatrix = "\n"; } -MODULE_SCOPE int +static time_t ToSeconds( - int Hours, - int Minutes, - int Seconds, + time_t Hours, + time_t Minutes, + time_t Seconds, MERIDIAN Meridian) { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) { @@ -886,36 +957,65 @@ TclDatelex( } while (Count > 0); } } - + int -TclClockFreeScan( +TclClockOldscanObjCmd( + ClientData clientData, /* Unused */ Tcl_Interp *interp, /* Tcl interpreter */ - DateInfo *info) /* Input and result parameters */ + int objc, /* Count of paraneters */ + Tcl_Obj *const *objv) /* Parameters */ { + Tcl_Obj *result, *resultElement; + int yr, mo, da; + DateInfo dateInfo; + DateInfo* info = &dateInfo; int status; - /* - * yyInput = stringToParse; - * - * ClockInitDateInfo(info) should be executed to pre-init info; - */ + if (objc != 5) { + Tcl_WrongNumArgs(interp, 1, objv, + "stringToParse baseYear baseMonth baseDay" ); + return TCL_ERROR; + } - yyDSTmode = DSTmaybe; + yyInput = Tcl_GetString( objv[1] ); + dateInfo.dateStart = yyInput; - info->messages = Tcl_NewObj(); - info->separatrix = ""; - Tcl_IncrRefCount(info->messages); + yyHaveDate = 0; + if (Tcl_GetIntFromObj(interp, objv[2], &yr) != TCL_OK + || Tcl_GetIntFromObj(interp, objv[3], &mo) != TCL_OK + || Tcl_GetIntFromObj(interp, objv[4], &da) != TCL_OK) { + return TCL_ERROR; + } + yyYear = yr; yyMonth = mo; yyDay = da; - info->dateStart = yyInput; - status = yyparse(info); + yyHaveTime = 0; + yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; + + yyHaveZone = 0; + yyTimezone = 0; yyDSTmode = DSTmaybe; + + yyHaveOrdinalMonth = 0; + yyMonthOrdinal = 0; + + yyHaveDay = 0; + yyDayOrdinal = 0; yyDayNumber = 0; + + yyHaveRel = 0; + yyRelMonth = 0; yyRelDay = 0; yyRelSeconds = 0; yyRelPointer = NULL; + + dateInfo.messages = Tcl_NewObj(); + dateInfo.separatrix = ""; + Tcl_IncrRefCount(dateInfo.messages); + + status = yyparse(&dateInfo); if (status == 1) { - Tcl_SetObjResult(interp, info->messages); - Tcl_DecrRefCount(info->messages); + Tcl_SetObjResult(interp, dateInfo.messages); + Tcl_DecrRefCount(dateInfo.messages); Tcl_SetErrorCode(interp, "TCL", "VALUE", "DATE", "PARSE", NULL); return TCL_ERROR; } else if (status == 2) { Tcl_SetObjResult(interp, Tcl_NewStringObj("memory exhausted", -1)); - Tcl_DecrRefCount(info->messages); + Tcl_DecrRefCount(dateInfo.messages); Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); return TCL_ERROR; } else if (status != 0) { @@ -923,11 +1023,11 @@ TclClockFreeScan( "from date parser. Please " "report this error as a " "bug in Tcl.", -1)); - Tcl_DecrRefCount(info->messages); + Tcl_DecrRefCount(dateInfo.messages); Tcl_SetErrorCode(interp, "TCL", "BUG", NULL); return TCL_ERROR; } - Tcl_DecrRefCount(info->messages); + Tcl_DecrRefCount(dateInfo.messages); if (yyHaveDate > 1) { Tcl_SetObjResult(interp, @@ -960,40 +1060,6 @@ TclClockFreeScan( return TCL_ERROR; } - return TCL_OK; -} - -int -TclClockOldscanObjCmd( - ClientData clientData, /* Unused */ - Tcl_Interp *interp, /* Tcl interpreter */ - int objc, /* Count of paraneters */ - Tcl_Obj *const *objv) /* Parameters */ -{ - Tcl_Obj *result, *resultElement; - int yr, mo, da; - DateInfo dateInfo; - DateInfo* info = &dateInfo; - - if (objc != 5) { - Tcl_WrongNumArgs(interp, 1, objv, - "stringToParse baseYear baseMonth baseDay" ); - return TCL_ERROR; - } - - yyInput = Tcl_GetString( objv[1] ); - - if (Tcl_GetIntFromObj(interp, objv[2], &yr) != TCL_OK - || Tcl_GetIntFromObj(interp, objv[3], &mo) != TCL_OK - || Tcl_GetIntFromObj(interp, objv[4], &da) != TCL_OK) { - return TCL_ERROR; - } - yyYear = yr; yyMonth = mo; yyDay = da; - - if (TclClockFreeScan(interp, info) != TCL_OK) { - return TCL_ERROR; - } - result = Tcl_NewObj(); resultElement = Tcl_NewObj(); if (yyHaveDate) { @@ -1045,9 +1111,9 @@ TclClockOldscanObjCmd( resultElement = Tcl_NewObj(); if (yyHaveOrdinalMonth) { Tcl_ListObjAppendElement(interp, resultElement, - Tcl_NewIntObj((int) yyMonthOrdinalIncr)); - Tcl_ListObjAppendElement(interp, resultElement, Tcl_NewIntObj((int) yyMonthOrdinal)); + Tcl_ListObjAppendElement(interp, resultElement, + Tcl_NewIntObj((int) yyMonth)); } Tcl_ListObjAppendElement(interp, result, resultElement); diff --git a/generic/tclInt.h b/generic/tclInt.h index e1ddb36..14d7179 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2914,7 +2914,6 @@ MODULE_SCOPE int TclFindDictElement(Tcl_Interp *interp, const char *dict, int dictLength, const char **elementPtr, const char **nextPtr, int *sizePtr, int *literalPtr); -MODULE_SCOPE Tcl_Obj * Tcl_DictObjSmartRef(Tcl_Interp *interp, Tcl_Obj *); /* TIP #280 - Modified token based evulation, with line information. */ MODULE_SCOPE int TclEvalEx(Tcl_Interp *interp, const char *script, int numBytes, int flags, int line, @@ -3198,22 +3197,10 @@ MODULE_SCOPE int TclpLoadMemory(Tcl_Interp *interp, void *buffer, MODULE_SCOPE void TclInitThreadStorage(void); MODULE_SCOPE void TclFinalizeThreadDataThread(void); MODULE_SCOPE void TclFinalizeThreadStorage(void); - #ifdef TCL_WIDE_CLICKS MODULE_SCOPE Tcl_WideInt TclpGetWideClicks(void); MODULE_SCOPE double TclpWideClicksToNanoseconds(Tcl_WideInt clicks); -MODULE_SCOPE double TclpWideClickInMicrosec(void); -#else -# ifdef _WIN32 -# define TCL_WIDE_CLICKS 1 -MODULE_SCOPE Tcl_WideInt TclpGetWideClicks(void); -MODULE_SCOPE double TclpWideClickInMicrosec(void); -# define TclpWideClicksToNanoseconds(clicks) \ - ((double)(clicks) * TclpWideClickInMicrosec() * 1000) -# endif #endif -MODULE_SCOPE Tcl_WideInt TclpGetMicroseconds(void); - MODULE_SCOPE int TclZlibInit(Tcl_Interp *interp); MODULE_SCOPE void * TclpThreadCreateKey(void); MODULE_SCOPE void TclpThreadDeleteKey(void *keyPtr); @@ -3485,9 +3472,6 @@ MODULE_SCOPE int Tcl_ThrowObjCmd(ClientData dummy, Tcl_Interp *interp, MODULE_SCOPE int Tcl_TimeObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -MODULE_SCOPE int Tcl_TimeRateObjCmd(ClientData clientData, - Tcl_Interp *interp, int objc, - Tcl_Obj *const objv[]); MODULE_SCOPE int Tcl_TraceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -4889,13 +4873,6 @@ typedef struct NRE_callback { #define Tcl_Free(ptr) TclpFree(ptr) #endif -/* - * Other externals. - */ - -MODULE_SCOPE size_t TclEnvEpoch; /* Epoch of the tcl environment - * (if changed with tcl-env). */ - #endif /* _TCLINT */ /* diff --git a/generic/tclStrIdxTree.c b/generic/tclStrIdxTree.c deleted file mode 100644 index 557d575..0000000 --- a/generic/tclStrIdxTree.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * tclStrIdxTree.c -- - * - * Contains the routines for managing string index tries in Tcl. - * - * This code is back-ported from the tclSE engine, by Serg G. Brester. - * - * Copyright (c) 2016 by Sergey G. Brester aka sebres. All rights reserved. - * - * See the file "license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * ----------------------------------------------------------------------- - * - * String index tries are prepaired structures used for fast greedy search of the string - * (index) by unique string prefix as key. - * - * Index tree build for two lists together can be explained in the following datagram - * - * Lists: - * - * {Januar Februar Maerz April Mai Juni Juli August September Oktober November Dezember} - * {Jnr Fbr Mrz Apr Mai Jni Jli Agt Spt Okt Nvb Dzb} - * - * Index-Tree: - * - * j 0 * ... - * anuar 1 * - * u 0 * a 0 - * ni 6 * pril 4 - * li 7 * ugust 8 - * n 0 * gt 8 - * r 1 * s 9 - * i 6 * eptember 9 - * li 7 * pt 9 - * f 2 * oktober 10 - * ebruar 2 * n 11 - * br 2 * ovember 11 - * m 0 * vb 11 - * a 0 * d 12 - * erz 3 * ezember 12 - * i 5 * zb 12 - * rz 3 * - * ... - * - * Thereby value 0 shows pure group items (corresponding ambigous matches). - * But the group may have a value if it contains only same values - * (see for example group "f" above). - * - * StrIdxTree's are very fast, so: - * build of above-mentioned tree takes about 10 microseconds. - * search of string index in this tree takes fewer as 0.1 microseconds. - * - */ - -#include "tclInt.h" -#include "tclStrIdxTree.h" - - -/* - *---------------------------------------------------------------------- - * - * TclStrIdxTreeSearch -- - * - * Find largest part of string "start" in indexed tree (case sensitive). - * - * Also used for building of string index tree. - * - * Results: - * Return position of UTF character in start after last equal character - * and found item (with parent). - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE const char* -TclStrIdxTreeSearch( - TclStrIdxTree **foundParent, /* Return value of found sub tree (used for tree build) */ - TclStrIdx **foundItem, /* Return value of found item */ - TclStrIdxTree *tree, /* Index tree will be browsed */ - const char *start, /* UTF string to find in tree */ - const char *end) /* End of string */ -{ - TclStrIdxTree *parent = tree, *prevParent = tree; - TclStrIdx *item = tree->firstPtr, *prevItem = NULL; - const char *s = start, *f, *cin, *cinf, *prevf; - int offs = 0; - - if (item == NULL) { - goto done; - } - - /* search in tree */ - do { - cinf = cin = TclGetString(item->key) + offs; - f = TclUtfFindEqualNCInLwr(s, end, cin, cin + item->length, &cinf); - /* if something was found */ - if (f > s) { - /* if whole string was found */ - if (f >= end) { - start = f; - goto done; - }; - /* set new offset and shift start string */ - offs += cinf - cin; - s = f; - /* if match item, go deeper as long as possible */ - if (offs >= item->length && item->childTree.firstPtr) { - /* save previuosly found item (if not ambigous) for - * possible fallback (few greedy match) */ - if (item->value != NULL) { - prevf = f; - prevItem = item; - prevParent = parent; - } - parent = &item->childTree; - item = item->childTree.firstPtr; - continue; - } - /* no children - return this item and current chars found */ - start = f; - goto done; - } - - item = item->nextPtr; - - } while (item != NULL); - - /* fallback (few greedy match) not ambigous (has a value) */ - if (prevItem != NULL) { - item = prevItem; - parent = prevParent; - start = prevf; - } - -done: - - if (foundParent) - *foundParent = parent; - if (foundItem) - *foundItem = item; - return start; -} - -MODULE_SCOPE void -TclStrIdxTreeFree( - TclStrIdx *tree) -{ - while (tree != NULL) { - TclStrIdx *t; - Tcl_DecrRefCount(tree->key); - if (tree->childTree.firstPtr != NULL) { - TclStrIdxTreeFree(tree->childTree.firstPtr); - } - t = tree, tree = tree->nextPtr; - ckfree(t); - } -} - -/* - * Several bidirectional list primitives - */ -inline void -TclStrIdxTreeInsertBranch( - TclStrIdxTree *parent, - register TclStrIdx *item, - register TclStrIdx *child) -{ - if (parent->firstPtr == child) - parent->firstPtr = item; - if (parent->lastPtr == child) - parent->lastPtr = item; - if ( (item->nextPtr = child->nextPtr) ) { - item->nextPtr->prevPtr = item; - child->nextPtr = NULL; - } - if ( (item->prevPtr = child->prevPtr) ) { - item->prevPtr->nextPtr = item; - child->prevPtr = NULL; - } - item->childTree.firstPtr = child; - item->childTree.lastPtr = child; -} - -inline void -TclStrIdxTreeAppend( - register TclStrIdxTree *parent, - register TclStrIdx *item) -{ - if (parent->lastPtr != NULL) { - parent->lastPtr->nextPtr = item; - } - item->prevPtr = parent->lastPtr; - item->nextPtr = NULL; - parent->lastPtr = item; - if (parent->firstPtr == NULL) { - parent->firstPtr = item; - } -} - - -/* - *---------------------------------------------------------------------- - * - * TclStrIdxTreeBuildFromList -- - * - * Build or extend string indexed tree from tcl list. - * If the values not given the values of built list are indices starts with 1. - * Value of 0 is thereby reserved to the ambigous values. - * - * Important: by multiple lists, optimal tree can be created only if list with - * larger strings used firstly. - * - * Results: - * Returns a standard Tcl result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -MODULE_SCOPE int -TclStrIdxTreeBuildFromList( - TclStrIdxTree *idxTree, - int lstc, - Tcl_Obj **lstv, - ClientData *values) -{ - Tcl_Obj **lwrv; - int i, ret = TCL_ERROR; - ClientData val; - const char *s, *e, *f; - TclStrIdx *item; - - /* create lowercase reflection of the list keys */ - - lwrv = ckalloc(sizeof(Tcl_Obj*) * lstc); - if (lwrv == NULL) { - return TCL_ERROR; - } - for (i = 0; i < lstc; i++) { - lwrv[i] = Tcl_DuplicateObj(lstv[i]); - if (lwrv[i] == NULL) { - return TCL_ERROR; - } - Tcl_IncrRefCount(lwrv[i]); - lwrv[i]->length = Tcl_UtfToLower(TclGetString(lwrv[i])); - } - - /* build index tree of the list keys */ - for (i = 0; i < lstc; i++) { - TclStrIdxTree *foundParent = idxTree; - e = s = TclGetString(lwrv[i]); - e += lwrv[i]->length; - val = values ? values[i] : INT2PTR(i+1); - - /* ignore empty keys (impossible to index it) */ - if (lwrv[i]->length == 0) continue; - - item = NULL; - if (idxTree->firstPtr != NULL) { - TclStrIdx *foundItem; - f = TclStrIdxTreeSearch(&foundParent, &foundItem, - idxTree, s, e); - /* if common prefix was found */ - if (f > s) { - /* ignore element if fulfilled or ambigous */ - if (f == e) { - continue; - } - /* if shortest key was found with the same value, - * just replace its current key with longest key */ - if ( foundItem->value == val - && foundItem->length < lwrv[i]->length - && foundItem->childTree.firstPtr == NULL - ) { - Tcl_SetObjRef(foundItem->key, lwrv[i]); - foundItem->length = lwrv[i]->length; - continue; - } - /* split tree (e. g. j->(jan,jun) + jul == j->(jan,ju->(jun,jul)) ) - * but don't split by fulfilled child of found item ( ii->iii->iiii ) */ - if (foundItem->length != (f - s)) { - /* first split found item (insert one between parent and found + new one) */ - item = ckalloc(sizeof(*item)); - if (item == NULL) { - goto done; - } - Tcl_InitObjRef(item->key, foundItem->key); - item->length = f - s; - /* set value or mark as ambigous if not the same value of both */ - item->value = (foundItem->value == val) ? val : NULL; - /* insert group item between foundParent and foundItem */ - TclStrIdxTreeInsertBranch(foundParent, item, foundItem); - foundParent = &item->childTree; - } else { - /* the new item should be added as child of found item */ - foundParent = &foundItem->childTree; - } - } - } - /* append item at end of found parent */ - item = ckalloc(sizeof(*item)); - if (item == NULL) { - goto done; - } - item->childTree.lastPtr = item->childTree.firstPtr = NULL; - Tcl_InitObjRef(item->key, lwrv[i]); - item->length = lwrv[i]->length; - item->value = val; - TclStrIdxTreeAppend(foundParent, item); - }; - - ret = TCL_OK; - -done: - - if (lwrv != NULL) { - for (i = 0; i < lstc; i++) { - Tcl_DecrRefCount(lwrv[i]); - } - ckfree(lwrv); - } - - if (ret != TCL_OK) { - if (idxTree->firstPtr != NULL) { - TclStrIdxTreeFree(idxTree->firstPtr); - } - } - - return ret; -} - - -static void -StrIdxTreeObj_DupIntRepProc(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); -static void -StrIdxTreeObj_FreeIntRepProc(Tcl_Obj *objPtr); -static void -StrIdxTreeObj_UpdateStringProc(Tcl_Obj *objPtr); - -Tcl_ObjType StrIdxTreeObjType = { - "str-idx-tree", /* name */ - StrIdxTreeObj_FreeIntRepProc, /* freeIntRepProc */ - StrIdxTreeObj_DupIntRepProc, /* dupIntRepProc */ - StrIdxTreeObj_UpdateStringProc, /* updateStringProc */ - NULL /* setFromAnyProc */ -}; - -MODULE_SCOPE Tcl_Obj* -TclStrIdxTreeNewObj() -{ - Tcl_Obj *objPtr = Tcl_NewObj(); - objPtr->internalRep.twoPtrValue.ptr1 = NULL; - objPtr->internalRep.twoPtrValue.ptr2 = NULL; - objPtr->typePtr = &StrIdxTreeObjType; - /* return tree root in internal representation */ - return objPtr; -} - -static void -StrIdxTreeObj_DupIntRepProc(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr) -{ - /* follow links (smart pointers) */ - if ( srcPtr->internalRep.twoPtrValue.ptr1 != NULL - && srcPtr->internalRep.twoPtrValue.ptr2 == NULL - ) { - srcPtr = (Tcl_Obj*)srcPtr->internalRep.twoPtrValue.ptr1; - } - /* create smart pointer to it (ptr1 != NULL, ptr2 = NULL) */ - Tcl_InitObjRef(*((Tcl_Obj **)©Ptr->internalRep.twoPtrValue.ptr1), - srcPtr); - copyPtr->internalRep.twoPtrValue.ptr2 = NULL; - copyPtr->typePtr = &StrIdxTreeObjType; -} - -static void -StrIdxTreeObj_FreeIntRepProc(Tcl_Obj *objPtr) -{ - /* follow links (smart pointers) */ - if ( objPtr->internalRep.twoPtrValue.ptr1 != NULL - && objPtr->internalRep.twoPtrValue.ptr2 == NULL - ) { - /* is a link */ - Tcl_UnsetObjRef(*((Tcl_Obj **)&objPtr->internalRep.twoPtrValue.ptr1)); - } else { - /* is a tree */ - TclStrIdxTree *tree = (TclStrIdxTree*)&objPtr->internalRep.twoPtrValue.ptr1; - if (tree->firstPtr != NULL) { - TclStrIdxTreeFree(tree->firstPtr); - } - objPtr->internalRep.twoPtrValue.ptr1 = NULL; - objPtr->internalRep.twoPtrValue.ptr2 = NULL; - } - objPtr->typePtr = NULL; -}; - -static void -StrIdxTreeObj_UpdateStringProc(Tcl_Obj *objPtr) -{ - /* currently only dummy empty string possible */ - objPtr->length = 0; - objPtr->bytes = &tclEmptyString; -}; - -MODULE_SCOPE TclStrIdxTree * -TclStrIdxTreeGetFromObj(Tcl_Obj *objPtr) { - /* follow links (smart pointers) */ - if (objPtr->typePtr != &StrIdxTreeObjType) { - return NULL; - } - if ( objPtr->internalRep.twoPtrValue.ptr1 != NULL - && objPtr->internalRep.twoPtrValue.ptr2 == NULL - ) { - objPtr = (Tcl_Obj*)objPtr->internalRep.twoPtrValue.ptr1; - } - /* return tree root in internal representation */ - return (TclStrIdxTree*)&objPtr->internalRep.twoPtrValue.ptr1; -} - -/* - * Several debug primitives - */ -#if 0 -/* currently unused, debug resp. test purposes only */ - -void -TclStrIdxTreePrint( - Tcl_Interp *interp, - TclStrIdx *tree, - int offs) -{ - Tcl_Obj *obj[2]; - const char *s; - Tcl_InitObjRef(obj[0], Tcl_NewStringObj("::puts", -1)); - while (tree != NULL) { - s = TclGetString(tree->key) + offs; - Tcl_InitObjRef(obj[1], Tcl_ObjPrintf("%*s%.*s\t:%d", - offs, "", tree->length - offs, s, tree->value)); - Tcl_PutsObjCmd(NULL, interp, 2, obj); - Tcl_UnsetObjRef(obj[1]); - if (tree->childTree.firstPtr != NULL) { - TclStrIdxTreePrint(interp, tree->childTree.firstPtr, tree->length); - } - tree = tree->nextPtr; - } - Tcl_UnsetObjRef(obj[0]); -} - - -MODULE_SCOPE int -TclStrIdxTreeTestObjCmd( - ClientData clientData, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]) -{ - const char *cs, *cin, *ret; - - static const char *const options[] = { - "index", "puts-index", "findequal", - NULL - }; - enum optionInd { - O_INDEX, O_PUTS_INDEX, O_FINDEQUAL - }; - int optionIndex; - - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, ""); - return TCL_ERROR; - } - if (Tcl_GetIndexFromObj(interp, objv[1], options, - "option", 0, &optionIndex) != TCL_OK) { - Tcl_SetErrorCode(interp, "CLOCK", "badOption", - Tcl_GetString(objv[1]), NULL); - return TCL_ERROR; - } - switch (optionIndex) { - case O_FINDEQUAL: - if (objc < 4) { - Tcl_WrongNumArgs(interp, 1, objv, ""); - return TCL_ERROR; - } - cs = TclGetString(objv[2]); - cin = TclGetString(objv[3]); - ret = TclUtfFindEqual( - cs, cs + objv[1]->length, cin, cin + objv[2]->length); - Tcl_SetObjResult(interp, Tcl_NewIntObj(ret - cs)); - break; - case O_INDEX: - case O_PUTS_INDEX: - - if (1) { - Tcl_Obj **lstv; - int i, lstc; - TclStrIdxTree idxTree = {NULL, NULL}; - i = 1; - while (++i < objc) { - if (TclListObjGetElements(interp, objv[i], - &lstc, &lstv) != TCL_OK) { - return TCL_ERROR; - }; - TclStrIdxTreeBuildFromList(&idxTree, lstc, lstv, NULL); - } - if (optionIndex == O_PUTS_INDEX) { - TclStrIdxTreePrint(interp, idxTree.firstPtr, 0); - } - TclStrIdxTreeFree(idxTree.firstPtr); - } - break; - } - - return TCL_OK; -} - -#endif - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ diff --git a/generic/tclStrIdxTree.h b/generic/tclStrIdxTree.h deleted file mode 100644 index 6ed5170..0000000 --- a/generic/tclStrIdxTree.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * tclStrIdxTree.h -- - * - * Declarations of string index tries and other primitives currently - * back-ported from tclSE. - * - * Copyright (c) 2016 Serg 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. - */ - -#ifndef _TCLSTRIDXTREE_H -#define _TCLSTRIDXTREE_H - - -/* - * Main structures declarations of index tree and entry - */ - -typedef struct TclStrIdxTree { - struct TclStrIdx *firstPtr; - struct TclStrIdx *lastPtr; -} TclStrIdxTree; - -typedef struct TclStrIdx { - struct TclStrIdxTree childTree; - struct TclStrIdx *nextPtr; - struct TclStrIdx *prevPtr; - Tcl_Obj *key; - int length; - ClientData value; -} TclStrIdx; - - -/* - *---------------------------------------------------------------------- - * - * TclUtfFindEqual, TclUtfFindEqualNC -- - * - * Find largest part of string cs in string cin (case sensitive and not). - * - * Results: - * Return position of UTF character in cs after last equal character. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static inline const char * -TclUtfFindEqual( - register const char *cs, /* UTF string to find in cin. */ - register const char *cse, /* End of cs */ - register const char *cin, /* UTF string will be browsed. */ - register const char *cine) /* End of cin */ -{ - register const char *ret = cs; - Tcl_UniChar ch1, ch2; - do { - cs += TclUtfToUniChar(cs, &ch1); - cin += TclUtfToUniChar(cin, &ch2); - if (ch1 != ch2) break; - } while ((ret = cs) < cse && cin < cine); - return ret; -} - -static inline const char * -TclUtfFindEqualNC( - register const char *cs, /* UTF string to find in cin. */ - register const char *cse, /* End of cs */ - register const char *cin, /* UTF string will be browsed. */ - register const char *cine, /* End of cin */ - const char **cinfnd) /* Return position in cin */ -{ - register const char *ret = cs; - Tcl_UniChar ch1, ch2; - do { - cs += TclUtfToUniChar(cs, &ch1); - cin += TclUtfToUniChar(cin, &ch2); - if (ch1 != ch2) { - ch1 = Tcl_UniCharToLower(ch1); - ch2 = Tcl_UniCharToLower(ch2); - if (ch1 != ch2) break; - } - *cinfnd = cin; - } while ((ret = cs) < cse && cin < cine); - return ret; -} - -static inline const char * -TclUtfFindEqualNCInLwr( - register const char *cs, /* UTF string (in anycase) to find in cin. */ - register const char *cse, /* End of cs */ - register const char *cin, /* UTF string (in lowercase) will be browsed. */ - register const char *cine, /* End of cin */ - const char **cinfnd) /* Return position in cin */ -{ - register const char *ret = cs; - Tcl_UniChar ch1, ch2; - do { - cs += TclUtfToUniChar(cs, &ch1); - cin += TclUtfToUniChar(cin, &ch2); - if (ch1 != ch2) { - ch1 = Tcl_UniCharToLower(ch1); - if (ch1 != ch2) break; - } - *cinfnd = cin; - } while ((ret = cs) < cse && cin < cine); - return ret; -} - -static inline const char * -TclUtfNext( - register const char *src) /* The current location in the string. */ -{ - if (((unsigned char) *(src)) < 0xC0) { - return ++src; - } else { - Tcl_UniChar ch; - return src + TclUtfToUniChar(src, &ch); - } -} - - -/* - * Primitives to safe set, reset and free references. - */ - -#define Tcl_UnsetObjRef(obj) \ - if (obj != NULL) { Tcl_DecrRefCount(obj); obj = NULL; } -#define Tcl_InitObjRef(obj, val) \ - obj = val; if (obj) { Tcl_IncrRefCount(obj); } -#define Tcl_SetObjRef(obj, val) \ -if (1) { \ - Tcl_Obj *nval = val; \ - if (obj != nval) { \ - Tcl_Obj *prev = obj; \ - Tcl_InitObjRef(obj, nval); \ - if (prev != NULL) { Tcl_DecrRefCount(prev); }; \ - } \ -} - -/* - * Prototypes of module functions. - */ - -MODULE_SCOPE const char* - TclStrIdxTreeSearch(TclStrIdxTree **foundParent, - TclStrIdx **foundItem, TclStrIdxTree *tree, - const char *start, const char *end); - -MODULE_SCOPE int TclStrIdxTreeBuildFromList(TclStrIdxTree *idxTree, - int lstc, Tcl_Obj **lstv, ClientData *values); - -MODULE_SCOPE Tcl_Obj* - TclStrIdxTreeNewObj(); - -MODULE_SCOPE TclStrIdxTree* - TclStrIdxTreeGetFromObj(Tcl_Obj *objPtr); - -#if 1 - -MODULE_SCOPE int TclStrIdxTreeTestObjCmd(ClientData, Tcl_Interp *, - int, Tcl_Obj *const objv[]); -#endif - -#endif /* _TCLSTRIDXTREE_H */ diff --git a/generic/tclUtf.c b/generic/tclUtf.c index eec4068..b33bf6a 100644 --- a/generic/tclUtf.c +++ b/generic/tclUtf.c @@ -292,9 +292,7 @@ Tcl_UtfToUniChar( */ *chPtr = (Tcl_UniChar) (((byte & 0x1F) << 6) | (src[1] & 0x3F)); - if ((*chPtr == 0) || (*chPtr > 0x7f)) { - return 2; - } + return 2; } /* @@ -309,9 +307,7 @@ Tcl_UtfToUniChar( *chPtr = (Tcl_UniChar) (((byte & 0x0F) << 12) | ((src[1] & 0x3F) << 6) | (src[2] & 0x3F)); - if (*chPtr > 0x7ff) { - return 3; - } + return 3; } /* @@ -328,9 +324,7 @@ Tcl_UtfToUniChar( *chPtr = (Tcl_UniChar) (((byte & 0x0E) << 18) | ((src[1] & 0x3F) << 12) | ((src[2] & 0x3F) << 6) | (src[3] & 0x3F)); - if ((*chPtr <= 0x10ffff) && (*chPtr > 0xffff)) { - return 4; - } + return 4; } /* |