summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-01-10 22:23:37 (GMT)
committersebres <sebres@users.sourceforge.net>2017-01-10 22:23:37 (GMT)
commit62ddcc3daa746fc8be02e6b118e0c923ec227793 (patch)
tree36a0fdfa03ce4ff3c3ad39a71e6e359cdf89eaaf
parentdc9796c6e858828861b1339b4734bab19cccca4c (diff)
downloadtcl-62ddcc3daa746fc8be02e6b118e0c923ec227793.zip
tcl-62ddcc3daa746fc8be02e6b118e0c923ec227793.tar.gz
tcl-62ddcc3daa746fc8be02e6b118e0c923ec227793.tar.bz2
[temp-commit]: not ready
-rw-r--r--generic/tclClock.c167
-rw-r--r--generic/tclClockFmt.c319
-rw-r--r--generic/tclDate.h32
-rw-r--r--library/msgcat/msgcat.tcl54
-rw-r--r--tests-perf/clock.perf.tcl13
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**