Tcl Source Code

Check-in [fac0e545be]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge 8.7
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk | main
Files: files | file ages | folders
SHA3-256: fac0e545be001613d972f0540569516ede379e9de1180672d29bfcfa4d75f233
User & Date: dkf 2024-04-12 13:32:53
Context
2024-04-12
14:02
Merge 8.7 check-in: 4ccd8c6c4b user: dkf tags: trunk, main
13:32
Merge 8.7 check-in: fac0e545be user: dkf tags: trunk, main
13:25
More cleaning up, notably no more symbols starting with _ as they're a reserved 'namespace' check-in: bfb42faa67 user: dkf tags: core-8-branch
09:47
Merge 8.7 check-in: 95201cd0f6 user: jan.nijtmans tags: trunk, main
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclClock.c.

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
 */

static int		ConvertUTCToLocalUsingTable(Tcl_Interp *,
			    TclDateFields *, Tcl_Size, Tcl_Obj *const[],
			    Tcl_WideInt *rangesVal);
static int		ConvertUTCToLocalUsingC(Tcl_Interp *,
			    TclDateFields *, int);
static int		ConvertLocalToUTC(void *clientData, Tcl_Interp *,
			    TclDateFields *, Tcl_Obj *timezoneObj, int);
static int		ConvertLocalToUTCUsingTable(Tcl_Interp *,
			    TclDateFields *, int, Tcl_Obj *const[],
			    Tcl_WideInt *rangesVal);
static int		ConvertLocalToUTCUsingC(Tcl_Interp *,
			    TclDateFields *, int);
static int		ClockConfigureObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static void		GetYearWeekDay(TclDateFields *, int);
static void		GetGregorianEraYearDay(TclDateFields *, int);
static void		GetMonthDay(TclDateFields *);
static Tcl_WideInt	WeekdayOnOrBefore(int, Tcl_WideInt);
static Tcl_ObjCmdProc	ClockClicksObjCmd;
static Tcl_ObjCmdProc	ClockConvertlocaltoutcObjCmd;
static int		ClockGetDateFields(void *clientData,
			    Tcl_Interp *interp, TclDateFields *fields,
			    Tcl_Obj *timezoneObj, int changeover);
static Tcl_ObjCmdProc	ClockGetdatefieldsObjCmd;
static Tcl_ObjCmdProc	ClockGetjuliandayfromerayearmonthdayObjCmd;
static Tcl_ObjCmdProc	ClockGetjuliandayfromerayearweekdayObjCmd;
static Tcl_ObjCmdProc	ClockGetenvObjCmd;
static Tcl_ObjCmdProc	ClockMicrosecondsObjCmd;







|














|







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
 */

static int		ConvertUTCToLocalUsingTable(Tcl_Interp *,
			    TclDateFields *, Tcl_Size, Tcl_Obj *const[],
			    Tcl_WideInt *rangesVal);
static int		ConvertUTCToLocalUsingC(Tcl_Interp *,
			    TclDateFields *, int);
static int		ConvertLocalToUTC(ClockClientData *, Tcl_Interp *,
			    TclDateFields *, Tcl_Obj *timezoneObj, int);
static int		ConvertLocalToUTCUsingTable(Tcl_Interp *,
			    TclDateFields *, int, Tcl_Obj *const[],
			    Tcl_WideInt *rangesVal);
static int		ConvertLocalToUTCUsingC(Tcl_Interp *,
			    TclDateFields *, int);
