Bug Summary

File:d/tcltime.c
Warning:line 485, column 9
Duplicate code detected
Note:line 486, column 9
Similar code here

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tcltime.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c tcltime.c
1/*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.1 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://mozilla.org/.
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9 * the License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Original Code is AOLserver Code and related documentation
13 * distributed by AOL.
14 *
15 * The Initial Developer of the Original Code is America Online,
16 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
17 * Inc. All Rights Reserved.
18 *
19 * Alternatively, the contents of this file may be used under the terms
20 * of the GNU General Public License (the "GPL"), in which case the
21 * provisions of GPL are applicable instead of those above. If you wish
22 * to allow use of your version of this file only under the terms of the
23 * GPL and not to allow others to use your version of this file under the
24 * License, indicate your decision by deleting the provisions above and
25 * replace them with the notice and other provisions required by the GPL.
26 * If you do not delete the provisions above, a recipient may use your
27 * version of this file under either the License or the GPL.
28 */
29
30
31/*
32 * tcltime.c --
33 *
34 * A Tcl interface to the Ns_Time microsecond resolution time
35 * routines and some time formatting commands.
36 */
37
38#include "nsd.h"
39
40/*
41 * math.h is only needed for round()
42 *
43 * But older Microsoft Windows compilers do not include round() in math.h! So
44 * for them, use the hack below:
45 */
46#if defined(_MSC_VER) && _MSC_VER <= 1600
47static double round(double val) { return floor(val + 0.5); }
48#else
49#include <math.h>
50#endif
51
52/*
53 * Local functions defined in this file
54 */
55
56static void SetTimeInternalRep(Tcl_Obj *objPtr, const Ns_Time *timePtr)
57 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
58
59static int SetTimeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
60 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
61
62static void UpdateStringOfTime(Tcl_Obj *objPtr)
63 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
64
65static int TmObjCmd(ClientData isGmt, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
66 NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
67
68static int GetTimeFromString(Tcl_Interp *interp, const char *str, char separator, Ns_Time *tPtr)
69 NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4)));
70
71static void DblValueToNstime(Ns_Time *timePtr, double dblValue)
72 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
73
74static double ParseTimeUnit(const char *str)
75 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
76
77/*
78 * Local variables defined in this file.
79 */
80
81static const Tcl_ObjType timeType = {
82 "ns:time",
83 NULL((void*)0),
84 NULL((void*)0),
85 UpdateStringOfTime,
86 SetTimeFromAny
87};
88
89static const Tcl_ObjType *intTypePtr;
90static Ns_ObjvValueRange poslongRange0 = {0, LONG_MAX9223372036854775807L};
91static Ns_ObjvTimeRange nonnegTimeRange = {{0, 0}, {LONG_MAX9223372036854775807L, 0}};
92
93
94/*
95 *----------------------------------------------------------------------
96 *
97 * NsTclInitTimeType --
98 *
99 * Initialize Ns_Time Tcl_Obj type.
100 *
101 * Results:
102 * None.
103 *
104 * Side effects:
105 * None.
106 *
107 *----------------------------------------------------------------------
108 */
109
110void
111NsTclInitTimeType(void)
112{
113#ifndef _WIN32
114 Tcl_Obj obj;
115 if (sizeof(obj.internalRep) < sizeof(Ns_Time)) {
116 Tcl_Panic("NsTclInitObjs: sizeof(obj.internalRep) < sizeof(Ns_Time)");
117 }
118#endif
119 intTypePtr = Tcl_GetObjType("int");
120 if (intTypePtr == NULL((void*)0)) {
121 Tcl_Panic("NsTclInitObjs: no int type");
122 }
123 Tcl_RegisterObjType(&timeType);
124}
125
126
127/*
128 *----------------------------------------------------------------------
129 *
130 * Ns_TclNewTimeObj --
131 *
132 * Creates new time object.
133 *
134 * Results:
135 * Pointer to new time object.
136 *
137 * Side effects:
138 * None.
139 *
140 *----------------------------------------------------------------------
141 */
142
143Tcl_Obj *
144Ns_TclNewTimeObj(const Ns_Time *timePtr)
145{
146 Tcl_Obj *objPtr = Tcl_NewObj();
147
148 NS_NONNULL_ASSERT(timePtr != NULL)((void) (0));
149
150 Tcl_InvalidateStringRep(objPtr);
151 SetTimeInternalRep(objPtr, timePtr);
152
153 return objPtr;
154}
155
156
157/*
158 *----------------------------------------------------------------------
159 *
160 * Ns_TclSetTimeObj --
161 *
162 * Set a Tcl_Obj to an Ns_Time object.
163 *
164 * Results:
165 * None.
166 *
167 * Side effects:
168 * String rep is invalidated and internal rep is set.
169 *
170 *----------------------------------------------------------------------
171 */
172
173void
174Ns_TclSetTimeObj(Tcl_Obj *objPtr, const Ns_Time *timePtr)
175{
176
177 NS_NONNULL_ASSERT(timePtr != NULL)((void) (0));
178 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
179
180 if (Tcl_IsShared(objPtr)((objPtr)->refCount > 1)) {
181 Tcl_Panic("Ns_TclSetTimeObj called with shared object");
182 }
183 Tcl_InvalidateStringRep(objPtr);
184 SetTimeInternalRep(objPtr, timePtr);
185}
186
187
188/*
189 *----------------------------------------------------------------------
190 *
191 * Ns_TclGetTimeFromObj --
192 *
193 * Return the internal value of an Ns_Time Tcl_Obj. If the value is
194 * specified as integer, the value is interpreted as seconds.
195 *
196 * Results:
197 * TCL_OK or TCL_ERROR if not a valid Ns_Time.
198 *
199 * Side effects:
200 * Object is converted to Ns_Time type if necessary.
201 *
202 *----------------------------------------------------------------------
203 */
204int
205Ns_TclGetTimeFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Ns_Time *timePtr)
206{
207 long sec;
208 int result = TCL_OK0;
209
210 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
211 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
212 NS_NONNULL_ASSERT(timePtr != NULL)((void) (0));
213
214 if (objPtr->typePtr == intTypePtr) {
215 if (likely(Tcl_GetLongFromObj(interp, objPtr, &sec) == TCL_OK)(__builtin_expect((Tcl_GetLongFromObj(interp, objPtr, &sec
) == 0), 1))
) {
216 timePtr->sec = sec;
217 timePtr->usec = 0;
218 } else {
219 result = TCL_ERROR1;
220 }
221 } else {
222 if (objPtr->typePtr != &timeType) {
223 if (unlikely(Tcl_ConvertToType(interp, objPtr, &timeType) != TCL_OK)(__builtin_expect((Tcl_ConvertToType(interp, objPtr, &timeType
) != 0), 0))
) {
224 result = TCL_ERROR1;
225 }
226 }
227 if (likely(objPtr->typePtr == &timeType)(__builtin_expect((objPtr->typePtr == &timeType), 1))) {
228 timePtr->sec = (time_t)objPtr->internalRep.twoPtrValue.ptr1;
229 timePtr->usec = PTR2LONG(objPtr->internalRep.twoPtrValue.ptr2)((long)(intptr_t)(objPtr->internalRep.twoPtrValue.ptr2));
230 }
231 }
232 return result;
233}
234
235
236
237/*
238 *----------------------------------------------------------------------
239 *
240 * Ns_TclGetTimePtrFromObj --
241 *
242 * Convert the Tcl_Obj to an Ns_Time type and return a pointer to
243 * its internal representation.
244 *
245 * Results:
246 * TCL_OK or TCL_ERROR if not a valid Ns_Time.
247 *
248 * Side effects:
249 * None.
250 *
251 *----------------------------------------------------------------------
252 */
253int
254Ns_TclGetTimePtrFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Ns_Time **timePtrPtr)
255{
256 int result = TCL_OK0;
257
258 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
259 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
260 NS_NONNULL_ASSERT(timePtrPtr != NULL)((void) (0));
261
262 if (objPtr->typePtr != &timeType) {
263 if (unlikely(Tcl_ConvertToType(interp, objPtr, &timeType) != TCL_OK)(__builtin_expect((Tcl_ConvertToType(interp, objPtr, &timeType
) != 0), 0))
) {
264 result = TCL_ERROR1;
265 }
266 }
267 if (likely(objPtr->typePtr == &timeType)(__builtin_expect((objPtr->typePtr == &timeType), 1))) {
268 *timePtrPtr = ((Ns_Time *) (void *) &objPtr->internalRep);
269 }
270
271 return result;
272}
273
274
275
276/*
277 *----------------------------------------------------------------------
278 *
279 * NsTclTimeObjCmd --
280 *
281 * Implements "ns_time".
282 *
283 * Results:
284 * Tcl result.
285 *
286 * Side effects:
287 * See docs.
288 *
289 *----------------------------------------------------------------------
290 */
291
292int
293NsTclTimeObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
294{
295 int opt, rc = TCL_OK0;
296 Ns_Time resultTime;
297
298 static const char *const opts[] = {
299 "adjust", "diff", "format", "get", "incr", "make",
300 "seconds", "microseconds", NULL((void*)0)
301 };
302 enum {
303 TAdjustIdx, TDiffIdx, TFormatIdx, TGetIdx, TIncrIdx, TMakeIdx,
304 TSecondsIdx, TMicroSecondsIdx
305 };
306
307 if (objc < 2) {
308 Tcl_SetObjResult(interp, Tcl_NewLongObj((long)time(NULL((void*)0))));
309 return TCL_OK0;
310 }
311
312 if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,Tcl_GetIndexFromObjStruct(interp, objv[1], opts, sizeof(char *
), "option", 0, &opt)
313 &opt)Tcl_GetIndexFromObjStruct(interp, objv[1], opts, sizeof(char *
), "option", 0, &opt)
!= TCL_OK0) {
314 return TCL_ERROR1;
315 }
316
317 switch (opt) {
318 case TGetIdx:
319 {
320 Ns_GetTime(&resultTime);
321 Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&resultTime));
322 }
323 break;
324
325 case TMakeIdx:
326 {
327 Ns_ObjvSpec largs[] = {
328 {"sec", Ns_ObjvLong, &resultTime.sec, &poslongRange0},
329 {"?usec", Ns_ObjvLong, &resultTime.usec, &poslongRange0},
330 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
331 };
332
333 if (Ns_ParseObjv(NULL((void*)0), largs, interp, 2, objc, objv) != NS_OK) {
334 rc = TCL_ERROR1;
335 } else {
336 Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&resultTime));
337 }
338 }
339 break;
340
341 case TIncrIdx:
342 {
343 Ns_Time t2 = {0, 0};
344 Ns_Time *tPtr;
345 Ns_ObjvSpec largs[] = {
346 {"time", Ns_ObjvTime, &tPtr, &nonnegTimeRange},
347 {"sec", Ns_ObjvLong, &t2.sec, &poslongRange0},
348 {"?usec", Ns_ObjvLong, &t2.usec, &poslongRange0},
349 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
350 };
351
352 if (Ns_ParseObjv(NULL((void*)0), largs, interp, 2, objc, objv) != NS_OK) {
353 rc = TCL_ERROR1;
354 } else {
355 resultTime = *tPtr;
356 Ns_IncrTime(&resultTime, t2.sec, t2.usec);
357 Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&resultTime));
358 }
359 }
360 break;
361
362 case TDiffIdx:
363 {
364 Ns_Time *tPtr1, *tPtr2;
365 Ns_ObjvSpec largs[] = {
366 {"time1", Ns_ObjvTime, &tPtr1, NULL((void*)0)},
367 {"time2", Ns_ObjvTime, &tPtr2, NULL((void*)0)},
368 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
369 };
370
371 if (Ns_ParseObjv(NULL((void*)0), largs, interp, 2, objc, objv) != NS_OK) {
372 rc = TCL_ERROR1;
373 } else {
374 (void)Ns_DiffTime(tPtr1, tPtr2, &resultTime);
375 Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&resultTime));
376 }
377 }
378 break;
379
380 case TAdjustIdx:
381 {
382 Ns_Time *tPtr;
383 Ns_ObjvSpec largs[] = {
384 {"time", Ns_ObjvTime, &tPtr, NULL((void*)0)},
385 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
386 };
387
388 if (Ns_ParseObjv(NULL((void*)0), largs, interp, 2, objc, objv) != NS_OK) {
389 rc = TCL_ERROR1;
390 } else {
391 resultTime = *tPtr;
392 Ns_AdjTime(&resultTime);
393 Tcl_SetObjResult(interp, Ns_TclNewTimeObj(&resultTime));
394 }
395 }
396 break;
397
398 case TSecondsIdx:
399 case TMicroSecondsIdx:
400 {
401 Ns_Time *tPtr;
402 Ns_ObjvSpec largs[] = {
403 {"time", Ns_ObjvTime, &tPtr, NULL((void*)0)},
404 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
405 };
406
407 if (Ns_ParseObjv(NULL((void*)0), largs, interp, 2, objc, objv) != NS_OK) {
408 rc = TCL_ERROR1;
409 } else {
410 Tcl_SetObjResult(interp, Tcl_NewLongObj((long)(opt == TSecondsIdx ?
411 tPtr->sec : tPtr->usec)));
412 }
413 }
414 break;
415
416 case TFormatIdx:
417 {
418 Ns_Time *tPtr;
419 Ns_ObjvSpec largs[] = {
420 {"time", Ns_ObjvTime, &tPtr, NULL((void*)0)},
421 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
422 };
423
424 if (Ns_ParseObjv(NULL((void*)0), largs, interp, 2, objc, objv) != NS_OK) {
425 rc = TCL_ERROR1;
426 } else {
427 Tcl_DString ds, *dsPtr = &ds;
428
429 Tcl_DStringInit(dsPtr);
430 Ns_DStringAppendTime(dsPtr, tPtr);
431
432 Tcl_DStringResult(interp, dsPtr);
433 }
434 }
435 break;
436
437 default:
438 /* unexpected value */
439 assert(opt && 0)((void) (0));
440 rc = TCL_ERROR1;
441 break;
442 }
443
444 return rc;
445}
446
447
448/*
449 *----------------------------------------------------------------------
450 *
451 * NsTclLocalTimeObjCmd, NsTclGmTimeObjCmd --
452 *
453 * Implements "ns_gmtime" and "ns_localtime".
454 *
455 * Results:
456 * Tcl result.
457 *
458 * Side effects:
459 * ns_localtime depends on the timezone of the server process.
460 *
461 *----------------------------------------------------------------------
462 */
463
464static int
465TmObjCmd(ClientData isGmt, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
466{
467 int rc = TCL_OK0;
468
469 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
470
471 if (objc != 1) {
472 Tcl_WrongNumArgs(interp, 1, objv, NS_EMPTY_STRING);
473 rc = TCL_ERROR1;
474 } else {
475 time_t now;
476 const struct tm *ptm;
477 Tcl_Obj *objPtr[9];
478
479 now = time(NULL((void*)0));
480 if (PTR2INT(isGmt)((int)(intptr_t)(isGmt)) != 0) {
481 ptm = ns_gmtime(&now);
482 } else {
483 ptm = ns_localtime(&now);
484 }
485 objPtr[0] = Tcl_NewIntObj(ptm->tm_sec);
Duplicate code detected
486 objPtr[1] = Tcl_NewIntObj(ptm->tm_min);
Similar code here
487 objPtr[2] = Tcl_NewIntObj(ptm->tm_hour);
488 objPtr[3] = Tcl_NewIntObj(ptm->tm_mday);
489 objPtr[4] = Tcl_NewIntObj(ptm->tm_mon);
490 objPtr[5] = Tcl_NewIntObj(ptm->tm_year);
491 objPtr[6] = Tcl_NewIntObj(ptm->tm_wday);
492 objPtr[7] = Tcl_NewIntObj(ptm->tm_yday);
493 objPtr[8] = Tcl_NewIntObj(ptm->tm_isdst);
494 Tcl_SetListObj(Tcl_GetObjResult(interp), 9, objPtr);
495 }
496
497 return rc;
498}
499
500int
501NsTclGmTimeObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
502{
503 return TmObjCmd(INT2PTR(1)((void *)(intptr_t)(1)), interp, objc, objv);
504}
505
506int
507NsTclLocalTimeObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
508{
509 return TmObjCmd(NULL((void*)0), interp, objc, objv);
510}
511
512
513/*
514 *----------------------------------------------------------------------
515 *
516 * NsTclSleepObjCmd --
517 *
518 * Sleep with millisecond resolution.
519 * Implements "ns_sleep".
520 *
521 * Results:
522 * Tcl Result.
523 *
524 * Side effects:
525 * None.
526 *
527 *----------------------------------------------------------------------
528 */
529
530int
531NsTclSleepObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
532{
533 int rc = TCL_OK0;
534 Ns_Time *tPtr = NULL((void*)0);
535 Ns_ObjvSpec args[] = {
536 {"timespec", Ns_ObjvTime, &tPtr, &nonnegTimeRange},
537 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
538 };
539
540 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
541 rc = TCL_ERROR1;
542 } else {
543 time_t ms;
544
545 assert(tPtr != NULL)((void) (0));
546 ms = Ns_TimeToMilliseconds(tPtr);
547 if (ms > 0) {
548 Tcl_Sleep((int)ms);
549 }
550 }
551
552 return rc;
553}
554
555
556/*
557 *----------------------------------------------------------------------
558 *
559 * NsTclStrftimeObjCmd --
560 *
561 * Implements "ns_fmttime".
562 *
563 * Results:
564 * Tcl result.
565 *
566 * Side effects:
567 * Depends on the timezone of the server process.
568 *
569 *----------------------------------------------------------------------
570 */
571
572int
573NsTclStrftimeObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
574{
575 int result = TCL_OK0;
576 long sec = 0;
577 char *fmt = (char *)"%c";
578 Ns_ObjvValueRange range = {0, LONG_MAX9223372036854775807L};
579 Ns_ObjvSpec args[] = {
580 {"time", Ns_ObjvLong, &sec, &range},
581 {"?fmt", Ns_ObjvString, &fmt, NULL((void*)0)},
582 {NULL((void*)0), NULL((void*)0), NULL((void*)0), NULL((void*)0)}
583 };
584
585 if (Ns_ParseObjv(NULL((void*)0), args, interp, 1, objc, objv) != NS_OK) {
586 result = TCL_ERROR1;
587
588 } else {
589 char buf[200];
590 size_t bufLength;
591 time_t t;
592
593 t = sec;
594 bufLength = strftime(buf, sizeof(buf), fmt, ns_localtime(&t));
595 if (unlikely(bufLength == 0u)(__builtin_expect((bufLength == 0u), 0))) {
596 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "invalid time: ",
597 Tcl_GetString(objv[1]), (char *)0L);
598 result = TCL_ERROR1;
599 } else {
600 Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, (int)bufLength));
601 }
602 }
603
604 return result;
605}
606
607
608/*
609 *----------------------------------------------------------------------
610 *
611 * UpdateStringOfTime --
612 *
613 * Update the string representation for an Ns_Time object.
614 *
615 * Note: This procedure does not free an existing old string rep
616 * so storage will be lost if this has not already been done.
617 *
618 * Results:
619 * None.
620 *
621 * Side effects:
622 * The object's string is set to a valid string that results from
623 * the Ns_Time-to-string conversion.
624 *
625 *----------------------------------------------------------------------
626 */
627
628static void
629UpdateStringOfTime(Tcl_Obj *objPtr)
630{
631 Ns_Time *timePtr;
632 int len;
633 char buf[(TCL_INTEGER_SPACE24 * 2) + 1];
634
635 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
636
637 timePtr = (Ns_Time *) (void *) &objPtr->internalRep;
638 Ns_AdjTime(timePtr);
639
640 if (timePtr->usec == 0 && timePtr->sec >= 0) {
641 len = ns_uint64toa(buf, (uint64_t)timePtr->sec);
642 } else {
643 len = snprintf(buf, sizeof(buf), "%" PRId64 ":%ld",__builtin___snprintf_chk (buf, sizeof(buf), 2 - 1, __builtin_object_size
(buf, 2 > 1), "%" "l" "d" ":%ld", (int64_t)timePtr->sec
, timePtr->usec)
644 (int64_t)timePtr->sec, timePtr->usec)__builtin___snprintf_chk (buf, sizeof(buf), 2 - 1, __builtin_object_size
(buf, 2 > 1), "%" "l" "d" ":%ld", (int64_t)timePtr->sec
, timePtr->usec)
;
645 }
646 Ns_TclSetStringRep(objPtr, buf, len);
647}
648
649
650/*
651 *----------------------------------------------------------------------
652 *
653 * ParseTimeUnit --
654 *
655 * Parse time units specified after an integer or a float. Note
656 * that the smallest possible time value is 1 μs based on the
657 * internal representation.
658 *
659 * Accepted time units are:
660 * μs, ms, s, m, h, d, w, y
661 *
662 * Results:
663 * Multiplier relative to seconds.
664 *
665 * Side effects:
666 * None.
667 *
668 *----------------------------------------------------------------------
669 */
670static double ParseTimeUnit(const char *str)
671{
672 double multiplier = 1.0;
673
674 /*
675 * Skip whitespace
676 */
677 while (CHARTYPE(space, *str)(((*__ctype_b_loc ())[(int) (((int)((unsigned char)(*str))))]
& (unsigned short int) _ISspace))
!= 0) {
678 str++;
679 }
680
681 if (*str == 's' && *(str+1) == '\0') {
682 multiplier = 1.0;
683 } else if (*str == 'm' && *(str+1) == '\0') {
684 multiplier = 60.0;
685 } else if (*str == 'h' && *(str+1) == '\0') {
686 multiplier = 3600.0;
687 } else if (*str == 'd' && *(str+1) == '\0') {
688 multiplier = 86400.0;
689 } else if (*str == 'w' && *(str+1) == '\0') {
690 multiplier = 604800.0;
691 } else if (*str == 'y' && *(str+1) == '\0') {
692 multiplier = 31536000.0;
693 } else if (*str == 'm' && *(str+1) == 's' && *(str+2) == '\0') {
694 multiplier = 0.001;
695 } else if (*str == '\xce' && *(str+1) == '\xbc' && *(str+2) == 's' && *(str+3) == '\0') {
696 /* μ */
697 multiplier = 0.000001;
698 } else if (*str != '\0') {
699 Ns_Log(Warning, "ignoring time unit '%s'", str);
700 }
701
702 return multiplier;
703}
704
705/*
706 *----------------------------------------------------------------------
707 *
708 * DblValueToNstime --
709 *
710 * Convert double value (in seconds) to a NaviServer time value.
711 *
712 * Results:
713 * None.
714 *
715 * Side effects:
716 * Updating the Ns_Time value in the first argument.
717 *
718 *----------------------------------------------------------------------
719 */
720static void
721DblValueToNstime( Ns_Time *timePtr, double dblValue)
722{
723 NS_NONNULL_ASSERT(timePtr != NULL)((void) (0));
724
725 if (dblValue < 0.0) {
726 double posValue = -dblValue;
727
728 /*
729 * Calculate with the positive value.
730 */
731 timePtr->sec = (long)(posValue);
732 timePtr->usec = (long)round((posValue - (double)timePtr->sec) * 1000000.0);
733
734 /*
735 * Fill in the minus sign at the right place.
736 */
737 if (timePtr->sec == 0) {
738 timePtr->usec = -timePtr->usec;
739 } else {
740 timePtr->sec = -timePtr->sec;
741 }
742
743 } else {
744 timePtr->sec = (long)(dblValue);
745 timePtr->usec = (long)round((dblValue - (double)timePtr->sec) * 1000000.0);
746 }
747 /* fprintf(stderr, "gen dbltime %f final sec %ld usec %.06ld float %.10f %.10f long %ld\n",
748 dblValue, timePtr->sec, timePtr->usec,
749 (dblValue - (double)timePtr->sec),
750 round((dblValue - (double)timePtr->sec) * 1000000.0),
751 (long)((dblValue - (double)timePtr->sec)));
752 */
753}
754
755
756
757/*
758 *----------------------------------------------------------------------
759 *
760 * GetTimeFromString --
761 *
762 * Try to fill ns_Time struct from a string based on a specified
763 * separator (':' or '.'). The colon separator is for the classical
764 * NaviServer time format "sec:usec", whereas the dot is used for the
765 * floating point format.
766 *
767 * Results:
768 * TCL_OK, TCL_ERROR or TCL_CONTINUE
769 *
770 * Side effects:
771 * Fill in sec und usec in the specified Ns_Time on success.
772 *
773 *----------------------------------------------------------------------
774 */
775
776static int
777GetTimeFromString(Tcl_Interp *interp, const char *str, char separator, Ns_Time *tPtr)
778{
779 /*
780 * Look for the separator
781 */
782 char *sep;
783 int result = TCL_CONTINUE4;
784 bool_Bool isNegative;
785
786 NS_NONNULL_ASSERT(str != NULL)((void) (0));
787 NS_NONNULL_ASSERT(tPtr != NULL)((void) (0));
788
789 isNegative = (*str == '-');
790 sep = strchr(str, INTCHAR(separator)((int)((unsigned char)((separator)))));
791
792 if (sep != NULL((void*)0)) {
793 int intValue;
794
795 /*
796 * First get sec from the string.
797 */
798 if (unlikely(sep == str)(__builtin_expect((sep == str), 0))) {
799 /*
800 * The first character was the separator, treat sec as 0.
801 */
802 tPtr->sec = 0L;
803
804 } else {
805 /*
806 * Overwrite the separator with a null-byte to make the
807 * first part null-terminated.
808 */
809 *sep = '\0';
810 result = Tcl_GetInt(interp, str, &intValue);
811 *sep = separator;
812 if (result != TCL_OK0) {
813 result = TCL_ERROR1;
814 } else {
815 tPtr->sec = (long)intValue;
816 result = TCL_OK0;
817 }
818 }
819
820 /*
821 * Get usec
822 */
823 if (result != TCL_ERROR1) {
824 /*
825 * When the separator is a dot, try to get the value in floating
826 * point format, which are fractions of a second.
827 */
828 if (separator == '.') {
829 double dblValue;
830
831 if (Tcl_GetDouble(interp, sep, &dblValue) != TCL_OK0) {
832 char *ptr = NULL((void*)0), *p = sep;
833 long fraction;
834
835 /*
836 * Skip separator
837 */
838 p ++;
839
840 fraction = strtol(p, &ptr, 10);
841 if (likely(p != ptr)(__builtin_expect((p != ptr), 1))) {
842 /*
843 * We could parse at least a part of the string as
844 * integer.
845 */
846 double multiplier = ParseTimeUnit(ptr);
847 double dblFraction = (double)fraction;
848
849 /*
850 * Shift fraction value to right of the fraction
851 * point.
852 */
853 while (p != ptr) {
854 dblFraction /= 10.0;
855 p ++;
856 }
857 if (isNegative) {
858 /* fprintf(stderr, "GetTimeFromString neg value\n"
859 "GetTimeFromString multiplier %.10f sec %ld dblFraction %.12f\n",
860 multiplier, tPtr->sec, dblFraction);*/
861 DblValueToNstime(tPtr, -1 *
862 multiplier * ((double)llabs(tPtr->sec) + dblFraction));
863 } else {
864 DblValueToNstime(tPtr, multiplier * ((double)tPtr->sec + dblFraction));
865 }
866
867 result = TCL_OK0;
868
869 } else {
870 result = TCL_ERROR1;
871 }
872 } else {
873 tPtr->usec = (long)(dblValue * 1000000.0);
874 result = TCL_OK0;
875 }
876
877 } else {
878 /*
879 * The separator must be the ":", the traditional
880 * separator for sec:usec.
881 */
882 assert(separator == ':')((void) (0));
883
884 if (Tcl_GetInt(interp, sep+1, &intValue) != TCL_OK0) {
885 result = TCL_ERROR1;
886 } else {
887 tPtr->usec = (long)intValue;
888 result = TCL_OK0;
889 }
890 }
891 }
892 } else if (separator == '.') {
893 char *ptr = NULL((void*)0);
894 long sec;
895
896 /*
897 * No separator found, so try to interpret the string as integer
898 */
899 sec = strtol(str, &ptr, 10);
900
901 if (likely(str != ptr)(__builtin_expect((str != ptr), 1))) {
902 /*
903 * We could parse at least a part of the string as integer.
904 */
905 double multiplier = ParseTimeUnit(ptr);
906
907 if (multiplier == 1.0) {
908 tPtr->sec = sec;
909 tPtr->usec = 0;
910 } else {
911 DblValueToNstime(tPtr, multiplier * (double)sec);
912 }
913 result = TCL_OK0;
914 }
915 }
916
917 return result;
918}
919
920
921/*
922 *----------------------------------------------------------------------
923 *
924 * SetTimeFromAny --
925 *
926 * Attempt to generate an Ns_Time internal respresentation for the Tcl
927 * object. It interprets integer as seconds, but allows as well the form
928 * sec:usec, sec.fraction, or number plus time unit.
929 *
930 * Results:
931 * The return value is a standard object Tcl result. If an error occurs
932 * during conversion, an error message is left in the interpreter's
933 * result unless "interp" is NULL.
934 *
935 * Side effects:
936 * If no error occurs, an int is stored as "objPtr"s internal
937 * representation.
938 *
939 *----------------------------------------------------------------------
940 */
941static int
942SetTimeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
943{
944 Ns_Time t;
945 long sec;
946 int result = TCL_OK0;
947
948 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
949 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
950
951 if (objPtr->typePtr == intTypePtr) {
952 /*
953 * When the type is "int", usec is 0.
954 */
955 if (Tcl_GetLongFromObj(interp, objPtr, &sec) != TCL_OK0) {
956 result = TCL_ERROR1;
957 } else {
958 t.sec = sec;
959 t.usec = 0;
960 }
961 } else {
962 result = Ns_GetTimeFromString(interp, Tcl_GetString(objPtr), &t);
963 }
964
965 if (result == TCL_OK0) {
966 /* Ns_AdjTime(&t); */
967 SetTimeInternalRep(objPtr, &t);
968 }
969
970 return result;
971}
972
973
974
975/*
976 *----------------------------------------------------------------------
977 *
978 * Ns_GetTimeFromString --
979 *
980 * Convert string to time structure. Check, if the string contains the
981 * classical NaviServer separator for sec:usec and interpret the string
982 * in this format. If not, check if this has a "." as separator, and use
983 * a floating point notation. An optional time unit (ms, s, m, h, d) can
984 * be specified immediately after the numeric part.
985 *
986 * Results:
987 * Tcl result code.
988 *
989 * Side effects:
990 * If an error occurs and interp is given, leave error message in the
991 * interp.
992 *
993 *----------------------------------------------------------------------
994 */
995int
996Ns_GetTimeFromString(Tcl_Interp *interp, const char *str, Ns_Time *tPtr)
997{
998 int result;
999
1000 NS_NONNULL_ASSERT(str != NULL)((void) (0));
1001 NS_NONNULL_ASSERT(tPtr != NULL)((void) (0));
1002
1003 result = GetTimeFromString(interp, str, ':', tPtr);
1004 if (result == TCL_CONTINUE4) {
1005 result = GetTimeFromString(interp, str, '.', tPtr);
1006 }
1007 if (result == TCL_CONTINUE4) {
1008 /*
1009 * We should not come here, since the GetTimeFromString() with
1010 * '.' handles also integers and sets error messages.
1011 */
1012 Ns_TclPrintfResult(interp, "expected time value but got \"%s\"", str);
1013 result = TCL_ERROR1;
1014 }
1015 /* fprintf(stderr, "GetTimeFromString final " NS_TIME_FMT " -- %d\n", tPtr->sec, tPtr->usec, result);*/
1016
1017 return result;
1018}
1019
1020
1021/*
1022 *----------------------------------------------------------------------
1023 *
1024 * SetTimeInternalRep --
1025 *
1026 * Set the internal Ns_Time, freeing a previous internal rep if
1027 * necessary.
1028 *
1029 * Results:
1030 * None.
1031 *
1032 * Side effects:
1033 * Object will be an Ns_Time type.
1034 *
1035 *----------------------------------------------------------------------
1036 */
1037
1038static void
1039SetTimeInternalRep(Tcl_Obj *objPtr, const Ns_Time *timePtr)
1040{
1041 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
1042 NS_NONNULL_ASSERT(timePtr != NULL)((void) (0));
1043
1044 Ns_TclSetTwoPtrValue(objPtr, &timeType,
1045 INT2PTR(timePtr->sec)((void *)(intptr_t)(timePtr->sec)), INT2PTR(timePtr->usec)((void *)(intptr_t)(timePtr->usec)));
1046}
1047
1048
1049/*
1050 * Local Variables:
1051 * mode: c
1052 * c-basic-offset: 4
1053 * fill-column: 78
1054 * indent-tabs-mode: nil
1055 * End:
1056 */