File: | d/tcltime.c |
Warning: | line 485, column 9 Duplicate code detected |
Note: | line 486, column 9 Similar code here |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 |
47 | static 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 | |
56 | static 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 | |
59 | static int SetTimeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr) |
60 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))); |
61 | |
62 | static void UpdateStringOfTime(Tcl_Obj *objPtr) |
63 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); |
64 | |
65 | static int TmObjCmd(ClientData isGmt, Tcl_Interp *interp, int objc, Tcl_Obj *const* objv) |
66 | NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))); |
67 | |
68 | static 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 | |
71 | static void DblValueToNstime(Ns_Time *timePtr, double dblValue) |
72 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); |
73 | |
74 | static double ParseTimeUnit(const char *str) |
75 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); |
76 | |
77 | /* |
78 | * Local variables defined in this file. |
79 | */ |
80 | |
81 | static const Tcl_ObjType timeType = { |
82 | "ns:time", |
83 | NULL((void*)0), |
84 | NULL((void*)0), |
85 | UpdateStringOfTime, |
86 | SetTimeFromAny |
87 | }; |
88 | |
89 | static const Tcl_ObjType *intTypePtr; |
90 | static Ns_ObjvValueRange poslongRange0 = {0, LONG_MAX9223372036854775807L}; |
91 | static 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 | |
110 | void |
111 | NsTclInitTimeType(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 | |
143 | Tcl_Obj * |
144 | Ns_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 | |
173 | void |
174 | Ns_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 | */ |
204 | int |
205 | Ns_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 | */ |
253 | int |
254 | Ns_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 | |
292 | int |
293 | NsTclTimeObjCmd(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 | |
464 | static int |
465 | TmObjCmd(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 | |
500 | int |
501 | NsTclGmTimeObjCmd(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 | |
506 | int |
507 | NsTclLocalTimeObjCmd(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 | |
530 | int |
531 | NsTclSleepObjCmd(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 | |
572 | int |
573 | NsTclStrftimeObjCmd(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 | |
628 | static void |
629 | UpdateStringOfTime(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 | */ |
670 | static 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 | */ |
720 | static void |
721 | DblValueToNstime( 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 | |
776 | static int |
777 | GetTimeFromString(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 | */ |
941 | static int |
942 | SetTimeFromAny(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 | */ |
995 | int |
996 | Ns_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 | |
1038 | static void |
1039 | SetTimeInternalRep(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 | */ |