static int		ClockConfigureObjCmd(void *clientData,
			    Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static void		GetYearWeekDay(TclDateFields *, int);
static void		GetGregorianEraYearDay(TclDateFields *, int);
static void		GetMonthDay(TclDateFields *);
static Tcl_WideInt	WeekdayOnOrBefore(int, Tcl_WideInt);
static Tcl_ObjCmdProc	ClockClicksObjCmd;
static Tcl_ObjCmdProc	ClockConvertlocaltoutcObjCmd;
static int		ClockGetDateFields(ClockClientData *,
			    Tcl_Interp *interp, TclDateFields *fields,
			    Tcl_Obj *timezoneObj, int changeover);
static Tcl_ObjCmdProc	ClockGetdatefieldsObjCmd;
static Tcl_ObjCmdProc	ClockGetjuliandayfromerayearmonthdayObjCmd;
static Tcl_ObjCmdProc	ClockGetjuliandayfromerayearweekdayObjCmd;
static Tcl_ObjCmdProc	ClockGetenvObjCmd;
static Tcl_ObjCmdProc	ClockMicrosecondsObjCmd;
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
     * Create the client data, which is a refcounted literal pool.
     */

    data = (ClockClientData *)Tcl_Alloc(sizeof(ClockClientData));
    data->refCount = 0;
    data->literals = (Tcl_Obj **)Tcl_Alloc(LIT__END * sizeof(Tcl_Obj*));
    for (i = 0; i < LIT__END; ++i) {
	TclInitObjRef(data->literals[i], Tcl_NewStringObj(Literals[i], -1));

    }
    data->mcLiterals = NULL;
    data->mcLitIdxs = NULL;
    data->mcDicts = NULL;
    data->lastTZEpoch = 0;
    data->currentYearCentury = ClockDefaultYearCentury;
    data->yearOfCenturySwitch = ClockDefaultCenturySwitch;







|
>







193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
     * Create the client data, which is a refcounted literal pool.
     */

    data = (ClockClientData *)Tcl_Alloc(sizeof(ClockClientData));
    data->refCount = 0;
    data->literals = (Tcl_Obj **)Tcl_Alloc(LIT__END * sizeof(Tcl_Obj*));
    for (i = 0; i < LIT__END; ++i) {
	TclInitObjRef(data->literals[i], Tcl_NewStringObj(
		Literals[i], TCL_AUTO_LENGTH));
    }
    data->mcLiterals = NULL;
    data->mcLitIdxs = NULL;
    data->mcDicts = NULL;
    data->lastTZEpoch = 0;
    data->currentYearCentury = ClockDefaultYearCentury;
    data->yearOfCenturySwitch = ClockDefaultCenturySwitch;
370
371
372
373
374
375
376
377

378
379
380
381
382
383
384
385
386
387
388
389

390
391
392
393
394
395
396
/*
 *----------------------------------------------------------------------
 *
 * SavePrevTimezoneObj --
 *
 *	Used to store previously used/cached time zone (makes it reusable).
 *
 *	This enables faster switch between time zones (e. g. to convert from one to another).

 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static inline void
SavePrevTimezoneObj(
    ClockClientData *dataPtr)	/* Client data containing literal pool */
{
    Tcl_Obj *timezoneObj = dataPtr->lastSetupTimeZone;

    if (timezoneObj && timezoneObj != dataPtr->prevSetupTimeZone) {
	TclSetObjRef(dataPtr->prevSetupTimeZoneUnnorm, dataPtr->lastSetupTimeZoneUnnorm);
	TclSetObjRef(dataPtr->prevSetupTimeZone, timezoneObj);
	TclSetObjRef(dataPtr->prevSetupTZData, dataPtr->lastSetupTZData);
    }
}








|
>






<





>







371
372
373
374
375
376
377
378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
393
394
395
396
397
398
/*
 *----------------------------------------------------------------------
 *
 * SavePrevTimezoneObj --
 *
 *	Used to store previously used/cached time zone (makes it reusable).
 *
 *	This enables faster switch between time zones (e. g. to convert from
 *	one to another).
 *
 * Results:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static inline void
SavePrevTimezoneObj(
    ClockClientData *dataPtr)	/* Client data containing literal pool */
{
    Tcl_Obj *timezoneObj = dataPtr->lastSetupTimeZone;

    if (timezoneObj && timezoneObj != dataPtr->prevSetupTimeZone) {
	TclSetObjRef(dataPtr->prevSetupTimeZoneUnnorm, dataPtr->lastSetupTimeZoneUnnorm);
	TclSetObjRef(dataPtr->prevSetupTimeZone, timezoneObj);
	TclSetObjRef(dataPtr->prevSetupTZData, dataPtr->lastSetupTZData);
    }
}

682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701

702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
 *----------------------------------------------------------------------
 */

Tcl_Obj *
ClockMCDict(
    ClockFmtScnCmdArgs *opts)
{
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;

    /* if dict not yet retrieved */
    if (opts->mcDictObj == NULL) {

	/* if locale was not yet used */
	if (!(opts->flags & CLF_LOCALE_USED)) {
	    opts->localeObj = NormLocaleObj((ClockClientData *) opts->clientData, opts->interp,
		    opts->localeObj, &opts->mcDictObj);

	    if (opts->localeObj == NULL) {
		Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
			"locale not specified and no default locale set", -1));

		Tcl_SetErrorCode(opts->interp, "CLOCK", "badOption", (char *)NULL);
		return NULL;
	    }
	    opts->flags |= CLF_LOCALE_USED;

	    /* check locale literals already available (on demand creation) */
	    if (dataPtr->mcLiterals == NULL) {
		int i;

		dataPtr->mcLiterals = (Tcl_Obj **)
			Tcl_Alloc(MCLIT__END * sizeof(Tcl_Obj*));
		for (i = 0; i < MCLIT__END; ++i) {
		    TclInitObjRef(dataPtr->mcLiterals[i],
			    Tcl_NewStringObj(MsgCtLiterals[i], -1));
		}
	    }
	}

	/* check or obtain mcDictObj (be sure it's modifiable) */
	if (opts->mcDictObj == NULL || opts->mcDictObj->refCount > 1) {
	    int ref = 1;







|






|




|
>












|
|







684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
 *----------------------------------------------------------------------
 */

Tcl_Obj *
ClockMCDict(
    ClockFmtScnCmdArgs *opts)
{
    ClockClientData *dataPtr = opts->dataPtr;

    /* if dict not yet retrieved */
    if (opts->mcDictObj == NULL) {

	/* if locale was not yet used */
	if (!(opts->flags & CLF_LOCALE_USED)) {
	    opts->localeObj = NormLocaleObj(dataPtr, opts->interp,
		    opts->localeObj, &opts->mcDictObj);

	    if (opts->localeObj == NULL) {
		Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
			"locale not specified and no default locale set",
			TCL_AUTO_LENGTH));
		Tcl_SetErrorCode(opts->interp, "CLOCK", "badOption", (char *)NULL);
		return NULL;
	    }
	    opts->flags |= CLF_LOCALE_USED;

	    /* check locale literals already available (on demand creation) */
	    if (dataPtr->mcLiterals == NULL) {
		int i;

		dataPtr->mcLiterals = (Tcl_Obj **)
			Tcl_Alloc(MCLIT__END * sizeof(Tcl_Obj*));
		for (i = 0; i < MCLIT__END; ++i) {
		    TclInitObjRef(dataPtr->mcLiterals[i], Tcl_NewStringObj(
			    MsgCtLiterals[i], TCL_AUTO_LENGTH));
		}
	    }
	}

	/* check or obtain mcDictObj (be sure it's modifiable) */
	if (opts->mcDictObj == NULL || opts->mcDictObj->refCount > 1) {
	    int ref = 1;
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
 */

Tcl_Obj *
ClockMCGet(
    ClockFmtScnCmdArgs *opts,
    int mcKey)
{
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;
    Tcl_Obj *valObj = NULL;

    if (opts->mcDictObj == NULL) {
	ClockMCDict(opts);
	if (opts->mcDictObj == NULL) {
	    return NULL;
	}
    }

    Tcl_DictObjGet(opts->interp, opts->mcDictObj,
	    dataPtr->mcLiterals[mcKey], &valObj);

    return valObj; /* or NULL in obscure case if Tcl_DictObjGet failed */
}

/*
 *----------------------------------------------------------------------
 *
 * ClockMCGetIdx --







<










|
<







794
795
796
797
798
799
800

801
802
803
804
805
806
807
808
809
810
811

812
813
814
815
816
817
818
 */

Tcl_Obj *
ClockMCGet(
    ClockFmtScnCmdArgs *opts,
    int mcKey)
{

    Tcl_Obj *valObj = NULL;

    if (opts->mcDictObj == NULL) {
	ClockMCDict(opts);
	if (opts->mcDictObj == NULL) {
	    return NULL;
	}
    }

    Tcl_DictObjGet(opts->interp, opts->mcDictObj,
	    opts->dataPtr->mcLiterals[mcKey], &valObj);

    return valObj; /* or NULL in obscure case if Tcl_DictObjGet failed */
}

/*
 *----------------------------------------------------------------------
 *
 * ClockMCGetIdx --
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
 */

MODULE_SCOPE Tcl_Obj *
ClockMCGetIdx(
    ClockFmtScnCmdArgs *opts,
    int mcKey)
{
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;
    Tcl_Obj *valObj = NULL;

    if (opts->mcDictObj == NULL) {
	ClockMCDict(opts);
	if (opts->mcDictObj == NULL) {
	    return NULL;
	}







|







828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
 */

MODULE_SCOPE Tcl_Obj *
ClockMCGetIdx(
    ClockFmtScnCmdArgs *opts,
    int mcKey)
{
    ClockClientData *dataPtr = opts->dataPtr;
    Tcl_Obj *valObj = NULL;

    if (opts->mcDictObj == NULL) {
	ClockMCDict(opts);
	if (opts->mcDictObj == NULL) {
	    return NULL;
	}
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919

int
ClockMCSetIdx(
    ClockFmtScnCmdArgs *opts,
    int mcKey,
    Tcl_Obj *valObj)
{
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;

    if (opts->mcDictObj == NULL) {
	ClockMCDict(opts);
	if (opts->mcDictObj == NULL) {
	    return TCL_ERROR;
	}
    }

    /* if literal storage for indices not yet created */
    if (dataPtr->mcLitIdxs == NULL) {
	int i;

	dataPtr->mcLitIdxs = (Tcl_Obj **)Tcl_Alloc(MCLIT__END * sizeof(Tcl_Obj*));
	for (i = 0; i < MCLIT__END; ++i) {
	    TclInitObjRef(dataPtr->mcLitIdxs[i],
		    Tcl_NewStringObj(MsgCtLitIdxs[i], -1));
	}
    }

    return Tcl_DictObjPut(opts->interp, opts->mcDictObj,
	    dataPtr->mcLitIdxs[mcKey], valObj);
}

static void
TimezoneLoaded(
    ClockClientData *dataPtr,
    Tcl_Obj *timezoneObj,	/* Name of zone was loaded */
    Tcl_Obj *tzUnnormObj)	/* Name of zone was loaded */
{
    /* don't overwrite last-setup with GMT (special case) */
    if (timezoneObj == dataPtr->literals[LIT_GMT]) {
	/* mark GMT zone loaded */
	if (dataPtr->gmtSetupTimeZone == NULL) {
	    TclSetObjRef(dataPtr->gmtSetupTimeZone,
		dataPtr->literals[LIT_GMT]);
	}
	TclSetObjRef(dataPtr->gmtSetupTimeZoneUnnorm, tzUnnormObj);
	return;
    }

    /* last setup zone loaded */
    if (dataPtr->lastSetupTimeZone != timezoneObj) {







|















|


















|







871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920

int
ClockMCSetIdx(
    ClockFmtScnCmdArgs *opts,
    int mcKey,
    Tcl_Obj *valObj)
{
    ClockClientData *dataPtr = opts->dataPtr;

    if (opts->mcDictObj == NULL) {
	ClockMCDict(opts);
	if (opts->mcDictObj == NULL) {
	    return TCL_ERROR;
	}
    }

    /* if literal storage for indices not yet created */
    if (dataPtr->mcLitIdxs == NULL) {
	int i;

	dataPtr->mcLitIdxs = (Tcl_Obj **)Tcl_Alloc(MCLIT__END * sizeof(Tcl_Obj*));
	for (i = 0; i < MCLIT__END; ++i) {
	    TclInitObjRef(dataPtr->mcLitIdxs[i],
		    Tcl_NewStringObj(MsgCtLitIdxs[i], TCL_AUTO_LENGTH));
	}
    }

    return Tcl_DictObjPut(opts->interp, opts->mcDictObj,
	    dataPtr->mcLitIdxs[mcKey], valObj);
}

static void
TimezoneLoaded(
    ClockClientData *dataPtr,
    Tcl_Obj *timezoneObj,	/* Name of zone was loaded */
    Tcl_Obj *tzUnnormObj)	/* Name of zone was loaded */
{
    /* don't overwrite last-setup with GMT (special case) */
    if (timezoneObj == dataPtr->literals[LIT_GMT]) {
	/* mark GMT zone loaded */
	if (dataPtr->gmtSetupTimeZone == NULL) {
	    TclSetObjRef(dataPtr->gmtSetupTimeZone,
		    dataPtr->literals[LIT_GMT]);
	}
	TclSetObjRef(dataPtr->gmtSetupTimeZoneUnnorm, tzUnnormObj);
	return;
    }

    /* last setup zone loaded */
    if (dataPtr->lastSetupTimeZone != timezoneObj) {
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
 *	The tzdata can be cached in ClockClientData structure.
 *
 *----------------------------------------------------------------------
 */

static inline Tcl_Obj *
ClockGetTZData(
    void *clientData,		/* Opaque pointer to literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    Tcl_Obj *timezoneObj)	/* Name of the timezone */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;
    Tcl_Obj *ret, **out = NULL;

    /* if cached (if already setup this one) */
    if (timezoneObj == dataPtr->lastSetupTimeZone
	    || timezoneObj == dataPtr->lastSetupTimeZoneUnnorm) {
	if (dataPtr->lastSetupTZData != NULL) {
	    return dataPtr->lastSetupTZData;







|



<







1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182

1183
1184
1185
1186
1187
1188
1189
 *	The tzdata can be cached in ClockClientData structure.
 *
 *----------------------------------------------------------------------
 */

static inline Tcl_Obj *
ClockGetTZData(
    ClockClientData *dataPtr,	/* Opaque pointer to literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    Tcl_Obj *timezoneObj)	/* Name of the timezone */
{

    Tcl_Obj *ret, **out = NULL;

    /* if cached (if already setup this one) */
    if (timezoneObj == dataPtr->lastSetupTimeZone
	    || timezoneObj == dataPtr->lastSetupTimeZoneUnnorm) {
	if (dataPtr->lastSetupTZData != NULL) {
	    return dataPtr->lastSetupTZData;
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
 *	Returns normalized timezone object.
 *
 *----------------------------------------------------------------------
 */

static Tcl_Obj *
ClockGetSystemTimeZone(
    void *clientData,		/* Opaque pointer to literal pool, etc. */
    Tcl_Interp *interp)		/* Tcl interpreter */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;

    /* if known (cached and same epoch) - return now */
    if (dataPtr->systemTimeZone != NULL
	    && dataPtr->lastTZEpoch == TzsetIfNecessary()) {
	return dataPtr->systemTimeZone;
    }

    TclUnsetObjRef(dataPtr->systemTimeZone);







|


<
<







1241
1242
1243
1244
1245
1246
1247
1248
1249
1250


1251
1252
1253
1254
1255
1256
1257
 *	Returns normalized timezone object.
 *
 *----------------------------------------------------------------------
 */

static Tcl_Obj *
ClockGetSystemTimeZone(
    ClockClientData *dataPtr,	/* Pointer to literal pool, etc. */
    Tcl_Interp *interp)		/* Tcl interpreter */
{


    /* if known (cached and same epoch) - return now */
    if (dataPtr->systemTimeZone != NULL
	    && dataPtr->lastTZEpoch == TzsetIfNecessary()) {
	return dataPtr->systemTimeZone;
    }

    TclUnsetObjRef(dataPtr->systemTimeZone);
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
 *	Returns normalized timezone object.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
ClockSetupTimeZone(
    void *clientData,		/* Opaque pointer to literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    Tcl_Obj *timezoneObj)
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;
    int loaded;
    Tcl_Obj *callargs[2];

    /* if cached (if already setup this one) */
    if (timezoneObj == dataPtr->literals[LIT_GMT]
	    && dataPtr->gmtSetupTZData != NULL) {
	return timezoneObj;







|



<







1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288

1289
1290
1291
1292
1293
1294
1295
 *	Returns normalized timezone object.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj *
ClockSetupTimeZone(
    ClockClientData *dataPtr,	/* Pointer to literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    Tcl_Obj *timezoneObj)
{

    int loaded;
    Tcl_Obj *callargs[2];

    /* if cached (if already setup this one) */
    if (timezoneObj == dataPtr->literals[LIT_GMT]
	    && dataPtr->gmtSetupTZData != NULL) {
	return timezoneObj;
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
 *	leaves an error message in the interpreter result.
 *
 *----------------------------------------------------------------------
 */

static int
ClockConvertlocaltoutcObjCmd(
    void *clientData,		/* Client data */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const *objv)	/* Parameter vector */
{
    ClockClientData *data = (ClockClientData *)clientData;
    Tcl_Obj *secondsObj;
    Tcl_Obj *dict;
    int changeover;
    TclDateFields fields;
    int created = 0;
    int status;

    fields.tzName = NULL;
    /*
     * Check params and convert time.
     */

    if (objc != 4) {
	Tcl_WrongNumArgs(interp, 1, objv, "dict timezone changeover");
	return TCL_ERROR;
    }
    dict = objv[1];
    if (Tcl_DictObjGet(interp, dict, data->literals[LIT_LOCALSECONDS],
	    &secondsObj)!= TCL_OK) {
	return TCL_ERROR;
    }
    if (secondsObj == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj("key \"localseconds\" not "
		"found in dictionary", -1));
	return TCL_ERROR;
    }
    if ((TclGetWideIntFromObj(interp, secondsObj, &fields.localSeconds) != TCL_OK)
	    || (TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK)
	    || ConvertLocalToUTC(clientData, interp, &fields, objv[2], changeover)) {
	return TCL_ERROR;
    }

    /*
     * Copy-on-write; set the 'seconds' field in the dictionary and place the
     * modified dictionary in the interpreter result.
     */

    if (Tcl_IsShared(dict)) {
	dict = Tcl_DuplicateObj(dict);
	created = 1;
	Tcl_IncrRefCount(dict);
    }
    status = Tcl_DictObjPut(interp, dict, data->literals[LIT_SECONDS],
	    Tcl_NewWideIntObj(fields.seconds));
    if (status == TCL_OK) {
	Tcl_SetObjResult(interp, dict);
    }
    if (created) {
	Tcl_DecrRefCount(dict);
    }







|




|

















|





|




|













|







1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
 *	leaves an error message in the interpreter result.
 *
 *----------------------------------------------------------------------
 */

static int
ClockConvertlocaltoutcObjCmd(
    void *clientData,		/* Literal table */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const *objv)	/* Parameter vector */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;
    Tcl_Obj *secondsObj;
    Tcl_Obj *dict;
    int changeover;
    TclDateFields fields;
    int created = 0;
    int status;

    fields.tzName = NULL;
    /*
     * Check params and convert time.
     */

    if (objc != 4) {
	Tcl_WrongNumArgs(interp, 1, objv, "dict timezone changeover");
	return TCL_ERROR;
    }
    dict = objv[1];
    if (Tcl_DictObjGet(interp, dict, dataPtr->literals[LIT_LOCALSECONDS],
	    &secondsObj)!= TCL_OK) {
	return TCL_ERROR;
    }
    if (secondsObj == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj("key \"localseconds\" not "
		"found in dictionary", TCL_AUTO_LENGTH));
	return TCL_ERROR;
    }
    if ((TclGetWideIntFromObj(interp, secondsObj, &fields.localSeconds) != TCL_OK)
	    || (TclGetIntFromObj(interp, objv[3], &changeover) != TCL_OK)
	    || ConvertLocalToUTC(dataPtr, interp, &fields, objv[2], changeover)) {
	return TCL_ERROR;
    }

    /*
     * Copy-on-write; set the 'seconds' field in the dictionary and place the
     * modified dictionary in the interpreter result.
     */

    if (Tcl_IsShared(dict)) {
	dict = Tcl_DuplicateObj(dict);
	created = 1;
	Tcl_IncrRefCount(dict);
    }
    status = Tcl_DictObjPut(interp, dict, dataPtr->literals[LIT_SECONDS],
	    Tcl_NewWideIntObj(fields.seconds));
    if (status == TCL_OK) {
	Tcl_SetObjResult(interp, dict);
    }
    if (created) {
	Tcl_DecrRefCount(dict);
    }
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
    void *clientData,		/* Opaque pointer to literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const *objv)	/* Parameter vector */
{
    TclDateFields fields;
    Tcl_Obj *dict;
    ClockClientData *data = (ClockClientData *)clientData;
    Tcl_Obj *const *lit = data->literals;
    int changeover;

    fields.tzName = NULL;

    /*
     * Check params.
     */







|
|







1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
    void *clientData,		/* Opaque pointer to literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const *objv)	/* Parameter vector */
{
    TclDateFields fields;
    Tcl_Obj *dict;
    ClockClientData *dataPtr = (ClockClientData *)clientData;
    Tcl_Obj *const *lit = dataPtr->literals;
    int changeover;

    fields.tzName = NULL;

    /*
     * Check params.
     */
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
    if (TclHasInternalRep(objv[1], &tclBignumType)) {
	Tcl_SetObjResult(interp, lit[LIT_INTEGER_VALUE_TOO_LARGE]);
	return TCL_ERROR;
    }

    /* Extract fields */

    if (ClockGetDateFields(clientData, interp, &fields, objv[2],
	    changeover) != TCL_OK) {
	return TCL_ERROR;
    }

    /* Make dict of fields */

    dict = Tcl_NewDictObj();







|







1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
    if (TclHasInternalRep(objv[1], &tclBignumType)) {
	Tcl_SetObjResult(interp, lit[LIT_INTEGER_VALUE_TOO_LARGE]);
	return TCL_ERROR;
    }

    /* Extract fields */

    if (ClockGetDateFields(dataPtr, interp, &fields, objv[2],
	    changeover) != TCL_OK) {
	return TCL_ERROR;
    }

    /* Make dict of fields */

    dict = Tcl_NewDictObj();
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
 *	Returns a standard Tcl result.
 *
 *----------------------------------------------------------------------
 */

int
ClockGetDateFields(
    void *clientData,		/* Client data of the interpreter */
    Tcl_Interp *interp,		/* Tcl interpreter */
    TclDateFields *fields,	/* Pointer to result fields, where
				 * fields->seconds contains date to extract */
    Tcl_Obj *timezoneObj,	/* Time zone object or NULL for gmt */
    int changeover)		/* Julian Day Number */
{
    /*
     * Convert UTC time to local.
     */

    if (ConvertUTCToLocal(clientData, interp, fields, timezoneObj,
	    changeover) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Extract Julian day and seconds of the day.
     */

    ClockExtractJDAndSODFromSeconds(fields->julianDay, fields->secondOfDay,
	fields->localSeconds);

    /*
     * Convert to Julian or Gregorian calendar.
     */

    GetGregorianEraYearDay(fields, changeover);
    GetMonthDay(fields);







|










|









|







1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
 *	Returns a standard Tcl result.
 *
 *----------------------------------------------------------------------
 */

int
ClockGetDateFields(
    ClockClientData *dataPtr,	/* Literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    TclDateFields *fields,	/* Pointer to result fields, where
				 * fields->seconds contains date to extract */
    Tcl_Obj *timezoneObj,	/* Time zone object or NULL for gmt */
    int changeover)		/* Julian Day Number */
{
    /*
     * Convert UTC time to local.
     */

    if (ConvertUTCToLocal(dataPtr, interp, fields, timezoneObj,
	    changeover) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Extract Julian day and seconds of the day.
     */

    ClockExtractJDAndSODFromSeconds(fields->julianDay, fields->secondOfDay,
	    fields->localSeconds);

    /*
     * Convert to Julian or Gregorian calendar.
     */

    GetGregorianEraYearDay(fields, changeover);
    GetMonthDay(fields);
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
    Tcl_Obj *value = NULL;

    if (Tcl_DictObjGet(interp, dict, key, &value) != TCL_OK) {
	return TCL_ERROR;
    }
    if (value == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"expected key(s) not found in dictionary", -1));
	return TCL_ERROR;
    }
    return Tcl_GetIndexFromObj(interp, value, eras, "era", TCL_EXACT, storePtr);
}

static int
FetchIntField(
    Tcl_Interp *interp,
    Tcl_Obj *dict,
    Tcl_Obj *key,
    int *storePtr)
{
    Tcl_Obj *value = NULL;

    if (Tcl_DictObjGet(interp, dict, key, &value) != TCL_OK) {
	return TCL_ERROR;
    }
    if (value == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"expected key(s) not found in dictionary", -1));
	return TCL_ERROR;
    }
    return TclGetIntFromObj(interp, value, storePtr);
}

static int
ClockGetjuliandayfromerayearmonthdayObjCmd(







|



















|







1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
    Tcl_Obj *value = NULL;

    if (Tcl_DictObjGet(interp, dict, key, &value) != TCL_OK) {
	return TCL_ERROR;
    }
    if (value == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"expected key(s) not found in dictionary", TCL_AUTO_LENGTH));
	return TCL_ERROR;
    }
    return Tcl_GetIndexFromObj(interp, value, eras, "era", TCL_EXACT, storePtr);
}

static int
FetchIntField(
    Tcl_Interp *interp,
    Tcl_Obj *dict,
    Tcl_Obj *key,
    int *storePtr)
{
    Tcl_Obj *value = NULL;

    if (Tcl_DictObjGet(interp, dict, key, &value) != TCL_OK) {
	return TCL_ERROR;
    }
    if (value == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"expected key(s) not found in dictionary", TCL_AUTO_LENGTH));
	return TCL_ERROR;
    }
    return TclGetIntFromObj(interp, value, storePtr);
}

static int
ClockGetjuliandayfromerayearmonthdayObjCmd(
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
 *	in the interpreter result on failure.
 *
 *----------------------------------------------------------------------
 */

static int
ConvertLocalToUTC(
    void *clientData,		/* Client data of the interpreter */
    Tcl_Interp *interp,		/* Tcl interpreter */
    TclDateFields *fields,	/* Fields of the time */
    Tcl_Obj *timezoneObj,	/* Time zone */
    int changeover)		/* Julian Day of the Gregorian transition */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;
    Tcl_Obj *tzdata;		/* Time zone data */
    Tcl_Size rowc;		/* Number of rows in tzdata */
    Tcl_Obj **rowv;		/* Pointers to the rows */
    Tcl_WideInt seconds;
    ClockLastTZOffs * ltzoc = NULL;

    /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */







|





<







1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859

1860
1861
1862
1863
1864
1865
1866
 *	in the interpreter result on failure.
 *
 *----------------------------------------------------------------------
 */

static int
ConvertLocalToUTC(
    ClockClientData *dataPtr,	/* Literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    TclDateFields *fields,	/* Fields of the time */
    Tcl_Obj *timezoneObj,	/* Time zone */
    int changeover)		/* Julian Day of the Gregorian transition */
{

    Tcl_Obj *tzdata;		/* Time zone data */
    Tcl_Size rowc;		/* Number of rows in tzdata */
    Tcl_Obj **rowv;		/* Pointers to the rows */
    Tcl_WideInt seconds;
    ClockLastTZOffs * ltzoc = NULL;

    /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
	}
    }

    /*
     * Unpack the tz data.
     */

    tzdata = ClockGetTZData(clientData, interp, timezoneObj);
    if (tzdata == NULL) {
	return TCL_ERROR;
    }

    if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) {
	return TCL_ERROR;
    }







|







1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
	}
    }

    /*
     * Unpack the tz data.
     */

    tzdata = ClockGetTZData(dataPtr, interp, timezoneObj);
    if (tzdata == NULL) {
	return TCL_ERROR;
    }

    if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) {
	return TCL_ERROR;
    }
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
    /*
     * If conversion fails, report an error.
     */

    if (localErrno != 0
	    || (fields->seconds == -1 && timeVal.tm_yday == -1)) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"time value too large/small to represent", -1));
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------







|







2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
    /*
     * If conversion fails, report an error.
     */

    if (localErrno != 0
	    || (fields->seconds == -1 && timeVal.tm_yday == -1)) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"time value too large/small to represent", TCL_AUTO_LENGTH));
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
 *	Populates the 'tzName' and 'tzOffset' fields.
 *
 *----------------------------------------------------------------------
 */

int
ConvertUTCToLocal(
    void *clientData,		/* Client data of the interpreter */
    Tcl_Interp *interp,		/* Tcl interpreter */
    TclDateFields *fields,	/* Fields of the time */
    Tcl_Obj *timezoneObj,	/* Time zone */
    int changeover)		/* Julian Day of the Gregorian transition */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;
    Tcl_Obj *tzdata;		/* Time zone data */
    Tcl_Size rowc;		/* Number of rows in tzdata */
    Tcl_Obj **rowv;		/* Pointers to the rows */
    ClockLastTZOffs * ltzoc = NULL;

    /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */
    if (timezoneObj == dataPtr->literals[LIT_GMT]) {
	fields->localSeconds = fields->seconds;
	fields->tzOffset = 0;
	if (dataPtr->gmtTZName == NULL) {
	    Tcl_Obj *tzName;

	    tzdata = ClockGetTZData(clientData, interp, timezoneObj);
	    if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK
		    || Tcl_ListObjIndex(interp, rowv[0], 3, &tzName) != TCL_OK) {
		return TCL_ERROR;
	    }
	    TclSetObjRef(dataPtr->gmtTZName, tzName);
	}
	TclSetObjRef(fields->tzName, dataPtr->gmtTZName);







|





<












|







2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151

2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
 *	Populates the 'tzName' and 'tzOffset' fields.
 *
 *----------------------------------------------------------------------
 */

int
ConvertUTCToLocal(
    ClockClientData *dataPtr,	/* Literal pool, etc. */
    Tcl_Interp *interp,		/* Tcl interpreter */
    TclDateFields *fields,	/* Fields of the time */
    Tcl_Obj *timezoneObj,	/* Time zone */
    int changeover)		/* Julian Day of the Gregorian transition */
{

    Tcl_Obj *tzdata;		/* Time zone data */
    Tcl_Size rowc;		/* Number of rows in tzdata */
    Tcl_Obj **rowv;		/* Pointers to the rows */
    ClockLastTZOffs * ltzoc = NULL;

    /* fast phase-out for shared GMT-object (don't need to convert UTC 2 UTC) */
    if (timezoneObj == dataPtr->literals[LIT_GMT]) {
	fields->localSeconds = fields->seconds;
	fields->tzOffset = 0;
	if (dataPtr->gmtTZName == NULL) {
	    Tcl_Obj *tzName;

	    tzdata = ClockGetTZData(dataPtr, interp, timezoneObj);
	    if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK
		    || Tcl_ListObjIndex(interp, rowv[0], 3, &tzName) != TCL_OK) {
		return TCL_ERROR;
	    }
	    TclSetObjRef(dataPtr->gmtTZName, tzName);
	}
	TclSetObjRef(fields->tzName, dataPtr->gmtTZName);
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
	}
    }

    /*
     * Unpack the tz data.
     */

    tzdata = ClockGetTZData(clientData, interp, timezoneObj);
    if (tzdata == NULL) {
	return TCL_ERROR;
    }

    if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) {
	return TCL_ERROR;
    }







|







2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
	}
    }

    /*
     * Unpack the tz data.
     */

    tzdata = ClockGetTZData(dataPtr, interp, timezoneObj);
    if (tzdata == NULL) {
	return TCL_ERROR;
    }

    if (TclListObjGetElements(interp, tzdata, &rowc, &rowv) != TCL_OK) {
	return TCL_ERROR;
    }
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
    /*
     * Use 'localtime' to determine local year, month, day, time of day.
     */

    tock = (time_t) fields->seconds;
    if ((Tcl_WideInt) tock != fields->seconds) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"number too large to represent as a Posix time", -1));
	Tcl_SetErrorCode(interp, "CLOCK", "argTooLarge", (char *)NULL);
	return TCL_ERROR;
    }
    TzsetIfNecessary();
    timeVal = ThreadSafeLocalTime(&tock);
    if (timeVal == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"localtime failed (clock value may be too "
		"large/small to represent)", -1));
	Tcl_SetErrorCode(interp, "CLOCK", "localtimeFailed", (char *)NULL);
	return TCL_ERROR;
    }

    /*
     * Fill in the date in 'fields' and use it to derive Julian Day.
     */







|








|







2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
    /*
     * Use 'localtime' to determine local year, month, day, time of day.
     */

    tock = (time_t) fields->seconds;
    if ((Tcl_WideInt) tock != fields->seconds) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"number too large to represent as a Posix time", TCL_AUTO_LENGTH));
	Tcl_SetErrorCode(interp, "CLOCK", "argTooLarge", (char *)NULL);
	return TCL_ERROR;
    }
    TzsetIfNecessary();
    timeVal = ThreadSafeLocalTime(&tock);
    if (timeVal == NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"localtime failed (clock value may be too "
		"large/small to represent)", TCL_AUTO_LENGTH));
	Tcl_SetErrorCode(interp, "CLOCK", "localtimeFailed", (char *)NULL);
	return TCL_ERROR;
    }

    /*
     * Fill in the date in 'fields' and use it to derive Julian Day.
     */
