summaryrefslogtreecommitdiffstats
path: root/generic/tclClockFmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclClockFmt.c')
-rw-r--r--generic/tclClockFmt.c3137
1 files changed, 0 insertions, 3137 deletions
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:
- */