From 62ddcc3daa746fc8be02e6b118e0c923ec227793 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 10 Jan 2017 22:23:37 +0000 Subject: [temp-commit]: not ready --- generic/tclClock.c | 167 ++++++++++++++++++++---- generic/tclClockFmt.c | 319 ++++++++++++++++++++++++++++++++++++++-------- generic/tclDate.h | 32 ++++- library/msgcat/msgcat.tcl | 54 +++++++- tests-perf/clock.perf.tcl | 13 +- 5 files changed, 501 insertions(+), 84 deletions(-) diff --git a/generic/tclClock.c b/generic/tclClock.c index a4edb82..1e9abba 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -59,6 +59,8 @@ typedef enum ClockLiteral { LIT_TZDATA, LIT_GETSYSTEMTIMEZONE, LIT_SETUPTIMEZONE, + LIT_GETSYSTEMLOCALE, + LIT_SETUPLOCALE, #if 0 LIT_FREESCAN, #endif @@ -81,6 +83,8 @@ static const char *const Literals[] = { "::tcl::clock::TZData", "::tcl::clock::GetSystemTimeZone", "::tcl::clock::SetupTimeZone", + "::tcl::clock::mclocale", + "::tcl::clock::EnterLocale", #if 0 "::tcl::clock::FreeScan" #endif @@ -176,24 +180,6 @@ static void TzsetIfNecessary(void); static void ClockDeleteCmdProc(ClientData); /* - * 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); }; \ - } \ -} - -/* * Structure containing description of "native" clock commands to create. */ @@ -610,12 +596,18 @@ ClockGetSystemTimeZone( return dataPtr->SystemTimeZone; } + Tcl_UnsetObjRef(dataPtr->SystemTimeZone); + Tcl_UnsetObjRef(data->SystemSetupTZData); + literals = dataPtr->literals; if (Tcl_EvalObjv(interp, 1, &literals[LIT_GETSYSTEMTIMEZONE], 0) != TCL_OK) { return NULL; } - return Tcl_GetObjResult(interp); + if (dataPtr->SystemTimeZone == NULL) { + Tcl_SetObjRef(dataPtr->SystemTimeZone, Tcl_GetObjResult(interp)); + } + return dataPtr->SystemTimeZone; } /* *---------------------------------------------------------------------- @@ -694,6 +686,124 @@ ClockFormatNumericTimeZone(int z) { /* *---------------------------------------------------------------------- + */ +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; +} +/* + *---------------------------------------------------------------------- * * ClockConvertlocaltoutcObjCmd -- * @@ -2440,11 +2550,7 @@ _ClockParseFmtScnArgs( * Extract values for the keywords. */ - resOpts->formatObj = NULL; - resOpts->localeObj = NULL; - resOpts->timezoneObj = NULL; - resOpts->baseObj = NULL; - resOpts->flags = 0; + memset(resOpts, 0, sizeof(*resOpts)); for (i = 2; i < objc; i+=2) { if (Tcl_GetIndexFromObj(interp, objv[i], options[forScan], "option", 0, &optionIndex) != TCL_OK) { @@ -2488,6 +2594,9 @@ _ClockParseFmtScnArgs( resOpts->timezoneObj = litPtr[LIT_GMT]; } + resOpts->clientData = clientData; + resOpts->interp = interp; + return TCL_OK; } @@ -2562,7 +2671,7 @@ ClockParseformatargsObjCmd( * Return options as a list. */ - Tcl_SetObjResult(interp, Tcl_NewListObj(3, (Tcl_Obj**)&resOpts)); + Tcl_SetObjResult(interp, Tcl_NewListObj(3, (Tcl_Obj**)&resOpts.formatObj)); return TCL_OK; } @@ -2632,13 +2741,19 @@ ClockScanObjCmd( } } - /* Get the data for time changes in the given zone */ + /* Setup timezone and locale */ 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 ad04e4b..17181d9 100644 --- a/generic/tclClockFmt.c +++ b/generic/tclClockFmt.c @@ -454,32 +454,170 @@ Tcl_GetClockFrmScnFromObj( return fss; } + +static int +LocaleListSearch(ClockFmtScnCmdArgs *opts, + DateInfo *info, const char * mcKey, int *val) +{ + Tcl_Obj *callargs[4] = {NULL, NULL, NULL, NULL}, **lstv; + int i, lstc; + Tcl_Obj *valObj = NULL; + int ret = TCL_RETURN; + + /* 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); -#define AllocTokenInChain(tok, chain, tokCnt) \ - if (++(tok) >= (chain) + (tokCnt)) { \ - (char *)(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))); + for (i = 0; i < 4; i++) { + Tcl_IncrRefCount(callargs[i]); + } + if (Tcl_EvalObjv(opts->interp, 4, callargs, 0) != TCL_OK) { + goto done; + } + + Tcl_InitObjRef(valObj, Tcl_GetObjResult(opts->interp)); + + /* is a list */ + if (TclListObjGetElements(opts->interp, valObj, &lstc, &lstv) != TCL_OK) { + goto done; + } + + /* 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 + && strncasecmp(yyInput, s, l) == 0 + ) { + *val = i; + yyInput += l; + break; + } + } + + 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]); + } + + return ret; +} + +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_ERROR; + } + + return TCL_OK; +}; + +static int +ClockScnToken_Month_Proc(ClockFmtScnCmdArgs *opts, + DateInfo *info, ClockScanToken *tok) +{ + /* + 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_ERROR; + } + yyMonth = (val % 12) + 1; + return TCL_OK; + */ + + int val; + int ret = LocaleListSearch(opts, info, "MONTHS_FULL", &val); + if (ret != TCL_OK) { + if (ret == TCL_RETURN) { + return ret; + } + ret = LocaleListSearch(opts, info, "MONTHS_ABBREV", &val); + if (ret != TCL_OK) { + return ret; + } + } + + yyMonth = val + 1; + return TCL_OK; + +} + + +static int +ClockScnToken_LocaleListMatcher_Proc(ClockFmtScnCmdArgs *opts, + DateInfo *info, ClockScanToken *tok) +{ + int val; + + int ret = LocaleListSearch(opts, info, (char *)tok->map->data, &val); + if (ret != TCL_OK) { + return ret; + } + + *(time_t *)(((char *)info) + tok->map->offs) = val; + + return TCL_OK; +} -const char *ScnSTokenMapChars = - "dmyYHMSJs"; + +static const char *ScnSTokenMapIndex = + "dmbyYHMSJCs"; static ClockScanTokenMap ScnSTokenMap[] = { - /* %d */ + /* %d %e */ {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.dayOfMonth), NULL}, /* %m */ {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.month), NULL}, + /* %b %B %h */ + {CTOKT_PARSER, CLF_DATE, 0, 0, 0, + ClockScnToken_Month_Proc}, /* %y */ {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.year), NULL}, /* %Y */ - {CTOKT_DIGIT, CLF_DATE, 1, 4, TclOffset(DateInfo, date.year), + {CTOKT_DIGIT, CLF_DATE | CLF_CENTURY, 1, 4, TclOffset(DateInfo, date.year), NULL}, /* %H */ {CTOKT_DIGIT, CLF_TIME, 1, 2, TclOffset(DateInfo, date.hour), @@ -493,11 +631,41 @@ static ClockScanTokenMap ScnSTokenMap[] = { /* %J */ {CTOKT_DIGIT, CLF_DATE | CLF_JULIANDAY, 1, 0xffff, TclOffset(DateInfo, date.julianDay), NULL}, + /* %C */ + {CTOKT_DIGIT, CLF_DATE | CLF_CENTURY, 1, 2, TclOffset(DateInfo, dateCentury), + NULL}, /* %s */ {CTOKT_DIGIT, CLF_LOCALSEC | CLF_SIGNED, 1, 0xffff, TclOffset(DateInfo, date.localSeconds), NULL}, }; -const char *ScnSpecTokenMapChars = +static const char *ScnSTokenWrapMapIndex[2] = { + "eBh", + "dbb" +}; + +static const char *ScnETokenMapIndex = + ""; +static ClockScanTokenMap ScnETokenMap[] = { + {0, 0, 0} +}; +static const char *ScnETokenWrapMapIndex[2] = { + "", + "" +}; + +static const char *ScnOTokenMapIndex = + "d"; +static ClockScanTokenMap ScnOTokenMap[] = { + /* %Od %Oe */ + {CTOKT_PARSER, CLF_DATE, 0, 0, TclOffset(DateInfo, date.dayOfMonth), + ClockScnToken_LocaleListMatcher_Proc, "LOCALE_NUMERALS"}, +}; +static const char *ScnOTokenWrapMapIndex[2] = { + "e", + "d" +}; + +static const char *ScnSpecTokenMapIndex = " "; static ClockScanTokenMap ScnSpecTokenMap[] = { {CTOKT_SPACE, 0, 1, 0xffff, 0, @@ -508,6 +676,17 @@ static ClockScanTokenMap ScnWordTokenMap = { CTOKT_WORD, 0, 1, 0, 0, NULL }; + + +#define AllocTokenInChain(tok, chain, tokCnt) \ + if (++(tok) >= (chain) + (tokCnt)) { \ + (char *)(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))); /* *---------------------------------------------------------------------- @@ -546,10 +725,14 @@ ClockGetOrParseScanFormat( fss->scnTok = tok = ckalloc(sizeof(*tok) * CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE); memset(tok, 0, sizeof(*(tok))); - strFmt = TclGetString(formatObj); - for (e = p = strFmt, e += formatObj->length; p != e; p++) { + strFmt = HashEntry4FmtScn(fss)->key.string; + for (e = p = strFmt, e += strlen(strFmt); p != e; p++) { switch (*p) { case '%': + if (1) { + ClockScanTokenMap * maps = ScnSTokenMap; + const char *mapIndex = ScnSTokenMapIndex, + **wrapIndex = ScnSTokenWrapMapIndex; if (p+1 >= e) { goto word_tok; } @@ -565,43 +748,62 @@ ClockGetOrParseScanFormat( continue; break; case 'E': - goto ext_tok_E; + maps = ScnETokenMap, + mapIndex = ScnETokenMapIndex, + wrapIndex = ScnETokenWrapMapIndex; + p++; break; case 'O': - goto ext_tok_O; + maps = ScnOTokenMap, + mapIndex = ScnOTokenMapIndex, + wrapIndex = ScnOTokenWrapMapIndex; + p++; break; - default: - cp = strchr(ScnSTokenMapChars, *p); + } + /* search direct index */ + cp = strchr(mapIndex, *p); + if (!cp || *cp == '\0') { + /* search wrapper index (multiple chars for same token) */ + cp = strchr(wrapIndex[0], *p); if (!cp || *cp == '\0') { p--; goto word_tok; } - tok->map = &ScnSTokenMap[cp - ScnSTokenMapChars]; - /* calculate look ahead value by standing together tokens */ - if (tok > fss->scnTok) { - ClockScanToken *prevTok = tok - 1; - unsigned int lookAhead = tok->map->minSize; - - while (prevTok >= fss->scnTok) { - if (prevTok->map->type != tok->map->type) { - break; - } - prevTok->lookAhead += lookAhead; - prevTok--; + cp = strchr(mapIndex, wrapIndex[1][cp - wrapIndex[0]]); + if (!cp || *cp == '\0') { /* unexpected, but ... */ + #ifdef DEBUG + Tcl_Panic("token \"%c\" has no map in wrapper resolver", *p); + #endif + p--; + goto word_tok; + } + } + tok->map = &ScnSTokenMap[cp - mapIndex]; + tok->tokWord.start = p; + /* calculate look ahead value by standing together tokens */ + if (tok > fss->scnTok) { + ClockScanToken *prevTok = tok - 1; + unsigned int lookAhead = tok->map->minSize; + + while (prevTok >= fss->scnTok) { + if (prevTok->map->type != tok->map->type) { + break; } + prevTok->lookAhead += lookAhead; + prevTok--; } - /* next token */ - AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); - break; } + /* next token */ + AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); + } break; case ' ': - cp = strchr(ScnSpecTokenMapChars, *p); + cp = strchr(ScnSpecTokenMapIndex, *p); if (!cp || *cp == '\0') { p--; goto word_tok; } - tok->map = &ScnSpecTokenMap[cp - ScnSpecTokenMapChars]; + tok->map = &ScnSpecTokenMap[cp - ScnSpecTokenMapIndex]; AllocTokenInChain(tok, fss->scnTok, fss->scnTokC); break; default: @@ -619,20 +821,10 @@ word_tok: } continue; } + break; } continue; - -ext_tok_E: - - /*******************/ - continue; - -ext_tok_O: - - /*******************/ - continue; - } done: @@ -677,17 +869,20 @@ ClockScan( } } info->dateStart = yyInput = p; + 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) { + if ( !(opts->flags & CLF_STRICT) + && (map->type != CTOKT_SPACE && map->type != CTOKT_WORD) + ) { while (p < end && isspace(UCHAR(*p))) { p++; } - yyInput = p; } + yyInput = p; switch (map->type) { case CTOKT_DIGIT: @@ -753,6 +948,20 @@ ClockScan( flags |= map->flags; } break; + case CTOKT_PARSER: + switch (map->parser(opts, info, tok)) { + case TCL_OK: + break; + case TCL_RETURN: + goto done; + break; + default: + goto error; + break; + }; + p = yyInput; + flags |= map->flags; + break; case CTOKT_SPACE: /* at least one space in strict mode */ if (opts->flags & CLF_STRICT) { @@ -803,10 +1012,14 @@ ClockScan( info->flags |= CLF_INVALIDATE_SECONDS|CLF_INVALIDATE_JULIANDAY; if (yyYear < 100) { - if (yyYear >= dataPtr->yearOfCenturySwitch) { - yyYear -= 100; + if (!(flags & CLF_CENTURY)) { + if (yyYear >= dataPtr->yearOfCenturySwitch) { + yyYear -= 100; + } + yyYear += dataPtr->currentYearCentury; + } else { + yyYear += info->dateCentury * 100; } - yyYear += dataPtr->currentYearCentury; } yydate.era = CE; } diff --git a/generic/tclDate.h b/generic/tclDate.h index 2010178..d97cd3c 100644 --- a/generic/tclDate.h +++ b/generic/tclDate.h @@ -36,6 +36,24 @@ #define CLF_INVALIDATE_SECONDS (1 << 8) /* assemble localSeconds (and seconds at end) */ /* + * 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); }; \ + } \ +} + +/* * Structure containing the fields used in [clock format] and [clock scan] */ @@ -79,6 +97,7 @@ typedef struct TclDateFields { typedef struct DateInfo { const char *dateStart; const char *dateInput; + const char *dateEnd; TclDateFields date; @@ -110,6 +129,8 @@ typedef struct DateInfo { time_t dateDigitCount; + time_t dateCentury; + Tcl_Obj* messages; /* Error messages */ const char* separatrix; /* String separating messages */ } DateInfo; @@ -157,6 +178,9 @@ ClockInitDateInfo(DateInfo *info) { #define CLF_STRICT (1 << 8) 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 */ @@ -233,6 +257,7 @@ typedef struct ClockScanToken ClockScanToken; typedef int ClockScanTokenProc( + ClockFmtScnCmdArgs *opts, DateInfo *info, ClockScanToken *tok); @@ -241,10 +266,11 @@ typedef int ClockScanTokenProc( #define CLF_JULIANDAY (1 << 3) #define CLF_TIME (1 << 4) #define CLF_LOCALSEC (1 << 5) +#define CLF_CENTURY (1 << 6) #define CLF_SIGNED (1 << 8) typedef enum _CLCKTOK_TYPE { - CTOKT_DIGIT = 1, CTOKT_SPACE, CTOKT_WORD + CTOKT_DIGIT = 1, CTOKT_PARSER, CTOKT_SPACE, CTOKT_WORD } CLCKTOK_TYPE; typedef struct ClockFmtScnStorage ClockFmtScnStorage; @@ -260,6 +286,7 @@ typedef struct ClockScanTokenMap { unsigned short int maxSize; unsigned short int offs; ClockScanTokenProc *parser; + void *data; } ClockScanTokenMap; typedef struct ClockScanToken { @@ -271,6 +298,9 @@ typedef struct ClockScanToken { } tokWord; } ClockScanToken; +#define ClockScnTokenChar(tok) \ + *tok->tokWord.start; + typedef struct ClockFmtScnStorage { int objRefCount; /* Reference count shared across threads */ ClockScanToken *scnTok; diff --git a/library/msgcat/msgcat.tcl b/library/msgcat/msgcat.tcl index 928474d..7f23568 100644 --- a/library/msgcat/msgcat.tcl +++ b/library/msgcat/msgcat.tcl @@ -11,7 +11,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. -package require Tcl 8.5- +package require Tcl 8.5 # When the version number changes, be sure to update the pkgIndex.tcl file, # and the installation directory in the Makefiles. package provide msgcat 1.6.0 @@ -225,6 +225,56 @@ 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. +# +# Arguments: +# 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} { + variable Msgs + + if {$loc eq {C}} { + set loclist [PackagePreferences $ns] + } else { + variable PackageConfig + # if {![dict exists $PackageConfig $ns $loc]} { + # set loc [mclocale] + # } + set loclist [dict get $PackageConfig locales $ns $loc] + } + 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] + } 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 } + } +} + # msgcat::mcexists -- # # Check if a catalog item is set or if mc would invoke mcunknown. @@ -488,6 +538,7 @@ proc msgcat::mcpackagelocale {subcommand {locale ""}} { set loclist [GetPreferences $locale] set locale [lindex $loclist 0] dict set PackageConfig loclist $ns $loclist + dict set PackageConfig locales $ns $locale $loclist # load eventual missing locales set loadedLocales [dict get $PackageConfig loadedlocales $ns] @@ -521,6 +572,7 @@ proc msgcat::mcpackagelocale {subcommand {locale ""}} { [dict get $PackageConfig loadedlocales $ns] $LoadedLocales] dict unset PackageConfig loadedlocales $ns dict unset PackageConfig loclist $ns + dict unset PackageConfig locales $ns # unset keys not in global loaded locales if {[dict exists $Msgs $ns]} { diff --git a/tests-perf/clock.perf.tcl b/tests-perf/clock.perf.tcl index 9267684..fd24068 100644 --- a/tests-perf/clock.perf.tcl +++ b/tests-perf/clock.perf.tcl @@ -121,10 +121,17 @@ proc test-scan {{reptime 1000}} { # Scan : julian day with time (greedy match): {clock scan "2451545 102030" -format "%J%H%M%S"} + # 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 + {clock scan {1970 Jan 02} -format {%C%y %b %Od} -locale en -gmt 1} + + break + # Scan : zone only {clock scan "CET" -format "%z"} {clock scan "EST" -format "%z"} - #{**STOP** : Wed Nov 25 01:00:00 CET 2015} + {**STOP** : Wed Nov 25 01:00:00 CET 2015} # # Scan : long format test (allock chain) # {clock scan "25.11.2015" -format "%d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y" -base 0 -gmt 1} @@ -194,8 +201,8 @@ proc test-other {{reptime 1000}} { proc test {{reptime 1000}} { puts "" - #test-scan $reptime - test-freescan $reptime + test-scan $reptime + #test-freescan $reptime test-other $reptime puts \n**OK** -- cgit v0.12