3045
3046
3047
3048
3049
3050
3051
3052

3053
3054
3055
3056
3057
3058
3059
	Tcl_WCharToUtfDString(varValue, -1, &ds);
	Tcl_DStringResult(interp, &ds);
    }
#else
    varName = TclGetString(objv[1]);
    varValue = getenv(varName);
    if (varValue != NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(varValue, -1));

    }
#endif
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------







|
>







3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
	Tcl_WCharToUtfDString(varValue, -1, &ds);
	Tcl_DStringResult(interp, &ds);
    }
#else
    varName = TclGetString(objv[1]);
    varValue = getenv(varName);
    if (varValue != NULL) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		varValue, TCL_AUTO_LENGTH));
    }
#endif
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
    }
    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclpGetMicroseconds()));
    return TCL_OK;
}

static inline void
ClockInitFmtScnArgs(
    void *clientData,
    Tcl_Interp *interp,
    ClockFmtScnCmdArgs *opts)
{
    memset(opts, 0, sizeof(*opts));
    opts->clientData = clientData;
    opts->interp = interp;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ClockParseFmtScnArgs --







|




|







3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
    }
    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclpGetMicroseconds()));
    return TCL_OK;
}

static inline void
ClockInitFmtScnArgs(
    ClockClientData *dataPtr,
    Tcl_Interp *interp,
    ClockFmtScnCmdArgs *opts)
{
    memset(opts, 0, sizeof(*opts));
    opts->dataPtr = dataPtr;
    opts->interp = interp;
}

/*
 *-----------------------------------------------------------------------------
 *
 * ClockParseFmtScnArgs --
3266
3267
3268
3269
3270
3271
3272

3273
3274
3275

3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387

3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
 * Results:
 *	Returns a standard Tcl result, and stores parsed options
 *	(format, the locale, timezone and base) in structure "opts".
 *
 *-----------------------------------------------------------------------------
 */


#define CLC_FMT_ARGS	(0)
#define CLC_SCN_ARGS	(1 << 0)
#define CLC_ADD_ARGS	(1 << 1)


static int
ClockParseFmtScnArgs(
    ClockFmtScnCmdArgs *opts,	/* Result vector: format, locale, timezone... */
    TclDateFields      *date,	/* Extracted date-time corresponding base
				 * (by scan or add) resp. clockval (by format) */
    int objc,			/* Parameter count */
    Tcl_Obj *const objv[],	/* Parameter vector */
    int flags,			/* Flags, differentiates between format, scan, add */
    const char *syntax)		/* Syntax of the current command */
{
    Tcl_Interp *interp = opts->interp;
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;
    int gmtFlag = 0;
    static const char *const options[] = {
	"-base", "-format", "-gmt", "-locale", "-timezone", "-validate", NULL
    };
    enum optionInd {
	CLC_ARGS_BASE, CLC_ARGS_FORMAT, CLC_ARGS_GMT, CLC_ARGS_LOCALE,
	CLC_ARGS_TIMEZONE, CLC_ARGS_VALIDATE
    };
    int optionIndex;		/* Index of an option. */
    int saw = 0;		/* Flag == 1 if option was seen already. */
    int i, baseIdx;
    Tcl_WideInt baseVal;	/* Base time, expressed in seconds from the Epoch */

    if (flags & CLC_SCN_ARGS) {
    	/* default flags (from configure) */
    	opts->flags |= dataPtr->defFlags & (CLF_VALIDATE);
    } else {
    	/* clock value (as current base) */
	opts->baseObj = objv[(baseIdx = 1)];
	saw |= (1 << CLC_ARGS_BASE);
    }

    /*
     * Extract values for the keywords.
     */

    for (i = 2; i < objc; i+=2) {
	/* bypass integers (offsets) by "clock add" */
	if (flags & CLC_ADD_ARGS) {
	    Tcl_WideInt num;

	    if (TclGetWideIntFromObj(NULL, objv[i], &num) == TCL_OK) {
		continue;
	    }
	}
	/* get option */
	if (Tcl_GetIndexFromObj(interp, objv[i], options,
		"option", 0, &optionIndex) != TCL_OK) {
	    goto badOptionMsg;
	}
	/* if already specified */
	if (saw & (1 << optionIndex)) {
	    if (!(flags & CLC_SCN_ARGS)
		    && optionIndex == CLC_ARGS_BASE) {
		goto badOptionMsg;
	    }
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "bad option \"%s\": doubly present",
		    TclGetString(objv[i])));
	    goto badOption;
	}
	switch (optionIndex) {
	case CLC_ARGS_FORMAT:
	    if (flags & CLC_ADD_ARGS) {
		goto badOptionMsg;
	    }
	    opts->formatObj = objv[i + 1];
	    break;
	case CLC_ARGS_GMT:
	    if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &gmtFlag) != TCL_OK){
		return TCL_ERROR;
	    }
	    break;
	case CLC_ARGS_LOCALE:
	    opts->localeObj = objv[i + 1];
	    break;
	case CLC_ARGS_TIMEZONE:
	    opts->timezoneObj = objv[i + 1];
	    break;
	case CLC_ARGS_BASE:
	    opts->baseObj = objv[(baseIdx = i + 1)];
	    break;
	case CLC_ARGS_VALIDATE:
	    if (!(flags & CLC_SCN_ARGS)) {
		goto badOptionMsg;
	    } else {
		int val;

		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &val) != TCL_OK) {
		    return TCL_ERROR;
		}
		if (val) {
		    opts->flags |= CLF_VALIDATE;
		} else {
		    opts->flags &= ~CLF_VALIDATE;
		}
	    }
	    break;
	}
	saw |= (1 << optionIndex);
    }

    /*
     * Check options.
     */

    if ((saw & (1 << CLC_ARGS_GMT))
	    && (saw & (1 << CLC_ARGS_TIMEZONE))) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj("cannot use -gmt and -timezone in same call", -1));

	Tcl_SetErrorCode(interp, "CLOCK", "gmtWithTimezone", (char *)NULL);
	return TCL_ERROR;
    }
    if (gmtFlag) {
	opts->timezoneObj = dataPtr->literals[LIT_GMT];
    } else if (opts->timezoneObj == NULL
	    || TclGetString(opts->timezoneObj) == NULL
	    || opts->timezoneObj->length == 0) {
	/* If time zone not specified use system time zone */
	opts->timezoneObj = ClockGetSystemTimeZone(opts->clientData, interp);
	if (opts->timezoneObj == NULL) {
	    return TCL_ERROR;
	}
    }

    /* Setup timezone (normalize object if needed and load TZ on demand) */

    opts->timezoneObj = ClockSetupTimeZone(opts->clientData, interp, opts->timezoneObj);
    if (opts->timezoneObj == NULL) {
	return TCL_ERROR;
    }

    /* Base (by scan or add) or clock value (by format) */

    if (opts->baseObj != NULL) {
	Tcl_Obj *baseObj = opts->baseObj;

	/* bypass integer recognition if looks like option "-now" */
	if ((baseObj->bytes && baseObj->length == 4 && *(baseObj->bytes + 1) == 'n')
		|| TclGetWideIntFromObj(NULL, baseObj, &baseVal) != TCL_OK) {
	    /* we accept "-now" as current date-time */
	    static const char *const nowOpts[] = {
		"-now", NULL
	    };
	    int idx;








>
|
|
|
>




|



|



|













|

|



|








|













<
|









|



















|















|








|
>









|







|










|







3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328

3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
 * Results:
 *	Returns a standard Tcl result, and stores parsed options
 *	(format, the locale, timezone and base) in structure "opts".
 *
 *-----------------------------------------------------------------------------
 */

typedef enum ClockOperation {
    CLC_OP_FMT = 0,		/* Doing [clock format] */
    CLC_OP_SCN,			/* Doing [clock scan] */
    CLC_OP_ADD			/* Doing [clock add] */
} ClockOperation;

static int
ClockParseFmtScnArgs(
    ClockFmtScnCmdArgs *opts,	/* Result vector: format, locale, timezone... */
    TclDateFields *date,	/* Extracted date-time corresponding base
				 * (by scan or add) resp. clockval (by format) */
    int objc,			/* Parameter count */
    Tcl_Obj *const objv[],	/* Parameter vector */
    ClockOperation operation,	/* What operation are we doing: format, scan, add */
    const char *syntax)		/* Syntax of the current command */
{
    Tcl_Interp *interp = opts->interp;
    ClockClientData *dataPtr = opts->dataPtr;
    int gmtFlag = 0;
    static const char *const options[] = {
	"-base", "-format", "-gmt", "-locale", "-timezone", "-validate", NULL
    };
    enum optionInd {
	CLC_ARGS_BASE, CLC_ARGS_FORMAT, CLC_ARGS_GMT, CLC_ARGS_LOCALE,
	CLC_ARGS_TIMEZONE, CLC_ARGS_VALIDATE
    };
    int optionIndex;		/* Index of an option. */
    int saw = 0;		/* Flag == 1 if option was seen already. */
    int i, baseIdx;
    Tcl_WideInt baseVal;	/* Base time, expressed in seconds from the Epoch */

    if (operation == CLC_OP_SCN) {
    	/* default flags (from configure) */
    	opts->flags |= dataPtr->defFlags & CLF_VALIDATE;
    } else {
    	/* clock value (as current base) */
	opts->baseObj = objv[(baseIdx = 1)];
	saw |= 1 << CLC_ARGS_BASE;
    }

    /*
     * Extract values for the keywords.
     */

    for (i = 2; i < objc; i+=2) {
	/* bypass integers (offsets) by "clock add" */
	if (operation == CLC_OP_ADD) {
	    Tcl_WideInt num;

	    if (TclGetWideIntFromObj(NULL, objv[i], &num) == TCL_OK) {
		continue;
	    }
	}
	/* get option */
	if (Tcl_GetIndexFromObj(interp, objv[i], options,
		"option", 0, &optionIndex) != TCL_OK) {
	    goto badOptionMsg;
	}
	/* if already specified */
	if (saw & (1 << optionIndex)) {

	    if (operation != CLC_OP_SCN && optionIndex == CLC_ARGS_BASE) {
		goto badOptionMsg;
	    }
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
		    "bad option \"%s\": doubly present",
		    TclGetString(objv[i])));
	    goto badOption;
	}
	switch (optionIndex) {
	case CLC_ARGS_FORMAT:
	    if (operation == CLC_OP_ADD) {
		goto badOptionMsg;
	    }
	    opts->formatObj = objv[i + 1];
	    break;
	case CLC_ARGS_GMT:
	    if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &gmtFlag) != TCL_OK){
		return TCL_ERROR;
	    }
	    break;
	case CLC_ARGS_LOCALE:
	    opts->localeObj = objv[i + 1];
	    break;
	case CLC_ARGS_TIMEZONE:
	    opts->timezoneObj = objv[i + 1];
	    break;
	case CLC_ARGS_BASE:
	    opts->baseObj = objv[(baseIdx = i + 1)];
	    break;
	case CLC_ARGS_VALIDATE:
	    if (operation != CLC_OP_SCN) {
		goto badOptionMsg;
	    } else {
		int val;

		if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &val) != TCL_OK) {
		    return TCL_ERROR;
		}
		if (val) {
		    opts->flags |= CLF_VALIDATE;
		} else {
		    opts->flags &= ~CLF_VALIDATE;
		}
	    }
	    break;
	}
	saw |= 1 << optionIndex;
    }

    /*
     * Check options.
     */

    if ((saw & (1 << CLC_ARGS_GMT))
	    && (saw & (1 << CLC_ARGS_TIMEZONE))) {
	Tcl_SetObjResult(interp, Tcl_NewStringObj(
		"cannot use -gmt and -timezone in same call", TCL_AUTO_LENGTH));
	Tcl_SetErrorCode(interp, "CLOCK", "gmtWithTimezone", (char *)NULL);
	return TCL_ERROR;
    }
    if (gmtFlag) {
	opts->timezoneObj = dataPtr->literals[LIT_GMT];
    } else if (opts->timezoneObj == NULL
	    || TclGetString(opts->timezoneObj) == NULL
	    || opts->timezoneObj->length == 0) {
	/* If time zone not specified use system time zone */
	opts->timezoneObj = ClockGetSystemTimeZone(dataPtr, interp);
	if (opts->timezoneObj == NULL) {
	    return TCL_ERROR;
	}
    }

    /* Setup timezone (normalize object if needed and load TZ on demand) */

    opts->timezoneObj = ClockSetupTimeZone(dataPtr, interp, opts->timezoneObj);
    if (opts->timezoneObj == NULL) {
	return TCL_ERROR;
    }

    /* Base (by scan or add) or clock value (by format) */

    if (opts->baseObj != NULL) {
	Tcl_Obj *baseObj = opts->baseObj;

	/* bypass integer recognition if looks like option "-now" */
	if ((baseObj->bytes && baseObj->length == 4 && baseObj->bytes[1] == 'n')
		|| TclGetWideIntFromObj(NULL, baseObj, &baseVal) != TCL_OK) {
	    /* we accept "-now" as current date-time */
	    static const char *const nowOpts[] = {
		"-now", NULL
	    };
	    int idx;

3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
	    && dataPtr->lastBase.date.seconds == baseVal
	    && (!(dataPtr->lastBase.date.flags & CLF_CTZ)
	    || dataPtr->lastTZEpoch == TzsetIfNecessary())) {
	memcpy(date, &dataPtr->lastBase.date, ClockCacheableDateFieldsSize);
    } else {
	/* extact fields from base */
	date->seconds = baseVal;
	if (ClockGetDateFields(opts->clientData, interp, date, opts->timezoneObj,
	      GREGORIAN_CHANGE_DATE) != TCL_OK) {
	    /* TODO - GREGORIAN_CHANGE_DATE should be locale-dependent */
	    return TCL_ERROR;
	}
	/* cache last base */
	memcpy(&dataPtr->lastBase.date, date, ClockCacheableDateFieldsSize);
	TclSetObjRef(dataPtr->lastBase.timezoneObj, opts->timezoneObj);







|







3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
	    && dataPtr->lastBase.date.seconds == baseVal
	    && (!(dataPtr->lastBase.date.flags & CLF_CTZ)
	    || dataPtr->lastTZEpoch == TzsetIfNecessary())) {
	memcpy(date, &dataPtr->lastBase.date, ClockCacheableDateFieldsSize);
    } else {
	/* extact fields from base */
	date->seconds = baseVal;
	if (ClockGetDateFields(dataPtr, interp, date, opts->timezoneObj,
	      GREGORIAN_CHANGE_DATE) != TCL_OK) {
	    /* TODO - GREGORIAN_CHANGE_DATE should be locale-dependent */
	    return TCL_ERROR;
	}
	/* cache last base */
	memcpy(&dataPtr->lastBase.date, date, ClockCacheableDateFieldsSize);
	TclSetObjRef(dataPtr->lastBase.timezoneObj, opts->timezoneObj);
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
ClockFormatObjCmd(
    void *clientData,		/* Client data containing literal pool */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const objv[])	/* Parameter values */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;

    static const char *syntax = "clock format clockval|-now "
	    "?-format string? "
	    "?-gmt boolean? "
	    "?-locale LOCALE? ?-timezone ZONE?";
    int ret;
    ClockFmtScnCmdArgs opts;	/* Format, locale, timezone and base */
    DateFormat dateFmt;		/* Common structure used for formatting */







<







3516
3517
3518
3519
3520
3521
3522

3523
3524
3525
3526
3527
3528
3529
ClockFormatObjCmd(
    void *clientData,		/* Client data containing literal pool */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const objv[])	/* Parameter values */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;

    static const char *syntax = "clock format clockval|-now "
	    "?-format string? "
	    "?-gmt boolean? "
	    "?-locale LOCALE? ?-timezone ZONE?";
    int ret;
    ClockFmtScnCmdArgs opts;	/* Format, locale, timezone and base */
    DateFormat dateFmt;		/* Common structure used for formatting */
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556

    memset(&dateFmt, 0, sizeof(dateFmt));

    /*
     * Extract values for the keywords.
     */

    ClockInitFmtScnArgs(clientData, interp, &opts);
    ret = ClockParseFmtScnArgs(&opts, &dateFmt.date, objc, objv,
	    CLC_FMT_ARGS, "-format, -gmt, -locale, or -timezone");
    if (ret != TCL_OK) {
	goto done;
    }

    /* Default format */
    if (opts.formatObj == NULL) {
	opts.formatObj = dataPtr->literals[LIT__DEFAULT_FORMAT];







|

|







3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553

    memset(&dateFmt, 0, sizeof(dateFmt));

    /*
     * Extract values for the keywords.
     */

    ClockInitFmtScnArgs(dataPtr, interp, &opts);
    ret = ClockParseFmtScnArgs(&opts, &dateFmt.date, objc, objv,
	    CLC_OP_FMT, "-format, -gmt, -locale, or -timezone");
    if (ret != TCL_OK) {
	goto done;
    }

    /* Default format */
    if (opts.formatObj == NULL) {
	opts.formatObj = dataPtr->literals[LIT__DEFAULT_FORMAT];
3587
3588
3589
3590
3591
3592
3593

3594
3595
3596
3597
3598
3599
3600
int
ClockScanObjCmd(
    void *clientData,		/* Client data containing literal pool */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const objv[])	/* Parameter values */
{

    static const char *syntax = "clock scan string "
	    "?-base seconds? "
	    "?-format string? "
	    "?-gmt boolean? "
	    "?-locale LOCALE? ?-timezone ZONE? ?-validate boolean?";
    int ret;
    ClockFmtScnCmdArgs opts;	/* Format, locale, timezone and base */







>







3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
int
ClockScanObjCmd(
    void *clientData,		/* Client data containing literal pool */
    Tcl_Interp *interp,		/* Tcl interpreter */
    int objc,			/* Parameter count */
    Tcl_Obj *const objv[])	/* Parameter values */
{
    ClockClientData *dataPtr = (ClockClientData *)clientData;
    static const char *syntax = "clock scan string "
	    "?-base seconds? "
	    "?-format string? "
	    "?-gmt boolean? "
	    "?-locale LOCALE? ?-timezone ZONE? ?-validate boolean?";
    int ret;
    ClockFmtScnCmdArgs opts;	/* Format, locale, timezone and base */
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642

    ClockInitDateInfo(&yy);

    /*
     * Extract values for the keywords.
     */

    ClockInitFmtScnArgs(clientData, interp, &opts);
    ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv,
	    CLC_SCN_ARGS, "-base, -format, -gmt, -locale, -timezone or -validate");
    if (ret != TCL_OK) {
	goto done;
    }

    /* seconds are in localSeconds (relative base date), so reset time here */
    yyHour = yyMinutes = yySeconds = yySecondOfDay = 0; yyMeridian = MER24;

    /* If free scan */
    if (opts.formatObj == NULL) {
	/* Use compiled version of FreeScan - */

	/* [SB] TODO: Perhaps someday we'll localize the legacy code. Right now,
	 * it's not localized. */
	if (opts.localeObj != NULL) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
		    "legacy [clock scan] does not support -locale", -1));
	    Tcl_SetErrorCode(interp, "CLOCK", "flagWithLegacyFormat", (char *)NULL);
	    ret = TCL_ERROR;
	    goto done;
	}
	ret = ClockFreeScan(&yy, objv[1], &opts);
    } else {
	/* Use compiled version of Scan - */







|

|















|







3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640

    ClockInitDateInfo(&yy);

    /*
     * Extract values for the keywords.
     */

    ClockInitFmtScnArgs(dataPtr, interp, &opts);
    ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv,
	    CLC_OP_SCN, "-base, -format, -gmt, -locale, -timezone or -validate");
    if (ret != TCL_OK) {
	goto done;
    }

    /* seconds are in localSeconds (relative base date), so reset time here */
    yyHour = yyMinutes = yySeconds = yySecondOfDay = 0; yyMeridian = MER24;

    /* If free scan */
    if (opts.formatObj == NULL) {
	/* Use compiled version of FreeScan - */

	/* [SB] TODO: Perhaps someday we'll localize the legacy code. Right now,
	 * it's not localized. */
	if (opts.localeObj != NULL) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
		    "legacy [clock scan] does not support -locale", TCL_AUTO_LENGTH));
	    Tcl_SetErrorCode(interp, "CLOCK", "flagWithLegacyFormat", (char *)NULL);
	    ret = TCL_ERROR;
	    goto done;
	}
	ret = ClockFreeScan(&yy, objv[1], &opts);
    } else {
	/* Use compiled version of Scan - */
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
	}
	info->flags |= CLF_ASSEMBLE_SECONDS;
	info->flags &= ~CLF_ASSEMBLE_JULIANDAY;
    }

    /* some overflow checks */
    if (info->flags & CLF_JULIANDAY) {
	ClockClientData *dataPtr = (ClockClientData *)opts->clientData;

	double curJDN = (double)yydate.julianDay
		+ ((double)yySecondOfDay - SECONDS_PER_DAY/2) / SECONDS_PER_DAY;
	if (curJDN > dataPtr->maxJDN) {
	    Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
		    "requested date too large to represent", -1));
	    Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", (char *)NULL);
	    return TCL_ERROR;
	}
    }

    /* Local seconds to UTC (stored in yydate.seconds) */

    if (info->flags & (CLF_ASSEMBLE_SECONDS)) {
	yydate.localSeconds =
		-210866803200LL
		+ (SECONDS_PER_DAY * yydate.julianDay)
		+ (yySecondOfDay % SECONDS_PER_DAY);
    }

    if (info->flags & (CLF_ASSEMBLE_SECONDS|CLF_LOCALSEC)) {
	if (ConvertLocalToUTC(opts->clientData, opts->interp, &yydate,
		opts->timezoneObj, GREGORIAN_CHANGE_DATE) != TCL_OK) {
	    return TCL_ERROR;
	}
    }

    /* Increment UTC seconds with relative time */








