blob: 53ac8531b34964d45a02a19b03214a2f5443c53a [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 {
Bram Moolenaar7e935772022-01-20 14:25:57 +000096 // xgettext:no-c-format
Bram Moolenaar0a8fed62020-02-14 13:22:17 +010097 (void)strftime(buf, sizeof(buf) - 1, _("%a %b %d %H:%M:%S %Y"),
98 curtime);
99# ifdef MSWIN
100 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
101 {
102 char_u *to_free = NULL;
103 int len;
104
105 acp_to_enc((char_u *)buf, (int)strlen(buf), &to_free, &len);
106 if (to_free != NULL)
107 {
108 STRCPY(buf, to_free);
109 vim_free(to_free);
110 }
111 }
112# endif
113 }
114#else
115 STRCPY(buf, "(unknown)");
116#endif
117 if (add_newline)
118 STRCAT(buf, "\n");
119 return buf;
120}
121
122#if defined(FEAT_EVAL) || defined(PROTO)
123
124#if defined(MACOS_X)
125# include <time.h> // for time_t
126#endif
127
128/*
129 * "localtime()" function
130 */
131 void
132f_localtime(typval_T *argvars UNUSED, typval_T *rettv)
133{
134 rettv->vval.v_number = (varnumber_T)time(NULL);
135}
136
137# if defined(FEAT_RELTIME)
138/*
139 * Convert a List to proftime_T.
140 * Return FAIL when there is something wrong.
141 */
142 static int
143list2proftime(typval_T *arg, proftime_T *tm)
144{
145 long n1, n2;
146 int error = FALSE;
147
148 if (arg->v_type != VAR_LIST || arg->vval.v_list == NULL
149 || arg->vval.v_list->lv_len != 2)
150 return FAIL;
151 n1 = list_find_nr(arg->vval.v_list, 0L, &error);
152 n2 = list_find_nr(arg->vval.v_list, 1L, &error);
153# ifdef MSWIN
154 tm->HighPart = n1;
155 tm->LowPart = n2;
156# else
157 tm->tv_sec = n1;
158 tm->tv_usec = n2;
159# endif
160 return error ? FAIL : OK;
161}
162# endif // FEAT_RELTIME
163
164/*
165 * "reltime()" function
166 */
167 void
168f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
169{
170# ifdef FEAT_RELTIME
171 proftime_T res;
172 proftime_T start;
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200173 long n1, n2;
174
175 if (rettv_list_alloc(rettv) != OK)
176 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100177
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200178 if (in_vim9script()
179 && (check_for_opt_list_arg(argvars, 0) == FAIL
180 || (argvars[0].v_type != VAR_UNKNOWN
181 && check_for_opt_list_arg(argvars, 1) == FAIL)))
182 return;
183
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100184 if (argvars[0].v_type == VAR_UNKNOWN)
185 {
186 // No arguments: get current time.
187 profile_start(&res);
188 }
189 else if (argvars[1].v_type == VAR_UNKNOWN)
190 {
191 if (list2proftime(&argvars[0], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200192 {
193 if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000194 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100195 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200196 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100197 profile_end(&res);
198 }
199 else
200 {
201 // Two arguments: compute the difference.
202 if (list2proftime(&argvars[0], &start) == FAIL
203 || list2proftime(&argvars[1], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200204 {
205 if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000206 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100207 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200208 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100209 profile_sub(&res, &start);
210 }
211
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100212# ifdef MSWIN
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200213 n1 = res.HighPart;
214 n2 = res.LowPart;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100215# else
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200216 n1 = res.tv_sec;
217 n2 = res.tv_usec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100218# endif
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200219 list_append_number(rettv->vval.v_list, (varnumber_T)n1);
220 list_append_number(rettv->vval.v_list, (varnumber_T)n2);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100221# endif
222}
223
224# ifdef FEAT_FLOAT
225/*
226 * "reltimefloat()" function
227 */
228 void
229f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv)
230{
231# ifdef FEAT_RELTIME
232 proftime_T tm;
233# endif
234
235 rettv->v_type = VAR_FLOAT;
236 rettv->vval.v_float = 0;
237# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200238 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
239 return;
240
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100241 if (list2proftime(&argvars[0], &tm) == OK)
242 rettv->vval.v_float = profile_float(&tm);
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200243 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000244 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100245# endif
246}
247# endif
248
249/*
250 * "reltimestr()" function
251 */
252 void
253f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
254{
255# ifdef FEAT_RELTIME
256 proftime_T tm;
257# endif
258
259 rettv->v_type = VAR_STRING;
260 rettv->vval.v_string = NULL;
261# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200262 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
263 return;
264
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100265 if (list2proftime(&argvars[0], &tm) == OK)
266 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200267 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000268 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100269# endif
270}
271
272# if defined(HAVE_STRFTIME) || defined(PROTO)
273/*
274 * "strftime({format}[, {time}])" function
275 */
276 void
277f_strftime(typval_T *argvars, typval_T *rettv)
278{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100279 struct tm tmval;
280 struct tm *curtime;
281 time_t seconds;
282 char_u *p;
283
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200284 if (in_vim9script()
285 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200286 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200287 return;
288
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100289 rettv->v_type = VAR_STRING;
290
291 p = tv_get_string(&argvars[0]);
292 if (argvars[1].v_type == VAR_UNKNOWN)
293 seconds = time(NULL);
294 else
295 seconds = (time_t)tv_get_number(&argvars[1]);
296 curtime = vim_localtime(&seconds, &tmval);
297 // MSVC returns NULL for an invalid value of seconds.
298 if (curtime == NULL)
299 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
300 else
301 {
K.Takata2c4a1d02021-05-28 15:49:34 +0200302# ifdef MSWIN
303 WCHAR result_buf[256];
304 WCHAR *wp;
305
306 wp = enc_to_utf16(p, NULL);
307 if (wp != NULL)
K.Takataeeec2542021-06-02 13:28:16 +0200308 (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
K.Takata2c4a1d02021-05-28 15:49:34 +0200309 else
310 result_buf[0] = NUL;
311 rettv->vval.v_string = utf16_to_enc(result_buf, NULL);
312 vim_free(wp);
313# else
314 char_u result_buf[256];
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100315 vimconv_T conv;
316 char_u *enc;
317
318 conv.vc_type = CONV_NONE;
319 enc = enc_locale();
320 convert_setup(&conv, p_enc, enc);
321 if (conv.vc_type != CONV_NONE)
322 p = string_convert(&conv, p, NULL);
323 if (p != NULL)
324 (void)strftime((char *)result_buf, sizeof(result_buf),
325 (char *)p, curtime);
326 else
327 result_buf[0] = NUL;
328
329 if (conv.vc_type != CONV_NONE)
330 vim_free(p);
331 convert_setup(&conv, enc, p_enc);
332 if (conv.vc_type != CONV_NONE)
333 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
334 else
335 rettv->vval.v_string = vim_strsave(result_buf);
336
337 // Release conversion descriptors
338 convert_setup(&conv, NULL, NULL);
339 vim_free(enc);
K.Takata2c4a1d02021-05-28 15:49:34 +0200340# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100341 }
342}
343# endif
344
345# if defined(HAVE_STRPTIME) || defined(PROTO)
346/*
347 * "strptime({format}, {timestring})" function
348 */
349 void
350f_strptime(typval_T *argvars, typval_T *rettv)
351{
352 struct tm tmval;
353 char_u *fmt;
354 char_u *str;
355 vimconv_T conv;
356 char_u *enc;
357
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200358 if (in_vim9script()
359 && (check_for_string_arg(argvars, 0) == FAIL
360 || check_for_string_arg(argvars, 1) == FAIL))
361 return;
362
Bram Moolenaara80faa82020-04-12 19:37:17 +0200363 CLEAR_FIELD(tmval);
Bram Moolenaarea1233f2020-06-10 16:54:13 +0200364 tmval.tm_isdst = -1;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100365 fmt = tv_get_string(&argvars[0]);
366 str = tv_get_string(&argvars[1]);
367
368 conv.vc_type = CONV_NONE;
369 enc = enc_locale();
370 convert_setup(&conv, p_enc, enc);
371 if (conv.vc_type != CONV_NONE)
372 fmt = string_convert(&conv, fmt, NULL);
373 if (fmt == NULL
374 || strptime((char *)str, (char *)fmt, &tmval) == NULL
375 || (rettv->vval.v_number = mktime(&tmval)) == -1)
376 rettv->vval.v_number = 0;
377
378 if (conv.vc_type != CONV_NONE)
379 vim_free(fmt);
380 convert_setup(&conv, NULL, NULL);
381 vim_free(enc);
382}
383# endif
384
385# if defined(FEAT_TIMERS) || defined(PROTO)
386static timer_T *first_timer = NULL;
387static long last_timer_id = 0;
388
389/*
390 * Return time left until "due". Negative if past "due".
391 */
392 long
393proftime_time_left(proftime_T *due, proftime_T *now)
394{
395# ifdef MSWIN
396 LARGE_INTEGER fr;
397
398 if (now->QuadPart > due->QuadPart)
399 return 0;
400 QueryPerformanceFrequency(&fr);
401 return (long)(((double)(due->QuadPart - now->QuadPart)
402 / (double)fr.QuadPart) * 1000);
403# else
404 if (now->tv_sec > due->tv_sec)
405 return 0;
406 return (due->tv_sec - now->tv_sec) * 1000
407 + (due->tv_usec - now->tv_usec) / 1000;
408# endif
409}
410
411/*
412 * Insert a timer in the list of timers.
413 */
414 static void
415insert_timer(timer_T *timer)
416{
417 timer->tr_next = first_timer;
418 timer->tr_prev = NULL;
419 if (first_timer != NULL)
420 first_timer->tr_prev = timer;
421 first_timer = timer;
422 did_add_timer = TRUE;
423}
424
425/*
426 * Take a timer out of the list of timers.
427 */
428 static void
429remove_timer(timer_T *timer)
430{
431 if (timer->tr_prev == NULL)
432 first_timer = timer->tr_next;
433 else
434 timer->tr_prev->tr_next = timer->tr_next;
435 if (timer->tr_next != NULL)
436 timer->tr_next->tr_prev = timer->tr_prev;
437}
438
439 static void
440free_timer(timer_T *timer)
441{
442 free_callback(&timer->tr_callback);
443 vim_free(timer);
444}
445
446/*
447 * Create a timer and return it. NULL if out of memory.
448 * Caller should set the callback.
449 */
450 timer_T *
451create_timer(long msec, int repeat)
452{
453 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
454 long prev_id = last_timer_id;
455
456 if (timer == NULL)
457 return NULL;
458 if (++last_timer_id <= prev_id)
459 // Overflow! Might cause duplicates...
460 last_timer_id = 0;
461 timer->tr_id = last_timer_id;
462 insert_timer(timer);
463 if (repeat != 0)
464 timer->tr_repeat = repeat - 1;
465 timer->tr_interval = msec;
466
467 profile_setlimit(msec, &timer->tr_due);
468 return timer;
469}
470
471/*
472 * Invoke the callback of "timer".
473 */
474 static void
475timer_callback(timer_T *timer)
476{
477 typval_T rettv;
478 typval_T argv[2];
479
480 argv[0].v_type = VAR_NUMBER;
481 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
482 argv[1].v_type = VAR_UNKNOWN;
483
Bram Moolenaar745b9382022-01-27 13:55:35 +0000484 rettv.v_type = VAR_UNKNOWN;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100485 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
486 clear_tv(&rettv);
487}
488
489/*
490 * Call timers that are due.
491 * Return the time in msec until the next timer is due.
492 * Returns -1 if there are no pending timers.
493 */
494 long
495check_due_timer(void)
496{
497 timer_T *timer;
498 timer_T *timer_next;
499 long this_due;
500 long next_due = -1;
501 proftime_T now;
502 int did_one = FALSE;
503 int need_update_screen = FALSE;
504 long current_id = last_timer_id;
505
Bram Moolenaar48d0ac72022-01-07 20:40:08 +0000506 // Don't run any timers while exiting, dealing with an error or at the
507 // debug prompt.
508 if (exiting || aborting() || debug_mode)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100509 return next_due;
510
511 profile_start(&now);
512 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
513 {
514 timer_next = timer->tr_next;
515
516 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
517 continue;
518 this_due = proftime_time_left(&timer->tr_due, &now);
519 if (this_due <= 1)
520 {
521 // Save and restore a lot of flags, because the timer fires while
522 // waiting for a character, which might be halfway a command.
523 int save_timer_busy = timer_busy;
524 int save_vgetc_busy = vgetc_busy;
525 int save_did_emsg = did_emsg;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200526 int prev_uncaught_emsg = uncaught_emsg;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100527 int save_called_emsg = called_emsg;
528 int save_must_redraw = must_redraw;
529 int save_trylevel = trylevel;
530 int save_did_throw = did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100531 int save_need_rethrow = need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100532 int save_ex_pressedreturn = get_pressedreturn();
533 int save_may_garbage_collect = may_garbage_collect;
534 except_T *save_current_exception = current_exception;
535 vimvars_save_T vvsave;
536
537 // Create a scope for running the timer callback, ignoring most of
538 // the current scope, such as being inside a try/catch.
539 timer_busy = timer_busy > 0 || vgetc_busy > 0;
540 vgetc_busy = 0;
541 called_emsg = 0;
542 did_emsg = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100543 must_redraw = 0;
544 trylevel = 0;
545 did_throw = FALSE;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100546 need_rethrow = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100547 current_exception = NULL;
548 may_garbage_collect = FALSE;
549 save_vimvars(&vvsave);
550
Bram Moolenaar22286892020-11-05 20:50:51 +0100551 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100552 timer->tr_firing = TRUE;
553 timer_callback(timer);
554 timer->tr_firing = FALSE;
555
Bram Moolenaar22286892020-11-05 20:50:51 +0100556 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100557 timer_next = timer->tr_next;
558 did_one = TRUE;
559 timer_busy = save_timer_busy;
560 vgetc_busy = save_vgetc_busy;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200561 if (uncaught_emsg > prev_uncaught_emsg)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100562 ++timer->tr_emsg_count;
563 did_emsg = save_did_emsg;
564 called_emsg = save_called_emsg;
565 trylevel = save_trylevel;
566 did_throw = save_did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100567 need_rethrow = save_need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100568 current_exception = save_current_exception;
569 restore_vimvars(&vvsave);
570 if (must_redraw != 0)
571 need_update_screen = TRUE;
572 must_redraw = must_redraw > save_must_redraw
573 ? must_redraw : save_must_redraw;
574 set_pressedreturn(save_ex_pressedreturn);
575 may_garbage_collect = save_may_garbage_collect;
576
577 // Only fire the timer again if it repeats and stop_timer() wasn't
578 // called while inside the callback (tr_id == -1).
579 if (timer->tr_repeat != 0 && timer->tr_id != -1
580 && timer->tr_emsg_count < 3)
581 {
582 profile_setlimit(timer->tr_interval, &timer->tr_due);
583 this_due = proftime_time_left(&timer->tr_due, &now);
584 if (this_due < 1)
585 this_due = 1;
586 if (timer->tr_repeat > 0)
587 --timer->tr_repeat;
588 }
589 else
590 {
591 this_due = -1;
592 remove_timer(timer);
593 free_timer(timer);
594 }
595 }
596 if (this_due > 0 && (next_due == -1 || next_due > this_due))
597 next_due = this_due;
598 }
599
600 if (did_one)
Bram Moolenaare5050712021-12-09 10:51:05 +0000601 redraw_after_callback(need_update_screen, FALSE);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100602
603#ifdef FEAT_BEVAL_TERM
604 if (bevalexpr_due_set)
605 {
606 this_due = proftime_time_left(&bevalexpr_due, &now);
607 if (this_due <= 1)
608 {
609 bevalexpr_due_set = FALSE;
610 if (balloonEval == NULL)
611 {
612 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
613 balloonEvalForTerm = TRUE;
614 }
615 if (balloonEval != NULL)
616 {
617 general_beval_cb(balloonEval, 0);
618 setcursor();
619 out_flush();
620 }
621 }
622 else if (next_due == -1 || next_due > this_due)
623 next_due = this_due;
624 }
625#endif
626#ifdef FEAT_TERMINAL
627 // Some terminal windows may need their buffer updated.
628 next_due = term_check_timers(next_due, &now);
629#endif
630
631 return current_id != last_timer_id ? 1 : next_due;
632}
633
634/*
635 * Find a timer by ID. Returns NULL if not found;
636 */
637 static timer_T *
638find_timer(long id)
639{
640 timer_T *timer;
641
642 if (id >= 0)
643 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200644 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100645 if (timer->tr_id == id)
646 return timer;
647 }
648 return NULL;
649}
650
651
652/*
653 * Stop a timer and delete it.
654 */
655 void
656stop_timer(timer_T *timer)
657{
658 if (timer->tr_firing)
659 // Free the timer after the callback returns.
660 timer->tr_id = -1;
661 else
662 {
663 remove_timer(timer);
664 free_timer(timer);
665 }
666}
667
668 static void
669stop_all_timers(void)
670{
671 timer_T *timer;
672 timer_T *timer_next;
673
674 for (timer = first_timer; timer != NULL; timer = timer_next)
675 {
676 timer_next = timer->tr_next;
677 stop_timer(timer);
678 }
679}
680
681 static void
682add_timer_info(typval_T *rettv, timer_T *timer)
683{
684 list_T *list = rettv->vval.v_list;
685 dict_T *dict = dict_alloc();
686 dictitem_T *di;
687 long remaining;
688 proftime_T now;
689
690 if (dict == NULL)
691 return;
692 list_append_dict(list, dict);
693
694 dict_add_number(dict, "id", timer->tr_id);
695 dict_add_number(dict, "time", (long)timer->tr_interval);
696
697 profile_start(&now);
698 remaining = proftime_time_left(&timer->tr_due, &now);
699 dict_add_number(dict, "remaining", (long)remaining);
700
701 dict_add_number(dict, "repeat",
Bram Moolenaar95b2dd02021-12-09 18:42:57 +0000702 (long)(timer->tr_repeat < 0 ? -1
703 : timer->tr_repeat + (timer->tr_firing ? 0 : 1)));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100704 dict_add_number(dict, "paused", (long)(timer->tr_paused));
705
706 di = dictitem_alloc((char_u *)"callback");
707 if (di != NULL)
708 {
709 if (dict_add(dict, di) == FAIL)
710 vim_free(di);
711 else
712 put_callback(&timer->tr_callback, &di->di_tv);
713 }
714}
715
716 static void
717add_timer_info_all(typval_T *rettv)
718{
719 timer_T *timer;
720
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200721 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100722 if (timer->tr_id != -1)
723 add_timer_info(rettv, timer);
724}
725
726/*
727 * Mark references in partials of timers.
728 */
729 int
730set_ref_in_timer(int copyID)
731{
732 int abort = FALSE;
733 timer_T *timer;
734 typval_T tv;
735
736 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
737 {
738 if (timer->tr_callback.cb_partial != NULL)
739 {
740 tv.v_type = VAR_PARTIAL;
741 tv.vval.v_partial = timer->tr_callback.cb_partial;
742 }
743 else
744 {
745 tv.v_type = VAR_FUNC;
746 tv.vval.v_string = timer->tr_callback.cb_name;
747 }
748 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
749 }
750 return abort;
751}
752
753# if defined(EXITFREE) || defined(PROTO)
754 void
755timer_free_all()
756{
757 timer_T *timer;
758
759 while (first_timer != NULL)
760 {
761 timer = first_timer;
762 remove_timer(timer);
763 free_timer(timer);
764 }
765}
766# endif
767
768/*
769 * "timer_info([timer])" function
770 */
771 void
772f_timer_info(typval_T *argvars, typval_T *rettv)
773{
774 timer_T *timer = NULL;
775
776 if (rettv_list_alloc(rettv) != OK)
777 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200778
779 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
780 return;
781
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100782 if (argvars[0].v_type != VAR_UNKNOWN)
783 {
784 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200785 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100786 else
787 {
788 timer = find_timer((int)tv_get_number(&argvars[0]));
789 if (timer != NULL)
790 add_timer_info(rettv, timer);
791 }
792 }
793 else
794 add_timer_info_all(rettv);
795}
796
797/*
798 * "timer_pause(timer, paused)" function
799 */
800 void
801f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
802{
803 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200804
805 if (in_vim9script()
806 && (check_for_number_arg(argvars, 0) == FAIL
807 || check_for_bool_arg(argvars, 1) == FAIL))
808 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100809
810 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200811 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100812 else
813 {
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200814 int paused = (int)tv_get_bool(&argvars[1]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100815 timer = find_timer((int)tv_get_number(&argvars[0]));
816 if (timer != NULL)
817 timer->tr_paused = paused;
818 }
819}
820
821/*
822 * "timer_start(time, callback [, options])" function
823 */
824 void
825f_timer_start(typval_T *argvars, typval_T *rettv)
826{
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200827 long msec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100828 timer_T *timer;
829 int repeat = 0;
830 callback_T callback;
831 dict_T *dict;
832
833 rettv->vval.v_number = -1;
834 if (check_secure())
835 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200836
837 if (in_vim9script()
838 && (check_for_number_arg(argvars, 0) == FAIL
839 || check_for_opt_dict_arg(argvars, 2) == FAIL))
840 return;
841
842 msec = (long)tv_get_number(&argvars[0]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100843 if (argvars[2].v_type != VAR_UNKNOWN)
844 {
845 if (argvars[2].v_type != VAR_DICT
846 || (dict = argvars[2].vval.v_dict) == NULL)
847 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000848 semsg(_(e_invalid_argument_str), tv_get_string(&argvars[2]));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100849 return;
850 }
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100851 if (dict_has_key(dict, "repeat"))
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100852 repeat = dict_get_number(dict, (char_u *)"repeat");
853 }
854
855 callback = get_callback(&argvars[1]);
856 if (callback.cb_name == NULL)
857 return;
Bram Moolenaar745b9382022-01-27 13:55:35 +0000858 if (in_vim9script() && *callback.cb_name == NUL)
859 {
860 // empty callback is not useful for a timer
861 emsg(_(e_invalid_callback_argument));
862 free_callback(&callback);
863 return;
864 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100865
866 timer = create_timer(msec, repeat);
867 if (timer == NULL)
868 free_callback(&callback);
869 else
870 {
871 set_callback(&timer->tr_callback, &callback);
872 rettv->vval.v_number = (varnumber_T)timer->tr_id;
873 }
874}
875
876/*
877 * "timer_stop(timer)" function
878 */
879 void
880f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
881{
882 timer_T *timer;
883
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200884 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
885 return;
886
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100887 if (argvars[0].v_type != VAR_NUMBER)
888 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200889 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100890 return;
891 }
892 timer = find_timer((int)tv_get_number(&argvars[0]));
893 if (timer != NULL)
894 stop_timer(timer);
895}
896
897/*
898 * "timer_stopall()" function
899 */
900 void
901f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
902{
903 stop_all_timers();
904}
905
906# endif // FEAT_TIMERS
907
908# if defined(STARTUPTIME) || defined(PROTO)
909static struct timeval prev_timeval;
910
911# ifdef MSWIN
912/*
913 * Windows doesn't have gettimeofday(), although it does have struct timeval.
914 */
915 static int
916gettimeofday(struct timeval *tv, char *dummy UNUSED)
917{
918 long t = clock();
919 tv->tv_sec = t / CLOCKS_PER_SEC;
920 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
921 return 0;
922}
923# endif
924
925/*
926 * Save the previous time before doing something that could nest.
927 * set "*tv_rel" to the time elapsed so far.
928 */
929 void
930time_push(void *tv_rel, void *tv_start)
931{
932 *((struct timeval *)tv_rel) = prev_timeval;
933 gettimeofday(&prev_timeval, NULL);
934 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
935 - ((struct timeval *)tv_rel)->tv_usec;
936 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
937 - ((struct timeval *)tv_rel)->tv_sec;
938 if (((struct timeval *)tv_rel)->tv_usec < 0)
939 {
940 ((struct timeval *)tv_rel)->tv_usec += 1000000;
941 --((struct timeval *)tv_rel)->tv_sec;
942 }
943 *(struct timeval *)tv_start = prev_timeval;
944}
945
946/*
947 * Compute the previous time after doing something that could nest.
948 * Subtract "*tp" from prev_timeval;
949 * Note: The arguments are (void *) to avoid trouble with systems that don't
950 * have struct timeval.
951 */
952 void
953time_pop(
954 void *tp) // actually (struct timeval *)
955{
956 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
957 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
958 if (prev_timeval.tv_usec < 0)
959 {
960 prev_timeval.tv_usec += 1000000;
961 --prev_timeval.tv_sec;
962 }
963}
964
965 static void
966time_diff(struct timeval *then, struct timeval *now)
967{
968 long usec;
969 long msec;
970
971 usec = now->tv_usec - then->tv_usec;
972 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
973 usec = usec % 1000L;
974 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
975}
976
977 void
978time_msg(
979 char *mesg,
980 void *tv_start) // only for do_source: start time; actually
981 // (struct timeval *)
982{
983 static struct timeval start;
984 struct timeval now;
985
986 if (time_fd != NULL)
987 {
988 if (strstr(mesg, "STARTING") != NULL)
989 {
990 gettimeofday(&start, NULL);
991 prev_timeval = start;
992 fprintf(time_fd, "\n\ntimes in msec\n");
993 fprintf(time_fd, " clock self+sourced self: sourced script\n");
994 fprintf(time_fd, " clock elapsed: other lines\n\n");
995 }
996 gettimeofday(&now, NULL);
997 time_diff(&start, &now);
998 if (((struct timeval *)tv_start) != NULL)
999 {
1000 fprintf(time_fd, " ");
1001 time_diff(((struct timeval *)tv_start), &now);
1002 }
1003 fprintf(time_fd, " ");
1004 time_diff(&prev_timeval, &now);
1005 prev_timeval = now;
1006 fprintf(time_fd, ": %s\n", mesg);
1007 }
1008}
1009# endif // STARTUPTIME
1010#endif // FEAT_EVAL
1011
1012#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1013/*
1014 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1015 * Returns -1 when encountering EOF.
1016 */
1017 time_T
1018get8ctime(FILE *fd)
1019{
1020 int c;
1021 time_T n = 0;
1022 int i;
1023
1024 for (i = 0; i < 8; ++i)
1025 {
1026 c = getc(fd);
1027 if (c == EOF) return -1;
1028 n = (n << 8) + c;
1029 }
1030 return n;
1031}
1032
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001033/*
1034 * Write time_T to file "fd" in 8 bytes.
1035 * Returns FAIL when the write failed.
1036 */
1037 int
1038put_time(FILE *fd, time_T the_time)
1039{
1040 char_u buf[8];
1041
1042 time_to_bytes(the_time, buf);
1043 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1044}
1045
1046/*
1047 * Write time_T to "buf[8]".
1048 */
1049 void
1050time_to_bytes(time_T the_time, char_u *buf)
1051{
1052 int c;
1053 int i;
1054 int bi = 0;
1055 time_T wtime = the_time;
1056
1057 // time_T can be up to 8 bytes in size, more than long_u, thus we
1058 // can't use put_bytes() here.
1059 // Another problem is that ">>" may do an arithmetic shift that keeps the
1060 // sign. This happens for large values of wtime. A cast to long_u may
1061 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1062 // it's safe to assume that long_u is 4 bytes or more and when using 8
1063 // bytes the top bit won't be set.
1064 for (i = 7; i >= 0; --i)
1065 {
1066 if (i + 1 > (int)sizeof(time_T))
1067 // ">>" doesn't work well when shifting more bits than avail
1068 buf[bi++] = 0;
1069 else
1070 {
K.Takatac351dc12022-01-24 11:24:08 +00001071# if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001072 c = (int)(wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001073# else
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001074 c = (int)((long_u)wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001075# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001076 buf[bi++] = c;
1077 }
1078 }
1079}
1080
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001081#endif
1082
1083/*
1084 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1085 */
1086 void
1087add_time(char_u *buf, size_t buflen, time_t tt)
1088{
1089#ifdef HAVE_STRFTIME
1090 struct tm tmval;
1091 struct tm *curtime;
1092
1093 if (vim_time() - tt >= 100)
1094 {
1095 curtime = vim_localtime(&tt, &tmval);
1096 if (vim_time() - tt < (60L * 60L * 12L))
1097 // within 12 hours
1098 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
1099 else
1100 // longer ago
1101 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1102 }
1103 else
1104#endif
1105 {
1106 long seconds = (long)(vim_time() - tt);
1107
1108 vim_snprintf((char *)buf, buflen,
1109 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1110 seconds);
1111 }
1112}