summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclClock.c448
-rw-r--r--generic/tclClockFmt.c193
-rw-r--r--generic/tclDate.h45
-rwxr-xr-xlibrary/clock.tcl79
-rw-r--r--library/msgcat/msgcat.tcl113
-rw-r--r--tests-perf/clock.perf.tcl31
-rw-r--r--tests/clock.test8
7 files changed, 608 insertions, 309 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c
index 1e9abba..166a4b3 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -59,8 +59,7 @@ typedef enum ClockLiteral {
LIT_TZDATA,
LIT_GETSYSTEMTIMEZONE,
LIT_SETUPTIMEZONE,
- LIT_GETSYSTEMLOCALE,
- LIT_SETUPLOCALE,
+ LIT_MCGET, LIT_TCL_CLOCK,
#if 0
LIT_FREESCAN,
#endif
@@ -83,13 +82,17 @@ static const char *const Literals[] = {
"::tcl::clock::TZData",
"::tcl::clock::GetSystemTimeZone",
"::tcl::clock::SetupTimeZone",
- "::tcl::clock::mclocale",
- "::tcl::clock::EnterLocale",
+ "::msgcat::mcget", "::tcl::clock"
#if 0
"::tcl::clock::FreeScan"
#endif
};
+/* 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_");
+
static const char *const eras[] = { "CE", "BCE", NULL };
/*
@@ -257,9 +260,10 @@ TclClockInit(
data->refCount = 0;
data->literals = ckalloc(LIT__END * sizeof(Tcl_Obj*));
for (i = 0; i < LIT__END; ++i) {
- data->literals[i] = Tcl_NewStringObj(Literals[i], -1);
- Tcl_IncrRefCount(data->literals[i]);
+ Tcl_InitObjRef(data->literals[i], Tcl_NewStringObj(Literals[i], -1));
}
+ data->mcLiterals = NULL;
+ data->mcLitIdxs = NULL;
data->LastTZEpoch = 0;
data->currentYearCentury = ClockDefaultYearCentury;
data->yearOfCenturySwitch = ClockDefaultCenturySwitch;
@@ -273,6 +277,12 @@ TclClockInit(
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;
@@ -323,6 +333,12 @@ ClockConfigureClear(
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);
@@ -341,6 +357,18 @@ ClockDeleteCmdProc(
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);
@@ -355,9 +383,9 @@ ClockDeleteCmdProc(
inline Tcl_Obj *
NormTimezoneObj(
ClockClientData *dataPtr, /* Client data containing literal pool */
- Tcl_Obj * timezoneObj)
+ Tcl_Obj *timezoneObj)
{
- const char * tz;
+ const char *tz;
if ( timezoneObj == dataPtr->LastUnnormSetupTimeZone
&& dataPtr->LastSetupTimeZone != NULL
) {
@@ -399,6 +427,208 @@ NormTimezoneObj(
/*
*----------------------------------------------------------------------
*/
+static Tcl_Obj *
+NormLocaleObj(
+ ClockClientData *dataPtr, /* Client data containing literal pool */
+ Tcl_Obj *localeObj,
+ Tcl_Obj **mcDictObj)
+{
+ const char *loc;
+ if ( localeObj == NULL || localeObj == dataPtr->CurrentLocale
+ || localeObj == dataPtr->literals[LIT_C]
+ ) {
+ *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
+ || strcmp(loc, TclGetString(dataPtr->CurrentLocale)) == 0
+ )
+ ) {
+ *mcDictObj = dataPtr->CurrentLocaleDict;
+ localeObj = dataPtr->CurrentLocale;
+ }
+ else
+ if (dataPtr->LastUsedLocale != NULL &&
+ (localeObj == dataPtr->LastUsedLocale
+ || strcmp(loc, TclGetString(dataPtr->LastUsedLocale)) == 0
+ )
+ ) {
+ *mcDictObj = dataPtr->LastUsedLocaleDict;
+ Tcl_SetObjRef(dataPtr->LastUnnormUsedLocale, localeObj);
+ localeObj = dataPtr->LastUsedLocale;
+ }
+ else
+ if (
+ strcmp(loc, Literals[LIT_C]) == 0
+ ) {
+ *mcDictObj = dataPtr->CurrentLocaleDict;
+ localeObj = dataPtr->CurrentLocale;
+ }
+ else
+ {
+ *mcDictObj = NULL;
+ }
+ return localeObj;
+}
+
+/*
+ *----------------------------------------------------------------------
+ */
+MODULE_SCOPE Tcl_Obj *
+ClockMCDict(ClockFmtScnCmdArgs *opts)
+{
+ ClockClientData *dataPtr = opts->clientData;
+ Tcl_Obj *callargs[3];
+
+ /* 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->localeObj, &opts->mcDictObj);
+
+ if (opts->localeObj == NULL) {
+ Tcl_SetResult(opts->interp,
+ "locale not specified and no default locale set", TCL_STATIC);
+ 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) {
+ /* get msgcat dictionary - ::msgcat::mcget ::tcl::clock locale */
+ callargs[0] = dataPtr->literals[LIT_MCGET];
+ callargs[1] = dataPtr->literals[LIT_TCL_CLOCK];
+ callargs[2] = opts->localeObj;
+
+ if (Tcl_EvalObjv(opts->interp, 3, callargs, 0) != TCL_OK) {
+ return NULL;
+ }
+
+ opts->mcDictObj = Tcl_GetObjResult(opts->interp);
+ if ( opts->localeObj == dataPtr->CurrentLocale ) {
+ Tcl_SetObjRef(dataPtr->CurrentLocaleDict, opts->mcDictObj);
+ } else {
+ Tcl_SetObjRef(dataPtr->LastUsedLocale, opts->localeObj);
+ Tcl_UnsetObjRef(dataPtr->LastUnnormUsedLocale);
+ Tcl_SetObjRef(dataPtr->LastUsedLocaleDict, opts->mcDictObj);
+ }
+ }
+ }
+
+ return opts->mcDictObj;
+}
+
+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 */
+}
+
+MODULE_SCOPE Tcl_Obj *
+ClockMCGetListIdxDict(
+ 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 dictionray,
+ * if not available - create from list */
+
+ if (Tcl_DictObjGet(NULL, opts->mcDictObj,
+ dataPtr->mcLitIdxs[mcKey], &valObj) != TCL_OK
+ ) {
+ Tcl_Obj **lstv, *intObj;
+ int i, lstc;
+
+ if (dataPtr->mcLitIdxs == NULL) {
+ 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));
+ }
+ }
+
+ if (Tcl_DictObjGet(opts->interp, opts->mcDictObj,
+ dataPtr->mcLiterals[mcKey], &valObj) != TCL_OK) {
+ return NULL;
+ };
+ if (TclListObjGetElements(opts->interp, valObj,
+ &lstc, &lstv) != TCL_OK) {
+ return NULL;
+ };
+
+ valObj = Tcl_NewDictObj();
+ for (i = 0; i < lstc; i++) {
+ intObj = Tcl_NewIntObj(i);
+ if (Tcl_DictObjPut(opts->interp, valObj,
+ lstv[i], intObj) != TCL_OK
+ ) {
+ Tcl_DecrRefCount(valObj);
+ Tcl_DecrRefCount(intObj);
+ return NULL;
+ }
+ };
+
+ if (Tcl_DictObjPut(opts->interp, opts->mcDictObj,
+ dataPtr->mcLitIdxs[mcKey], valObj) != TCL_OK
+ ) {
+ Tcl_DecrRefCount(valObj);
+ return NULL;
+ }
+ };
+
+ return valObj;
+}
+
+/*
+ *----------------------------------------------------------------------
+ */
static int
ClockConfigureObjCmd(
ClientData clientData, /* Client data containing literal pool */
@@ -410,23 +640,25 @@ ClockConfigureObjCmd(
Tcl_Obj **litPtr = dataPtr->literals;
static const char *const options[] = {
- "-system-tz", "-setup-tz", "-clear",
+ "-system-tz", "-setup-tz", "-default-locale",
+ "-clear",
"-year-century", "-century-switch",
NULL
};
enum optionInd {
- CLOCK_SYSTEM_TZ, CLOCK_SETUP_TZ, CLOCK_CLEAR_CACHE,
+ 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+=2) {
- if (Tcl_GetIndexFromObj(interp, objv[i], options,
+ 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]), NULL);
+ Tcl_GetString(objv[i-1]), NULL);
return TCL_ERROR;
}
switch (optionIndex) {
@@ -434,24 +666,24 @@ ClockConfigureObjCmd(
if (1) {
/* validate current tz-epoch */
unsigned long lastTZEpoch = TzsetGetEpoch();
- if (i+1 < objc) {
- if (dataPtr->SystemTimeZone != objv[i+1]) {
- Tcl_SetObjRef(dataPtr->SystemTimeZone, objv[i+1]);
+ if (i < objc) {
+ if (dataPtr->SystemTimeZone != objv[i]) {
+ Tcl_SetObjRef(dataPtr->SystemTimeZone, objv[i]);
Tcl_UnsetObjRef(dataPtr->SystemSetupTZData);
}
dataPtr->LastTZEpoch = lastTZEpoch;
}
- if (dataPtr->SystemTimeZone != NULL
+ if (i+1 >= objc && dataPtr->SystemTimeZone != NULL
&& dataPtr->LastTZEpoch == lastTZEpoch) {
Tcl_SetObjResult(interp, dataPtr->SystemTimeZone);
}
}
break;
case CLOCK_SETUP_TZ:
- if (i+1 < objc) {
+ if (i < objc) {
/* differentiate GMT and system zones, because used often */
- Tcl_Obj *timezoneObj = NormTimezoneObj(dataPtr, objv[i+1]);
- Tcl_SetObjRef(dataPtr->LastUnnormSetupTimeZone, objv[i+1]);
+ 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);
@@ -463,7 +695,7 @@ ClockConfigureObjCmd(
}
switch (optionIndex) {
case CLOCK_SETUP_GMT:
- if (i+1 < objc) {
+ if (i < objc) {
if (dataPtr->GMTSetupTimeZone != timezoneObj) {
Tcl_SetObjRef(dataPtr->GMTSetupTimeZone, timezoneObj);
Tcl_UnsetObjRef(dataPtr->GMTSetupTZData);
@@ -471,7 +703,7 @@ ClockConfigureObjCmd(
}
break;
case CLOCK_SETUP_TZ:
- if (i+1 < objc) {
+ if (i < objc) {
if (dataPtr->AnySetupTimeZone != timezoneObj) {
Tcl_SetObjRef(dataPtr->AnySetupTimeZone, timezoneObj);
Tcl_UnsetObjRef(dataPtr->AnySetupTZData);
@@ -480,35 +712,52 @@ ClockConfigureObjCmd(
break;
}
}
- if (dataPtr->LastSetupTimeZone != NULL) {
+ 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+1 < objc) {
+ if (i < objc) {
int year;
- if (TclGetIntFromObj(interp, objv[i+1], &year) != TCL_OK) {
+ if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) {
return TCL_ERROR;
}
dataPtr->currentYearCentury = year;
- Tcl_SetObjResult(interp, objv[i+1]);
+ if (i+1 >= objc) {
+ Tcl_SetObjResult(interp, objv[i]);
+ }
continue;
- }
- Tcl_SetObjResult(interp,
- Tcl_NewIntObj(dataPtr->currentYearCentury));
+ }
+ if (i+1 >= objc) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewIntObj(dataPtr->currentYearCentury));
+ }
break;
case CLOCK_CENTURY_SWITCH:
- if (i+1 < objc) {
+ if (i < objc) {
int year;
- if (TclGetIntFromObj(interp, objv[i+1], &year) != TCL_OK) {
+ if (TclGetIntFromObj(interp, objv[i], &year) != TCL_OK) {
return TCL_ERROR;
}
dataPtr->yearOfCenturySwitch = year;
- Tcl_SetObjResult(interp, objv[i+1]);
+ Tcl_SetObjResult(interp, objv[i]);
continue;
- }
- Tcl_SetObjResult(interp,
- Tcl_NewIntObj(dataPtr->yearOfCenturySwitch));
+ }
+ if (i+1 >= objc) {
+ Tcl_SetObjResult(interp,
+ Tcl_NewIntObj(dataPtr->yearOfCenturySwitch));
+ }
break;
case CLOCK_CLEAR_CACHE:
ClockConfigureClear(dataPtr);
@@ -597,7 +846,7 @@ ClockGetSystemTimeZone(
}
Tcl_UnsetObjRef(dataPtr->SystemTimeZone);
- Tcl_UnsetObjRef(data->SystemSetupTZData);
+ Tcl_UnsetObjRef(dataPtr->SystemSetupTZData);
literals = dataPtr->literals;
@@ -683,125 +932,6 @@ ClockFormatNumericTimeZone(int z) {
}
return Tcl_ObjPrintf("%c%02d%02d", sign, h, m);
}
-
-/*
- *----------------------------------------------------------------------
- */
-inline Tcl_Obj *
-NormLocaleObj(
- ClockClientData *dataPtr, /* Client data containing literal pool */
- Tcl_Obj *localeObj)
-{
- const char * tz;
- if ( localeObj == dataPtr->LastUnnormSetupLocale
- && dataPtr->LastSetupLocale != NULL
- ) {
- return dataPtr->LastSetupLocale;
- }
- if ( localeObj == dataPtr->LastSetupLocale
- || localeObj == dataPtr->literals[LIT_CLOCALE]
- || localeObj == dataPtr->SystemLocale
- || localeObj == dataPtr->AnySetupLocale
- ) {
- return localeObj;
- }
-
- tz = TclGetString(localeObj);
- if (dataPtr->AnySetupLocale != NULL &&
- (localeObj == dataPtr->AnySetupLocale
- || strcmp(tz, TclGetString(dataPtr->AnySetupLocale)) == 0
- )
- ) {
- localeObj = dataPtr->AnySetupLocale;
- }
- else
- if (dataPtr->SystemLocale != NULL &&
- (localeObj == dataPtr->SystemLocale
- || strcmp(tz, TclGetString(dataPtr->SystemLocale)) == 0
- )
- ) {
- localeObj = dataPtr->SystemLocale;
- }
- else
- if (
- strcmp(tz, Literals[LIT_GMT]) == 0
- ) {
- localeObj = dataPtr->literals[LIT_GMT];
- }
- return localeObj;
-}
-/*
- *----------------------------------------------------------------------
- */
-static Tcl_Obj *
-ClockGetSystemLocale(
- ClientData clientData, /* Opaque pointer to literal pool, etc. */
- Tcl_Interp *interp) /* Tcl interpreter */
-{
- ClockClientData *dataPtr = clientData;
- Tcl_Obj **literals;
-
- /* if known (cached) - return now */
- if (dataPtr->SystemLocale != NULL) {
- return dataPtr->SystemLocale;
- }
-
- literals = dataPtr->literals;
-
- if (Tcl_EvalObjv(interp, 1, &literals[LIT_GETSYSTEMLOCALE], 0) != TCL_OK) {
- return NULL;
- }
- Tcl_SetObjRef(dataPtr->SystemLocale, Tcl_GetObjResult(interp));
- return dataPtr->SystemLocale;
-}
-/*
- *----------------------------------------------------------------------
- */
-static Tcl_Obj *
-ClockSetupLocale(
- ClientData clientData, /* Opaque pointer to literal pool, etc. */
- Tcl_Interp *interp, /* Tcl interpreter */
- Tcl_Obj *localeObj)
-{
- ClockClientData *dataPtr = clientData;
- Tcl_Obj **literals = dataPtr->literals;
- Tcl_Obj *callargs[2];
-
- if (localeObj == NULL || localeObj == dataPtr->SystemLocale) {
- if (dataPtr->SystemLocale == NULL) {
- return ClockGetSystemLocale(clientData, interp);
- }
- return dataPtr->SystemLocale;
- }
-
-#if 0
- /* if cached (if already setup this one) */
- if ( dataPtr->LastSetupTimeZone != NULL
- && ( localeObj == 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;
- }
-
-#endif
- callargs[0] = literals[LIT_SETUPLOCALE];
- callargs[1] = localeObj;
-
- if (Tcl_EvalObjv(interp, 2, callargs, 0) == TCL_OK) {
- return localeObj; // dataPtr->CurrentLocale;
- }
- return NULL;
-}
/*
*----------------------------------------------------------------------
*
@@ -2741,19 +2871,13 @@ ClockScanObjCmd(
}
}
- /* Setup timezone and locale */
+ /* Setup timezone (normalize object id needed and load TZ on demand) */
opts.timezoneObj = ClockSetupTimeZone(clientData, interp, opts.timezoneObj);
if (opts.timezoneObj == NULL) {
return TCL_ERROR;
}
- opts.localeObj = ClockSetupLocale(clientData, interp, opts.localeObj);
- if (opts.localeObj == NULL) {
- return TCL_ERROR;
- }
-
-
ClockInitDateInfo(info);
yydate.tzName = NULL;
diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c
index 17181d9..a3c7f20 100644
--- a/generic/tclClockFmt.c
+++ b/generic/tclClockFmt.c
@@ -454,45 +454,55 @@ Tcl_GetClockFrmScnFromObj(
return fss;
}
-
+
+/*
+ * DetermineGreedySearchLen --
+ *
+ * Determine min/max lengths as exact as possible (speed, greedy match)
+ *
+ */
+inline
+void DetermineGreedySearchLen(ClockFmtScnCmdArgs *opts,
+ DateInfo *info, ClockScanToken *tok, int *minLen, int *maxLen)
+{
+ register const char*p = yyInput;
+ *minLen = 0;
+ *maxLen = info->dateEnd - p;
+
+ /* if no tokens anymore */
+ if (!(tok+1)->map) {
+ /* should match to end or first space */
+ while (!isspace(UCHAR(*p)) && ++p < info->dateEnd) {};
+ *minLen = p - yyInput;
+ }
+}
static int
LocaleListSearch(ClockFmtScnCmdArgs *opts,
- DateInfo *info, const char * mcKey, int *val)
+ DateInfo *info, int mcKey, int *val,
+ int minLen, int maxLen)
{
- Tcl_Obj *callargs[4] = {NULL, NULL, NULL, NULL}, **lstv;
- int i, lstc;
- Tcl_Obj *valObj = NULL;
- int ret = TCL_RETURN;
+ Tcl_Obj **lstv;
+ int lstc, i, l;
+ const char *s;
+ Tcl_Obj *valObj;
/* get msgcat value */
- TclNewLiteralStringObj(callargs[0], "::msgcat::mcget");
- TclNewLiteralStringObj(callargs[1], "::tcl::clock");
- callargs[2] = opts->localeObj;
- if (opts->localeObj == NULL) {
- TclNewLiteralStringObj(callargs[1], "c");
- }
- callargs[3] = Tcl_NewStringObj(mcKey, -1);
-
- for (i = 0; i < 4; i++) {
- Tcl_IncrRefCount(callargs[i]);
- }
- if (Tcl_EvalObjv(opts->interp, 4, callargs, 0) != TCL_OK) {
- goto done;
+ valObj = ClockMCGet(opts, mcKey);
+ if (valObj == NULL) {
+ return TCL_ERROR;
}
- Tcl_InitObjRef(valObj, Tcl_GetObjResult(opts->interp));
-
/* is a list */
if (TclListObjGetElements(opts->interp, valObj, &lstc, &lstv) != TCL_OK) {
- goto done;
+ return TCL_ERROR;
}
/* search in list */
for (i = 0; i < lstc; i++) {
- const char *s = TclGetString(lstv[i]);
- int l = lstv[i]->length;
- if ( l <= info->dateEnd - yyInput
+ s = TclGetString(lstv[i]);
+ l = lstv[i]->length;
+ if ( l >= minLen && l <= maxLen
&& strncasecmp(yyInput, s, l) == 0
) {
*val = i;
@@ -501,21 +511,11 @@ LocaleListSearch(ClockFmtScnCmdArgs *opts,
}
}
- ret = TCL_OK;
/* if not found */
- if (i >= lstc) {
- ret = TCL_ERROR;
- }
-
-done:
-
- Tcl_UnsetObjRef(valObj);
-
- for (i = 0; i < 4; i++) {
- Tcl_UnsetObjRef(callargs[i]);
+ if (i < lstc) {
+ return TCL_OK;
}
-
- return ret;
+ return TCL_RETURN;
}
static int
@@ -535,12 +535,34 @@ StaticListSearch(ClockFmtScnCmdArgs *opts,
}
s++;
}
- if (*s == NULL) {
- return TCL_ERROR;
+ if (*s != NULL) {
+ return TCL_OK;
}
-
- return TCL_OK;
-};
+ return TCL_RETURN;
+}
+
+inline const char *
+FindWordEnd(
+ ClockScanToken *tok,
+ register const char * p, const char * end)
+{
+ register const char *x = tok->tokWord.start;
+ if (x == tok->tokWord.end) { /* single char word */
+ if (*p != *x) {
+ /* no match -> error */
+ return NULL;
+ }
+ return ++p;
+ }
+ /* multi-char word */
+ while (*p++ == *x++) {
+ if (x >= tok->tokWord.end || p >= end) {
+ /* no match -> error */
+ return NULL;
+ }
+ };
+ return p;
+}
static int
ClockScnToken_Month_Proc(ClockFmtScnCmdArgs *opts,
@@ -560,19 +582,26 @@ ClockScnToken_Month_Proc(ClockFmtScnCmdArgs *opts,
};
int val;
if (StaticListSearch(opts, info, months, &val) != TCL_OK) {
- return TCL_ERROR;
+ return TCL_RETURN;
}
yyMonth = (val % 12) + 1;
return TCL_OK;
*/
- int val;
- int ret = LocaleListSearch(opts, info, "MONTHS_FULL", &val);
+ int ret, val;
+ int minLen;
+ int maxLen;
+
+ DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen);
+
+ ret = LocaleListSearch(opts, info, MCLIT_MONTHS_FULL, &val,
+ minLen, maxLen);
if (ret != TCL_OK) {
+ /* if not found */
if (ret == TCL_RETURN) {
- return ret;
+ ret = LocaleListSearch(opts, info, MCLIT_MONTHS_ABBREV, &val,
+ minLen, maxLen);
}
- ret = LocaleListSearch(opts, info, "MONTHS_ABBREV", &val);
if (ret != TCL_OK) {
return ret;
}
@@ -588,9 +617,14 @@ static int
ClockScnToken_LocaleListMatcher_Proc(ClockFmtScnCmdArgs *opts,
DateInfo *info, ClockScanToken *tok)
{
- int val;
+ int ret, val;
+ int minLen;
+ int maxLen;
+
+ DetermineGreedySearchLen(opts, info, tok, &minLen, &maxLen);
- int ret = LocaleListSearch(opts, info, (char *)tok->map->data, &val);
+ ret = LocaleListSearch(opts, info, (int)tok->map->data, &val,
+ minLen, maxLen);
if (ret != TCL_OK) {
return ret;
}
@@ -639,8 +673,8 @@ static ClockScanTokenMap ScnSTokenMap[] = {
NULL},
};
static const char *ScnSTokenWrapMapIndex[2] = {
- "eBh",
- "dbb"
+ "eNBh",
+ "dmbb"
};
static const char *ScnETokenMapIndex =
@@ -654,11 +688,14 @@ static const char *ScnETokenWrapMapIndex[2] = {
};
static const char *ScnOTokenMapIndex =
- "d";
+ "dm";
static ClockScanTokenMap ScnOTokenMap[] = {
/* %Od %Oe */
{CTOKT_PARSER, CLF_DATE, 0, 0, TclOffset(DateInfo, date.dayOfMonth),
- ClockScnToken_LocaleListMatcher_Proc, "LOCALE_NUMERALS"},
+ ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
+ /* %Om */
+ {CTOKT_PARSER, CLF_DATE, 0, 0, TclOffset(DateInfo, date.month),
+ ClockScnToken_LocaleListMatcher_Proc, (void *)MCLIT_LOCALE_NUMERALS},
};
static const char *ScnOTokenWrapMapIndex[2] = {
"e",
@@ -730,7 +767,7 @@ ClockGetOrParseScanFormat(
switch (*p) {
case '%':
if (1) {
- ClockScanTokenMap * maps = ScnSTokenMap;
+ ClockScanTokenMap * scnMap = ScnSTokenMap;
const char *mapIndex = ScnSTokenMapIndex,
**wrapIndex = ScnSTokenWrapMapIndex;
if (p+1 >= e) {
@@ -748,13 +785,13 @@ ClockGetOrParseScanFormat(
continue;
break;
case 'E':
- maps = ScnETokenMap,
+ scnMap = ScnETokenMap,
mapIndex = ScnETokenMapIndex,
wrapIndex = ScnETokenWrapMapIndex;
p++;
break;
case 'O':
- maps = ScnOTokenMap,
+ scnMap = ScnOTokenMap,
mapIndex = ScnOTokenMapIndex,
wrapIndex = ScnOTokenWrapMapIndex;
p++;
@@ -778,7 +815,7 @@ ClockGetOrParseScanFormat(
goto word_tok;
}
}
- tok->map = &ScnSTokenMap[cp - mapIndex];
+ tok->map = &scnMap[cp - mapIndex];
tok->tokWord.start = p;
/* calculate look ahead value by standing together tokens */
if (tok > fss->scnTok) {
@@ -928,7 +965,7 @@ ClockScan(
size = p - yyInput;
if (size < map->minSize) {
/* missing input -> error */
- goto error;
+ goto not_match;
}
/* string 2 number, put number into info structure by offset */
p = yyInput; x = p + size;
@@ -953,10 +990,10 @@ ClockScan(
case TCL_OK:
break;
case TCL_RETURN:
- goto done;
+ goto not_match;
break;
default:
- goto error;
+ goto done;
break;
};
p = yyInput;
@@ -967,7 +1004,7 @@ ClockScan(
if (opts->flags & CLF_STRICT) {
if (!isspace(UCHAR(*p))) {
/* unmatched -> error */
- goto error;
+ goto not_match;
}
p++;
}
@@ -976,21 +1013,13 @@ ClockScan(
}
break;
case CTOKT_WORD:
- x = tok->tokWord.start;
- if (x == tok->tokWord.end) { /* single char word */
- if (*p != *x) {
- /* no match -> error */
- goto error;
- }
- p++;
- continue;
- }
- /* multi-char word */
- while (p < end && x < tok->tokWord.end && *p++ == *x++) {};
- if (x < tok->tokWord.end) {
+ x = FindWordEnd(tok, p, end);
+ if (!x) {
/* no match -> error */
- goto error;
+ goto not_match;
}
+ p = x;
+ continue;
break;
}
}
@@ -1002,7 +1031,7 @@ ClockScan(
/* check end was reached */
if (p < end) {
/* something after last token - wrong format */
- goto error;
+ goto not_match;
}
/* invalidate result */
@@ -1045,15 +1074,15 @@ ClockScan(
overflow:
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "integer value too large to represent", -1));
- Tcl_SetErrorCode(interp, "CLOCK", "integervalueTooLarge", NULL);
+ Tcl_SetResult(interp, "requested date too large to represent",
+ TCL_STATIC);
+ Tcl_SetErrorCode(interp, "CLOCK", "dateTooLarge", NULL);
goto done;
-error:
+not_match:
- Tcl_SetResult(interp,
- "input string does not match supplied format", TCL_STATIC);
+ Tcl_SetResult(interp, "input string does not match supplied format",
+ TCL_STATIC);
Tcl_SetErrorCode(interp, "CLOCK", "badInputString", NULL);
done:
diff --git a/generic/tclDate.h b/generic/tclDate.h
index d97cd3c..1c51a02 100644
--- a/generic/tclDate.h
+++ b/generic/tclDate.h
@@ -35,6 +35,22 @@
#define CLF_INVALIDATE_JULIANDAY (1 << 7) /* assemble julianDay using year, month, etc. */
#define CLF_INVALIDATE_SECONDS (1 << 8) /* assemble localSeconds (and seconds at end) */
+
+/*
+ * Enumeration of the msgcat literals used in [clock]
+ */
+
+typedef enum ClockMsgCtLiteral {
+ MCLIT_MONTHS_FULL, MCLIT_MONTHS_ABBREV,
+ MCLIT_LOCALE_NUMERALS,
+ MCLIT__END
+} ClockMsgCtLiteral;
+
+#define CLOCK_LOCALE_LITERAL_ARRAY(litarr, pref) static const char *const litarr[] = { \
+ pref "MONTHS_FULL", pref "MONTHS_ABBREV", \
+ pref "LOCALE_NUMERALS", \
+}
+
/*
* Primitives to safe set, reset and free references.
*/
@@ -176,16 +192,19 @@ ClockInitDateInfo(DateInfo *info) {
#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 */
+ 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 only) */
int flags; /* Flags control scanning */
+
+ Tcl_Obj *mcDictObj; /* Current dictionary of tcl::clock package for given localeObj*/
} ClockFmtScnCmdArgs;
/*
@@ -194,7 +213,11 @@ typedef struct ClockFmtScnCmdArgs {
typedef struct ClockClientData {
int refCount; /* Number of live references. */
- Tcl_Obj **literals; /* Pool of object literals. */
+ 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;
@@ -208,6 +231,13 @@ typedef struct ClockClientData {
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;
@@ -328,6 +358,15 @@ MODULE_SCOPE time_t ToSeconds(time_t Hours, time_t Minutes,
MODULE_SCOPE int TclClockFreeScan(Tcl_Interp *interp, DateInfo *info);
+/* tclClock.c module declarations */
+
+MODULE_SCOPE Tcl_Obj *
+ ClockMCDict(ClockFmtScnCmdArgs *opts);
+MODULE_SCOPE Tcl_Obj *
+ ClockMCGet(ClockFmtScnCmdArgs *opts, int mcKey);
+MODULE_SCOPE Tcl_Obj *
+ ClockMCGetListIdxDict(ClockFmtScnCmdArgs *opts, int mcKey);
+
/* tclClockFmt.c module declarations */
MODULE_SCOPE ClockFmtScnStorage *
diff --git a/library/clock.tcl b/library/clock.tcl
index ebbecb9..b4632b1 100755
--- a/library/clock.tcl
+++ b/library/clock.tcl
@@ -289,8 +289,9 @@ proc ::tcl::clock::Initialize {} {
# Default configuration
- # configure -year-century 2000 \
- # -century-switch 38
+ configure -default-locale [mclocale]
+ #configure -year-century 2000 \
+ # -century-switch 38
# Translation table to map Windows TZI onto cities, so that the Olson
# rules can apply. In some cases the mapping is ambiguous, so it's wise
@@ -2105,6 +2106,51 @@ proc ::tcl::clock::MakeParseCodeFromFields { dateFields parseActions } {
#----------------------------------------------------------------------
#
+# GetSystemTimeZone --
+#
+# Determines the system time zone, which is the default for the
+# 'clock' command if no other zone is supplied.
+#
+# Parameters:
+# None.
+#
+# Results:
+# Returns the system time zone.
+#
+# Side effects:
+# Stores the sustem time zone in engine configuration, since
+# determining it may be an expensive process.
+#
+#----------------------------------------------------------------------
+
+proc ::tcl::clock::GetSystemLocale {} {
+ if { $::tcl_platform(platform) ne {windows} } {
+ # On a non-windows platform, the 'system' locale is the same as
+ # the 'current' locale
+
+ return [mclocale]
+ }
+
+ # On a windows platform, the 'system' locale is adapted from the
+ # 'current' locale by applying the date and time formats from the
+ # Control Panel. First, load the 'current' locale if it's not yet
+ # loaded
+
+ mcpackagelocale set [mclocale]
+
+ # Make a new locale string for the system locale, and get the
+ # Control Panel information
+
+ set locale [mclocale]_windows
+ if { ! [mcpackagelocale present $locale] } {
+ LoadWindowsDateTimeFormats $locale
+ }
+
+ return $locale
+}
+
+#----------------------------------------------------------------------
+#
# EnterLocale --
#
# Switch [mclocale] to a given locale if necessary
@@ -2121,33 +2167,12 @@ proc ::tcl::clock::MakeParseCodeFromFields { dateFields parseActions } {
#----------------------------------------------------------------------
proc ::tcl::clock::EnterLocale { locale } {
- if { $locale eq {system} } {
- if { $::tcl_platform(platform) ne {windows} } {
- # On a non-windows platform, the 'system' locale is the same as
- # the 'current' locale
-
- set locale current
- } else {
- # On a windows platform, the 'system' locale is adapted from the
- # 'current' locale by applying the date and time formats from the
- # Control Panel. First, load the 'current' locale if it's not yet
- # loaded
-
- mcpackagelocale set [mclocale]
-
- # Make a new locale string for the system locale, and get the
- # Control Panel information
-
- set locale [mclocale]_windows
- if { ! [mcpackagelocale present $locale] } {
- LoadWindowsDateTimeFormats $locale
- }
- }
- }
- if { $locale eq {current}} {
+ switch -- $locale system {
+ set locale [GetSystemLocale]
+ } current {
set locale [mclocale]
}
- # Eventually load the locale
+ # Select the locale, eventually load it
mcpackagelocale set $locale
}
diff --git a/library/msgcat/msgcat.tcl b/library/msgcat/msgcat.tcl
index 7f23568..3fd2cdb 100644
--- a/library/msgcat/msgcat.tcl
+++ b/library/msgcat/msgcat.tcl
@@ -227,52 +227,61 @@ proc msgcat::mc {src args} {
# msgcat::mcget --
#
-# Find the translation for the given string based on the given
-# locale setting. Check the given namespace first, then look in each
-# parent namespace until the source is found. If additional args are
-# specified, use the format command to work them into the traslated
-# string.
-# If no catalog item is found, mcunknown is called in the caller frame
-# and its result is returned.
+# Return the translation for the given string based on the given
+# locale setting or the whole dictionary object of the package/locale.
+# Searching of catalog is similar to "msgcat::mc".
+#
+# Contrary to "msgcat::mc" may additionally load a package catalog
+# on demand.
#
# Arguments:
-# src The string to translate.
-# args Args to pass to the format command
+# ns The package namespace (as catalog selector).
+# loc The locale used for translation.
+# {src} The string to translate.
+# {args} Args to pass to the format command
#
# Results:
# Returns the translated string. Propagates errors thrown by the
# format command.
-proc msgcat::mcget {ns loc src args} {
+proc msgcat::mcget {ns loc args} {
variable Msgs
if {$loc eq {C}} {
set loclist [PackagePreferences $ns]
+ set loc [lindex $loclist 0]
} else {
+ set loc [string tolower $loc]
variable PackageConfig
- # if {![dict exists $PackageConfig $ns $loc]} {
- # set loc [mclocale]
- # }
- set loclist [dict get $PackageConfig locales $ns $loc]
+ # get locales list for given locale (de_de -> {de_de de {}})
+ if {[catch {
+ set loclist [dict get $PackageConfig locales $ns $loc]
+ }]} {
+ # lazy load catalog on demand
+ mcpackagelocale load $loc $ns
+ set loclist [dict get $PackageConfig locales $ns $loc]
+ }
}
+ if {![llength $args]} {
+ # get whole catalog:
+ return [msgcat::Merge $ns $loclist]
+ }
+ set src [lindex $args 0]
+ # search translation for each locale (regarding parent namespaces)
for {set nscur $ns} {$nscur != ""} {set nscur [namespace parent $nscur]} {
foreach loc $loclist {
if {[dict exists $Msgs $nscur $loc $src]} {
- if {[llength $args]} {
- return [format [dict get $Msgs $nscur $loc $src] {*}$args]
+ if {[llength $args] > 1} {
+ return [format [dict get $Msgs $nscur $loc $src] \
+ {*}[lrange $args 1 end]]
} else {
return [dict get $Msgs $nscur $loc $src]
}
}
}
}
- # call package local or default unknown command
- set args [linsert $args 0 $loclist $src]
- switch -exact -- [Invoke unknowncmd $args $ns result 1] {
- 0 { return [uplevel 1 [linsert $args 0 [namespace origin mcunknown]]] }
- 1 { return [DefaultUnknown {*}$args] }
- default { return $result }
- }
+ # get with package default locale
+ mcget $ns [lindex $loclist 0] {*}$args
}
# msgcat::mcexists --
@@ -465,6 +474,10 @@ proc msgcat::mcloadedlocales {subcommand} {
# items, if the former locale was the default locale.
# Returns the normalized set locale.
# The default locale is taken, if locale is not given.
+# load
+# Load a package locale without set it (lazy loading from mcget).
+# Returns the normalized set locale.
+# The default locale is taken, if locale is not given.
# get
# Get the locale valid for this package.
# isset
@@ -492,7 +505,7 @@ proc msgcat::mcloadedlocales {subcommand} {
# Results:
# Empty string, if not stated differently for the subcommand
-proc msgcat::mcpackagelocale {subcommand {locale ""}} {
+proc msgcat::mcpackagelocale {subcommand {locale ""} {ns ""}} {
# todo: implement using an ensemble
variable Loclist
variable LoadedLocales
@@ -512,7 +525,9 @@ proc msgcat::mcpackagelocale {subcommand {locale ""}} {
}
set locale [string tolower $locale]
}
- set ns [uplevel 1 {::namespace current}]
+ if {$ns eq ""} {
+ set ns [uplevel 1 {::namespace current}]
+ }
switch -exact -- $subcommand {
get { return [lindex [PackagePreferences $ns] 0] }
@@ -520,7 +535,7 @@ proc msgcat::mcpackagelocale {subcommand {locale ""}} {
loaded { return [PackageLocales $ns] }
present { return [expr {$locale in [PackageLocales $ns]} ]}
isset { return [dict exists $PackageConfig loclist $ns] }
- set { # set a package locale or add a package locale
+ set - load { # set a package locale or add a package locale
# Copy the default locale if no package locale set so far
if {![dict exists $PackageConfig loclist $ns]} {
@@ -530,18 +545,21 @@ proc msgcat::mcpackagelocale {subcommand {locale ""}} {
# Check if changed
set loclist [dict get $PackageConfig loclist $ns]
- if {! [info exists locale] || $locale eq [lindex $loclist 0] } {
+ if {[llength [info level 0]] == 2 || $locale eq [lindex $loclist 0] } {
return [lindex $loclist 0]
}
# Change loclist
set loclist [GetPreferences $locale]
set locale [lindex $loclist 0]
- dict set PackageConfig loclist $ns $loclist
- dict set PackageConfig locales $ns $locale $loclist
+ if {$subcommand eq {set}} {
+ # set loclist
+ dict set PackageConfig loclist $ns $loclist
+ }
# load eventual missing locales
set loadedLocales [dict get $PackageConfig loadedlocales $ns]
+ dict set PackageConfig locales $ns $locale $loclist
if {$locale in $loadedLocales} { return $locale }
set loadLocales [ListComplement $loadedLocales $loclist]
dict set PackageConfig loadedlocales $ns\
@@ -899,6 +917,39 @@ proc msgcat::Load {ns locales {callbackonly 0}} {
return $x
}
+# msgcat::Merge --
+#
+# Merge message catalog dictionaries to one dictionary.
+#
+# Arguments:
+# ns Namespace (equal package) to load the message catalog.
+# locales List of locales to merge.
+#
+# Results:
+# Returns the merged dictionary of message catalogs.
+proc msgcat::Merge {ns locales} {
+ variable Merged
+ if {![catch {
+ set mrgcat [dict get $Merged $ns [set loc [lindex $locales 0]]]
+ }]} {
+ return $mrgcat
+ }
+ variable Msgs
+ # Merge sequential locales (in reverse order, e. g. {} -> en -> en_en):
+ if {[llength $locales] > 1} {
+ set mrgcat [msgcat::Merge $ns [lrange $locales 1 end]]
+ catch {
+ set mrgcat [dict merge $mrgcat [dict get $Msgs $ns $loc]]
+ }
+ } else {
+ catch {
+ set mrgcat [dict get $Msgs $ns $loc]
+ }
+ }
+ dict set Merged $ns $loc $mrgcat
+ return $mrgcat
+}
+
# msgcat::Invoke --
#
# Invoke a set of registered callbacks.
@@ -971,6 +1022,7 @@ proc msgcat::Invoke {index arglist {ns ""} {resultname ""} {failerror 0}} {
proc msgcat::mcset {locale src {dest ""}} {
variable Msgs
+ variable Merged
if {[llength [info level 0]] == 3} { ;# dest not specified
set dest $src
}
@@ -980,6 +1032,7 @@ proc msgcat::mcset {locale src {dest ""}} {
set locale [string tolower $locale]
dict set Msgs $ns $locale $src $dest
+ dict unset Merged $ns
return $dest
}
@@ -1019,6 +1072,7 @@ proc msgcat::mcflset {src {dest ""}} {
proc msgcat::mcmset {locale pairs} {
variable Msgs
+ variable Merged
set length [llength $pairs]
if {$length % 2} {
@@ -1032,6 +1086,7 @@ proc msgcat::mcmset {locale pairs} {
foreach {src dest} $pairs {
dict set Msgs $ns $locale $src $dest
}
+ dict unset Merged $ns
return [expr {$length / 2}]
}
diff --git a/tests-perf/clock.perf.tcl b/tests-perf/clock.perf.tcl
index fd24068..a005648 100644
--- a/tests-perf/clock.perf.tcl
+++ b/tests-perf/clock.perf.tcl
@@ -19,10 +19,11 @@
## set testing defaults:
set ::env(TCL_TZ) :CET
-## warm-up (load clock.tcl, system zones, etc.):
+## warm-up (load clock.tcl, system zones, locales, etc.):
clock scan "" -gmt 1
clock scan ""
clock scan "" -timezone :CET
+clock scan "" -format "" -locale en
## ------------------------------------------
@@ -37,6 +38,20 @@ proc _test_get_commands {lst} {
proc _test_out_total {} {
upvar _ _
+ if {![llength $_(ittm)]} {
+ puts ""
+ return
+ }
+
+ set mintm 0x7fffffff
+ set maxtm 0
+ set i 0
+ foreach tm $_(ittm) {
+ if {$tm > $maxtm} {set maxtm $tm; set maxi $i}
+ if {$tm < $mintm} {set mintm $tm; set mini $i}
+ incr i
+ }
+
puts [string repeat ** 40]
puts [format "Total %d cases in %.2f sec.:" [llength $_(itcnt)] [expr {[llength $_(itcnt)] * $_(reptime) / 1000.0}]]
lset _(m) 0 [format %.6f [expr [join $_(ittm) +]]]
@@ -48,6 +63,16 @@ proc _test_out_total {} {
lset _(m) 2 [expr {[lindex $_(m) 2] / [llength $_(itcnt)]}]
lset _(m) 4 [expr {[lindex $_(m) 2] * (1000 / $_(reptime))}]
puts $_(m)
+ puts "Min:"
+ lset _(m) 0 [lindex $_(ittm) $mini]
+ lset _(m) 2 [lindex $_(itcnt) $mini]
+ lset _(m) 2 [lindex $_(itrate) $mini]
+ puts $_(m)
+ puts "Max:"
+ lset _(m) 0 [lindex $_(ittm) $maxi]
+ lset _(m) 2 [lindex $_(itcnt) $maxi]
+ lset _(m) 2 [lindex $_(itrate) $maxi]
+ puts $_(m)
puts [string repeat ** 40]
puts ""
}
@@ -123,8 +148,10 @@ proc test-scan {{reptime 1000}} {
# Scan : century, lookup table month
{clock scan {1970 Jan 2} -format {%C%y %b %d} -locale en -gmt 1}
- # Scan : century, lookup table month and day
+ # Scan : century, lookup table month and day (both entries are first)
{clock scan {1970 Jan 02} -format {%C%y %b %Od} -locale en -gmt 1}
+ # Scan : century, lookup table month and day (list scan: entries with position 12 / 31)
+ {clock scan {2016 Dec 31} -format {%C%y %b %Od} -locale en -gmt 1}
break
diff --git a/tests/clock.test b/tests/clock.test
index 9e3dbd7..22b5bc1 100644
--- a/tests/clock.test
+++ b/tests/clock.test
@@ -18553,12 +18553,12 @@ test clock-6.8 {input of seconds} {
} 9223372036854775807
test clock-6.9 {input of seconds - overflow} {
- list [catch {clock scan -9223372036854775809 -format %s -gmt true} result] $result
-} {1 {integer value too large to represent}}
+ list [catch {clock scan -9223372036854775809 -format %s -gmt true} result] $result $::errorCode
+} {1 {requested date too large to represent} {CLOCK dateTooLarge}}
test clock-6.10 {input of seconds - overflow} {
- list [catch {clock scan 9223372036854775808 -format %s -gmt true} result] $result
-} {1 {integer value too large to represent}}
+ list [catch {clock scan 9223372036854775808 -format %s -gmt true} result] $result $::errorCode
+} {1 {requested date too large to represent} {CLOCK dateTooLarge}}
test clock-6.11 {input of seconds - two values} {
clock scan {1 2} -format {%s %s} -gmt true