<
<


|

|







|






|
|







3716
3717
3718
3719
3720
3721
3722


3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
	}
	info->flags |= CLF_ASSEMBLE_SECONDS;
	info->flags &= ~CLF_ASSEMBLE_JULIANDAY;
    }

    /* some overflow checks */
    if (info->flags & CLF_JULIANDAY) {


	double curJDN = (double)yydate.julianDay
		+ ((double)yySecondOfDay - SECONDS_PER_DAY/2) / SECONDS_PER_DAY;
	if (curJDN > opts->dataPtr->maxJDN) {
	    Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
		    "requested date too large to represent", TCL_AUTO_LENGTH));
	    Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", (char *)NULL);
	    return TCL_ERROR;
	}
    }

    /* Local seconds to UTC (stored in yydate.seconds) */

    if (info->flags & CLF_ASSEMBLE_SECONDS) {
	yydate.localSeconds =
		-210866803200LL
		+ (SECONDS_PER_DAY * yydate.julianDay)
		+ (yySecondOfDay % SECONDS_PER_DAY);
    }

    if (info->flags & (CLF_ASSEMBLE_SECONDS | CLF_LOCALSEC)) {
	if (ConvertLocalToUTC(opts->dataPtr, opts->interp, &yydate,
		opts->timezoneObj, GREGORIAN_CHANGE_DATE) != TCL_OK) {
	    return TCL_ERROR;
	}
    }

    /* Increment UTC seconds with relative time */

