blob: 2fb0c39317b83410dd9d4f6f792c2edf1e1d51c4 [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;
Ernie Rael076de792023-03-16 21:43:15 +0000166 tm->tv_fsec = n2;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100167# 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;
Ernie Rael076de792023-03-16 21:43:15 +0000225 n2 = res.tv_fsec;
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{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100261 rettv->v_type = VAR_STRING;
262 rettv->vval.v_string = NULL;
263# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200264 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
265 return;
266
Bram Moolenaar2a003172023-03-17 18:50:48 +0000267 proftime_T tm;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100268 if (list2proftime(&argvars[0], &tm) == OK)
Ernie Rael076de792023-03-16 21:43:15 +0000269 {
270# ifdef MSWIN
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100271 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
Ernie Rael076de792023-03-16 21:43:15 +0000272# else
Bram Moolenaar2a003172023-03-17 18:50:48 +0000273 static char buf[50];
Ernie Rael076de792023-03-16 21:43:15 +0000274 long usec = tm.tv_fsec / (TV_FSEC_SEC / 1000000);
Bram Moolenaar2a003172023-03-17 18:50:48 +0000275 vim_snprintf(buf, sizeof(buf), "%3ld.%06ld", (long)tm.tv_sec, usec);
Ernie Rael076de792023-03-16 21:43:15 +0000276 rettv->vval.v_string = vim_strsave((char_u *)buf);
277# endif
278 }
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200279 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000280 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100281# endif
282}
283
284# if defined(HAVE_STRFTIME) || defined(PROTO)
285/*
286 * "strftime({format}[, {time}])" function
287 */
288 void
289f_strftime(typval_T *argvars, typval_T *rettv)
290{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100291 struct tm tmval;
292 struct tm *curtime;
293 time_t seconds;
294 char_u *p;
295
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200296 if (in_vim9script()
297 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200298 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200299 return;
300
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100301 rettv->v_type = VAR_STRING;
302
303 p = tv_get_string(&argvars[0]);
304 if (argvars[1].v_type == VAR_UNKNOWN)
305 seconds = time(NULL);
306 else
307 seconds = (time_t)tv_get_number(&argvars[1]);
308 curtime = vim_localtime(&seconds, &tmval);
309 // MSVC returns NULL for an invalid value of seconds.
310 if (curtime == NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100311 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000312 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
313 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100314 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000315
316# ifdef MSWIN
317 WCHAR result_buf[256];
318 WCHAR *wp;
319
320 wp = enc_to_utf16(p, NULL);
321 if (wp != NULL)
322 (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
323 else
324 result_buf[0] = NUL;
325 rettv->vval.v_string = utf16_to_enc(result_buf, NULL);
326 vim_free(wp);
327# else
328 char_u result_buf[256];
329 vimconv_T conv;
330 char_u *enc;
331
332 conv.vc_type = CONV_NONE;
333 enc = enc_locale();
334 convert_setup(&conv, p_enc, enc);
335 if (conv.vc_type != CONV_NONE)
336 p = string_convert(&conv, p, NULL);
337 if (p == NULL || strftime((char *)result_buf, sizeof(result_buf),
338 (char *)p, curtime) == 0)
339 result_buf[0] = NUL;
340
341 if (conv.vc_type != CONV_NONE)
342 vim_free(p);
343 convert_setup(&conv, enc, p_enc);
344 if (conv.vc_type != CONV_NONE)
345 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
346 else
347 rettv->vval.v_string = vim_strsave(result_buf);
348
349 // Release conversion descriptors
350 convert_setup(&conv, NULL, NULL);
351 vim_free(enc);
352# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100353}
354# endif
355
356# if defined(HAVE_STRPTIME) || defined(PROTO)
357/*
358 * "strptime({format}, {timestring})" function
359 */
360 void
361f_strptime(typval_T *argvars, typval_T *rettv)
362{
363 struct tm tmval;
364 char_u *fmt;
365 char_u *str;
366 vimconv_T conv;
367 char_u *enc;
368
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200369 if (in_vim9script()
370 && (check_for_string_arg(argvars, 0) == FAIL
371 || check_for_string_arg(argvars, 1) == FAIL))
372 return;
373
Bram Moolenaara80faa82020-04-12 19:37:17 +0200374 CLEAR_FIELD(tmval);
Bram Moolenaarea1233f2020-06-10 16:54:13 +0200375 tmval.tm_isdst = -1;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100376 fmt = tv_get_string(&argvars[0]);
377 str = tv_get_string(&argvars[1]);
378
379 conv.vc_type = CONV_NONE;
380 enc = enc_locale();
381 convert_setup(&conv, p_enc, enc);
382 if (conv.vc_type != CONV_NONE)
383 fmt = string_convert(&conv, fmt, NULL);
384 if (fmt == NULL
385 || strptime((char *)str, (char *)fmt, &tmval) == NULL
386 || (rettv->vval.v_number = mktime(&tmval)) == -1)
387 rettv->vval.v_number = 0;
388
389 if (conv.vc_type != CONV_NONE)
390 vim_free(fmt);
391 convert_setup(&conv, NULL, NULL);
392 vim_free(enc);
393}
394# endif
395
396# if defined(FEAT_TIMERS) || defined(PROTO)
397static timer_T *first_timer = NULL;
398static long last_timer_id = 0;
399
400/*
Ernie Rael076de792023-03-16 21:43:15 +0000401 * Return time left, in "msec", until "due". Negative if past "due".
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100402 */
403 long
404proftime_time_left(proftime_T *due, proftime_T *now)
405{
406# ifdef MSWIN
407 LARGE_INTEGER fr;
408
409 if (now->QuadPart > due->QuadPart)
410 return 0;
411 QueryPerformanceFrequency(&fr);
412 return (long)(((double)(due->QuadPart - now->QuadPart)
413 / (double)fr.QuadPart) * 1000);
414# else
415 if (now->tv_sec > due->tv_sec)
416 return 0;
417 return (due->tv_sec - now->tv_sec) * 1000
Ernie Rael076de792023-03-16 21:43:15 +0000418 + (due->tv_fsec - now->tv_fsec) / (TV_FSEC_SEC / 1000);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100419# endif
420}
421
422/*
423 * Insert a timer in the list of timers.
424 */
425 static void
426insert_timer(timer_T *timer)
427{
428 timer->tr_next = first_timer;
429 timer->tr_prev = NULL;
430 if (first_timer != NULL)
431 first_timer->tr_prev = timer;
432 first_timer = timer;
433 did_add_timer = TRUE;
434}
435
436/*
437 * Take a timer out of the list of timers.
438 */
439 static void
440remove_timer(timer_T *timer)
441{
442 if (timer->tr_prev == NULL)
443 first_timer = timer->tr_next;
444 else
445 timer->tr_prev->tr_next = timer->tr_next;
446 if (timer->tr_next != NULL)
447 timer->tr_next->tr_prev = timer->tr_prev;
448}
449
450 static void
451free_timer(timer_T *timer)
452{
453 free_callback(&timer->tr_callback);
454 vim_free(timer);
455}
456
457/*
458 * Create a timer and return it. NULL if out of memory.
459 * Caller should set the callback.
460 */
461 timer_T *
462create_timer(long msec, int repeat)
463{
464 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
465 long prev_id = last_timer_id;
466
467 if (timer == NULL)
468 return NULL;
469 if (++last_timer_id <= prev_id)
470 // Overflow! Might cause duplicates...
471 last_timer_id = 0;
472 timer->tr_id = last_timer_id;
473 insert_timer(timer);
474 if (repeat != 0)
475 timer->tr_repeat = repeat - 1;
476 timer->tr_interval = msec;
477
Bram Moolenaar9198de32022-08-27 21:30:03 +0100478 timer_start(timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100479 return timer;
480}
481
482/*
Bram Moolenaar9198de32022-08-27 21:30:03 +0100483 * (Re)start a timer.
484 */
485 void
486timer_start(timer_T *timer)
487{
488 profile_setlimit(timer->tr_interval, &timer->tr_due);
489 timer->tr_paused = FALSE;
490}
491
492/*
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100493 * Invoke the callback of "timer".
494 */
495 static void
496timer_callback(timer_T *timer)
497{
498 typval_T rettv;
499 typval_T argv[2];
500
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000501#ifdef FEAT_EVAL
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100502 if (ch_log_active())
503 {
504 callback_T *cb = &timer->tr_callback;
505
506 ch_log(NULL, "invoking timer callback %s",
507 cb->cb_partial != NULL ? cb->cb_partial->pt_name : cb->cb_name);
508 }
509#endif
510
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100511 argv[0].v_type = VAR_NUMBER;
512 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
513 argv[1].v_type = VAR_UNKNOWN;
514
Bram Moolenaar745b9382022-01-27 13:55:35 +0000515 rettv.v_type = VAR_UNKNOWN;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100516 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
517 clear_tv(&rettv);
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100518
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000519#ifdef FEAT_EVAL
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100520 ch_log(NULL, "timer callback finished");
521#endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100522}
523
524/*
525 * Call timers that are due.
526 * Return the time in msec until the next timer is due.
527 * Returns -1 if there are no pending timers.
528 */
529 long
530check_due_timer(void)
531{
532 timer_T *timer;
533 timer_T *timer_next;
534 long this_due;
535 long next_due = -1;
536 proftime_T now;
537 int did_one = FALSE;
538 int need_update_screen = FALSE;
539 long current_id = last_timer_id;
540
Bram Moolenaar48d0ac72022-01-07 20:40:08 +0000541 // Don't run any timers while exiting, dealing with an error or at the
542 // debug prompt.
543 if (exiting || aborting() || debug_mode)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100544 return next_due;
545
546 profile_start(&now);
547 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
548 {
549 timer_next = timer->tr_next;
550
551 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
552 continue;
553 this_due = proftime_time_left(&timer->tr_due, &now);
554 if (this_due <= 1)
555 {
556 // Save and restore a lot of flags, because the timer fires while
557 // waiting for a character, which might be halfway a command.
558 int save_timer_busy = timer_busy;
559 int save_vgetc_busy = vgetc_busy;
560 int save_did_emsg = did_emsg;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200561 int prev_uncaught_emsg = uncaught_emsg;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100562 int save_called_emsg = called_emsg;
563 int save_must_redraw = must_redraw;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100564 int save_ex_pressedreturn = get_pressedreturn();
565 int save_may_garbage_collect = may_garbage_collect;
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200566 vimvars_save_T vvsave;
567 exception_state_T estate;
568
569 exception_state_save(&estate);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100570
571 // Create a scope for running the timer callback, ignoring most of
572 // the current scope, such as being inside a try/catch.
573 timer_busy = timer_busy > 0 || vgetc_busy > 0;
574 vgetc_busy = 0;
575 called_emsg = 0;
576 did_emsg = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100577 must_redraw = 0;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100578 may_garbage_collect = FALSE;
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200579 exception_state_clear();
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100580 save_vimvars(&vvsave);
581
Bram Moolenaar22286892020-11-05 20:50:51 +0100582 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100583 timer->tr_firing = TRUE;
584 timer_callback(timer);
585 timer->tr_firing = FALSE;
586
Bram Moolenaar22286892020-11-05 20:50:51 +0100587 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100588 timer_next = timer->tr_next;
589 did_one = TRUE;
590 timer_busy = save_timer_busy;
591 vgetc_busy = save_vgetc_busy;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200592 if (uncaught_emsg > prev_uncaught_emsg)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100593 ++timer->tr_emsg_count;
594 did_emsg = save_did_emsg;
595 called_emsg = save_called_emsg;
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200596 exception_state_restore(&estate);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100597 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
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000675 if (id < 0)
676 return NULL;
677
678 FOR_ALL_TIMERS(timer)
679 if (timer->tr_id == id)
680 return timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100681 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 }
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100781 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100782 }
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;
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000794
795 timer_T *t;
796 FOR_ALL_TIMERS(t)
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100797 if (t == timer)
798 return TRUE;
799 return FALSE;
800}
801
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100802# if defined(EXITFREE) || defined(PROTO)
803 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000804timer_free_all(void)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100805{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100806 while (first_timer != NULL)
807 {
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100808 timer_T *timer = first_timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100809 remove_timer(timer);
810 free_timer(timer);
811 }
812}
813# endif
814
815/*
816 * "timer_info([timer])" function
817 */
818 void
819f_timer_info(typval_T *argvars, typval_T *rettv)
820{
821 timer_T *timer = NULL;
822
Bram Moolenaar93a10962022-06-16 11:42:09 +0100823 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100824 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200825
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100826 if (check_for_opt_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200827 return;
828
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100829 if (argvars[0].v_type != VAR_UNKNOWN)
830 {
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100831 timer = find_timer((int)tv_get_number(&argvars[0]));
832 if (timer != NULL)
833 add_timer_info(rettv, timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100834 }
835 else
836 add_timer_info_all(rettv);
837}
838
839/*
840 * "timer_pause(timer, paused)" function
841 */
842 void
843f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
844{
845 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200846
847 if (in_vim9script()
848 && (check_for_number_arg(argvars, 0) == FAIL
849 || check_for_bool_arg(argvars, 1) == FAIL))
850 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100851
852 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100853 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000854 emsg(_(e_number_expected));
855 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100856 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000857
858 int paused = (int)tv_get_bool(&argvars[1]);
859
860 timer = find_timer((int)tv_get_number(&argvars[0]));
861 if (timer != NULL)
862 timer->tr_paused = paused;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100863}
864
865/*
866 * "timer_start(time, callback [, options])" function
867 */
868 void
869f_timer_start(typval_T *argvars, typval_T *rettv)
870{
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200871 long msec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100872 timer_T *timer;
873 int repeat = 0;
874 callback_T callback;
875 dict_T *dict;
876
877 rettv->vval.v_number = -1;
878 if (check_secure())
879 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200880
881 if (in_vim9script()
882 && (check_for_number_arg(argvars, 0) == FAIL
883 || check_for_opt_dict_arg(argvars, 2) == FAIL))
884 return;
885
886 msec = (long)tv_get_number(&argvars[0]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100887 if (argvars[2].v_type != VAR_UNKNOWN)
888 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100889 if (check_for_nonnull_dict_arg(argvars, 2) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100890 return;
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100891
892 dict = argvars[2].vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100893 if (dict_has_key(dict, "repeat"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100894 repeat = dict_get_number(dict, "repeat");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100895 }
896
897 callback = get_callback(&argvars[1]);
898 if (callback.cb_name == NULL)
899 return;
Bram Moolenaar745b9382022-01-27 13:55:35 +0000900 if (in_vim9script() && *callback.cb_name == NUL)
901 {
902 // empty callback is not useful for a timer
903 emsg(_(e_invalid_callback_argument));
904 free_callback(&callback);
905 return;
906 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100907
908 timer = create_timer(msec, repeat);
909 if (timer == NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100910 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000911 free_callback(&callback);
912 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100913 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000914 set_callback(&timer->tr_callback, &callback);
915 if (callback.cb_free_name)
916 vim_free(callback.cb_name);
917 rettv->vval.v_number = (varnumber_T)timer->tr_id;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100918}
919
920/*
921 * "timer_stop(timer)" function
922 */
923 void
924f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
925{
926 timer_T *timer;
927
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100928 if (check_for_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200929 return;
930
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100931 timer = find_timer((int)tv_get_number(&argvars[0]));
932 if (timer != NULL)
933 stop_timer(timer);
934}
935
936/*
937 * "timer_stopall()" function
938 */
939 void
940f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
941{
942 stop_all_timers();
943}
944
945# endif // FEAT_TIMERS
946
947# if defined(STARTUPTIME) || defined(PROTO)
948static struct timeval prev_timeval;
949
950# ifdef MSWIN
951/*
952 * Windows doesn't have gettimeofday(), although it does have struct timeval.
953 */
954 static int
955gettimeofday(struct timeval *tv, char *dummy UNUSED)
956{
957 long t = clock();
958 tv->tv_sec = t / CLOCKS_PER_SEC;
959 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
960 return 0;
961}
962# endif
963
964/*
965 * Save the previous time before doing something that could nest.
966 * set "*tv_rel" to the time elapsed so far.
967 */
968 void
969time_push(void *tv_rel, void *tv_start)
970{
971 *((struct timeval *)tv_rel) = prev_timeval;
972 gettimeofday(&prev_timeval, NULL);
973 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
974 - ((struct timeval *)tv_rel)->tv_usec;
975 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
976 - ((struct timeval *)tv_rel)->tv_sec;
977 if (((struct timeval *)tv_rel)->tv_usec < 0)
978 {
979 ((struct timeval *)tv_rel)->tv_usec += 1000000;
980 --((struct timeval *)tv_rel)->tv_sec;
981 }
982 *(struct timeval *)tv_start = prev_timeval;
983}
984
985/*
986 * Compute the previous time after doing something that could nest.
987 * Subtract "*tp" from prev_timeval;
988 * Note: The arguments are (void *) to avoid trouble with systems that don't
989 * have struct timeval.
990 */
991 void
992time_pop(
993 void *tp) // actually (struct timeval *)
994{
995 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
996 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
997 if (prev_timeval.tv_usec < 0)
998 {
999 prev_timeval.tv_usec += 1000000;
1000 --prev_timeval.tv_sec;
1001 }
1002}
1003
1004 static void
1005time_diff(struct timeval *then, struct timeval *now)
1006{
1007 long usec;
1008 long msec;
1009
1010 usec = now->tv_usec - then->tv_usec;
1011 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
1012 usec = usec % 1000L;
1013 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
1014}
1015
1016 void
1017time_msg(
1018 char *mesg,
1019 void *tv_start) // only for do_source: start time; actually
1020 // (struct timeval *)
1021{
1022 static struct timeval start;
1023 struct timeval now;
1024
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001025 if (time_fd == NULL)
1026 return;
1027
1028 if (strstr(mesg, "STARTING") != NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001029 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001030 gettimeofday(&start, NULL);
1031 prev_timeval = start;
1032 fprintf(time_fd, "\n\ntimes in msec\n");
1033 fprintf(time_fd, " clock self+sourced self: sourced script\n");
1034 fprintf(time_fd, " clock elapsed: other lines\n\n");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001035 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001036 gettimeofday(&now, NULL);
1037 time_diff(&start, &now);
1038 if (((struct timeval *)tv_start) != NULL)
1039 {
1040 fprintf(time_fd, " ");
1041 time_diff(((struct timeval *)tv_start), &now);
1042 }
1043 fprintf(time_fd, " ");
1044 time_diff(&prev_timeval, &now);
1045 prev_timeval = now;
1046 fprintf(time_fd, ": %s\n", mesg);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001047}
1048# endif // STARTUPTIME
1049#endif // FEAT_EVAL
1050
1051#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1052/*
1053 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1054 * Returns -1 when encountering EOF.
1055 */
1056 time_T
1057get8ctime(FILE *fd)
1058{
1059 int c;
1060 time_T n = 0;
1061 int i;
1062
1063 for (i = 0; i < 8; ++i)
1064 {
1065 c = getc(fd);
1066 if (c == EOF) return -1;
1067 n = (n << 8) + c;
1068 }
1069 return n;
1070}
1071
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001072/*
1073 * Write time_T to file "fd" in 8 bytes.
1074 * Returns FAIL when the write failed.
1075 */
1076 int
1077put_time(FILE *fd, time_T the_time)
1078{
1079 char_u buf[8];
1080
1081 time_to_bytes(the_time, buf);
1082 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1083}
1084
1085/*
1086 * Write time_T to "buf[8]".
1087 */
1088 void
1089time_to_bytes(time_T the_time, char_u *buf)
1090{
1091 int c;
1092 int i;
1093 int bi = 0;
1094 time_T wtime = the_time;
1095
1096 // time_T can be up to 8 bytes in size, more than long_u, thus we
1097 // can't use put_bytes() here.
1098 // Another problem is that ">>" may do an arithmetic shift that keeps the
1099 // sign. This happens for large values of wtime. A cast to long_u may
1100 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1101 // it's safe to assume that long_u is 4 bytes or more and when using 8
1102 // bytes the top bit won't be set.
1103 for (i = 7; i >= 0; --i)
1104 {
1105 if (i + 1 > (int)sizeof(time_T))
1106 // ">>" doesn't work well when shifting more bits than avail
1107 buf[bi++] = 0;
1108 else
1109 {
K.Takatac351dc12022-01-24 11:24:08 +00001110# if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001111 c = (int)(wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001112# else
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001113 c = (int)((long_u)wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001114# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001115 buf[bi++] = c;
1116 }
1117 }
1118}
1119
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001120#endif
1121
1122/*
1123 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1124 */
1125 void
1126add_time(char_u *buf, size_t buflen, time_t tt)
1127{
1128#ifdef HAVE_STRFTIME
1129 struct tm tmval;
1130 struct tm *curtime;
Bram Moolenaara2e4e0f2022-10-15 16:29:03 +01001131 size_t n;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001132
1133 if (vim_time() - tt >= 100)
1134 {
1135 curtime = vim_localtime(&tt, &tmval);
1136 if (vim_time() - tt < (60L * 60L * 12L))
1137 // within 12 hours
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001138 n = strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001139 else
1140 // longer ago
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001141 n = strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1142 if (n == 0)
1143 buf[0] = NUL;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001144 }
1145 else
1146#endif
1147 {
1148 long seconds = (long)(vim_time() - tt);
1149
1150 vim_snprintf((char *)buf, buflen,
1151 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1152 seconds);
1153 }
1154}