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