3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
    DateInfo *info,		/* Clock scan info structure */
    ClockFmtScnCmdArgs *opts,	/* Scan options */
    int stage)			/* Stage to validate (1, 2 or 3 for both) */
{
    const char *errMsg = "", *errCode = "";
    TclDateFields temp;
    int tempCpyFlg = 0;
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;

#if 0
    printf("yyMonth %d, yyDay %d, yyDayOfYear %d, yyHour %d, yyMinutes %d, yySeconds %d, "
	    "yySecondOfDay %d, sec %d, daySec %d, tzOffset %d\n",
	    yyMonth, yyDay, yydate.dayOfYear, yyHour, yyMinutes, yySeconds,
	    yySecondOfDay, (int)yydate.localSeconds, (int)(yydate.localSeconds % SECONDS_PER_DAY),
	    yydate.tzOffset);
#endif

    if (!(stage & CLF_VALIDATE_S1) || !(opts->flags & CLF_VALIDATE_S1)) {
	goto stage_2;
    }
    opts->flags &= ~CLF_VALIDATE_S1; /* stage 1 is done */

    /* first year (used later in hath / daysInPriorMonths) */
    if ((info->flags & (CLF_YEAR|CLF_ISO8601YEAR))) {
	if ((info->flags & CLF_ISO8601YEAR)) {
	    if (yydate.iso8601Year < dataPtr->validMinYear
		    || yydate.iso8601Year > dataPtr->validMaxYear) {
		errMsg = "invalid iso year";
		errCode = "iso year";
		goto error;
	    }
	}
	if (info->flags & CLF_YEAR) {
	    if (yyYear < dataPtr->validMinYear
		    || yyYear > dataPtr->validMaxYear) {
		errMsg = "invalid year";
		errCode = "year";
		goto error;
	    }
	} else if ((info->flags & CLF_ISO8601YEAR)) {
	    yyYear = yydate.iso8601Year; /* used to recognize leap */
	}
	if ((info->flags & (CLF_ISO8601YEAR|CLF_YEAR))
		== (CLF_ISO8601YEAR|CLF_YEAR)) {
	    if (yyYear != yydate.iso8601Year) {
		errMsg = "ambiguous year";
		errCode = "year";
		goto error;
	    }
	}
    }







|















|


















|
|







3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
    DateInfo *info,		/* Clock scan info structure */
    ClockFmtScnCmdArgs *opts,	/* Scan options */
    int stage)			/* Stage to validate (1, 2 or 3 for both) */
{
    const char *errMsg = "", *errCode = "";
    TclDateFields temp;
    int tempCpyFlg = 0;
    ClockClientData *dataPtr = opts->dataPtr;

#if 0
    printf("yyMonth %d, yyDay %d, yyDayOfYear %d, yyHour %d, yyMinutes %d, yySeconds %d, "
	    "yySecondOfDay %d, sec %d, daySec %d, tzOffset %d\n",
	    yyMonth, yyDay, yydate.dayOfYear, yyHour, yyMinutes, yySeconds,
	    yySecondOfDay, (int)yydate.localSeconds, (int)(yydate.localSeconds % SECONDS_PER_DAY),
	    yydate.tzOffset);
#endif

    if (!(stage & CLF_VALIDATE_S1) || !(opts->flags & CLF_VALIDATE_S1)) {
	goto stage_2;
    }
    opts->flags &= ~CLF_VALIDATE_S1; /* stage 1 is done */

    /* first year (used later in hath / daysInPriorMonths) */
    if ((info->flags & (CLF_YEAR | CLF_ISO8601YEAR))) {
	if ((info->flags & CLF_ISO8601YEAR)) {
	    if (yydate.iso8601Year < dataPtr->validMinYear
		    || yydate.iso8601Year > dataPtr->validMaxYear) {
		errMsg = "invalid iso year";
		errCode = "iso year";
		goto error;
	    }
	}
	if (info->flags & CLF_YEAR) {
	    if (yyYear < dataPtr->validMinYear
		    || yyYear > dataPtr->validMaxYear) {
		errMsg = "invalid year";
		errCode = "year";
		goto error;
	    }
	} else if ((info->flags & CLF_ISO8601YEAR)) {
	    yyYear = yydate.iso8601Year; /* used to recognize leap */
	}
	if ((info->flags & (CLF_ISO8601YEAR | CLF_YEAR))
		== (CLF_ISO8601YEAR | CLF_YEAR)) {
	    if (yyYear != yydate.iso8601Year) {
		errMsg = "ambiguous year";
		errCode = "year";
		goto error;
	    }
	}
    }
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
    DateInfo *info,		/* Date fields used for parsing & converting
				 * simultaneously a yy-parse structure of the
				 * TclClockFreeScan */
    Tcl_Obj *strObj,		/* String containing the time to scan */
    ClockFmtScnCmdArgs *opts)	/* Command options */
{
    Tcl_Interp *interp = opts->interp;
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;

    int ret = TCL_ERROR;

    /*
     * Parse the date. The parser will fill a structure "info" with date,
     * time, time zone, relative month/day/seconds, relative weekday, ordinal
     * month.
     * Notice that many yy-defines point to values in the "info" or "date"
     * structure, e. g. yySecondOfDay -> info->date.secondOfDay or
     *			yyMonth -> info->date.month (same as yydate.month)
     */
    yyInput = TclGetString(strObj);

    if (TclClockFreeScan(interp, info) != TCL_OK) {
	Tcl_Obj *msg;

	TclNewObj(msg);
	Tcl_AppendPrintfToObj(msg, "unable to convert date-time string \"%s\": %s",
		TclGetString(strObj), Tcl_GetString(Tcl_GetObjResult(interp)));
	Tcl_SetObjResult(interp, msg);
	goto done;
    }

    /*
     * If the caller supplied a date in the string, update the date with
     * the value. If the caller didn't specify a time with the date, default to
     * midnight.







|
<













<
|
<
|
|
<







3958
3959
3960
3961
3962
3963
3964
3965

3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978

3979

3980
3981

3982
3983
3984
3985
3986
3987
3988
    DateInfo *info,		/* Date fields used for parsing & converting
				 * simultaneously a yy-parse structure of the
				 * TclClockFreeScan */
    Tcl_Obj *strObj,		/* String containing the time to scan */
    ClockFmtScnCmdArgs *opts)	/* Command options */
{
    Tcl_Interp *interp = opts->interp;
    ClockClientData *dataPtr = opts->dataPtr;

    int ret = TCL_ERROR;

    /*
     * Parse the date. The parser will fill a structure "info" with date,
     * time, time zone, relative month/day/seconds, relative weekday, ordinal
     * month.
     * Notice that many yy-defines point to values in the "info" or "date"
     * structure, e. g. yySecondOfDay -> info->date.secondOfDay or
     *			yyMonth -> info->date.month (same as yydate.month)
     */
    yyInput = TclGetString(strObj);

    if (TclClockFreeScan(interp, info) != TCL_OK) {

	Tcl_SetObjResult(interp, Tcl_ObjPrintf(

		"unable to convert date-time string \"%s\": %s",
		TclGetString(strObj), Tcl_GetString(Tcl_GetObjResult(interp))));

	goto done;
    }

    /*
     * If the caller supplied a date in the string, update the date with
     * the value. If the caller didn't specify a time with the date, default to
     * midnight.
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
	}
    }

    /*
     * Assemble date, time, zone into seconds-from-epoch
     */

    if ((info->flags & (CLF_TIME|CLF_HAVEDATE)) == CLF_HAVEDATE) {
	yySecondOfDay = 0;
	info->flags |= CLF_ASSEMBLE_SECONDS;
    } else if (info->flags & CLF_TIME) {
	yySecondOfDay = ToSeconds(yyHour, yyMinutes,
		yySeconds, yyMeridian);
	info->flags |= CLF_ASSEMBLE_SECONDS;
    } else if ((info->flags & (CLF_DAYOFWEEK|CLF_HAVEDATE)) == CLF_DAYOFWEEK
	    || (info->flags & CLF_ORDINALMONTH)
	    || ((info->flags & CLF_RELCONV)
	    && (yyRelMonth != 0 || yyRelDay != 0))) {
	yySecondOfDay = 0;
	info->flags |= CLF_ASSEMBLE_SECONDS;
    } else {
	yySecondOfDay = yydate.localSeconds % SECONDS_PER_DAY;







|



|
<

|







4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053

4054
4055
4056
4057
4058
4059
4060
4061
4062
	}
    }

    /*
     * Assemble date, time, zone into seconds-from-epoch
     */

    if ((info->flags & (CLF_TIME | CLF_HAVEDATE)) == CLF_HAVEDATE) {
	yySecondOfDay = 0;
	info->flags |= CLF_ASSEMBLE_SECONDS;
    } else if (info->flags & CLF_TIME) {
	yySecondOfDay = ToSeconds(yyHour, yyMinutes, yySeconds, yyMeridian);

	info->flags |= CLF_ASSEMBLE_SECONDS;
    } else if ((info->flags & (CLF_DAYOFWEEK | CLF_HAVEDATE)) == CLF_DAYOFWEEK
	    || (info->flags & CLF_ORDINALMONTH)
	    || ((info->flags & CLF_RELCONV)
	    && (yyRelMonth != 0 || yyRelDay != 0))) {
	yySecondOfDay = 0;
	info->flags |= CLF_ASSEMBLE_SECONDS;
    } else {
	yySecondOfDay = yydate.localSeconds % SECONDS_PER_DAY;
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
	    /* if the day doesn't exist in the current month, repair it */
	    h = hath[IsGregorianLeapYear(&yydate)][m];
	    if (yyDay > h) {
		yyDay = h;
	    }

	    /* on demand (lazy) assemble julianDay using new year, month, etc. */
	    info->flags |= CLF_ASSEMBLE_JULIANDAY|CLF_ASSEMBLE_SECONDS;

	    yyRelMonth = 0;
	}

	/* add days (or other parts aligned to days) */
	if (yyRelDay) {
	    /* assemble julianDay using new year, month, etc. */







|







4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
	    /* if the day doesn't exist in the current month, repair it */
	    h = hath[IsGregorianLeapYear(&yydate)][m];
	    if (yyDay > h) {
		yyDay = h;
	    }

	    /* on demand (lazy) assemble julianDay using new year, month, etc. */
	    info->flags |= CLF_ASSEMBLE_JULIANDAY | CLF_ASSEMBLE_SECONDS;

	    yyRelMonth = 0;
	}

	/* add days (or other parts aligned to days) */
	if (yyRelDay) {
	    /* assemble julianDay using new year, month, etc. */
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407


4408
4409
4410
4411
4412
4413
4414
4415

    ClockInitDateInfo(&yy);

    /*
     * Extract values for the keywords.
     */

    ClockInitFmtScnArgs(clientData, interp, &opts);
    ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv,
	    CLC_ADD_ARGS, "-gmt, -locale, or -timezone");
    if (ret != TCL_OK) {
	goto done;
    }

    /* time together as seconds of the day */
    yySecondOfDay = yySeconds = yydate.localSeconds % SECONDS_PER_DAY;
    /* seconds are in localSeconds (relative base date), so reset time here */


    yyHour = 0; yyMinutes = 0; yyMeridian = MER24;

    ret = TCL_ERROR;

    /*
     * Find each offset and process date increment
     */








|

|







>
>
|







4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408

    ClockInitDateInfo(&yy);

    /*
     * Extract values for the keywords.
     */

    ClockInitFmtScnArgs(dataPtr, interp, &opts);
    ret = ClockParseFmtScnArgs(&opts, &yy.date, objc, objv,
	    CLC_OP_ADD, "-gmt, -locale, or -timezone");
    if (ret != TCL_OK) {
	goto done;
    }

    /* time together as seconds of the day */
    yySecondOfDay = yySeconds = yydate.localSeconds % SECONDS_PER_DAY;
    /* seconds are in localSeconds (relative base date), so reset time here */
    yyHour = 0;
    yyMinutes = 0;
    yyMeridian = MER24;

    ret = TCL_ERROR;

    /*
     * Find each offset and process date increment
     */

4638
4639
4640
4641
4642
4643
4644

4645
4646
4647
4648

4649
4650
4651
4652
4653
4654





4655


4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691


4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
#define getenv(x) _wgetenv(L##x)
#else
#define WCHAR char
#define wcslen strlen
#define wcscmp strcmp
#define wcscpy strcpy
#endif


static size_t
TzsetIfNecessary(void)
{

    static WCHAR* tzWas = (WCHAR *)INT2PTR(-1);	 /* Previous value of TZ, protected by
					  * clockMutex. */
    static long long tzLastRefresh = 0;	 /* Used for latency before next refresh */
    static size_t tzWasEpoch = 0;        /* Epoch, signals that TZ changed */
    static size_t tzEnvEpoch = 0;        /* Last env epoch, for faster signaling,
					    that TZ changed via TCL */





    const WCHAR *tzIsNow;		 /* Current value of TZ */



    /*
     * Prevent performance regression on some platforms by resolving of system time zone:
     * small latency for check whether environment was changed (once per second)
     * no latency if environment was changed with tcl-env (compare both epoch values)
     */
    Tcl_Time now;
    Tcl_GetTime(&now);
    if (now.sec == tzLastRefresh && tzEnvEpoch == TclEnvEpoch) {
	return tzWasEpoch;
    }

    tzEnvEpoch = TclEnvEpoch;
    tzLastRefresh = now.sec;

    /* check in lock */
    Tcl_MutexLock(&clockMutex);
    tzIsNow = getenv("TCL_TZ");
    if (tzIsNow == NULL) {
	tzIsNow = getenv("TZ");
    }
    if (tzIsNow != NULL && (tzWas == NULL || tzWas == (WCHAR *)INT2PTR(-1)
	    || wcscmp(tzIsNow, tzWas) != 0)) {
	tzset();
	if (tzWas != NULL && tzWas != (WCHAR *)INT2PTR(-1)) {
	    Tcl_Free(tzWas);
	}
	tzWas = (WCHAR *)Tcl_Alloc(sizeof(WCHAR) * (wcslen(tzIsNow) + 1));
	wcscpy(tzWas, tzIsNow);
	tzWasEpoch++;
    } else if (tzIsNow == NULL && tzWas != NULL) {
	tzset();
	if (tzWas != (WCHAR *)INT2PTR(-1)) {
	    Tcl_Free(tzWas);
	}
	tzWas = NULL;


	tzWasEpoch++;
    }
    Tcl_MutexUnlock(&clockMutex);

    return tzWasEpoch;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */







>




>
|
<
|
|
|
|
>
>
>
>
>
|
>
>






|

|
|


|
|



|
|
|

|
|

|
|

|
|
|
|

|
|

|
>
>
|



|









4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644

4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
#define getenv(x) _wgetenv(L##x)
#else
#define WCHAR char
#define wcslen strlen
#define wcscmp strcmp
#define wcscpy strcpy
#endif
#define TZ_INIT_MARKER	((WCHAR *) INT2PTR(-1))

static size_t
TzsetIfNecessary(void)
{
    typedef struct ClockTzStatic {
	WCHAR *was;		/* Previous value of TZ. */

	long long lastRefresh;	/* Used for latency before next refresh. */
	size_t epoch;		/* Epoch, signals that TZ changed. */
	size_t envEpoch;	/* Last env epoch, for faster signaling,
				 * that TZ changed via TCL */
    } ClockTzStatic;
    static ClockTzStatic tz = {	/* Global timezone info; protected by
				 * clockMutex.*/
	TZ_INIT_MARKER, 0, 0, 0
    };
    const WCHAR *tzNow;		/* Current value of TZ. */
    Tcl_Time now;		/* Current time. */
    size_t epoch;		/* The tz.epoch that the TZ was read at. */

    /*
     * Prevent performance regression on some platforms by resolving of system time zone:
     * small latency for check whether environment was changed (once per second)
     * no latency if environment was changed with tcl-env (compare both epoch values)
     */

    Tcl_GetTime(&now);
    if (now.sec == tz.lastRefresh && tz.envEpoch == TclEnvEpoch) {
	return tz.epoch;
    }

    tz.envEpoch = TclEnvEpoch;
    tz.lastRefresh = now.sec;

    /* check in lock */
    Tcl_MutexLock(&clockMutex);
    tzNow = getenv("TCL_TZ");
    if (tzNow == NULL) {
	tzNow = getenv("TZ");
    }
    if (tzNow != NULL && (tz.was == NULL || tz.was == TZ_INIT_MARKER
	    || wcscmp(tzNow, tz.was) != 0)) {
	tzset();
	if (tz.was != NULL && tz.was != TZ_INIT_MARKER) {
	    Tcl_Free(tz.was);
	}
	tz.was = (WCHAR *) Tcl_Alloc(sizeof(WCHAR) * (wcslen(tzNow) + 1));
	wcscpy(tz.was, tzNow);
	epoch = ++tz.epoch;
    } else if (tzNow == NULL && tz.was != NULL) {
	tzset();
	if (tz.was != TZ_INIT_MARKER) {
	    Tcl_Free(tz.was);
	}
	tz.was = NULL;
	epoch = ++tz.epoch;
    } else {
	epoch = tz.epoch;
    }
    Tcl_MutexUnlock(&clockMutex);

    return epoch;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */

Changes to generic/tclClockFmt.c.

29
30
31
32
33
34
35





36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

216

217
218
219
220
221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
static void		ClockFrmScnFinalize(void *);

/*
 * Derivation of tclStringHashKeyType with another allocEntryProc
 */

static Tcl_HashKeyType ClockFmtScnStorageHashKeyType;






/*
 * 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 void
_str2int_no(
    int *out,
    const char *p,
    const char *e,
    int sign)
{
    /* assert(e <= p + 10); */
    int val = 0;

    /* overflow impossible for 10 digits ("9..9"), so no needs to check at all */
    while (p < e) {			/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign < 0) {
	val = -val;
    }
    *out = val;
}

static inline void
_str2wideInt_no(
    Tcl_WideInt *out,
    const char *p,
    const char *e,
    int sign)
{
    /* assert(e <= p + 18); */
    Tcl_WideInt val = 0;

    /* overflow impossible for 18 digits ("9..9"), so no needs to check at all */
    while (p < e) {			/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign < 0) {
	val = -val;
    }
    *out = val;
}

/* int & Tcl_WideInt overflows may happens here (expected case) */
#if defined(__GNUC__) || defined(__GNUG__)
# pragma GCC optimize("no-trapv")
#endif

static inline int
_str2int(
    int *out,
    const char *p,
    const char *e,
    int sign)
{
    int val = 0;
    /* overflow impossible for 10 digits ("9..9"), so no needs to check before */
    const char *eNO = p + 10;

    if (eNO > e) {
	eNO = e;
    }
    while (p < eNO) {				/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign >= 0) {
	while (p < e) {				/* check for overflow */
	    int prev = val;

	    val = val * 10 + (*p++ - '0');
	    if (val / 10 < prev) {
		return TCL_ERROR;
	    }
	}
    } else {
	val = -val;
	while (p < e) {				/* check for overflow */
	    int prev = val;

	    val = val * 10 - (*p++ - '0');
	    if (val / 10 > prev) {
		return TCL_ERROR;
	    }
	}
    }
    *out = val;
    return TCL_OK;
}

static inline int
_str2wideInt(
    Tcl_WideInt *out,
    const char *p,
    const char *e,
    int sign)
{
    Tcl_WideInt val = 0;
    /* overflow impossible for 18 digits ("9..9"), so no needs to check before */
    const char *eNO = p + 18;

    if (eNO > e) {
	eNO = e;
    }
    while (p < eNO) {				/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign >= 0) {
	while (p < e) {				/* check for overflow */
	    Tcl_WideInt prev = val;

	    val = val * 10 + (*p++ - '0');
	    if (val / 10 < prev) {
		return TCL_ERROR;
	    }
	}
    } else {
	val = -val;
	while (p < e) {				/* check for overflow */
	    Tcl_WideInt prev = val;

	    val = val * 10 - (*p++ - '0');
	    if (val / 10 > prev) {
		return TCL_ERROR;
	    }
	}
    }
    *out = val;
    return TCL_OK;
}

int
TclAtoWIe(
    Tcl_WideInt *out,
    const char *p,
    const char *e,
    int sign)
{
    return _str2wideInt(out, p, e, sign);
}

#if defined(__GNUC__) || defined(__GNUG__)
# pragma GCC reset_options
#endif

/*
 *----------------------------------------------------------------------
 *
 * _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,
    int val,
    char padchar,
    unsigned short width)
{
    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 {
	    char c = (val % 10);

	    val /= 10;
	    *p-- = '0' + c;
	} while (val > 0);
	/* fulling with pad-char */
	while (p >= buf) {
	    *p-- = padchar;
	}







>
>
>
>
>








|















|







>

|









|







>

|














|








>









>









>











|








>









>









>

















|









|













|






>
|
>












|
>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
static void		ClockFrmScnFinalize(void *);

/*
 * Derivation of tclStringHashKeyType with another allocEntryProc
 */

static Tcl_HashKeyType ClockFmtScnStorageHashKeyType;

#define IntFieldAt(info, offset) \
	((int *) (((char *) (info)) + (offset)))
#define WideFieldAt(info, offset) \
	((Tcl_WideInt *) (((char *) (info)) + (offset)))

/*
 * Clock scan and format facilities.
 */

/*
 *----------------------------------------------------------------------
 *
 * Clock_str2int, Clock_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 void
Clock_str2int_no(
    int *out,
    const char *p,
    const char *e,
    int sign)
{
    /* assert(e <= p + 10); */
    int val = 0;

    /* overflow impossible for 10 digits ("9..9"), so no needs to check at all */
    while (p < e) {				/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign < 0) {
	val = -val;
    }
    *out = val;
}

