comparison src/time.c @ 19396:a961efb326e5 v8.2.0256

patch 8.2.0256: time and timer related code is spread out Commit: https://github.com/vim/vim/commit/0a8fed6231c84e4e1b3a7dd6c0d95d3f98207fe0 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Feb 14 13:22:17 2020 +0100 patch 8.2.0256: time and timer related code is spread out Problem: Time and timer related code is spread out. Solution: Move time and timer related code to a new file. (Yegappan Lakshmanan, closes #5604)
author Bram Moolenaar <Bram@vim.org>
date Fri, 14 Feb 2020 13:30:05 +0100
parents
children 3ff714d765ba
comparison
equal deleted inserted replaced
19395:eb5ef6f5f58b 19396:a961efb326e5
1 /* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10 /*
11 * time.c: functions related to time and timers
12 */
13
14 #include "vim.h"
15
16 /*
17 * Cache of the current timezone name as retrieved from TZ, or an empty string
18 * where unset, up to 64 octets long including trailing null byte.
19 */
20 #if defined(HAVE_LOCALTIME_R) && defined(HAVE_TZSET)
21 static char tz_cache[64];
22 #endif
23
24 /*
25 * Call either localtime(3) or localtime_r(3) from POSIX libc time.h, with the
26 * latter version preferred for reentrancy.
27 *
28 * If we use localtime_r(3) and we have tzset(3) available, check to see if the
29 * environment variable TZ has changed since the last run, and call tzset(3) to
30 * update the global timezone variables if it has. This is because the POSIX
31 * standard doesn't require localtime_r(3) implementations to do that as it
32 * does with localtime(3), and we don't want to call tzset(3) every time.
33 */
34 static struct tm *
35 vim_localtime(
36 const time_t *timep, // timestamp for local representation
37 struct tm *result UNUSED) // pointer to caller return buffer
38 {
39 #ifdef HAVE_LOCALTIME_R
40 # ifdef HAVE_TZSET
41 char *tz; // pointer for TZ environment var
42
43 tz = (char *)mch_getenv((char_u *)"TZ");
44 if (tz == NULL)
45 tz = "";
46 if (STRNCMP(tz_cache, tz, sizeof(tz_cache) - 1) != 0)
47 {
48 tzset();
49 vim_strncpy((char_u *)tz_cache, (char_u *)tz, sizeof(tz_cache) - 1);
50 }
51 # endif // HAVE_TZSET
52 return localtime_r(timep, result);
53 #else
54 return localtime(timep);
55 #endif // HAVE_LOCALTIME_R
56 }
57
58 /*
59 * Return the current time in seconds. Calls time(), unless test_settime()
60 * was used.
61 */
62 time_T
63 vim_time(void)
64 {
65 # ifdef FEAT_EVAL
66 return time_for_testing == 0 ? time(NULL) : time_for_testing;
67 # else
68 return time(NULL);
69 # endif
70 }
71
72 /*
73 * Replacement for ctime(), which is not safe to use.
74 * Requires strftime(), otherwise returns "(unknown)".
75 * If "thetime" is invalid returns "(invalid)". Never returns NULL.
76 * When "add_newline" is TRUE add a newline like ctime() does.
77 * Uses a static buffer.
78 */
79 char *
80 get_ctime(time_t thetime, int add_newline)
81 {
82 static char buf[50];
83 #ifdef HAVE_STRFTIME
84 struct tm tmval;
85 struct tm *curtime;
86
87 curtime = vim_localtime(&thetime, &tmval);
88 // MSVC returns NULL for an invalid value of seconds.
89 if (curtime == NULL)
90 vim_strncpy((char_u *)buf, (char_u *)_("(Invalid)"), sizeof(buf) - 1);
91 else
92 {
93 (void)strftime(buf, sizeof(buf) - 1, _("%a %b %d %H:%M:%S %Y"),
94 curtime);
95 # ifdef MSWIN
96 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
97 {
98 char_u *to_free = NULL;
99 int len;
100
101 acp_to_enc((char_u *)buf, (int)strlen(buf), &to_free, &len);
102 if (to_free != NULL)
103 {
104 STRCPY(buf, to_free);
105 vim_free(to_free);
106 }
107 }
108 # endif
109 }
110 #else
111 STRCPY(buf, "(unknown)");
112 #endif
113 if (add_newline)
114 STRCAT(buf, "\n");
115 return buf;
116 }
117
118 #if defined(FEAT_EVAL) || defined(PROTO)
119
120 #if defined(MACOS_X)
121 # include <time.h> // for time_t
122 #endif
123
124 /*
125 * "localtime()" function
126 */
127 void
128 f_localtime(typval_T *argvars UNUSED, typval_T *rettv)
129 {
130 rettv->vval.v_number = (varnumber_T)time(NULL);
131 }
132
133 # if defined(FEAT_RELTIME)
134 /*
135 * Convert a List to proftime_T.
136 * Return FAIL when there is something wrong.
137 */
138 static int
139 list2proftime(typval_T *arg, proftime_T *tm)
140 {
141 long n1, n2;
142 int error = FALSE;
143
144 if (arg->v_type != VAR_LIST || arg->vval.v_list == NULL
145 || arg->vval.v_list->lv_len != 2)
146 return FAIL;
147 n1 = list_find_nr(arg->vval.v_list, 0L, &error);
148 n2 = list_find_nr(arg->vval.v_list, 1L, &error);
149 # ifdef MSWIN
150 tm->HighPart = n1;
151 tm->LowPart = n2;
152 # else
153 tm->tv_sec = n1;
154 tm->tv_usec = n2;
155 # endif
156 return error ? FAIL : OK;
157 }
158 # endif // FEAT_RELTIME
159
160 /*
161 * "reltime()" function
162 */
163 void
164 f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
165 {
166 # ifdef FEAT_RELTIME
167 proftime_T res;
168 proftime_T start;
169
170 if (argvars[0].v_type == VAR_UNKNOWN)
171 {
172 // No arguments: get current time.
173 profile_start(&res);
174 }
175 else if (argvars[1].v_type == VAR_UNKNOWN)
176 {
177 if (list2proftime(&argvars[0], &res) == FAIL)
178 return;
179 profile_end(&res);
180 }
181 else
182 {
183 // Two arguments: compute the difference.
184 if (list2proftime(&argvars[0], &start) == FAIL
185 || list2proftime(&argvars[1], &res) == FAIL)
186 return;
187 profile_sub(&res, &start);
188 }
189
190 if (rettv_list_alloc(rettv) == OK)
191 {
192 long n1, n2;
193
194 # ifdef MSWIN
195 n1 = res.HighPart;
196 n2 = res.LowPart;
197 # else
198 n1 = res.tv_sec;
199 n2 = res.tv_usec;
200 # endif
201 list_append_number(rettv->vval.v_list, (varnumber_T)n1);
202 list_append_number(rettv->vval.v_list, (varnumber_T)n2);
203 }
204 # endif
205 }
206
207 # ifdef FEAT_FLOAT
208 /*
209 * "reltimefloat()" function
210 */
211 void
212 f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv)
213 {
214 # ifdef FEAT_RELTIME
215 proftime_T tm;
216 # endif
217
218 rettv->v_type = VAR_FLOAT;
219 rettv->vval.v_float = 0;
220 # ifdef FEAT_RELTIME
221 if (list2proftime(&argvars[0], &tm) == OK)
222 rettv->vval.v_float = profile_float(&tm);
223 # endif
224 }
225 # endif
226
227 /*
228 * "reltimestr()" function
229 */
230 void
231 f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
232 {
233 # ifdef FEAT_RELTIME
234 proftime_T tm;
235 # endif
236
237 rettv->v_type = VAR_STRING;
238 rettv->vval.v_string = NULL;
239 # ifdef FEAT_RELTIME
240 if (list2proftime(&argvars[0], &tm) == OK)
241 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
242 # endif
243 }
244
245 # if defined(HAVE_STRFTIME) || defined(PROTO)
246 /*
247 * "strftime({format}[, {time}])" function
248 */
249 void
250 f_strftime(typval_T *argvars, typval_T *rettv)
251 {
252 char_u result_buf[256];
253 struct tm tmval;
254 struct tm *curtime;
255 time_t seconds;
256 char_u *p;
257
258 rettv->v_type = VAR_STRING;
259
260 p = tv_get_string(&argvars[0]);
261 if (argvars[1].v_type == VAR_UNKNOWN)
262 seconds = time(NULL);
263 else
264 seconds = (time_t)tv_get_number(&argvars[1]);
265 curtime = vim_localtime(&seconds, &tmval);
266 // MSVC returns NULL for an invalid value of seconds.
267 if (curtime == NULL)
268 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
269 else
270 {
271 vimconv_T conv;
272 char_u *enc;
273
274 conv.vc_type = CONV_NONE;
275 enc = enc_locale();
276 convert_setup(&conv, p_enc, enc);
277 if (conv.vc_type != CONV_NONE)
278 p = string_convert(&conv, p, NULL);
279 if (p != NULL)
280 (void)strftime((char *)result_buf, sizeof(result_buf),
281 (char *)p, curtime);
282 else
283 result_buf[0] = NUL;
284
285 if (conv.vc_type != CONV_NONE)
286 vim_free(p);
287 convert_setup(&conv, enc, p_enc);
288 if (conv.vc_type != CONV_NONE)
289 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
290 else
291 rettv->vval.v_string = vim_strsave(result_buf);
292
293 // Release conversion descriptors
294 convert_setup(&conv, NULL, NULL);
295 vim_free(enc);
296 }
297 }
298 # endif
299
300 # if defined(HAVE_STRPTIME) || defined(PROTO)
301 /*
302 * "strptime({format}, {timestring})" function
303 */
304 void
305 f_strptime(typval_T *argvars, typval_T *rettv)
306 {
307 struct tm tmval;
308 char_u *fmt;
309 char_u *str;
310 vimconv_T conv;
311 char_u *enc;
312
313 vim_memset(&tmval, NUL, sizeof(tmval));
314 fmt = tv_get_string(&argvars[0]);
315 str = tv_get_string(&argvars[1]);
316
317 conv.vc_type = CONV_NONE;
318 enc = enc_locale();
319 convert_setup(&conv, p_enc, enc);
320 if (conv.vc_type != CONV_NONE)
321 fmt = string_convert(&conv, fmt, NULL);
322 if (fmt == NULL
323 || strptime((char *)str, (char *)fmt, &tmval) == NULL
324 || (rettv->vval.v_number = mktime(&tmval)) == -1)
325 rettv->vval.v_number = 0;
326
327 if (conv.vc_type != CONV_NONE)
328 vim_free(fmt);
329 convert_setup(&conv, NULL, NULL);
330 vim_free(enc);
331 }
332 # endif
333
334 # if defined(FEAT_TIMERS) || defined(PROTO)
335 static timer_T *first_timer = NULL;
336 static long last_timer_id = 0;
337
338 /*
339 * Return time left until "due". Negative if past "due".
340 */
341 long
342 proftime_time_left(proftime_T *due, proftime_T *now)
343 {
344 # ifdef MSWIN
345 LARGE_INTEGER fr;
346
347 if (now->QuadPart > due->QuadPart)
348 return 0;
349 QueryPerformanceFrequency(&fr);
350 return (long)(((double)(due->QuadPart - now->QuadPart)
351 / (double)fr.QuadPart) * 1000);
352 # else
353 if (now->tv_sec > due->tv_sec)
354 return 0;
355 return (due->tv_sec - now->tv_sec) * 1000
356 + (due->tv_usec - now->tv_usec) / 1000;
357 # endif
358 }
359
360 /*
361 * Insert a timer in the list of timers.
362 */
363 static void
364 insert_timer(timer_T *timer)
365 {
366 timer->tr_next = first_timer;
367 timer->tr_prev = NULL;
368 if (first_timer != NULL)
369 first_timer->tr_prev = timer;
370 first_timer = timer;
371 did_add_timer = TRUE;
372 }
373
374 /*
375 * Take a timer out of the list of timers.
376 */
377 static void
378 remove_timer(timer_T *timer)
379 {
380 if (timer->tr_prev == NULL)
381 first_timer = timer->tr_next;
382 else
383 timer->tr_prev->tr_next = timer->tr_next;
384 if (timer->tr_next != NULL)
385 timer->tr_next->tr_prev = timer->tr_prev;
386 }
387
388 static void
389 free_timer(timer_T *timer)
390 {
391 free_callback(&timer->tr_callback);
392 vim_free(timer);
393 }
394
395 /*
396 * Create a timer and return it. NULL if out of memory.
397 * Caller should set the callback.
398 */
399 timer_T *
400 create_timer(long msec, int repeat)
401 {
402 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
403 long prev_id = last_timer_id;
404
405 if (timer == NULL)
406 return NULL;
407 if (++last_timer_id <= prev_id)
408 // Overflow! Might cause duplicates...
409 last_timer_id = 0;
410 timer->tr_id = last_timer_id;
411 insert_timer(timer);
412 if (repeat != 0)
413 timer->tr_repeat = repeat - 1;
414 timer->tr_interval = msec;
415
416 profile_setlimit(msec, &timer->tr_due);
417 return timer;
418 }
419
420 /*
421 * Invoke the callback of "timer".
422 */
423 static void
424 timer_callback(timer_T *timer)
425 {
426 typval_T rettv;
427 typval_T argv[2];
428
429 argv[0].v_type = VAR_NUMBER;
430 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
431 argv[1].v_type = VAR_UNKNOWN;
432
433 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
434 clear_tv(&rettv);
435 }
436
437 /*
438 * Call timers that are due.
439 * Return the time in msec until the next timer is due.
440 * Returns -1 if there are no pending timers.
441 */
442 long
443 check_due_timer(void)
444 {
445 timer_T *timer;
446 timer_T *timer_next;
447 long this_due;
448 long next_due = -1;
449 proftime_T now;
450 int did_one = FALSE;
451 int need_update_screen = FALSE;
452 long current_id = last_timer_id;
453
454 // Don't run any timers while exiting or dealing with an error.
455 if (exiting || aborting())
456 return next_due;
457
458 profile_start(&now);
459 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
460 {
461 timer_next = timer->tr_next;
462
463 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
464 continue;
465 this_due = proftime_time_left(&timer->tr_due, &now);
466 if (this_due <= 1)
467 {
468 // Save and restore a lot of flags, because the timer fires while
469 // waiting for a character, which might be halfway a command.
470 int save_timer_busy = timer_busy;
471 int save_vgetc_busy = vgetc_busy;
472 int save_did_emsg = did_emsg;
473 int save_called_emsg = called_emsg;
474 int save_must_redraw = must_redraw;
475 int save_trylevel = trylevel;
476 int save_did_throw = did_throw;
477 int save_ex_pressedreturn = get_pressedreturn();
478 int save_may_garbage_collect = may_garbage_collect;
479 except_T *save_current_exception = current_exception;
480 vimvars_save_T vvsave;
481
482 // Create a scope for running the timer callback, ignoring most of
483 // the current scope, such as being inside a try/catch.
484 timer_busy = timer_busy > 0 || vgetc_busy > 0;
485 vgetc_busy = 0;
486 called_emsg = 0;
487 did_emsg = FALSE;
488 did_uncaught_emsg = FALSE;
489 must_redraw = 0;
490 trylevel = 0;
491 did_throw = FALSE;
492 current_exception = NULL;
493 may_garbage_collect = FALSE;
494 save_vimvars(&vvsave);
495
496 timer->tr_firing = TRUE;
497 timer_callback(timer);
498 timer->tr_firing = FALSE;
499
500 timer_next = timer->tr_next;
501 did_one = TRUE;
502 timer_busy = save_timer_busy;
503 vgetc_busy = save_vgetc_busy;
504 if (did_uncaught_emsg)
505 ++timer->tr_emsg_count;
506 did_emsg = save_did_emsg;
507 called_emsg = save_called_emsg;
508 trylevel = save_trylevel;
509 did_throw = save_did_throw;
510 current_exception = save_current_exception;
511 restore_vimvars(&vvsave);
512 if (must_redraw != 0)
513 need_update_screen = TRUE;
514 must_redraw = must_redraw > save_must_redraw
515 ? must_redraw : save_must_redraw;
516 set_pressedreturn(save_ex_pressedreturn);
517 may_garbage_collect = save_may_garbage_collect;
518
519 // Only fire the timer again if it repeats and stop_timer() wasn't
520 // called while inside the callback (tr_id == -1).
521 if (timer->tr_repeat != 0 && timer->tr_id != -1
522 && timer->tr_emsg_count < 3)
523 {
524 profile_setlimit(timer->tr_interval, &timer->tr_due);
525 this_due = proftime_time_left(&timer->tr_due, &now);
526 if (this_due < 1)
527 this_due = 1;
528 if (timer->tr_repeat > 0)
529 --timer->tr_repeat;
530 }
531 else
532 {
533 this_due = -1;
534 remove_timer(timer);
535 free_timer(timer);
536 }
537 }
538 if (this_due > 0 && (next_due == -1 || next_due > this_due))
539 next_due = this_due;
540 }
541
542 if (did_one)
543 redraw_after_callback(need_update_screen);
544
545 #ifdef FEAT_BEVAL_TERM
546 if (bevalexpr_due_set)
547 {
548 this_due = proftime_time_left(&bevalexpr_due, &now);
549 if (this_due <= 1)
550 {
551 bevalexpr_due_set = FALSE;
552 if (balloonEval == NULL)
553 {
554 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
555 balloonEvalForTerm = TRUE;
556 }
557 if (balloonEval != NULL)
558 {
559 general_beval_cb(balloonEval, 0);
560 setcursor();
561 out_flush();
562 }
563 }
564 else if (next_due == -1 || next_due > this_due)
565 next_due = this_due;
566 }
567 #endif
568 #ifdef FEAT_TERMINAL
569 // Some terminal windows may need their buffer updated.
570 next_due = term_check_timers(next_due, &now);
571 #endif
572
573 return current_id != last_timer_id ? 1 : next_due;
574 }
575
576 /*
577 * Find a timer by ID. Returns NULL if not found;
578 */
579 static timer_T *
580 find_timer(long id)
581 {
582 timer_T *timer;
583
584 if (id >= 0)
585 {
586 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
587 if (timer->tr_id == id)
588 return timer;
589 }
590 return NULL;
591 }
592
593
594 /*
595 * Stop a timer and delete it.
596 */
597 void
598 stop_timer(timer_T *timer)
599 {
600 if (timer->tr_firing)
601 // Free the timer after the callback returns.
602 timer->tr_id = -1;
603 else
604 {
605 remove_timer(timer);
606 free_timer(timer);
607 }
608 }
609
610 static void
611 stop_all_timers(void)
612 {
613 timer_T *timer;
614 timer_T *timer_next;
615
616 for (timer = first_timer; timer != NULL; timer = timer_next)
617 {
618 timer_next = timer->tr_next;
619 stop_timer(timer);
620 }
621 }
622
623 static void
624 add_timer_info(typval_T *rettv, timer_T *timer)
625 {
626 list_T *list = rettv->vval.v_list;
627 dict_T *dict = dict_alloc();
628 dictitem_T *di;
629 long remaining;
630 proftime_T now;
631
632 if (dict == NULL)
633 return;
634 list_append_dict(list, dict);
635
636 dict_add_number(dict, "id", timer->tr_id);
637 dict_add_number(dict, "time", (long)timer->tr_interval);
638
639 profile_start(&now);
640 remaining = proftime_time_left(&timer->tr_due, &now);
641 dict_add_number(dict, "remaining", (long)remaining);
642
643 dict_add_number(dict, "repeat",
644 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1));
645 dict_add_number(dict, "paused", (long)(timer->tr_paused));
646
647 di = dictitem_alloc((char_u *)"callback");
648 if (di != NULL)
649 {
650 if (dict_add(dict, di) == FAIL)
651 vim_free(di);
652 else
653 put_callback(&timer->tr_callback, &di->di_tv);
654 }
655 }
656
657 static void
658 add_timer_info_all(typval_T *rettv)
659 {
660 timer_T *timer;
661
662 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
663 if (timer->tr_id != -1)
664 add_timer_info(rettv, timer);
665 }
666
667 /*
668 * Mark references in partials of timers.
669 */
670 int
671 set_ref_in_timer(int copyID)
672 {
673 int abort = FALSE;
674 timer_T *timer;
675 typval_T tv;
676
677 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
678 {
679 if (timer->tr_callback.cb_partial != NULL)
680 {
681 tv.v_type = VAR_PARTIAL;
682 tv.vval.v_partial = timer->tr_callback.cb_partial;
683 }
684 else
685 {
686 tv.v_type = VAR_FUNC;
687 tv.vval.v_string = timer->tr_callback.cb_name;
688 }
689 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
690 }
691 return abort;
692 }
693
694 # if defined(EXITFREE) || defined(PROTO)
695 void
696 timer_free_all()
697 {
698 timer_T *timer;
699
700 while (first_timer != NULL)
701 {
702 timer = first_timer;
703 remove_timer(timer);
704 free_timer(timer);
705 }
706 }
707 # endif
708
709 /*
710 * "timer_info([timer])" function
711 */
712 void
713 f_timer_info(typval_T *argvars, typval_T *rettv)
714 {
715 timer_T *timer = NULL;
716
717 if (rettv_list_alloc(rettv) != OK)
718 return;
719 if (argvars[0].v_type != VAR_UNKNOWN)
720 {
721 if (argvars[0].v_type != VAR_NUMBER)
722 emsg(_(e_number_exp));
723 else
724 {
725 timer = find_timer((int)tv_get_number(&argvars[0]));
726 if (timer != NULL)
727 add_timer_info(rettv, timer);
728 }
729 }
730 else
731 add_timer_info_all(rettv);
732 }
733
734 /*
735 * "timer_pause(timer, paused)" function
736 */
737 void
738 f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
739 {
740 timer_T *timer = NULL;
741 int paused = (int)tv_get_number(&argvars[1]);
742
743 if (argvars[0].v_type != VAR_NUMBER)
744 emsg(_(e_number_exp));
745 else
746 {
747 timer = find_timer((int)tv_get_number(&argvars[0]));
748 if (timer != NULL)
749 timer->tr_paused = paused;
750 }
751 }
752
753 /*
754 * "timer_start(time, callback [, options])" function
755 */
756 void
757 f_timer_start(typval_T *argvars, typval_T *rettv)
758 {
759 long msec = (long)tv_get_number(&argvars[0]);
760 timer_T *timer;
761 int repeat = 0;
762 callback_T callback;
763 dict_T *dict;
764
765 rettv->vval.v_number = -1;
766 if (check_secure())
767 return;
768 if (argvars[2].v_type != VAR_UNKNOWN)
769 {
770 if (argvars[2].v_type != VAR_DICT
771 || (dict = argvars[2].vval.v_dict) == NULL)
772 {
773 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
774 return;
775 }
776 if (dict_find(dict, (char_u *)"repeat", -1) != NULL)
777 repeat = dict_get_number(dict, (char_u *)"repeat");
778 }
779
780 callback = get_callback(&argvars[1]);
781 if (callback.cb_name == NULL)
782 return;
783
784 timer = create_timer(msec, repeat);
785 if (timer == NULL)
786 free_callback(&callback);
787 else
788 {
789 set_callback(&timer->tr_callback, &callback);
790 rettv->vval.v_number = (varnumber_T)timer->tr_id;
791 }
792 }
793
794 /*
795 * "timer_stop(timer)" function
796 */
797 void
798 f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
799 {
800 timer_T *timer;
801
802 if (argvars[0].v_type != VAR_NUMBER)
803 {
804 emsg(_(e_number_exp));
805 return;
806 }
807 timer = find_timer((int)tv_get_number(&argvars[0]));
808 if (timer != NULL)
809 stop_timer(timer);
810 }
811
812 /*
813 * "timer_stopall()" function
814 */
815 void
816 f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
817 {
818 stop_all_timers();
819 }
820
821 # endif // FEAT_TIMERS
822
823 # if defined(STARTUPTIME) || defined(PROTO)
824 static struct timeval prev_timeval;
825
826 # ifdef MSWIN
827 /*
828 * Windows doesn't have gettimeofday(), although it does have struct timeval.
829 */
830 static int
831 gettimeofday(struct timeval *tv, char *dummy UNUSED)
832 {
833 long t = clock();
834 tv->tv_sec = t / CLOCKS_PER_SEC;
835 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
836 return 0;
837 }
838 # endif
839
840 /*
841 * Save the previous time before doing something that could nest.
842 * set "*tv_rel" to the time elapsed so far.
843 */
844 void
845 time_push(void *tv_rel, void *tv_start)
846 {
847 *((struct timeval *)tv_rel) = prev_timeval;
848 gettimeofday(&prev_timeval, NULL);
849 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
850 - ((struct timeval *)tv_rel)->tv_usec;
851 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
852 - ((struct timeval *)tv_rel)->tv_sec;
853 if (((struct timeval *)tv_rel)->tv_usec < 0)
854 {
855 ((struct timeval *)tv_rel)->tv_usec += 1000000;
856 --((struct timeval *)tv_rel)->tv_sec;
857 }
858 *(struct timeval *)tv_start = prev_timeval;
859 }
860
861 /*
862 * Compute the previous time after doing something that could nest.
863 * Subtract "*tp" from prev_timeval;
864 * Note: The arguments are (void *) to avoid trouble with systems that don't
865 * have struct timeval.
866 */
867 void
868 time_pop(
869 void *tp) // actually (struct timeval *)
870 {
871 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
872 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
873 if (prev_timeval.tv_usec < 0)
874 {
875 prev_timeval.tv_usec += 1000000;
876 --prev_timeval.tv_sec;
877 }
878 }
879
880 static void
881 time_diff(struct timeval *then, struct timeval *now)
882 {
883 long usec;
884 long msec;
885
886 usec = now->tv_usec - then->tv_usec;
887 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
888 usec = usec % 1000L;
889 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
890 }
891
892 void
893 time_msg(
894 char *mesg,
895 void *tv_start) // only for do_source: start time; actually
896 // (struct timeval *)
897 {
898 static struct timeval start;
899 struct timeval now;
900
901 if (time_fd != NULL)
902 {
903 if (strstr(mesg, "STARTING") != NULL)
904 {
905 gettimeofday(&start, NULL);
906 prev_timeval = start;
907 fprintf(time_fd, "\n\ntimes in msec\n");
908 fprintf(time_fd, " clock self+sourced self: sourced script\n");
909 fprintf(time_fd, " clock elapsed: other lines\n\n");
910 }
911 gettimeofday(&now, NULL);
912 time_diff(&start, &now);
913 if (((struct timeval *)tv_start) != NULL)
914 {
915 fprintf(time_fd, " ");
916 time_diff(((struct timeval *)tv_start), &now);
917 }
918 fprintf(time_fd, " ");
919 time_diff(&prev_timeval, &now);
920 prev_timeval = now;
921 fprintf(time_fd, ": %s\n", mesg);
922 }
923 }
924 # endif // STARTUPTIME
925 #endif // FEAT_EVAL
926
927 #if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
928 /*
929 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
930 * Returns -1 when encountering EOF.
931 */
932 time_T
933 get8ctime(FILE *fd)
934 {
935 int c;
936 time_T n = 0;
937 int i;
938
939 for (i = 0; i < 8; ++i)
940 {
941 c = getc(fd);
942 if (c == EOF) return -1;
943 n = (n << 8) + c;
944 }
945 return n;
946 }
947
948 #ifdef _MSC_VER
949 # if (_MSC_VER <= 1200)
950 // This line is required for VC6 without the service pack. Also see the
951 // matching #pragma below.
952 # pragma optimize("", off)
953 # endif
954 #endif
955
956 /*
957 * Write time_T to file "fd" in 8 bytes.
958 * Returns FAIL when the write failed.
959 */
960 int
961 put_time(FILE *fd, time_T the_time)
962 {
963 char_u buf[8];
964
965 time_to_bytes(the_time, buf);
966 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
967 }
968
969 /*
970 * Write time_T to "buf[8]".
971 */
972 void
973 time_to_bytes(time_T the_time, char_u *buf)
974 {
975 int c;
976 int i;
977 int bi = 0;
978 time_T wtime = the_time;
979
980 // time_T can be up to 8 bytes in size, more than long_u, thus we
981 // can't use put_bytes() here.
982 // Another problem is that ">>" may do an arithmetic shift that keeps the
983 // sign. This happens for large values of wtime. A cast to long_u may
984 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
985 // it's safe to assume that long_u is 4 bytes or more and when using 8
986 // bytes the top bit won't be set.
987 for (i = 7; i >= 0; --i)
988 {
989 if (i + 1 > (int)sizeof(time_T))
990 // ">>" doesn't work well when shifting more bits than avail
991 buf[bi++] = 0;
992 else
993 {
994 #if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
995 c = (int)(wtime >> (i * 8));
996 #else
997 c = (int)((long_u)wtime >> (i * 8));
998 #endif
999 buf[bi++] = c;
1000 }
1001 }
1002 }
1003
1004 #ifdef _MSC_VER
1005 # if (_MSC_VER <= 1200)
1006 # pragma optimize("", on)
1007 # endif
1008 #endif
1009
1010 #endif
1011
1012 /*
1013 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1014 */
1015 void
1016 add_time(char_u *buf, size_t buflen, time_t tt)
1017 {
1018 #ifdef HAVE_STRFTIME
1019 struct tm tmval;
1020 struct tm *curtime;
1021
1022 if (vim_time() - tt >= 100)
1023 {
1024 curtime = vim_localtime(&tt, &tmval);
1025 if (vim_time() - tt < (60L * 60L * 12L))
1026 // within 12 hours
1027 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
1028 else
1029 // longer ago
1030 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1031 }
1032 else
1033 #endif
1034 {
1035 long seconds = (long)(vim_time() - tt);
1036
1037 vim_snprintf((char *)buf, buflen,
1038 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1039 seconds);
1040 }
1041 }