blob: f8e8c5afe29961fb0c0dcb6c9a038ac9d4134993 [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
Bram Moolenaar93a10962022-06-16 11:42:09 +0100175 if (rettv_list_alloc(rettv) == FAIL)
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200176 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
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100224/*
225 * "reltimefloat()" function
226 */
227 void
228f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv)
229{
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100230# ifdef FEAT_RELTIME
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100231 proftime_T tm;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100232# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100233
234 rettv->v_type = VAR_FLOAT;
235 rettv->vval.v_float = 0;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100236# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200237 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
238 return;
239
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100240 if (list2proftime(&argvars[0], &tm) == OK)
241 rettv->vval.v_float = profile_float(&tm);
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200242 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000243 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100244# endif
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100245}
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100246
247/*
248 * "reltimestr()" function
249 */
250 void
251f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
252{
253# ifdef FEAT_RELTIME
254 proftime_T tm;
255# endif
256
257 rettv->v_type = VAR_STRING;
258 rettv->vval.v_string = NULL;
259# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200260 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
261 return;
262
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100263 if (list2proftime(&argvars[0], &tm) == OK)
264 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200265 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000266 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100267# endif
268}
269
270# if defined(HAVE_STRFTIME) || defined(PROTO)
271/*
272 * "strftime({format}[, {time}])" function
273 */
274 void
275f_strftime(typval_T *argvars, typval_T *rettv)
276{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100277 struct tm tmval;
278 struct tm *curtime;
279 time_t seconds;
280 char_u *p;
281
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200282 if (in_vim9script()
283 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200284 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200285 return;
286
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100287 rettv->v_type = VAR_STRING;
288
289 p = tv_get_string(&argvars[0]);
290 if (argvars[1].v_type == VAR_UNKNOWN)
291 seconds = time(NULL);
292 else
293 seconds = (time_t)tv_get_number(&argvars[1]);
294 curtime = vim_localtime(&seconds, &tmval);
295 // MSVC returns NULL for an invalid value of seconds.
296 if (curtime == NULL)
297 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
298 else
299 {
K.Takata2c4a1d02021-05-28 15:49:34 +0200300# ifdef MSWIN
301 WCHAR result_buf[256];
302 WCHAR *wp;
303
304 wp = enc_to_utf16(p, NULL);
305 if (wp != NULL)
K.Takataeeec2542021-06-02 13:28:16 +0200306 (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
K.Takata2c4a1d02021-05-28 15:49:34 +0200307 else
308 result_buf[0] = NUL;
309 rettv->vval.v_string = utf16_to_enc(result_buf, NULL);
310 vim_free(wp);
311# else
312 char_u result_buf[256];
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100313 vimconv_T conv;
314 char_u *enc;
315
316 conv.vc_type = CONV_NONE;
317 enc = enc_locale();
318 convert_setup(&conv, p_enc, enc);
319 if (conv.vc_type != CONV_NONE)
320 p = string_convert(&conv, p, NULL);
321 if (p != NULL)
322 (void)strftime((char *)result_buf, sizeof(result_buf),
323 (char *)p, curtime);
324 else
325 result_buf[0] = NUL;
326
327 if (conv.vc_type != CONV_NONE)
328 vim_free(p);
329 convert_setup(&conv, enc, p_enc);
330 if (conv.vc_type != CONV_NONE)
331 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
332 else
333 rettv->vval.v_string = vim_strsave(result_buf);
334
335 // Release conversion descriptors
336 convert_setup(&conv, NULL, NULL);
337 vim_free(enc);
K.Takata2c4a1d02021-05-28 15:49:34 +0200338# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100339 }
340}
341# endif
342
343# if defined(HAVE_STRPTIME) || defined(PROTO)
344/*
345 * "strptime({format}, {timestring})" function
346 */
347 void
348f_strptime(typval_T *argvars, typval_T *rettv)
349{
350 struct tm tmval;
351 char_u *fmt;
352 char_u *str;
353 vimconv_T conv;
354 char_u *enc;
355
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200356 if (in_vim9script()
357 && (check_for_string_arg(argvars, 0) == FAIL
358 || check_for_string_arg(argvars, 1) == FAIL))
359 return;
360
Bram Moolenaara80faa82020-04-12 19:37:17 +0200361 CLEAR_FIELD(tmval);
Bram Moolenaarea1233f2020-06-10 16:54:13 +0200362 tmval.tm_isdst = -1;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100363 fmt = tv_get_string(&argvars[0]);
364 str = tv_get_string(&argvars[1]);
365
366 conv.vc_type = CONV_NONE;
367 enc = enc_locale();
368 convert_setup(&conv, p_enc, enc);
369 if (conv.vc_type != CONV_NONE)
370 fmt = string_convert(&conv, fmt, NULL);
371 if (fmt == NULL
372 || strptime((char *)str, (char *)fmt, &tmval) == NULL
373 || (rettv->vval.v_number = mktime(&tmval)) == -1)
374 rettv->vval.v_number = 0;
375
376 if (conv.vc_type != CONV_NONE)
377 vim_free(fmt);
378 convert_setup(&conv, NULL, NULL);
379 vim_free(enc);
380}
381# endif
382
383# if defined(FEAT_TIMERS) || defined(PROTO)
384static timer_T *first_timer = NULL;
385static long last_timer_id = 0;
386
387/*
388 * Return time left until "due". Negative if past "due".
389 */
390 long
391proftime_time_left(proftime_T *due, proftime_T *now)
392{
393# ifdef MSWIN
394 LARGE_INTEGER fr;
395
396 if (now->QuadPart > due->QuadPart)
397 return 0;
398 QueryPerformanceFrequency(&fr);
399 return (long)(((double)(due->QuadPart - now->QuadPart)
400 / (double)fr.QuadPart) * 1000);
401# else
402 if (now->tv_sec > due->tv_sec)
403 return 0;
404 return (due->tv_sec - now->tv_sec) * 1000
405 + (due->tv_usec - now->tv_usec) / 1000;
406# endif
407}
408
409/*
410 * Insert a timer in the list of timers.
411 */
412 static void
413insert_timer(timer_T *timer)
414{
415 timer->tr_next = first_timer;
416 timer->tr_prev = NULL;
417 if (first_timer != NULL)
418 first_timer->tr_prev = timer;
419 first_timer = timer;
420 did_add_timer = TRUE;
421}
422
423/*
424 * Take a timer out of the list of timers.
425 */
426 static void
427remove_timer(timer_T *timer)
428{
429 if (timer->tr_prev == NULL)
430 first_timer = timer->tr_next;
431 else
432 timer->tr_prev->tr_next = timer->tr_next;
433 if (timer->tr_next != NULL)
434 timer->tr_next->tr_prev = timer->tr_prev;
435}
436
437 static void
438free_timer(timer_T *timer)
439{
440 free_callback(&timer->tr_callback);
441 vim_free(timer);
442}
443
444/*
445 * Create a timer and return it. NULL if out of memory.
446 * Caller should set the callback.
447 */
448 timer_T *
449create_timer(long msec, int repeat)
450{
451 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
452 long prev_id = last_timer_id;
453
454 if (timer == NULL)
455 return NULL;
456 if (++last_timer_id <= prev_id)
457 // Overflow! Might cause duplicates...
458 last_timer_id = 0;
459 timer->tr_id = last_timer_id;
460 insert_timer(timer);
461 if (repeat != 0)
462 timer->tr_repeat = repeat - 1;
463 timer->tr_interval = msec;
464
Bram Moolenaar9198de32022-08-27 21:30:03 +0100465 timer_start(timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100466 return timer;
467}
468
469/*
Bram Moolenaar9198de32022-08-27 21:30:03 +0100470 * (Re)start a timer.
471 */
472 void
473timer_start(timer_T *timer)
474{
475 profile_setlimit(timer->tr_interval, &timer->tr_due);
476 timer->tr_paused = FALSE;
477}
478
479/*
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100480 * Invoke the callback of "timer".
481 */
482 static void
483timer_callback(timer_T *timer)
484{
485 typval_T rettv;
486 typval_T argv[2];
487
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100488#ifdef FEAT_JOB_CHANNEL
489 if (ch_log_active())
490 {
491 callback_T *cb = &timer->tr_callback;
492
493 ch_log(NULL, "invoking timer callback %s",
494 cb->cb_partial != NULL ? cb->cb_partial->pt_name : cb->cb_name);
495 }
496#endif
497
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100498 argv[0].v_type = VAR_NUMBER;
499 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
500 argv[1].v_type = VAR_UNKNOWN;
501
Bram Moolenaar745b9382022-01-27 13:55:35 +0000502 rettv.v_type = VAR_UNKNOWN;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100503 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
504 clear_tv(&rettv);
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100505
506#ifdef FEAT_JOB_CHANNEL
507 ch_log(NULL, "timer callback finished");
508#endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100509}
510
511/*
512 * Call timers that are due.
513 * Return the time in msec until the next timer is due.
514 * Returns -1 if there are no pending timers.
515 */
516 long
517check_due_timer(void)
518{
519 timer_T *timer;
520 timer_T *timer_next;
521 long this_due;
522 long next_due = -1;
523 proftime_T now;
524 int did_one = FALSE;
525 int need_update_screen = FALSE;
526 long current_id = last_timer_id;
527
Bram Moolenaar48d0ac72022-01-07 20:40:08 +0000528 // Don't run any timers while exiting, dealing with an error or at the
529 // debug prompt.
530 if (exiting || aborting() || debug_mode)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100531 return next_due;
532
533 profile_start(&now);
534 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
535 {
536 timer_next = timer->tr_next;
537
538 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
539 continue;
540 this_due = proftime_time_left(&timer->tr_due, &now);
541 if (this_due <= 1)
542 {
543 // Save and restore a lot of flags, because the timer fires while
544 // waiting for a character, which might be halfway a command.
545 int save_timer_busy = timer_busy;
546 int save_vgetc_busy = vgetc_busy;
547 int save_did_emsg = did_emsg;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200548 int prev_uncaught_emsg = uncaught_emsg;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100549 int save_called_emsg = called_emsg;
550 int save_must_redraw = must_redraw;
551 int save_trylevel = trylevel;
552 int save_did_throw = did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100553 int save_need_rethrow = need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100554 int save_ex_pressedreturn = get_pressedreturn();
555 int save_may_garbage_collect = may_garbage_collect;
556 except_T *save_current_exception = current_exception;
557 vimvars_save_T vvsave;
558
559 // Create a scope for running the timer callback, ignoring most of
560 // the current scope, such as being inside a try/catch.
561 timer_busy = timer_busy > 0 || vgetc_busy > 0;
562 vgetc_busy = 0;
563 called_emsg = 0;
564 did_emsg = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100565 must_redraw = 0;
566 trylevel = 0;
567 did_throw = FALSE;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100568 need_rethrow = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100569 current_exception = NULL;
570 may_garbage_collect = FALSE;
571 save_vimvars(&vvsave);
572
Bram Moolenaar22286892020-11-05 20:50:51 +0100573 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100574 timer->tr_firing = TRUE;
575 timer_callback(timer);
576 timer->tr_firing = FALSE;
577
Bram Moolenaar22286892020-11-05 20:50:51 +0100578 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100579 timer_next = timer->tr_next;
580 did_one = TRUE;
581 timer_busy = save_timer_busy;
582 vgetc_busy = save_vgetc_busy;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200583 if (uncaught_emsg > prev_uncaught_emsg)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100584 ++timer->tr_emsg_count;
585 did_emsg = save_did_emsg;
586 called_emsg = save_called_emsg;
587 trylevel = save_trylevel;
588 did_throw = save_did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100589 need_rethrow = save_need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100590 current_exception = save_current_exception;
591 restore_vimvars(&vvsave);
592 if (must_redraw != 0)
593 need_update_screen = TRUE;
594 must_redraw = must_redraw > save_must_redraw
595 ? must_redraw : save_must_redraw;
596 set_pressedreturn(save_ex_pressedreturn);
597 may_garbage_collect = save_may_garbage_collect;
598
599 // Only fire the timer again if it repeats and stop_timer() wasn't
600 // called while inside the callback (tr_id == -1).
601 if (timer->tr_repeat != 0 && timer->tr_id != -1
602 && timer->tr_emsg_count < 3)
603 {
604 profile_setlimit(timer->tr_interval, &timer->tr_due);
605 this_due = proftime_time_left(&timer->tr_due, &now);
606 if (this_due < 1)
607 this_due = 1;
608 if (timer->tr_repeat > 0)
609 --timer->tr_repeat;
610 }
611 else
612 {
613 this_due = -1;
Bram Moolenaar9198de32022-08-27 21:30:03 +0100614 if (timer->tr_keep)
615 timer->tr_paused = TRUE;
616 else
617 {
618 remove_timer(timer);
619 free_timer(timer);
620 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100621 }
622 }
623 if (this_due > 0 && (next_due == -1 || next_due > this_due))
624 next_due = this_due;
625 }
626
627 if (did_one)
Bram Moolenaare5050712021-12-09 10:51:05 +0000628 redraw_after_callback(need_update_screen, FALSE);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100629
630#ifdef FEAT_BEVAL_TERM
631 if (bevalexpr_due_set)
632 {
633 this_due = proftime_time_left(&bevalexpr_due, &now);
634 if (this_due <= 1)
635 {
636 bevalexpr_due_set = FALSE;
637 if (balloonEval == NULL)
638 {
639 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
640 balloonEvalForTerm = TRUE;
641 }
642 if (balloonEval != NULL)
643 {
644 general_beval_cb(balloonEval, 0);
645 setcursor();
646 out_flush();
647 }
648 }
649 else if (next_due == -1 || next_due > this_due)
650 next_due = this_due;
651 }
652#endif
653#ifdef FEAT_TERMINAL
654 // Some terminal windows may need their buffer updated.
655 next_due = term_check_timers(next_due, &now);
656#endif
657
658 return current_id != last_timer_id ? 1 : next_due;
659}
660
661/*
662 * Find a timer by ID. Returns NULL if not found;
663 */
664 static timer_T *
665find_timer(long id)
666{
667 timer_T *timer;
668
669 if (id >= 0)
670 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200671 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100672 if (timer->tr_id == id)
673 return timer;
674 }
675 return NULL;
676}
677
678
679/*
680 * Stop a timer and delete it.
681 */
682 void
683stop_timer(timer_T *timer)
684{
685 if (timer->tr_firing)
686 // Free the timer after the callback returns.
687 timer->tr_id = -1;
688 else
689 {
690 remove_timer(timer);
691 free_timer(timer);
692 }
693}
694
695 static void
696stop_all_timers(void)
697{
698 timer_T *timer;
699 timer_T *timer_next;
700
701 for (timer = first_timer; timer != NULL; timer = timer_next)
702 {
703 timer_next = timer->tr_next;
704 stop_timer(timer);
705 }
706}
707
708 static void
709add_timer_info(typval_T *rettv, timer_T *timer)
710{
711 list_T *list = rettv->vval.v_list;
712 dict_T *dict = dict_alloc();
713 dictitem_T *di;
714 long remaining;
715 proftime_T now;
716
717 if (dict == NULL)
718 return;
719 list_append_dict(list, dict);
720
721 dict_add_number(dict, "id", timer->tr_id);
722 dict_add_number(dict, "time", (long)timer->tr_interval);
723
724 profile_start(&now);
725 remaining = proftime_time_left(&timer->tr_due, &now);
726 dict_add_number(dict, "remaining", (long)remaining);
727
728 dict_add_number(dict, "repeat",
Bram Moolenaar95b2dd02021-12-09 18:42:57 +0000729 (long)(timer->tr_repeat < 0 ? -1
730 : timer->tr_repeat + (timer->tr_firing ? 0 : 1)));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100731 dict_add_number(dict, "paused", (long)(timer->tr_paused));
732
733 di = dictitem_alloc((char_u *)"callback");
734 if (di != NULL)
735 {
736 if (dict_add(dict, di) == FAIL)
737 vim_free(di);
738 else
739 put_callback(&timer->tr_callback, &di->di_tv);
740 }
741}
742
743 static void
744add_timer_info_all(typval_T *rettv)
745{
746 timer_T *timer;
747
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200748 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100749 if (timer->tr_id != -1)
750 add_timer_info(rettv, timer);
751}
752
753/*
754 * Mark references in partials of timers.
755 */
756 int
757set_ref_in_timer(int copyID)
758{
759 int abort = FALSE;
760 timer_T *timer;
761 typval_T tv;
762
763 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
764 {
765 if (timer->tr_callback.cb_partial != NULL)
766 {
767 tv.v_type = VAR_PARTIAL;
768 tv.vval.v_partial = timer->tr_callback.cb_partial;
769 }
770 else
771 {
772 tv.v_type = VAR_FUNC;
773 tv.vval.v_string = timer->tr_callback.cb_name;
774 }
775 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
776 }
777 return abort;
778}
779
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100780/*
781 * Return TRUE if "timer" exists in the list of timers.
782 */
783 int
784timer_valid(timer_T *timer)
785{
786 if (timer == NULL)
787 return FALSE;
788 for (timer_T *t = first_timer; t != NULL; t = t->tr_next)
789 if (t == timer)
790 return TRUE;
791 return FALSE;
792}
793
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100794# if defined(EXITFREE) || defined(PROTO)
795 void
796timer_free_all()
797{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100798 while (first_timer != NULL)
799 {
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100800 timer_T *timer = first_timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100801 remove_timer(timer);
802 free_timer(timer);
803 }
804}
805# endif
806
807/*
808 * "timer_info([timer])" function
809 */
810 void
811f_timer_info(typval_T *argvars, typval_T *rettv)
812{
813 timer_T *timer = NULL;
814
Bram Moolenaar93a10962022-06-16 11:42:09 +0100815 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100816 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200817
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100818 if (check_for_opt_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200819 return;
820
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100821 if (argvars[0].v_type != VAR_UNKNOWN)
822 {
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100823 timer = find_timer((int)tv_get_number(&argvars[0]));
824 if (timer != NULL)
825 add_timer_info(rettv, timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100826 }
827 else
828 add_timer_info_all(rettv);
829}
830
831/*
832 * "timer_pause(timer, paused)" function
833 */
834 void
835f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
836{
837 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200838
839 if (in_vim9script()
840 && (check_for_number_arg(argvars, 0) == FAIL
841 || check_for_bool_arg(argvars, 1) == FAIL))
842 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100843
844 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200845 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100846 else
847 {
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200848 int paused = (int)tv_get_bool(&argvars[1]);
Bram Moolenaar9198de32022-08-27 21:30:03 +0100849
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100850 timer = find_timer((int)tv_get_number(&argvars[0]));
851 if (timer != NULL)
852 timer->tr_paused = paused;
853 }
854}
855
856/*
857 * "timer_start(time, callback [, options])" function
858 */
859 void
860f_timer_start(typval_T *argvars, typval_T *rettv)
861{
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200862 long msec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100863 timer_T *timer;
864 int repeat = 0;
865 callback_T callback;
866 dict_T *dict;
867
868 rettv->vval.v_number = -1;
869 if (check_secure())
870 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200871
872 if (in_vim9script()
873 && (check_for_number_arg(argvars, 0) == FAIL
874 || check_for_opt_dict_arg(argvars, 2) == FAIL))
875 return;
876
877 msec = (long)tv_get_number(&argvars[0]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100878 if (argvars[2].v_type != VAR_UNKNOWN)
879 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100880 if (check_for_nonnull_dict_arg(argvars, 2) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100881 return;
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100882
883 dict = argvars[2].vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100884 if (dict_has_key(dict, "repeat"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100885 repeat = dict_get_number(dict, "repeat");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100886 }
887
888 callback = get_callback(&argvars[1]);
889 if (callback.cb_name == NULL)
890 return;
Bram Moolenaar745b9382022-01-27 13:55:35 +0000891 if (in_vim9script() && *callback.cb_name == NUL)
892 {
893 // empty callback is not useful for a timer
894 emsg(_(e_invalid_callback_argument));
895 free_callback(&callback);
896 return;
897 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100898
899 timer = create_timer(msec, repeat);
900 if (timer == NULL)
901 free_callback(&callback);
902 else
903 {
904 set_callback(&timer->tr_callback, &callback);
905 rettv->vval.v_number = (varnumber_T)timer->tr_id;
906 }
907}
908
909/*
910 * "timer_stop(timer)" function
911 */
912 void
913f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
914{
915 timer_T *timer;
916
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100917 if (check_for_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200918 return;
919
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100920 timer = find_timer((int)tv_get_number(&argvars[0]));
921 if (timer != NULL)
922 stop_timer(timer);
923}
924
925/*
926 * "timer_stopall()" function
927 */
928 void
929f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
930{
931 stop_all_timers();
932}
933
934# endif // FEAT_TIMERS
935
936# if defined(STARTUPTIME) || defined(PROTO)
937static struct timeval prev_timeval;
938
939# ifdef MSWIN
940/*
941 * Windows doesn't have gettimeofday(), although it does have struct timeval.
942 */
943 static int
944gettimeofday(struct timeval *tv, char *dummy UNUSED)
945{
946 long t = clock();
947 tv->tv_sec = t / CLOCKS_PER_SEC;
948 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
949 return 0;
950}
951# endif
952
953/*
954 * Save the previous time before doing something that could nest.
955 * set "*tv_rel" to the time elapsed so far.
956 */
957 void
958time_push(void *tv_rel, void *tv_start)
959{
960 *((struct timeval *)tv_rel) = prev_timeval;
961 gettimeofday(&prev_timeval, NULL);
962 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
963 - ((struct timeval *)tv_rel)->tv_usec;
964 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
965 - ((struct timeval *)tv_rel)->tv_sec;
966 if (((struct timeval *)tv_rel)->tv_usec < 0)
967 {
968 ((struct timeval *)tv_rel)->tv_usec += 1000000;
969 --((struct timeval *)tv_rel)->tv_sec;
970 }
971 *(struct timeval *)tv_start = prev_timeval;
972}
973
974/*
975 * Compute the previous time after doing something that could nest.
976 * Subtract "*tp" from prev_timeval;
977 * Note: The arguments are (void *) to avoid trouble with systems that don't
978 * have struct timeval.
979 */
980 void
981time_pop(
982 void *tp) // actually (struct timeval *)
983{
984 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
985 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
986 if (prev_timeval.tv_usec < 0)
987 {
988 prev_timeval.tv_usec += 1000000;
989 --prev_timeval.tv_sec;
990 }
991}
992
993 static void
994time_diff(struct timeval *then, struct timeval *now)
995{
996 long usec;
997 long msec;
998
999 usec = now->tv_usec - then->tv_usec;
1000 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
1001 usec = usec % 1000L;
1002 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
1003}
1004
1005 void
1006time_msg(
1007 char *mesg,
1008 void *tv_start) // only for do_source: start time; actually
1009 // (struct timeval *)
1010{
1011 static struct timeval start;
1012 struct timeval now;
1013
1014 if (time_fd != NULL)
1015 {
1016 if (strstr(mesg, "STARTING") != NULL)
1017 {
1018 gettimeofday(&start, NULL);
1019 prev_timeval = start;
1020 fprintf(time_fd, "\n\ntimes in msec\n");
1021 fprintf(time_fd, " clock self+sourced self: sourced script\n");
1022 fprintf(time_fd, " clock elapsed: other lines\n\n");
1023 }
1024 gettimeofday(&now, NULL);
1025 time_diff(&start, &now);
1026 if (((struct timeval *)tv_start) != NULL)
1027 {
1028 fprintf(time_fd, " ");
1029 time_diff(((struct timeval *)tv_start), &now);
1030 }
1031 fprintf(time_fd, " ");
1032 time_diff(&prev_timeval, &now);
1033 prev_timeval = now;
1034 fprintf(time_fd, ": %s\n", mesg);
1035 }
1036}
1037# endif // STARTUPTIME
1038#endif // FEAT_EVAL
1039
1040#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1041/*
1042 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1043 * Returns -1 when encountering EOF.
1044 */
1045 time_T
1046get8ctime(FILE *fd)
1047{
1048 int c;
1049 time_T n = 0;
1050 int i;
1051
1052 for (i = 0; i < 8; ++i)
1053 {
1054 c = getc(fd);
1055 if (c == EOF) return -1;
1056 n = (n << 8) + c;
1057 }
1058 return n;
1059}
1060
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001061/*
1062 * Write time_T to file "fd" in 8 bytes.
1063 * Returns FAIL when the write failed.
1064 */
1065 int
1066put_time(FILE *fd, time_T the_time)
1067{
1068 char_u buf[8];
1069
1070 time_to_bytes(the_time, buf);
1071 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1072}
1073
1074/*
1075 * Write time_T to "buf[8]".
1076 */
1077 void
1078time_to_bytes(time_T the_time, char_u *buf)
1079{
1080 int c;
1081 int i;
1082 int bi = 0;
1083 time_T wtime = the_time;
1084
1085 // time_T can be up to 8 bytes in size, more than long_u, thus we
1086 // can't use put_bytes() here.
1087 // Another problem is that ">>" may do an arithmetic shift that keeps the
1088 // sign. This happens for large values of wtime. A cast to long_u may
1089 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1090 // it's safe to assume that long_u is 4 bytes or more and when using 8
1091 // bytes the top bit won't be set.
1092 for (i = 7; i >= 0; --i)
1093 {
1094 if (i + 1 > (int)sizeof(time_T))
1095 // ">>" doesn't work well when shifting more bits than avail
1096 buf[bi++] = 0;
1097 else
1098 {
K.Takatac351dc12022-01-24 11:24:08 +00001099# if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001100 c = (int)(wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001101# else
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001102 c = (int)((long_u)wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001103# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001104 buf[bi++] = c;
1105 }
1106 }
1107}
1108
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001109#endif
1110
1111/*
1112 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1113 */
1114 void
1115add_time(char_u *buf, size_t buflen, time_t tt)
1116{
1117#ifdef HAVE_STRFTIME
1118 struct tm tmval;
1119 struct tm *curtime;
1120
1121 if (vim_time() - tt >= 100)
1122 {
1123 curtime = vim_localtime(&tt, &tmval);
1124 if (vim_time() - tt < (60L * 60L * 12L))
1125 // within 12 hours
1126 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
1127 else
1128 // longer ago
1129 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1130 }
1131 else
1132#endif
1133 {
1134 long seconds = (long)(vim_time() - tt);
1135
1136 vim_snprintf((char *)buf, buflen,
1137 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1138 seconds);
1139 }
1140}