static inline void
Clock_str2wideInt_no(
    Tcl_WideInt *out,
    const char *p,
    const char *e,
    int sign)
{
    /* assert(e <= p + 18); */
    Tcl_WideInt val = 0;

    /* overflow impossible for 18 digits ("9..9"), so no needs to check at all */
    while (p < e) {				/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign < 0) {
	val = -val;
    }
    *out = val;
}

/* int & Tcl_WideInt overflows may happens here (expected case) */
#if defined(__GNUC__) || defined(__GNUG__)
# pragma GCC optimize("no-trapv")
#endif

static inline int
Clock_str2int(
    int *out,
    const char *p,
    const char *e,
    int sign)
{
    int val = 0;
    /* overflow impossible for 10 digits ("9..9"), so no needs to check before */
    const char *eNO = p + 10;

    if (eNO > e) {
	eNO = e;
    }
    while (p < eNO) {				/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign >= 0) {
	while (p < e) {				/* check for overflow */
	    int prev = val;

	    val = val * 10 + (*p++ - '0');
	    if (val / 10 < prev) {
		return TCL_ERROR;
	    }
	}
    } else {
	val = -val;
	while (p < e) {				/* check for overflow */
	    int prev = val;

	    val = val * 10 - (*p++ - '0');
	    if (val / 10 > prev) {
		return TCL_ERROR;
	    }
	}
    }
    *out = val;
    return TCL_OK;
}

static inline int
Clock_str2wideInt(
    Tcl_WideInt *out,
    const char *p,
    const char *e,
    int sign)
{
    Tcl_WideInt val = 0;
    /* overflow impossible for 18 digits ("9..9"), so no needs to check before */
    const char *eNO = p + 18;

    if (eNO > e) {
	eNO = e;
    }
    while (p < eNO) {				/* never overflows */
	val = val * 10 + (*p++ - '0');
    }
    if (sign >= 0) {
	while (p < e) {				/* check for overflow */
	    Tcl_WideInt prev = val;

	    val = val * 10 + (*p++ - '0');
	    if (val / 10 < prev) {
		return TCL_ERROR;
	    }
	}
    } else {
	val = -val;
	while (p < e) {				/* check for overflow */
	    Tcl_WideInt prev = val;

	    val = val * 10 - (*p++ - '0');
	    if (val / 10 > prev) {
		return TCL_ERROR;
	    }
	}
    }
    *out = val;
    return TCL_OK;
}

int
TclAtoWIe(
    Tcl_WideInt *out,
    const char *p,
    const char *e,
    int sign)
{
    return Clock_str2wideInt(out, p, e, sign);
}

#if defined(__GNUC__) || defined(__GNUG__)
# pragma GCC reset_options
#endif

/*
 *----------------------------------------------------------------------
 *
 * Clock_itoaw, Clock_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 *
Clock_itoaw(
    char *buf,
    int val,
    char padchar,
    unsigned short width)
{
    char *p;
    static const 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 {
	    char c = val % 10;

	    val /= 10;
	    *p-- = '0' + c;
	} while (val > 0);
	/* fulling with pad-char */
	while (p >= buf) {
	    *p-- = padchar;
	}
250
251
252
253
254
255
256
257

258
259
260
261
262
263

