blob: 891a9f49edde23e15772db48d52a53f6240017b0 [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{
Dominique Pelle84d14cc2022-10-12 13:30:25 +010085 static char buf[100]; // hopefully enough for every language
Bram Moolenaar0a8fed62020-02-14 13:22:17 +010086#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)
Dominique Pelle84d14cc2022-10-12 13:30:25 +010093 vim_strncpy((char_u *)buf, (char_u *)_("(Invalid)"), sizeof(buf) - 2);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +010094 else
95 {
Bram Moolenaar7e935772022-01-20 14:25:57 +000096 // xgettext:no-c-format
Dominique Pelle84d14cc2022-10-12 13:30:25 +010097 if (strftime(buf, sizeof(buf) - 2, _("%a %b %d %H:%M:%S %Y"), curtime)
98 == 0)
99 {
100 // Quoting "man strftime":
101 // > If the length of the result string (including the terminating
102 // > null byte) would exceed max bytes, then strftime() returns 0,
103 // > and the contents of the array are undefined.
104 vim_strncpy((char_u *)buf, (char_u *)_("(Invalid)"),
105 sizeof(buf) - 2);
106 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100107# ifdef MSWIN
108 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
109 {
110 char_u *to_free = NULL;
111 int len;
112
113 acp_to_enc((char_u *)buf, (int)strlen(buf), &to_free, &len);
114 if (to_free != NULL)
115 {
Dominique Pelle84d14cc2022-10-12 13:30:25 +0100116 STRNCPY(buf, to_free, sizeof(buf) - 2);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100117 vim_free(to_free);
118 }
119 }
120# endif
121 }
122#else
123 STRCPY(buf, "(unknown)");
124#endif
125 if (add_newline)
126 STRCAT(buf, "\n");
127 return buf;
128}
129
130#if defined(FEAT_EVAL) || defined(PROTO)
131
132#if defined(MACOS_X)
133# include <time.h> // for time_t
134#endif
135
136/*
137 * "localtime()" function
138 */
139 void
140f_localtime(typval_T *argvars UNUSED, typval_T *rettv)
141{
142 rettv->vval.v_number = (varnumber_T)time(NULL);
143}
144
145# if defined(FEAT_RELTIME)
146/*
147 * Convert a List to proftime_T.
148 * Return FAIL when there is something wrong.
149 */
150 static int
151list2proftime(typval_T *arg, proftime_T *tm)
152{
153 long n1, n2;
154 int error = FALSE;
155
156 if (arg->v_type != VAR_LIST || arg->vval.v_list == NULL
157 || arg->vval.v_list->lv_len != 2)
158 return FAIL;
159 n1 = list_find_nr(arg->vval.v_list, 0L, &error);
160 n2 = list_find_nr(arg->vval.v_list, 1L, &error);
161# ifdef MSWIN
162 tm->HighPart = n1;
163 tm->LowPart = n2;
164# else
165 tm->tv_sec = n1;
166 tm->tv_usec = n2;
167# endif
168 return error ? FAIL : OK;
169}
170# endif // FEAT_RELTIME
171
172/*
173 * "reltime()" function
174 */
175 void
176f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
177{
178# ifdef FEAT_RELTIME
179 proftime_T res;
180 proftime_T start;
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200181 long n1, n2;
182
Bram Moolenaar93a10962022-06-16 11:42:09 +0100183 if (rettv_list_alloc(rettv) == FAIL)
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200184 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100185
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200186 if (in_vim9script()
187 && (check_for_opt_list_arg(argvars, 0) == FAIL
188 || (argvars[0].v_type != VAR_UNKNOWN
189 && check_for_opt_list_arg(argvars, 1) == FAIL)))
190 return;
191
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100192 if (argvars[0].v_type == VAR_UNKNOWN)
193 {
194 // No arguments: get current time.
195 profile_start(&res);
196 }
197 else if (argvars[1].v_type == VAR_UNKNOWN)
198 {
199 if (list2proftime(&argvars[0], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200200 {
201 if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000202 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100203 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200204 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100205 profile_end(&res);
206 }
207 else
208 {
209 // Two arguments: compute the difference.
210 if (list2proftime(&argvars[0], &start) == FAIL
211 || list2proftime(&argvars[1], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200212 {
213 if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000214 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100215 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200216 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100217 profile_sub(&res, &start);
218 }
219
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100220# ifdef MSWIN
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200221 n1 = res.HighPart;
222 n2 = res.LowPart;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100223# else
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200224 n1 = res.tv_sec;
225 n2 = res.tv_usec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100226# endif
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200227 list_append_number(rettv->vval.v_list, (varnumber_T)n1);
228 list_append_number(rettv->vval.v_list, (varnumber_T)n2);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100229# endif
230}
231
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100232/*
233 * "reltimefloat()" function
234 */
235 void
236f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv)
237{
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100238# ifdef FEAT_RELTIME
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100239 proftime_T tm;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100240# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100241
242 rettv->v_type = VAR_FLOAT;
243 rettv->vval.v_float = 0;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100244# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200245 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
246 return;
247
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100248 if (list2proftime(&argvars[0], &tm) == OK)
249 rettv->vval.v_float = profile_float(&tm);
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200250 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000251 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100252# endif
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100253}
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100254
255/*
256 * "reltimestr()" function
257 */
258 void
259f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
260{
261# ifdef FEAT_RELTIME
262 proftime_T tm;
263# endif
264
265 rettv->v_type = VAR_STRING;
266 rettv->vval.v_string = NULL;
267# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200268 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
269 return;
270
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100271 if (list2proftime(&argvars[0], &tm) == OK)
272 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200273 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000274 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100275# endif
276}
277
278# if defined(HAVE_STRFTIME) || defined(PROTO)
279/*
280 * "strftime({format}[, {time}])" function
281 */
282 void
283f_strftime(typval_T *argvars, typval_T *rettv)
284{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100285 struct tm tmval;
286 struct tm *curtime;
287 time_t seconds;
288 char_u *p;
289
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200290 if (in_vim9script()
291 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200292 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200293 return;
294
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100295 rettv->v_type = VAR_STRING;
296
297 p = tv_get_string(&argvars[0]);
298 if (argvars[1].v_type == VAR_UNKNOWN)
299 seconds = time(NULL);
300 else
301 seconds = (time_t)tv_get_number(&argvars[1]);
302 curtime = vim_localtime(&seconds, &tmval);
303 // MSVC returns NULL for an invalid value of seconds.
304 if (curtime == NULL)
305 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
306 else
307 {
K.Takata2c4a1d02021-05-28 15:49:34 +0200308# ifdef MSWIN
309 WCHAR result_buf[256];
310 WCHAR *wp;
311
312 wp = enc_to_utf16(p, NULL);
313 if (wp != NULL)
K.Takataeeec2542021-06-02 13:28:16 +0200314 (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
K.Takata2c4a1d02021-05-28 15:49:34 +0200315 else
316 result_buf[0] = NUL;
317 rettv->vval.v_string = utf16_to_enc(result_buf, NULL);
318 vim_free(wp);
319# else
320 char_u result_buf[256];
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100321 vimconv_T conv;
322 char_u *enc;
323
324 conv.vc_type = CONV_NONE;
325 enc = enc_locale();
326 convert_setup(&conv, p_enc, enc);
327 if (conv.vc_type != CONV_NONE)
328 p = string_convert(&conv, p, NULL);
Dominique Pelle84d14cc2022-10-12 13:30:25 +0100329 if (p == NULL || strftime((char *)result_buf, sizeof(result_buf),
330 (char *)p, curtime) == 0)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100331 result_buf[0] = NUL;
332
333 if (conv.vc_type != CONV_NONE)
334 vim_free(p);
335 convert_setup(&conv, enc, p_enc);
336 if (conv.vc_type != CONV_NONE)
337 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
338 else
339 rettv->vval.v_string = vim_strsave(result_buf);
340
341 // Release conversion descriptors
342 convert_setup(&conv, NULL, NULL);
343 vim_free(enc);
K.Takata2c4a1d02021-05-28 15:49:34 +0200344# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100345 }
346}
347# endif
348
349# if defined(HAVE_STRPTIME) || defined(PROTO)
350/*
351 * "strptime({format}, {timestring})" function
352 */
353 void
354f_strptime(typval_T *argvars, typval_T *rettv)
355{
356 struct tm tmval;
357 char_u *fmt;
358 char_u *str;
359 vimconv_T conv;
360 char_u *enc;
361
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200362 if (in_vim9script()
363 && (check_for_string_arg(argvars, 0) == FAIL
364 || check_for_string_arg(argvars, 1) == FAIL))
365 return;
366
Bram Moolenaara80faa82020-04-12 19:37:17 +0200367 CLEAR_FIELD(tmval);
Bram Moolenaarea1233f2020-06-10 16:54:13 +0200368 tmval.tm_isdst = -1;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100369 fmt = tv_get_string(&argvars[0]);
370 str = tv_get_string(&argvars[1]);
371
372 conv.vc_type = CONV_NONE;
373 enc = enc_locale();
374 convert_setup(&conv, p_enc, enc);
375 if (conv.vc_type != CONV_NONE)
376 fmt = string_convert(&conv, fmt, NULL);
377 if (fmt == NULL
378 || strptime((char *)str, (char *)fmt, &tmval) == NULL
379 || (rettv->vval.v_number = mktime(&tmval)) == -1)
380 rettv->vval.v_number = 0;
381
382 if (conv.vc_type != CONV_NONE)
383 vim_free(fmt);
384 convert_setup(&conv, NULL, NULL);
385 vim_free(enc);
386}
387# endif
388
389# if defined(FEAT_TIMERS) || defined(PROTO)
390static timer_T *first_timer = NULL;
391static long last_timer_id = 0;
392
393/*
394 * Return time left until "due". Negative if past "due".
395 */
396 long
397proftime_time_left(proftime_T *due, proftime_T *now)
398{
399# ifdef MSWIN
400 LARGE_INTEGER fr;
401
402 if (now->QuadPart > due->QuadPart)
403 return 0;
404 QueryPerformanceFrequency(&fr);
405 return (long)(((double)(due->QuadPart - now->QuadPart)
406 / (double)fr.QuadPart) * 1000);
407# else
408 if (now->tv_sec > due->tv_sec)
409 return 0;
410 return (due->tv_sec - now->tv_sec) * 1000
411 + (due->tv_usec - now->tv_usec) / 1000;
412# endif
413}
414
415/*
416 * Insert a timer in the list of timers.
417 */
418 static void
419insert_timer(timer_T *timer)
420{
421 timer->tr_next = first_timer;
422 timer->tr_prev = NULL;
423 if (first_timer != NULL)
424 first_timer->tr_prev = timer;
425 first_timer = timer;
426 did_add_timer = TRUE;
427}
428
429/*
430 * Take a timer out of the list of timers.
431 */
432 static void
433remove_timer(timer_T *timer)
434{
435 if (timer->tr_prev == NULL)
436 first_timer = timer->tr_next;
437 else
438 timer->tr_prev->tr_next = timer->tr_next;
439 if (timer->tr_next != NULL)
440 timer->tr_next->tr_prev = timer->tr_prev;
441}
442
443 static void
444free_timer(timer_T *timer)
445{
446 free_callback(&timer->tr_callback);
447 vim_free(timer);
448}
449
450/*
451 * Create a timer and return it. NULL if out of memory.
452 * Caller should set the callback.
453 */
454 timer_T *
455create_timer(long msec, int repeat)
456{
457 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
458 long prev_id = last_timer_id;
459
460 if (timer == NULL)
461 return NULL;
462 if (++last_timer_id <= prev_id)
463 // Overflow! Might cause duplicates...
464 last_timer_id = 0;
465 timer->tr_id = last_timer_id;
466 insert_timer(timer);
467 if (repeat != 0)
468 timer->tr_repeat = repeat - 1;
469 timer->tr_interval = msec;
470
Bram Moolenaar9198de32022-08-27 21:30:03 +0100471 timer_start(timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100472 return timer;
473}
474
475/*
Bram Moolenaar9198de32022-08-27 21:30:03 +0100476 * (Re)start a timer.
477 */
478 void
479timer_start(timer_T *timer)
480{
481 profile_setlimit(timer->tr_interval, &timer->tr_due);
482 timer->tr_paused = FALSE;
483}
484
485/*
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100486 * Invoke the callback of "timer".
487 */
488 static void
489timer_callback(timer_T *timer)
490{
491 typval_T rettv;
492 typval_T argv[2];
493
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000494#ifdef FEAT_EVAL
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100495 if (ch_log_active())
496 {
497 callback_T *cb = &timer->tr_callback;
498
499 ch_log(NULL, "invoking timer callback %s",
500 cb->cb_partial != NULL ? cb->cb_partial->pt_name : cb->cb_name);
501 }
502#endif
503
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100504 argv[0].v_type = VAR_NUMBER;
505 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
506 argv[1].v_type = VAR_UNKNOWN;
507
Bram Moolenaar745b9382022-01-27 13:55:35 +0000508 rettv.v_type = VAR_UNKNOWN;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100509 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
510 clear_tv(&rettv);
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100511
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000512#ifdef FEAT_EVAL
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100513 ch_log(NULL, "timer callback finished");
514#endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100515}
516
517/*
518 * Call timers that are due.
519 * Return the time in msec until the next timer is due.
520 * Returns -1 if there are no pending timers.
521 */
522 long
523check_due_timer(void)
524{
525 timer_T *timer;
526 timer_T *timer_next;
527 long this_due;
528 long next_due = -1;
529 proftime_T now;
530 int did_one = FALSE;
531 int need_update_screen = FALSE;
532 long current_id = last_timer_id;
533
Bram Moolenaar48d0ac72022-01-07 20:40:08 +0000534 // Don't run any timers while exiting, dealing with an error or at the
535 // debug prompt.
536 if (exiting || aborting() || debug_mode)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100537 return next_due;
538
539 profile_start(&now);
540 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
541 {
542 timer_next = timer->tr_next;
543
544 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
545 continue;
546 this_due = proftime_time_left(&timer->tr_due, &now);
547 if (this_due <= 1)
548 {
549 // Save and restore a lot of flags, because the timer fires while
550 // waiting for a character, which might be halfway a command.
551 int save_timer_busy = timer_busy;
552 int save_vgetc_busy = vgetc_busy;
553 int save_did_emsg = did_emsg;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200554 int prev_uncaught_emsg = uncaught_emsg;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100555 int save_called_emsg = called_emsg;
556 int save_must_redraw = must_redraw;
557 int save_trylevel = trylevel;
558 int save_did_throw = did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100559 int save_need_rethrow = need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100560 int save_ex_pressedreturn = get_pressedreturn();
561 int save_may_garbage_collect = may_garbage_collect;
562 except_T *save_current_exception = current_exception;
563 vimvars_save_T vvsave;
564
565 // Create a scope for running the timer callback, ignoring most of
566 // the current scope, such as being inside a try/catch.
567 timer_busy = timer_busy > 0 || vgetc_busy > 0;
568 vgetc_busy = 0;
569 called_emsg = 0;
570 did_emsg = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100571 must_redraw = 0;
572 trylevel = 0;
573 did_throw = FALSE;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100574 need_rethrow = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100575 current_exception = NULL;
576 may_garbage_collect = FALSE;
577 save_vimvars(&vvsave);
578
Bram Moolenaar22286892020-11-05 20:50:51 +0100579 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100580 timer->tr_firing = TRUE;
581 timer_callback(timer);
582 timer->tr_firing = FALSE;
583
Bram Moolenaar22286892020-11-05 20:50:51 +0100584 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100585 timer_next = timer->tr_next;
586 did_one = TRUE;
587 timer_busy = save_timer_busy;
588 vgetc_busy = save_vgetc_busy;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200589 if (uncaught_emsg > prev_uncaught_emsg)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100590 ++timer->tr_emsg_count;
591 did_emsg = save_did_emsg;
592 called_emsg = save_called_emsg;
593 trylevel = save_trylevel;
594 did_throw = save_did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100595 need_rethrow = save_need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100596 current_exception = save_current_exception;
597 restore_vimvars(&vvsave);
598 if (must_redraw != 0)
599 need_update_screen = TRUE;
600 must_redraw = must_redraw > save_must_redraw
601 ? must_redraw : save_must_redraw;
602 set_pressedreturn(save_ex_pressedreturn);
603 may_garbage_collect = save_may_garbage_collect;
604
605 // Only fire the timer again if it repeats and stop_timer() wasn't
606 // called while inside the callback (tr_id == -1).
607 if (timer->tr_repeat != 0 && timer->tr_id != -1
608 && timer->tr_emsg_count < 3)
609 {
610 profile_setlimit(timer->tr_interval, &timer->tr_due);
611 this_due = proftime_time_left(&timer->tr_due, &now);
612 if (this_due < 1)
613 this_due = 1;
614 if (timer->tr_repeat > 0)
615 --timer->tr_repeat;
616 }
617 else
618 {
619 this_due = -1;
Bram Moolenaar9198de32022-08-27 21:30:03 +0100620 if (timer->tr_keep)
621 timer->tr_paused = TRUE;
622 else
623 {
624 remove_timer(timer);
625 free_timer(timer);
626 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100627 }
628 }
629 if (this_due > 0 && (next_due == -1 || next_due > this_due))
630 next_due = this_due;
631 }
632
633 if (did_one)
Bram Moolenaare5050712021-12-09 10:51:05 +0000634 redraw_after_callback(need_update_screen, FALSE);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100635
636#ifdef FEAT_BEVAL_TERM
637 if (bevalexpr_due_set)
638 {
639 this_due = proftime_time_left(&bevalexpr_due, &now);
640 if (this_due <= 1)
641 {
642 bevalexpr_due_set = FALSE;
643 if (balloonEval == NULL)
644 {
645 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
646 balloonEvalForTerm = TRUE;
647 }
648 if (balloonEval != NULL)
649 {
650 general_beval_cb(balloonEval, 0);
651 setcursor();
652 out_flush();
653 }
654 }
655 else if (next_due == -1 || next_due > this_due)
656 next_due = this_due;
657 }
658#endif
659#ifdef FEAT_TERMINAL
660 // Some terminal windows may need their buffer updated.
661 next_due = term_check_timers(next_due, &now);
662#endif
663
664 return current_id != last_timer_id ? 1 : next_due;
665}
666
667/*
668 * Find a timer by ID. Returns NULL if not found;
669 */
670 static timer_T *
671find_timer(long id)
672{
673 timer_T *timer;
674
675 if (id >= 0)
676 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200677 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100678 if (timer->tr_id == id)
679 return timer;
680 }
681 return NULL;
682}
683
684
685/*
686 * Stop a timer and delete it.
687 */
688 void
689stop_timer(timer_T *timer)
690{
691 if (timer->tr_firing)
692 // Free the timer after the callback returns.
693 timer->tr_id = -1;
694 else
695 {
696 remove_timer(timer);
697 free_timer(timer);
698 }
699}
700
701 static void
702stop_all_timers(void)
703{
704 timer_T *timer;
705 timer_T *timer_next;
706
707 for (timer = first_timer; timer != NULL; timer = timer_next)
708 {
709 timer_next = timer->tr_next;
710 stop_timer(timer);
711 }
712}
713
714 static void
715add_timer_info(typval_T *rettv, timer_T *timer)
716{
717 list_T *list = rettv->vval.v_list;
718 dict_T *dict = dict_alloc();
719 dictitem_T *di;
720 long remaining;
721 proftime_T now;
722
723 if (dict == NULL)
724 return;
725 list_append_dict(list, dict);
726
727 dict_add_number(dict, "id", timer->tr_id);
728 dict_add_number(dict, "time", (long)timer->tr_interval);
729
730 profile_start(&now);
731 remaining = proftime_time_left(&timer->tr_due, &now);
732 dict_add_number(dict, "remaining", (long)remaining);
733
734 dict_add_number(dict, "repeat",
Bram Moolenaar95b2dd02021-12-09 18:42:57 +0000735 (long)(timer->tr_repeat < 0 ? -1
736 : timer->tr_repeat + (timer->tr_firing ? 0 : 1)));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100737 dict_add_number(dict, "paused", (long)(timer->tr_paused));
738
739 di = dictitem_alloc((char_u *)"callback");
740 if (di != NULL)
741 {
742 if (dict_add(dict, di) == FAIL)
743 vim_free(di);
744 else
745 put_callback(&timer->tr_callback, &di->di_tv);
746 }
747}
748
749 static void
750add_timer_info_all(typval_T *rettv)
751{
752 timer_T *timer;
753
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200754 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100755 if (timer->tr_id != -1)
756 add_timer_info(rettv, timer);
757}
758
759/*
760 * Mark references in partials of timers.
761 */
762 int
763set_ref_in_timer(int copyID)
764{
765 int abort = FALSE;
766 timer_T *timer;
767 typval_T tv;
768
769 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
770 {
771 if (timer->tr_callback.cb_partial != NULL)
772 {
773 tv.v_type = VAR_PARTIAL;
774 tv.vval.v_partial = timer->tr_callback.cb_partial;
775 }
776 else
777 {
778 tv.v_type = VAR_FUNC;
779 tv.vval.v_string = timer->tr_callback.cb_name;
780 }
781 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
782 }
783 return abort;
784}
785
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100786/*
787 * Return TRUE if "timer" exists in the list of timers.
788 */
789 int
790timer_valid(timer_T *timer)
791{
792 if (timer == NULL)
793 return FALSE;
794 for (timer_T *t = first_timer; t != NULL; t = t->tr_next)
795 if (t == timer)
796 return TRUE;
797 return FALSE;
798}
799
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100800# if defined(EXITFREE) || defined(PROTO)
801 void
802timer_free_all()
803{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100804 while (first_timer != NULL)
805 {
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100806 timer_T *timer = first_timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100807 remove_timer(timer);
808 free_timer(timer);
809 }
810}
811# endif
812
813/*
814 * "timer_info([timer])" function
815 */
816 void
817f_timer_info(typval_T *argvars, typval_T *rettv)
818{
819 timer_T *timer = NULL;
820
Bram Moolenaar93a10962022-06-16 11:42:09 +0100821 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100822 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200823
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100824 if (check_for_opt_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200825 return;
826
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100827 if (argvars[0].v_type != VAR_UNKNOWN)
828 {
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100829 timer = find_timer((int)tv_get_number(&argvars[0]));
830 if (timer != NULL)
831 add_timer_info(rettv, timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100832 }
833 else
834 add_timer_info_all(rettv);
835}
836
837/*
838 * "timer_pause(timer, paused)" function
839 */
840 void
841f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
842{
843 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200844
845 if (in_vim9script()
846 && (check_for_number_arg(argvars, 0) == FAIL
847 || check_for_bool_arg(argvars, 1) == FAIL))
848 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100849
850 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200851 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100852 else
853 {
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200854 int paused = (int)tv_get_bool(&argvars[1]);
Bram Moolenaar9198de32022-08-27 21:30:03 +0100855
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100856 timer = find_timer((int)tv_get_number(&argvars[0]));
857 if (timer != NULL)
858 timer->tr_paused = paused;
859 }
860}
861
862/*
863 * "timer_start(time, callback [, options])" function
864 */
865 void
866f_timer_start(typval_T *argvars, typval_T *rettv)
867{
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200868 long msec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100869 timer_T *timer;
870 int repeat = 0;
871 callback_T callback;
872 dict_T *dict;
873
874 rettv->vval.v_number = -1;
875 if (check_secure())
876 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200877
878 if (in_vim9script()
879 && (check_for_number_arg(argvars, 0) == FAIL
880 || check_for_opt_dict_arg(argvars, 2) == FAIL))
881 return;
882
883 msec = (long)tv_get_number(&argvars[0]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100884 if (argvars[2].v_type != VAR_UNKNOWN)
885 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100886 if (check_for_nonnull_dict_arg(argvars, 2) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100887 return;
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100888
889 dict = argvars[2].vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100890 if (dict_has_key(dict, "repeat"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100891 repeat = dict_get_number(dict, "repeat");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100892 }
893
894 callback = get_callback(&argvars[1]);
895 if (callback.cb_name == NULL)
896 return;
Bram Moolenaar745b9382022-01-27 13:55:35 +0000897 if (in_vim9script() && *callback.cb_name == NUL)
898 {
899 // empty callback is not useful for a timer
900 emsg(_(e_invalid_callback_argument));
901 free_callback(&callback);
902 return;
903 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100904
905 timer = create_timer(msec, repeat);
906 if (timer == NULL)
907 free_callback(&callback);
908 else
909 {
910 set_callback(&timer->tr_callback, &callback);
911 rettv->vval.v_number = (varnumber_T)timer->tr_id;
912 }
913}
914
915/*
916 * "timer_stop(timer)" function
917 */
918 void
919f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
920{
921 timer_T *timer;
922
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100923 if (check_for_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200924 return;
925
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100926 timer = find_timer((int)tv_get_number(&argvars[0]));
927 if (timer != NULL)
928 stop_timer(timer);
929}
930
931/*
932 * "timer_stopall()" function
933 */
934 void
935f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
936{
937 stop_all_timers();
938}
939
940# endif // FEAT_TIMERS
941
942# if defined(STARTUPTIME) || defined(PROTO)
943static struct timeval prev_timeval;
944
945# ifdef MSWIN
946/*
947 * Windows doesn't have gettimeofday(), although it does have struct timeval.
948 */
949 static int
950gettimeofday(struct timeval *tv, char *dummy UNUSED)
951{
952 long t = clock();
953 tv->tv_sec = t / CLOCKS_PER_SEC;
954 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
955 return 0;
956}
957# endif
958
959/*
960 * Save the previous time before doing something that could nest.
961 * set "*tv_rel" to the time elapsed so far.
962 */
963 void
964time_push(void *tv_rel, void *tv_start)
965{
966 *((struct timeval *)tv_rel) = prev_timeval;
967 gettimeofday(&prev_timeval, NULL);
968 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
969 - ((struct timeval *)tv_rel)->tv_usec;
970 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
971 - ((struct timeval *)tv_rel)->tv_sec;
972 if (((struct timeval *)tv_rel)->tv_usec < 0)
973 {
974 ((struct timeval *)tv_rel)->tv_usec += 1000000;
975 --((struct timeval *)tv_rel)->tv_sec;
976 }
977 *(struct timeval *)tv_start = prev_timeval;
978}
979
980/*
981 * Compute the previous time after doing something that could nest.
982 * Subtract "*tp" from prev_timeval;
983 * Note: The arguments are (void *) to avoid trouble with systems that don't
984 * have struct timeval.
985 */
986 void
987time_pop(
988 void *tp) // actually (struct timeval *)
989{
990 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
991 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
992 if (prev_timeval.tv_usec < 0)
993 {
994 prev_timeval.tv_usec += 1000000;
995 --prev_timeval.tv_sec;
996 }
997}
998
999 static void
1000time_diff(struct timeval *then, struct timeval *now)
1001{
1002 long usec;
1003 long msec;
1004
1005 usec = now->tv_usec - then->tv_usec;
1006 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
1007 usec = usec % 1000L;
1008 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
1009}
1010
1011 void
1012time_msg(
1013 char *mesg,
1014 void *tv_start) // only for do_source: start time; actually
1015 // (struct timeval *)
1016{
1017 static struct timeval start;
1018 struct timeval now;
1019
1020 if (time_fd != NULL)
1021 {
1022 if (strstr(mesg, "STARTING") != NULL)
1023 {
1024 gettimeofday(&start, NULL);
1025 prev_timeval = start;
1026 fprintf(time_fd, "\n\ntimes in msec\n");
1027 fprintf(time_fd, " clock self+sourced self: sourced script\n");
1028 fprintf(time_fd, " clock elapsed: other lines\n\n");
1029 }
1030 gettimeofday(&now, NULL);
1031 time_diff(&start, &now);
1032 if (((struct timeval *)tv_start) != NULL)
1033 {
1034 fprintf(time_fd, " ");
1035 time_diff(((struct timeval *)tv_start), &now);
1036 }
1037 fprintf(time_fd, " ");
1038 time_diff(&prev_timeval, &now);
1039 prev_timeval = now;
1040 fprintf(time_fd, ": %s\n", mesg);
1041 }
1042}
1043# endif // STARTUPTIME
1044#endif // FEAT_EVAL
1045
1046#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1047/*
1048 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1049 * Returns -1 when encountering EOF.
1050 */
1051 time_T
1052get8ctime(FILE *fd)
1053{
1054 int c;
1055 time_T n = 0;
1056 int i;
1057
1058 for (i = 0; i < 8; ++i)
1059 {
1060 c = getc(fd);
1061 if (c == EOF) return -1;
1062 n = (n << 8) + c;
1063 }
1064 return n;
1065}
1066
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001067/*
1068 * Write time_T to file "fd" in 8 bytes.
1069 * Returns FAIL when the write failed.
1070 */
1071 int
1072put_time(FILE *fd, time_T the_time)
1073{
1074 char_u buf[8];
1075
1076 time_to_bytes(the_time, buf);
1077 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1078}
1079
1080/*
1081 * Write time_T to "buf[8]".
1082 */
1083 void
1084time_to_bytes(time_T the_time, char_u *buf)
1085{
1086 int c;
1087 int i;
1088 int bi = 0;
1089 time_T wtime = the_time;
1090
1091 // time_T can be up to 8 bytes in size, more than long_u, thus we
1092 // can't use put_bytes() here.
1093 // Another problem is that ">>" may do an arithmetic shift that keeps the
1094 // sign. This happens for large values of wtime. A cast to long_u may
1095 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1096 // it's safe to assume that long_u is 4 bytes or more and when using 8
1097 // bytes the top bit won't be set.
1098 for (i = 7; i >= 0; --i)
1099 {
1100 if (i + 1 > (int)sizeof(time_T))
1101 // ">>" doesn't work well when shifting more bits than avail
1102 buf[bi++] = 0;
1103 else
1104 {
K.Takatac351dc12022-01-24 11:24:08 +00001105# if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001106 c = (int)(wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001107# else
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001108 c = (int)((long_u)wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001109# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001110 buf[bi++] = c;
1111 }
1112 }
1113}
1114
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001115#endif
1116
1117/*
1118 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1119 */
1120 void
1121add_time(char_u *buf, size_t buflen, time_t tt)
1122{
1123#ifdef HAVE_STRFTIME
1124 struct tm tmval;
1125 struct tm *curtime;
Bram Moolenaara2e4e0f2022-10-15 16:29:03 +01001126 size_t n;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001127
1128 if (vim_time() - tt >= 100)
1129 {
1130 curtime = vim_localtime(&tt, &tmval);
1131 if (vim_time() - tt < (60L * 60L * 12L))
1132 // within 12 hours
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001133 n = strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001134 else
1135 // longer ago
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001136 n = strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1137 if (n == 0)
1138 buf[0] = NUL;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001139 }
1140 else
1141#endif
1142 {
1143 long seconds = (long)(vim_time() - tt);
1144
1145 vim_snprintf((char *)buf, buflen,
1146 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1147 seconds);
1148 }
1149}