264
265
266
267
268
269
270
    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 {
	    char c = (val % 10);

	    val /= 10;
	    *p-- = '0' - c;
	} while (val < 0);
    } else {
	do {
	    char c = (val % 10);

	    val /= 10;
	    *p-- = '0' + c;
	} while (val < 0);
    }
    /* sign by 0 padding */
    if (padchar != '0') {
	*p-- = '-';







|
>





|
>







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
    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 {
	    char c = val % 10;

	    val /= 10;
	    *p-- = '0' - c;
	} while (val < 0);
    } else {
	do {
	    char c = val % 10;

	    val /= 10;
	    *p-- = '0' + c;
	} while (val < 0);
    }
    /* sign by 0 padding */
    if (padchar != '0') {
	*p-- = '-';
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

301

302
303
304
305
306
307
308

309
310
311
312
313
314
315
char *
TclItoAw(
    char *buf,
    int val,
    char padchar,
    unsigned short width)
{
    return _itoaw(buf, val, padchar, width);
}

static inline char *
_witoaw(
    char *buf,
    Tcl_WideInt val,
    char padchar,
    unsigned short width)
{
    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 >= 10000000000LL) {
	    Tcl_WideInt val2 = val / 10000000000LL;

	    while (width <= 9 && val2 >= wrange[width]) {
		width++;
	    }
	    width += 10;
	} else {
	    while (width <= 9 && val >= wrange[width]) {
		width++;







|



|






>
|
>







>







301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
char *
TclItoAw(
    char *buf,
    int val,
    char padchar,
    unsigned short width)
{
    return Clock_itoaw(buf, val, padchar, width);
}

static inline char *
Clock_witoaw(
    char *buf,
    Tcl_WideInt val,
    char padchar,
    unsigned short width)
{
    char *p;
    static const int wrange[] = {
	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
    };

    /* positive integer */

    if (val >= 0) {
	/* check resp. recalculate width */
	if (val >= 10000000000LL) {
	    Tcl_WideInt val2 = val / 10000000000LL;

	    while (width <= 9 && val2 >= wrange[width]) {
		width++;
	    }
	    width += 10;
	} else {
	    while (width <= 9 && val >= wrange[width]) {
		width++;
336
337
338
339
340
341
342

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359

360
361
362
363
364
365

366
367
368
369
370
371
372
    if (!width) {
	width++;
    }
    /* check resp. recalculate width (regarding sign) */
    width--;
    if (val <= -10000000000LL) {
	Tcl_WideInt val2 = val / 10000000000LL;

	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 {
	    char c = (val % 10);

	    val /= 10;
	    *p-- = '0' - c;
	} while (val < 0);
    } else {
	do {
	    char c = (val % 10);

	    val /= 10;
	    *p-- = '0' + c;
	} while (val < 0);
    }
    /* sign by 0 padding */
    if (padchar != '0') {
	*p-- = '-';







>
















|
>





|
>







357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
    if (!width) {
	width++;
    }
    /* check resp. recalculate width (regarding sign) */
    width--;
    if (val <= -10000000000LL) {
	Tcl_WideInt val2 = val / 10000000000LL;

	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 {
	    char c = val % 10;

	    val /= 10;
	    *p-- = '0' - c;
	} while (val < 0);
    } else {
	do {
	    char c = val % 10;

	    val /= 10;
	    *p-- = '0' + c;
	} while (val < 0);
    }
    /* sign by 0 padding */
    if (padchar != '0') {
	*p-- = '-';
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
 *----------------------------------------------------------------------
 */

Tcl_Obj *
ClockLocalizeFormat(
    ClockFmtScnCmdArgs *opts)
{
    ClockClientData *dataPtr = (ClockClientData *)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;







|







924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
 *----------------------------------------------------------------------
 */

Tcl_Obj *
ClockLocalizeFormat(
    ClockFmtScnCmdArgs *opts)
{
    ClockClientData *dataPtr = opts->dataPtr;
    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;
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
    DateInfo *info,
    ClockScanToken *tok,
    int *minLenPtr,
    int *maxLenPtr)
{
    int minLen = tok->map->minSize;
    int maxLen;
    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) {







|
|







1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
    DateInfo *info,
    ClockScanToken *tok,
    int *minLenPtr,
    int *maxLenPtr)
{
    int minLen = tok->map->minSize;
    int maxLen;
    const char *p = yyInput + minLen;
    const char *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) {
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
	}

	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", (char *)NULL);
	    return TCL_ERROR;
	}
	info->date.dayOfWeek = val;
	yyInput++;
	return TCL_OK;
    }







|







1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
	}

	if (val == 0) {
	    val = 7;
	}
	if (val > 7) {
	    Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
		    "day of week is greater than 7", TCL_AUTO_LENGTH));
	    Tcl_SetErrorCode(opts->interp, "CLOCK", "badDayOfWeek", (char *)NULL);
	    return TCL_ERROR;
	}
	info->date.dayOfWeek = val;
	yyInput++;
	return TCL_OK;
    }
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636

static int
ClockScnToken_LocaleERA_Proc(
    ClockFmtScnCmdArgs *opts,
    DateInfo *info,
    ClockScanToken *tok)
{
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;

    int ret, val;
    int minLen, maxLen;
    Tcl_Obj *eraObj[6];

    DetermineGreedySearchLen(info, tok, &minLen, &maxLen);








|







1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660

static int
ClockScnToken_LocaleERA_Proc(
    ClockFmtScnCmdArgs *opts,
    DateInfo *info,
    ClockScanToken *tok)
{
    ClockClientData *dataPtr = opts->dataPtr;

    int ret, val;
    int minLen, maxLen;
    Tcl_Obj *eraObj[6];

    DetermineGreedySearchLen(info, tok, &minLen, &maxLen);

1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694

    ret = ClockStrIdxTreeSearch(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_JDN_Proc(







|







1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718

    ret = ClockStrIdxTreeSearch(info, idxTree, &val, minLen, maxLen);
    if (ret != TCL_OK) {
	return ret;
    }

    if (tok->map->offs > 0) {
	*IntFieldAt(info, tok->map->offs) = --val;
    }

    return TCL_OK;
}

static int
ClockScnToken_JDN_Proc(
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
    if (*p == '+' || *p == '-') {
	p++;
    }
    s = p;
    while (p < end && isdigit(UCHAR(*p))) {
	p++;
    }
    if (_str2wideInt(&intJD, s, p, (*yyInput != '-' ? 1 : -1)) != TCL_OK) {
	return TCL_RETURN;
    }
    yyInput = p;
    if (p >= end || *p++ != '.') { /* allow pure integer JDN */
	/* by astronomical JD the seconds of day offs is 12 hours */
	if (tok->map->offs) {
	    goto done;
	}
	/* calendar JD */
	yydate.julianDay = intJD;
	return TCL_OK;
    }
    s = p;
    while (p < end && isdigit(UCHAR(*p))) {
    	fractJDDiv *= 10;
	p++;
    }
    if (_str2int(&fractJD, s, p, 1) != TCL_OK) {
	return TCL_RETURN;
    }
    yyInput = p;

  done:
    /*
     * Build a date from julian day (integer and fraction).
     * Note, astronomical JDN starts at noon in opposite to calendar julianday.
     */

    fractJD = (int)tok->map->offs /* 0 for calendar or 43200 for astro JD */
	+ (int)((Tcl_WideInt)SECONDS_PER_DAY * fractJD / fractJDDiv);
    if (fractJD > SECONDS_PER_DAY) {
	fractJD %= SECONDS_PER_DAY;
	intJD += 1;
    }
    yydate.secondOfDay = fractJD;
    yydate.julianDay = intJD;








|

















|











|







1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
    if (*p == '+' || *p == '-') {
	p++;
    }
    s = p;
    while (p < end && isdigit(UCHAR(*p))) {
	p++;
    }
    if (Clock_str2wideInt(&intJD, s, p, (*yyInput != '-' ? 1 : -1)) != TCL_OK) {
	return TCL_RETURN;
    }
    yyInput = p;
    if (p >= end || *p++ != '.') { /* allow pure integer JDN */
	/* by astronomical JD the seconds of day offs is 12 hours */
	if (tok->map->offs) {
	    goto done;
	}
	/* calendar JD */
	yydate.julianDay = intJD;
	return TCL_OK;
    }
    s = p;
    while (p < end && isdigit(UCHAR(*p))) {
    	fractJDDiv *= 10;
	p++;
    }
    if (Clock_str2int(&fractJD, s, p, 1) != TCL_OK) {
	return TCL_RETURN;
    }
    yyInput = p;

  done:
    /*
     * Build a date from julian day (integer and fraction).
     * Note, astronomical JDN starts at noon in opposite to calendar julianday.
     */

    fractJD = (int)tok->map->offs /* 0 for calendar or 43200 for astro JD */
	    + (int)((Tcl_WideInt)SECONDS_PER_DAY * fractJD / fractJDDiv);
    if (fractJD > SECONDS_PER_DAY) {
	fractJD %= SECONDS_PER_DAY;
	intJD += 1;
    }
    yydate.secondOfDay = fractJD;
    yydate.julianDay = intJD;

1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851

	/* 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;
    }








|







1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875

	/* convert using dict */
    }

    /* try to apply new time zone */
    Tcl_IncrRefCount(tzObjStor);

    opts->timezoneObj = ClockSetupTimeZone(opts->dataPtr, opts->interp,
	    tzObjStor);

    Tcl_DecrRefCount(tzObjStor);
    if (opts->timezoneObj == NULL) {
	return TCL_ERROR;
    }

1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
    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;







|
|











|







1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
    s = p;
    while (p < end && isdigit(UCHAR(*p))) {
	p++;
    }
    if (p >= end || p - s < 4) {
	return TCL_RETURN;
    }
    if (Clock_str2int(&year, s, p - 3, 1) != TCL_OK
	    || Clock_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 (Clock_str2int(&fractDay, s, p, 1) != TCL_OK) {
	return TCL_RETURN;
    }
    yyInput = p;

    /* Build a date from year and fraction. */

    yydate.year = year + RODDENBERRY;
1931
1932
1933
1934
1935
1936
1937




1938
1939
1940
1941
1942
1943
1944
	    -210866803200LL
	    + (SECONDS_PER_DAY * yydate.julianDay)
	    + (SECONDS_PER_DAY * fractDay / fractDayDiv);

    return TCL_OK;
}





static const char *ScnSTokenMapIndex = "dmbyYHMSpJjCgGVazUsntQ";
static const ClockScanTokenMap ScnSTokenMap[] = {
    /* %d %e */
    {CTOKT_INT, CLF_DAYOFMONTH, 0, 1, 2, offsetof(DateInfo, date.dayOfMonth),
	NULL, NULL},
    /* %m %N */
    {CTOKT_INT, CLF_MONTH, 0, 1, 2, offsetof(DateInfo, date.month),







>
>
>
>







1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
	    -210866803200LL
	    + (SECONDS_PER_DAY * yydate.julianDay)
	    + (SECONDS_PER_DAY * fractDay / fractDayDiv);

    return TCL_OK;
}

/*
 * Descriptors for the various fields in [clock scan].
 */

static const char *ScnSTokenMapIndex = "dmbyYHMSpJjCgGVazUsntQ";
static const ClockScanTokenMap ScnSTokenMap[] = {
    /* %d %e */
    {CTOKT_INT, CLF_DAYOFMONTH, 0, 1, 2, offsetof(DateInfo, date.dayOfMonth),
	NULL, NULL},
    /* %m %N */
    {CTOKT_INT, CLF_MONTH, 0, 1, 2, offsetof(DateInfo, date.month),
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153

	/* estimate token count by % char and format length */
	fss->scnTokC = EstimateTokenCount(p, e);

	fss->scnSpaceCount = 0;

	scnTok = tok = (ClockScanToken *)Tcl_Alloc(sizeof(*tok) * fss->scnTokC);
	memset(tok, 0, sizeof(*(tok)));
	tokCnt = 1;
	while (p < e) {
	    switch (*p) {
	    case '%': {
		const ClockScanTokenMap *scnMap = ScnSTokenMap;
		const char *mapIndex = ScnSTokenMapIndex;
		const char **aliasIndex = ScnSTokenMapAliasIndex;







|







2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181

	/* estimate token count by % char and format length */
	fss->scnTokC = EstimateTokenCount(p, e);

	fss->scnSpaceCount = 0;

	scnTok = tok = (ClockScanToken *)Tcl_Alloc(sizeof(*tok) * fss->scnTokC);
	memset(tok, 0, sizeof(*tok));
	tokCnt = 1;
	while (p < e) {
	    switch (*p) {
	    case '%': {
		const ClockScanTokenMap *scnMap = ScnSTokenMap;
		const char *mapIndex = ScnSTokenMapIndex;
		const char **aliasIndex = ScnSTokenMapAliasIndex;
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255

2256
2257
2258
2259
2260
2261
2262
2263
		    /* increase space count used in format */
		    fss->scnSpaceCount++;
		    /* next token */
		    AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *);
		    tokCnt++;
		    continue;
		}
word_tok:
	    {
		ClockScanToken *wordTok = tok;

		if (tok > scnTok && (tok-1)->map == &ScnWordTokenMap) {
		    wordTok = tok - 1;
		}
		/* new word token */
		if (wordTok == tok) {
		    wordTok->tokWord.start = p;
		    wordTok->map = &ScnWordTokenMap;
		    AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *);







|


>
|







2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
		    /* increase space count used in format */
		    fss->scnSpaceCount++;
		    /* next token */
		    AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *);
		    tokCnt++;
		    continue;
		}
	    word_tok:
	    {
		ClockScanToken *wordTok = tok;

		if (tok > scnTok && (tok - 1)->map == &ScnWordTokenMap) {
		    wordTok = tok - 1;
		}
		/* new word token */
		if (wordTok == tok) {
		    wordTok->tokWord.start = p;
		    wordTok->map = &ScnWordTokenMap;
		    AllocTokenInChain(tok, scnTok, fss->scnTokC, ClockScanToken *);
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
	    }
	    }
	}

	/* calculate end distance value for each tokens */
	if (tok > scnTok) {
	    unsigned endDist = 0;
	    ClockScanToken *prevTok = tok-1;

	    while (prevTok >= scnTok) {
		prevTok->endDistance = endDist;
		if (prevTok->map->type != CTOKT_WORD) {
		    endDist += prevTok->map->minSize;
		} else {
		    endDist += prevTok->tokWord.end - prevTok->tokWord.start;







|







2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
	    }
	    }
	}

	/* calculate end distance value for each tokens */
	if (tok > scnTok) {
	    unsigned endDist = 0;
	    ClockScanToken *prevTok = tok - 1;

	    while (prevTok >= scnTok) {
		prevTok->endDistance = endDist;
		if (prevTok->map->type != CTOKT_WORD) {
		    endDist += prevTok->map->minSize;
		} else {
		    endDist += prevTok->tokWord.end - prevTok->tokWord.start;
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
 */
int
ClockScan(
    DateInfo *info,		/* Date fields used for parsing & converting */
    Tcl_Obj *strObj,		/* String containing the time to scan */
    ClockFmtScnCmdArgs *opts)	/* Command options */
{
    ClockClientData *dataPtr = (ClockClientData *)opts->clientData;
    ClockFmtScnStorage *fss;
    ClockScanToken *tok;
    const ClockScanTokenMap *map;
    const char *p, *x, *end;
    unsigned short flags = 0;
    int ret = TCL_ERROR;








|







2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
 */
int
ClockScan(
    DateInfo *info,		/* Date fields used for parsing & converting */
    Tcl_Obj *strObj,		/* String containing the time to scan */
    ClockFmtScnCmdArgs *opts)	/* Command options */
{
    ClockClientData *dataPtr = opts->dataPtr;
    ClockFmtScnStorage *fss;
    ClockScanToken *tok;
    const ClockScanTokenMap *map;
    const char *p, *x, *end;
    unsigned short flags = 0;
    int ret = TCL_ERROR;

2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
	    }
	    /* string 2 number, put number into info structure by offset */
	    if (map->offs) {
		p = yyInput;
		x = p + size;
		if (map->type == CTOKT_INT) {
		    if (size <= 10) {
			_str2int_no((int *)(((char *)info) + map->offs),
				p, x, sign);
		    } else {
			if (_str2int((int *)(((char *)info) + map->offs),
				p, x, sign) != TCL_OK) {
			    goto overflow;
			}
		    }
		    p = x;
		} else {
		    if (size <= 18) {
			_str2wideInt_no((Tcl_WideInt *)(((char *)info) + map->offs),
				p, x, sign);
		    } 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;
	}







|

|
<
|
|
<




|
|
|
<
|
|
<







2447
2448
2449
2450
2451
2452
2453
2454
2455
2456

2457
2458

2459
2460
2461
2462
2463
2464
2465

2466
2467

2468
2469
2470
2471
2472
2473
2474
	    }
	    /* string 2 number, put number into info structure by offset */
	    if (map->offs) {
		p = yyInput;
		x = p + size;
		if (map->type == CTOKT_INT) {
		    if (size <= 10) {
			Clock_str2int_no(IntFieldAt(info, map->offs),
				p, x, sign);
		    } else if (Clock_str2int(

			    IntFieldAt(info, map->offs), p, x, sign) != TCL_OK) {
			goto overflow;

		    }
		    p = x;
		} else {
		    if (size <= 18) {
			Clock_str2wideInt_no(
				WideFieldAt(info, map->offs), p, x, sign);
		    } else if (Clock_str2wideInt(

			    WideFieldAt(info, map->offs), p, x, sign) != TCL_OK) {
			goto overflow;

		    }
		    p = x;
		}
		flags = (flags & ~map->clearFlags) | map->flags;
	    }
	    break;
	}
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
  done:
    return ret;

    /* Error case reporting. */

  overflow:
    Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
	    "integer value too large to represent", -1));
    Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", (char *)NULL);
    goto done;

  not_match:
#if 1
    Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
	    "input string does not match supplied format", -1));
#else
    /* to debug where exactly scan breaks */
    Tcl_SetObjResult(opts->interp, Tcl_ObjPrintf(
	    "input string \"%s\" does not match supplied format \"%s\","
	    " locale \"%s\" - token \"%s\"",
	    info->dateStart, HashEntry4FmtScn(fss)->key.string,
	    TclGetString(opts->localeObj),







|






|







2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
  done:
    return ret;

    /* Error case reporting. */

  overflow:
    Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
	    "integer value too large to represent", TCL_AUTO_LENGTH));
    Tcl_SetErrorCode(opts->interp, "CLOCK", "dateTooLarge", (char *)NULL);
    goto done;

  not_match:
#if 1
    Tcl_SetObjResult(opts->interp, Tcl_NewStringObj(
	    "input string does not match supplied format", TCL_AUTO_LENGTH));
#else
    /* to debug where exactly scan breaks */
    Tcl_SetObjResult(opts->interp, Tcl_ObjPrintf(
	    "input string \"%s\" does not match supplied format \"%s\","
	    " locale \"%s\" - token \"%s\"",
	    info->dateStart, HashEntry4FmtScn(fss)->key.string,
	    TclGetString(opts->localeObj),
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793

    /* 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.secondOfDay / (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(
    TCL_UNUSED(ClockFmtScnCmdArgs *),
    DateFormat *dateFmt,
    ClockFormatToken *tok,







|

|







|







2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818

    /* 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 = Clock_itoaw(dateFmt->output,
	    dateFmt->date.year - RODDENBERRY, '0', 2);
    dateFmt->output = Clock_itoaw(dateFmt->output,
	    fractYear, '0', 3);
    *dateFmt->output++ = '.';
    /* be sure positive after decimal point (note: clock-value can be negative) */
    v = dateFmt->date.secondOfDay / (SECONDS_PER_DAY / 10);
    if (v < 0) {
	v = 10 + v;
    }
    dateFmt->output = Clock_itoaw(dateFmt->output, v, '0', 1);
    return TCL_OK;
}
static int
ClockFmtToken_WeekOfYear_Proc(
    TCL_UNUSED(ClockFmtScnCmdArgs *),
    DateFormat *dateFmt,
    ClockFormatToken *tok,
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
	fractJD = SECONDS_PER_DAY - fractJD;
    }

    /* 21 is max width of (negative) wide-int (rather smaller, but anyway a time fraction below) */
    if (FrmResultAllocate(dateFmt, 21) != TCL_OK) {
	return TCL_ERROR;
    }
    dateFmt->output = _witoaw(dateFmt->output, intJD, '0', 1);
    /* simplest cases .0 and .5 */
    if (!fractJD || fractJD == (SECONDS_PER_DAY / 2)) {
	/* point + 0 or 5 */
	if (FrmResultAllocate(dateFmt, 1 + 1) != TCL_OK) {
	    return TCL_ERROR;
	}
	*dateFmt->output++ = '.';







|







2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
	fractJD = SECONDS_PER_DAY - fractJD;
    }

    /* 21 is max width of (negative) wide-int (rather smaller, but anyway a time fraction below) */
    if (FrmResultAllocate(dateFmt, 21) != TCL_OK) {
	return TCL_ERROR;
    }
    dateFmt->output = Clock_witoaw(dateFmt->output, intJD, '0', 1);
    /* simplest cases .0 and .5 */
    if (!fractJD || fractJD == (SECONDS_PER_DAY / 2)) {
	/* point + 0 or 5 */
	if (FrmResultAllocate(dateFmt, 1 + 1) != TCL_OK) {
	    return TCL_ERROR;
	}
	*dateFmt->output++ = '.';
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
		(double)fractJD * JDN_MAX_PRECBOUND / SECONDS_PER_DAY + 0.5);

	/* point + integer (as time fraction after floating point) */
	if (FrmResultAllocate(dateFmt, 1 + JDN_MAX_PRECISION) != TCL_OK) {
	    return TCL_ERROR;
	}
	*dateFmt->output++ = '.';
	p = _itoaw(dateFmt->output, fractJD, '0', JDN_MAX_PRECISION);

	/* remove trailing zero's */
	dateFmt->output++;
	while (p > dateFmt->output && *(p-1) == '0') {
	    p--;
	}
	*p = '\0';
	dateFmt->output = p;
    }
    return TCL_OK;
}







|



|







2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
		(double)fractJD * JDN_MAX_PRECBOUND / SECONDS_PER_DAY + 0.5);

	/* point + integer (as time fraction after floating point) */
	if (FrmResultAllocate(dateFmt, 1 + JDN_MAX_PRECISION) != TCL_OK) {
	    return TCL_ERROR;
	}
	*dateFmt->output++ = '.';
	p = Clock_itoaw(dateFmt->output, fractJD, '0', JDN_MAX_PRECISION);

	/* remove trailing zero's */
	dateFmt->output++;
	while (p > dateFmt->output && p[-1] == '0') {
	    p--;
	}
	*p = '\0';
	dateFmt->output = p;
    }
    return TCL_OK;
}
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
	    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;
	Tcl_Size 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 = TclGetStringFromObj(objPtr, &len);
	if (FrmResultAllocate(dateFmt, len) != TCL_OK) {







|

|


|







|







2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
	    z = -z;
	    sign = '-';
	}
	if (FrmResultAllocate(dateFmt, 7) != TCL_OK) {
	    return TCL_ERROR;
	}
	*dateFmt->output++ = sign;
	dateFmt->output = Clock_itoaw(dateFmt->output, z / 3600, '0', 2);
	z %= 3600;
	dateFmt->output = Clock_itoaw(dateFmt->output, z / 60, '0', 2);
	z %= 60;
	if (z != 0) {
	    dateFmt->output = Clock_itoaw(dateFmt->output, z, '0', 2);
	}
    } else {
	Tcl_Obj * objPtr;
	const char *s;
	Tcl_Size len;

	/* convert seconds to local seconds to obtain tzName object */
	if (ConvertUTCToLocal(opts->dataPtr, opts->interp,
		&dateFmt->date, opts->timezoneObj,
		GREGORIAN_CHANGE_DATE) != TCL_OK) {
	    return TCL_ERROR;
	}
	objPtr = dateFmt->date.tzName;
	s = TclGetStringFromObj(objPtr, &len);
	if (FrmResultAllocate(dateFmt, len) != TCL_OK) {
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
    /* 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;
	Tcl_Size len;

	if (*tok->tokWord.start == 'C') {	/* %EC */







|
<


|
<







3008
3009
3010
3011
3012
3013
3014
3015

3016
3017
3018

3019
3020
3021
3022
3023
3024
3025
    /* 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 = Clock_itoaw(dateFmt->output, *val, '0', 2);

	} else {				/* %Ey */
	    *val = dateFmt->date.year % 100;
	    dateFmt->output = Clock_itoaw(dateFmt->output, *val, '0', 2);

	}
    } else {
	Tcl_Obj *objPtr;
	const char *s;
	Tcl_Size len;

	if (*tok->tokWord.start == 'C') {	/* %EC */
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045




3046
3047
3048
3049
3050
3051
3052
		    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 = TclGetStringFromObj(objPtr, &len);
	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 const ClockFormatTokenMap FmtSTokenMap[] = {
    /* %d */
    {CTOKT_INT, "0", 2, 0, 0, 0, offsetof(DateFormat, date.dayOfMonth), NULL, NULL},
    /* %e */
    {CTOKT_INT, " ", 2, 0, 0, 0, offsetof(DateFormat, date.dayOfMonth), NULL, NULL},







|
<













>
>
>
>







3047
3048
3049
3050
3051
3052
3053
3054

3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
		    return TCL_ERROR;
		}
	    } else {
		/* year as integer */
		if (FrmResultAllocate(dateFmt, 11) != TCL_OK) {
		    return TCL_ERROR;
		}
		dateFmt->output = Clock_itoaw(dateFmt->output, *val, '0', 2);

		return TCL_OK;
	    }
	}
	s = TclGetStringFromObj(objPtr, &len);
	if (FrmResultAllocate(dateFmt, len) != TCL_OK) {
	    return TCL_ERROR;
	}
	memcpy(dateFmt->output, s, len + 1);
	dateFmt->output += len;
    }
    return TCL_OK;
}

/*
 * Descriptors for the various fields in [clock format].
 */

static const char *FmtSTokenMapIndex =
    "demNbByYCHMSIklpaAuwUVzgGjJsntQ";
static const ClockFormatTokenMap FmtSTokenMap[] = {
    /* %d */
    {CTOKT_INT, "0", 2, 0, 0, 0, offsetof(DateFormat, date.dayOfMonth), NULL, NULL},
    /* %e */
    {CTOKT_INT, " ", 2, 0, 0, 0, offsetof(DateFormat, date.dayOfMonth), NULL, NULL},
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
	e = p = HashEntry4FmtScn(fss)->key.string;
	e += strlen(p);

	/* estimate token count by % char and format length */
	fss->fmtTokC = EstimateTokenCount(p, e);

	fmtTok = tok = (ClockFormatToken *)Tcl_Alloc(sizeof(*tok) * fss->fmtTokC);
	memset(tok, 0, sizeof(*(tok)));
	tokCnt = 1;
	while (p < e) {
	    switch (*p) {
	    case '%': {
		const ClockFormatTokenMap *fmtMap = FmtSTokenMap;
		const char *mapIndex =	FmtSTokenMapIndex;
		const char **aliasIndex = FmtSTokenMapAliasIndex;







|







3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
	e = p = HashEntry4FmtScn(fss)->key.string;
	e += strlen(p);

	/* estimate token count by % char and format length */
	fss->fmtTokC = EstimateTokenCount(p, e);

	fmtTok = tok = (ClockFormatToken *)Tcl_Alloc(sizeof(*tok) * fss->fmtTokC);
	memset(tok, 0, sizeof(*tok));
	tokCnt = 1;
	while (p < e) {
	    switch (*p) {
	    case '%': {
		const ClockFormatTokenMap *fmtMap = FmtSTokenMap;
		const char *mapIndex =	FmtSTokenMapIndex;
		const char **aliasIndex = FmtSTokenMapAliasIndex;
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
		p++;
		continue;
	    }
	    default:
	    word_tok: {
		ClockFormatToken *wordTok = tok;

		if (tok > fmtTok && (tok-1)->map == &FmtWordTokenMap) {
		    wordTok = tok-1;
		}
		if (wordTok == tok) {
		    wordTok->tokWord.start = p;
		    wordTok->map = &FmtWordTokenMap;
		    AllocTokenInChain(tok, fmtTok, fss->fmtTokC, ClockFormatToken *);
		    tokCnt++;
		}







|
|







3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
		p++;
		continue;
	    }
	    default:
	    word_tok: {
		ClockFormatToken *wordTok = tok;

		if (tok > fmtTok && (tok - 1)->map == &FmtWordTokenMap) {
		    wordTok = tok - 1;
		}
		if (wordTok == tok) {
		    wordTok->tokWord.start = p;
		    wordTok->map = &FmtWordTokenMap;
		    AllocTokenInChain(tok, fmtTok, fss->fmtTokC, ClockFormatToken *);
		    tokCnt++;
		}
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
    *dateFmt->output = '\0';

    /* do format each token */
    for (; tok->map != NULL; tok++) {
	map = tok->map;
	switch (map->type) {
	case CTOKT_INT: {
	    int val = (int)*(int *)(((char *)dateFmt) + map->offs);

	    if (map->fmtproc == NULL) {
		if (map->flags & CLFMT_DECR) {
		    val--;
		}
		if (map->flags & CLFMT_INCR) {
		    val++;







|







3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
    *dateFmt->output = '\0';

    /* do format each token */
    for (; tok->map != NULL; tok++) {
	map = tok->map;
	switch (map->type) {
	case CTOKT_INT: {
	    int val = *IntFieldAt(dateFmt, map->offs);

	    if (map->fmtproc == NULL) {
		if (map->flags & CLFMT_DECR) {
		    val--;
		}
		if (map->flags & CLFMT_INCR) {
		    val++;
3394
3395
3396
3397
3398
3399
3400

3401
3402
3403
3404
3405
3406
3407

3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
		}
	    }
	    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 CTOKT_WIDE: {
	    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) {







>
|






>

















|





|







3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
		}
	    }
	    if (!(map->flags & CLFMT_LOCALE_INDX)) {
		if (FrmResultAllocate(dateFmt, 11) != TCL_OK) {
		    goto error;
		}
		if (map->width) {
		    dateFmt->output = Clock_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 CTOKT_WIDE: {
	    Tcl_WideInt val = *WideFieldAt(dateFmt, map->offs);

	    if (FrmResultAllocate(dateFmt, 21) != TCL_OK) {
		goto error;
	    }
	    if (map->width) {
		dateFmt->output = Clock_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) {

Changes to generic/tclDate.h.

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
















67
68
69
70
71
72
73
74
75
76
#define JDAY_1_JAN_1_CE_GREGORIAN	1721426
#define ONE_CENTURY_GREGORIAN		36524	/* days */
#define FOUR_YEARS			1461	/* days */
#define ONE_YEAR			365	/* days */

#define RODDENBERRY			1946	/* Another epoch (Hi, Jeff!) */


#define CLF_OPTIONAL	       (1 << 0) /* token is non mandatory */
#define CLF_POSIXSEC	       (1 << 1)
#define CLF_LOCALSEC	       (1 << 2)
#define CLF_JULIANDAY	       (1 << 3)
#define CLF_TIME	       (1 << 4)
#define CLF_ZONE	       (1 << 5)
#define CLF_CENTURY	       (1 << 6)
#define CLF_DAYOFMONTH	       (1 << 7)
#define CLF_DAYOFYEAR	       (1 << 8)
#define CLF_MONTH	       (1 << 9)
#define CLF_YEAR	       (1 << 10)
#define CLF_DAYOFWEEK	       (1 << 11)
#define CLF_ISO8601YEAR	       (1 << 12)
#define CLF_ISO8601WEAK	       (1 << 13)
#define CLF_ISO8601CENTURY     (1 << 14)

#define CLF_SIGNED	       (1 << 15)

/* extra flags used outside of scan/format-tokens too (int, not a short int) */
#define CLF_RELCONV	       (1 << 17)
#define CLF_ORDINALMONTH       (1 << 18)

/* On demand (lazy) assemble flags */
#define CLF_ASSEMBLE_DATE      (1 << 28) /* assemble year, month, etc. using julianDay */
#define CLF_ASSEMBLE_JULIANDAY (1 << 29) /* assemble julianDay using year, month, etc. */
#define CLF_ASSEMBLE_SECONDS   (1 << 30) /* assemble localSeconds (and seconds at end) */

#define CLF_HAVEDATE	       (CLF_DAYOFMONTH|CLF_MONTH|CLF_YEAR)
#define CLF_DATE	       (CLF_JULIANDAY | CLF_DAYOFMONTH | CLF_DAYOFYEAR | \
				CLF_MONTH | CLF_YEAR | CLF_ISO8601YEAR | \
				CLF_DAYOFWEEK | CLF_ISO8601WEAK)

















#define TCL_MIN_SECONDS			-0x00F0000000000000LL
#define TCL_MAX_SECONDS			 0x00F0000000000000LL
#define TCL_INV_SECONDS			(TCL_MIN_SECONDS-1)

/*
 * Enumeration of the string literals used in [clock]
 */

typedef enum ClockLiteral {
    LIT__NIL,







|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|

<
<
<
|
<
<
<
<

|
|
|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52



53




54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#define JDAY_1_JAN_1_CE_GREGORIAN	1721426
#define ONE_CENTURY_GREGORIAN		36524	/* days */
#define FOUR_YEARS			1461	/* days */
#define ONE_YEAR			365	/* days */

#define RODDENBERRY			1946	/* Another epoch (Hi, Jeff!) */

enum DateInfoFlags {
    CLF_OPTIONAL = 1 << 0,	/* token is non mandatory */
    CLF_POSIXSEC = 1 << 1,
    CLF_LOCALSEC = 1 << 2,
    CLF_JULIANDAY = 1 << 3,
    CLF_TIME = 1 << 4,
    CLF_ZONE = 1 << 5,
    CLF_CENTURY = 1 << 6,
    CLF_DAYOFMONTH = 1 << 7,
    CLF_DAYOFYEAR = 1 << 8,
    CLF_MONTH = 1 << 9,
    CLF_YEAR = 1 << 10,
    CLF_DAYOFWEEK = 1 << 11,
    CLF_ISO8601YEAR = 1 << 12,
    CLF_ISO8601WEAK = 1 << 13,
    CLF_ISO8601CENTURY = 1 << 14,

    CLF_SIGNED = 1 << 15,




    /* Compounds */





    CLF_HAVEDATE = (CLF_DAYOFMONTH | CLF_MONTH | CLF_YEAR),
    CLF_DATE = (CLF_JULIANDAY | CLF_DAYOFMONTH | CLF_DAYOFYEAR
	    | CLF_MONTH | CLF_YEAR | CLF_ISO8601YEAR
	    | CLF_DAYOFWEEK | CLF_ISO8601WEAK),

    /*
     * Extra flags used outside of scan/format-tokens too (int, not a short).
     */

    CLF_RELCONV = 1 << 17,
    CLF_ORDINALMONTH = 1 << 18,

    /* On demand (lazy) assemble flags */

    CLF_ASSEMBLE_DATE = 1 << 28,/* assemble year, month, etc. using julianDay */
    CLF_ASSEMBLE_JULIANDAY = 1 << 29,
				/* assemble julianDay using year, month, etc. */
    CLF_ASSEMBLE_SECONDS = 1 << 30
				/* assemble localSeconds (and seconds at end) */
};

#define TCL_MIN_SECONDS		-0x00F0000000000000LL
#define TCL_MAX_SECONDS		 0x00F0000000000000LL
#define TCL_INV_SECONDS		(TCL_MIN_SECONDS - 1)

/*
 * Enumeration of the string literals used in [clock]
 */

typedef enum ClockLiteral {
    LIT__NIL,
144
145
146
147
148
149
150

151

152
153
154
155
156
157
158
159
160
161
    pref "LOCALE_NUMERALS", \
}

/*
 * Structure containing the fields used in [clock format] and [clock scan]
 */


#define CLF_CTZ		(1 << 4)


typedef struct TclDateFields {

    /* Cacheable fields:	 */

    Tcl_WideInt seconds;	/* Time expressed in seconds from the Posix
				 * epoch */
    Tcl_WideInt localSeconds;	/* Local time expressed in nominal seconds
				 * from the Posix epoch */
    int tzOffset;		/* Time zone offset in seconds east of







>
|
>


<







153
154
155
156
157
158
159
160
161
162
163
164

165
166
167
168
169
170
171
    pref "LOCALE_NUMERALS", \
}

/*
 * Structure containing the fields used in [clock format] and [clock scan]
 */

enum TclDateFieldsFlags {
    CLF_CTZ = (1 << 4)
};

typedef struct TclDateFields {

    /* Cacheable fields:	 */

    Tcl_WideInt seconds;	/* Time expressed in seconds from the Posix
				 * epoch */
    Tcl_WideInt localSeconds;	/* Local time expressed in nominal seconds
				 * from the Posix epoch */
    int tzOffset;		/* Time zone offset in seconds east of
266
267
268
269
270
271
272

273
274
275
276
277
278

279


280
281
282
283
284
285
286
287
288
    memset(info, 0, sizeof(DateInfo));
}

/*
 * Structure containing the command arguments supplied to [clock format] and [clock scan]
 */


#define CLF_VALIDATE_S1	(1 << 0)
#define CLF_VALIDATE_S2	(1 << 1)
#define CLF_VALIDATE	(CLF_VALIDATE_S1|CLF_VALIDATE_S2)
#define CLF_EXTENDED	(1 << 4)
#define CLF_STRICT	(1 << 8)
#define CLF_LOCALE_USED (1 << 15)




typedef struct ClockFmtScnCmdArgs {
    void *clientData;		/* Opaque pointer to literal pool, etc. */
    Tcl_Interp *interp;		/* Tcl interpreter */
    Tcl_Obj *formatObj;		/* Format */
    Tcl_Obj *localeObj;		/* Name of the locale where the time will be expressed. */
    Tcl_Obj *timezoneObj;	/* Default time zone in which the time will be expressed */
    Tcl_Obj *baseObj;		/* Base (scan and add) or clockValue (format) */
    int flags;			/* Flags control scanning */
    Tcl_Obj *mcDictObj;		/* Current dictionary of tcl::clock package for given localeObj*/







>
|
|
|
|
|
|
>

>
>

|







276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
    memset(info, 0, sizeof(DateInfo));
}

/*
 * Structure containing the command arguments supplied to [clock format] and [clock scan]
 */

enum ClockFmtScnCmdArgsFlags {
    CLF_VALIDATE_S1 = (1 << 0),
    CLF_VALIDATE_S2 = (1 << 1),
    CLF_VALIDATE = (CLF_VALIDATE_S1|CLF_VALIDATE_S2),
    CLF_EXTENDED = (1 << 4),
    CLF_STRICT = (1 << 8),
    CLF_LOCALE_USED = (1 << 15)
};

typedef struct ClockClientData ClockClientData;

typedef struct ClockFmtScnCmdArgs {
    ClockClientData *dataPtr;	/* Pointer to literal pool, etc. */
    Tcl_Interp *interp;		/* Tcl interpreter */
    Tcl_Obj *formatObj;		/* Format */
    Tcl_Obj *localeObj;		/* Name of the locale where the time will be expressed. */
    Tcl_Obj *timezoneObj;	/* Default time zone in which the time will be expressed */
    Tcl_Obj *baseObj;		/* Base (scan and add) or clockValue (format) */
    int flags;			/* Flags control scanning */
    Tcl_Obj *mcDictObj;		/* Current dictionary of tcl::clock package for given localeObj*/
418
419
420
421
422
423
424

425
426
427
428

429
430
431
432
433
434
435
    char *resMem;
    char *resEnd;
    char *output;
    TclDateFields date;
    Tcl_Obj *localeEra;
} DateFormat;


#define CLFMT_INCR	    (1 << 3)
#define CLFMT_DECR	    (1 << 4)
#define CLFMT_CALC	    (1 << 5)
#define CLFMT_LOCALE_INDX   (1 << 8)


typedef struct ClockFormatToken ClockFormatToken;

typedef int ClockFormatTokenProc(
	ClockFmtScnCmdArgs *opts,
	DateFormat *dateFmt,
	ClockFormatToken *tok,







>
|
|
|
|
>







432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
    char *resMem;
    char *resEnd;
    char *output;
    TclDateFields date;
    Tcl_Obj *localeEra;
} DateFormat;

enum ClockFormatTokenMapFlags {
    CLFMT_INCR = (1 << 3),
    CLFMT_DECR = (1 << 4),
    CLFMT_CALC = (1 << 5),
    CLFMT_LOCALE_INDX = (1 << 8)
};

typedef struct ClockFormatToken ClockFormatToken;

typedef int ClockFormatTokenProc(
	ClockFmtScnCmdArgs *opts,
	DateFormat *dateFmt,
	ClockFormatToken *tok,
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
MODULE_SCOPE int	IsGregorianLeapYear(TclDateFields *);
MODULE_SCOPE void	GetJulianDayFromEraYearWeekDay(
			    TclDateFields *fields, int changeover);
MODULE_SCOPE void	GetJulianDayFromEraYearMonthDay(
			    TclDateFields *fields, int changeover);
MODULE_SCOPE void	GetJulianDayFromEraYearDay(
			    TclDateFields *fields, int changeover);
MODULE_SCOPE int	ConvertUTCToLocal(void *clientData, Tcl_Interp *,
			    TclDateFields *, Tcl_Obj *timezoneObj, int);
MODULE_SCOPE Tcl_Obj *	LookupLastTransition(Tcl_Interp *, Tcl_WideInt,
			    Tcl_Size, Tcl_Obj *const *, Tcl_WideInt *rangesVal);
MODULE_SCOPE int	TclClockFreeScan(Tcl_Interp *interp, DateInfo *info);

/* tclClock.c module declarations */

MODULE_SCOPE Tcl_Obj *	ClockSetupTimeZone(void *clientData,
			    Tcl_Interp *interp, Tcl_Obj *timezoneObj);
MODULE_SCOPE Tcl_Obj *	ClockMCDict(ClockFmtScnCmdArgs *opts);
MODULE_SCOPE Tcl_Obj *	ClockMCGet(ClockFmtScnCmdArgs *opts, int mcKey);
MODULE_SCOPE Tcl_Obj *	ClockMCGetIdx(ClockFmtScnCmdArgs *opts, int mcKey);
MODULE_SCOPE int	ClockMCSetIdx(ClockFmtScnCmdArgs *opts, int mcKey,
			    Tcl_Obj *valObj);








|







|







525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
MODULE_SCOPE int	IsGregorianLeapYear(TclDateFields *);
MODULE_SCOPE void	GetJulianDayFromEraYearWeekDay(
			    TclDateFields *fields, int changeover);
MODULE_SCOPE void	GetJulianDayFromEraYearMonthDay(
			    TclDateFields *fields, int changeover);
MODULE_SCOPE void	GetJulianDayFromEraYearDay(
			    TclDateFields *fields, int changeover);
MODULE_SCOPE int	ConvertUTCToLocal(ClockClientData *dataPtr, Tcl_Interp *,
			    TclDateFields *, Tcl_Obj *timezoneObj, int);
MODULE_SCOPE Tcl_Obj *	LookupLastTransition(Tcl_Interp *, Tcl_WideInt,
			    Tcl_Size, Tcl_Obj *const *, Tcl_WideInt *rangesVal);
MODULE_SCOPE int	TclClockFreeScan(Tcl_Interp *interp, DateInfo *info);

/* tclClock.c module declarations */

MODULE_SCOPE Tcl_Obj *	ClockSetupTimeZone(ClockClientData *dataPtr,
			    Tcl_Interp *interp, Tcl_Obj *timezoneObj);
MODULE_SCOPE Tcl_Obj *	ClockMCDict(ClockFmtScnCmdArgs *opts);
MODULE_SCOPE Tcl_Obj *	ClockMCGet(ClockFmtScnCmdArgs *opts, int mcKey);
MODULE_SCOPE Tcl_Obj *	ClockMCGetIdx(ClockFmtScnCmdArgs *opts, int mcKey);
MODULE_SCOPE int	ClockMCSetIdx(ClockFmtScnCmdArgs *opts, int mcKey,
			    Tcl_Obj *valObj);

Changes to generic/tclStrIdxTree.c.

459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    Tcl_Interp *interp,
    TclStrIdx  *tree,
    int offs)
{
    Tcl_Obj *obj[2];
    const char *s;

    TclInitObjRef(obj[0], Tcl_NewStringObj("::puts", -1));
    while (tree != NULL) {
	s = TclGetString(tree->key) + offs;
	TclInitObjRef(obj[1], Tcl_ObjPrintf("%*s%.*s\t:%d",
		offs, "", tree->length - offs, s, tree->value));
	Tcl_PutsObjCmd(NULL, interp, 2, obj);
	TclUnsetObjRef(obj[1]);
	if (tree->childTree.firstPtr != NULL) {







|







459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    Tcl_Interp *interp,
    TclStrIdx  *tree,
    int offs)
{
    Tcl_Obj *obj[2];
    const char *s;

    TclInitObjRef(obj[0], Tcl_NewStringObj("::puts", TCL_AUTO_LENGTH));
    while (tree != NULL) {
	s = TclGetString(tree->key) + offs;
	TclInitObjRef(obj[1], Tcl_ObjPrintf("%*s%.*s\t:%d",
		offs, "", tree->length - offs, s, tree->value));
	Tcl_PutsObjCmd(NULL, interp, 2, obj);
	TclUnsetObjRef(obj[1]);
	if (tree->childTree.firstPtr != NULL) {