blob: 62b38b4bf9bee35b736c06de5d6569524a991cfa [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;
564 int save_trylevel = trylevel;
565 int save_did_throw = did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100566 int save_need_rethrow = need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100567 int save_ex_pressedreturn = get_pressedreturn();
568 int save_may_garbage_collect = may_garbage_collect;
569 except_T *save_current_exception = current_exception;
570 vimvars_save_T vvsave;
571
572 // Create a scope for running the timer callback, ignoring most of
573 // the current scope, such as being inside a try/catch.
574 timer_busy = timer_busy > 0 || vgetc_busy > 0;
575 vgetc_busy = 0;
576 called_emsg = 0;
577 did_emsg = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100578 must_redraw = 0;
579 trylevel = 0;
580 did_throw = FALSE;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100581 need_rethrow = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100582 current_exception = NULL;
583 may_garbage_collect = FALSE;
584 save_vimvars(&vvsave);
585
Bram Moolenaar22286892020-11-05 20:50:51 +0100586 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100587 timer->tr_firing = TRUE;
588 timer_callback(timer);
589 timer->tr_firing = FALSE;
590
Bram Moolenaar22286892020-11-05 20:50:51 +0100591 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100592 timer_next = timer->tr_next;
593 did_one = TRUE;
594 timer_busy = save_timer_busy;
595 vgetc_busy = save_vgetc_busy;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200596 if (uncaught_emsg > prev_uncaught_emsg)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100597 ++timer->tr_emsg_count;
598 did_emsg = save_did_emsg;
599 called_emsg = save_called_emsg;
600 trylevel = save_trylevel;
601 did_throw = save_did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100602 need_rethrow = save_need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100603 current_exception = save_current_exception;
604 restore_vimvars(&vvsave);
605 if (must_redraw != 0)
606 need_update_screen = TRUE;
607 must_redraw = must_redraw > save_must_redraw
608 ? must_redraw : save_must_redraw;
609 set_pressedreturn(save_ex_pressedreturn);
610 may_garbage_collect = save_may_garbage_collect;
611
612 // Only fire the timer again if it repeats and stop_timer() wasn't
613 // called while inside the callback (tr_id == -1).
614 if (timer->tr_repeat != 0 && timer->tr_id != -1
615 && timer->tr_emsg_count < 3)
616 {
617 profile_setlimit(timer->tr_interval, &timer->tr_due);
618 this_due = proftime_time_left(&timer->tr_due, &now);
619 if (this_due < 1)
620 this_due = 1;
621 if (timer->tr_repeat > 0)
622 --timer->tr_repeat;
623 }
624 else
625 {
626 this_due = -1;
Bram Moolenaar9198de32022-08-27 21:30:03 +0100627 if (timer->tr_keep)
628 timer->tr_paused = TRUE;
629 else
630 {
631 remove_timer(timer);
632 free_timer(timer);
633 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100634 }
635 }
636 if (this_due > 0 && (next_due == -1 || next_due > this_due))
637 next_due = this_due;
638 }
639
640 if (did_one)
Bram Moolenaare5050712021-12-09 10:51:05 +0000641 redraw_after_callback(need_update_screen, FALSE);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100642
643#ifdef FEAT_BEVAL_TERM
644 if (bevalexpr_due_set)
645 {
646 this_due = proftime_time_left(&bevalexpr_due, &now);
647 if (this_due <= 1)
648 {
649 bevalexpr_due_set = FALSE;
650 if (balloonEval == NULL)
651 {
652 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
653 balloonEvalForTerm = TRUE;
654 }
655 if (balloonEval != NULL)
656 {
657 general_beval_cb(balloonEval, 0);
658 setcursor();
659 out_flush();
660 }
661 }
662 else if (next_due == -1 || next_due > this_due)
663 next_due = this_due;
664 }
665#endif
666#ifdef FEAT_TERMINAL
667 // Some terminal windows may need their buffer updated.
668 next_due = term_check_timers(next_due, &now);
669#endif
670
671 return current_id != last_timer_id ? 1 : next_due;
672}
673
674/*
675 * Find a timer by ID. Returns NULL if not found;
676 */
677 static timer_T *
678find_timer(long id)
679{
680 timer_T *timer;
681
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000682 if (id < 0)
683 return NULL;
684
685 FOR_ALL_TIMERS(timer)
686 if (timer->tr_id == id)
687 return timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100688 return NULL;
689}
690
691
692/*
693 * Stop a timer and delete it.
694 */
695 void
696stop_timer(timer_T *timer)
697{
698 if (timer->tr_firing)
699 // Free the timer after the callback returns.
700 timer->tr_id = -1;
701 else
702 {
703 remove_timer(timer);
704 free_timer(timer);
705 }
706}
707
708 static void
709stop_all_timers(void)
710{
711 timer_T *timer;
712 timer_T *timer_next;
713
714 for (timer = first_timer; timer != NULL; timer = timer_next)
715 {
716 timer_next = timer->tr_next;
717 stop_timer(timer);
718 }
719}
720
721 static void
722add_timer_info(typval_T *rettv, timer_T *timer)
723{
724 list_T *list = rettv->vval.v_list;
725 dict_T *dict = dict_alloc();
726 dictitem_T *di;
727 long remaining;
728 proftime_T now;
729
730 if (dict == NULL)
731 return;
732 list_append_dict(list, dict);
733
734 dict_add_number(dict, "id", timer->tr_id);
735 dict_add_number(dict, "time", (long)timer->tr_interval);
736
737 profile_start(&now);
738 remaining = proftime_time_left(&timer->tr_due, &now);
739 dict_add_number(dict, "remaining", (long)remaining);
740
741 dict_add_number(dict, "repeat",
Bram Moolenaar95b2dd02021-12-09 18:42:57 +0000742 (long)(timer->tr_repeat < 0 ? -1
743 : timer->tr_repeat + (timer->tr_firing ? 0 : 1)));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100744 dict_add_number(dict, "paused", (long)(timer->tr_paused));
745
746 di = dictitem_alloc((char_u *)"callback");
747 if (di != NULL)
748 {
749 if (dict_add(dict, di) == FAIL)
750 vim_free(di);
751 else
752 put_callback(&timer->tr_callback, &di->di_tv);
753 }
754}
755
756 static void
757add_timer_info_all(typval_T *rettv)
758{
759 timer_T *timer;
760
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200761 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100762 if (timer->tr_id != -1)
763 add_timer_info(rettv, timer);
764}
765
766/*
767 * Mark references in partials of timers.
768 */
769 int
770set_ref_in_timer(int copyID)
771{
772 int abort = FALSE;
773 timer_T *timer;
774 typval_T tv;
775
776 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
777 {
778 if (timer->tr_callback.cb_partial != NULL)
779 {
780 tv.v_type = VAR_PARTIAL;
781 tv.vval.v_partial = timer->tr_callback.cb_partial;
782 }
783 else
784 {
785 tv.v_type = VAR_FUNC;
786 tv.vval.v_string = timer->tr_callback.cb_name;
787 }
788 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
789 }
790 return abort;
791}
792
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100793/*
794 * Return TRUE if "timer" exists in the list of timers.
795 */
796 int
797timer_valid(timer_T *timer)
798{
799 if (timer == NULL)
800 return FALSE;
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000801
802 timer_T *t;
803 FOR_ALL_TIMERS(t)
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100804 if (t == timer)
805 return TRUE;
806 return FALSE;
807}
808
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100809# if defined(EXITFREE) || defined(PROTO)
810 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000811timer_free_all(void)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100812{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100813 while (first_timer != NULL)
814 {
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100815 timer_T *timer = first_timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100816 remove_timer(timer);
817 free_timer(timer);
818 }
819}
820# endif
821
822/*
823 * "timer_info([timer])" function
824 */
825 void
826f_timer_info(typval_T *argvars, typval_T *rettv)
827{
828 timer_T *timer = NULL;
829
Bram Moolenaar93a10962022-06-16 11:42:09 +0100830 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100831 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200832
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100833 if (check_for_opt_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200834 return;
835
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100836 if (argvars[0].v_type != VAR_UNKNOWN)
837 {
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100838 timer = find_timer((int)tv_get_number(&argvars[0]));
839 if (timer != NULL)
840 add_timer_info(rettv, timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100841 }
842 else
843 add_timer_info_all(rettv);
844}
845
846/*
847 * "timer_pause(timer, paused)" function
848 */
849 void
850f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
851{
852 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200853
854 if (in_vim9script()
855 && (check_for_number_arg(argvars, 0) == FAIL
856 || check_for_bool_arg(argvars, 1) == FAIL))
857 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100858
859 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100860 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000861 emsg(_(e_number_expected));
862 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100863 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000864
865 int paused = (int)tv_get_bool(&argvars[1]);
866
867 timer = find_timer((int)tv_get_number(&argvars[0]));
868 if (timer != NULL)
869 timer->tr_paused = paused;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100870}
871
872/*
873 * "timer_start(time, callback [, options])" function
874 */
875 void
876f_timer_start(typval_T *argvars, typval_T *rettv)
877{
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200878 long msec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100879 timer_T *timer;
880 int repeat = 0;
881 callback_T callback;
882 dict_T *dict;
883
884 rettv->vval.v_number = -1;
885 if (check_secure())
886 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200887
888 if (in_vim9script()
889 && (check_for_number_arg(argvars, 0) == FAIL
890 || check_for_opt_dict_arg(argvars, 2) == FAIL))
891 return;
892
893 msec = (long)tv_get_number(&argvars[0]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100894 if (argvars[2].v_type != VAR_UNKNOWN)
895 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100896 if (check_for_nonnull_dict_arg(argvars, 2) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100897 return;
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100898
899 dict = argvars[2].vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100900 if (dict_has_key(dict, "repeat"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100901 repeat = dict_get_number(dict, "repeat");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100902 }
903
904 callback = get_callback(&argvars[1]);
905 if (callback.cb_name == NULL)
906 return;
Bram Moolenaar745b9382022-01-27 13:55:35 +0000907 if (in_vim9script() && *callback.cb_name == NUL)
908 {
909 // empty callback is not useful for a timer
910 emsg(_(e_invalid_callback_argument));
911 free_callback(&callback);
912 return;
913 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100914
915 timer = create_timer(msec, repeat);
916 if (timer == NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100917 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000918 free_callback(&callback);
919 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100920 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000921 set_callback(&timer->tr_callback, &callback);
922 if (callback.cb_free_name)
923 vim_free(callback.cb_name);
924 rettv->vval.v_number = (varnumber_T)timer->tr_id;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100925}
926
927/*
928 * "timer_stop(timer)" function
929 */
930 void
931f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
932{
933 timer_T *timer;
934
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100935 if (check_for_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200936 return;
937
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100938 timer = find_timer((int)tv_get_number(&argvars[0]));
939 if (timer != NULL)
940 stop_timer(timer);
941}
942
943/*
944 * "timer_stopall()" function
945 */
946 void
947f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
948{
949 stop_all_timers();
950}
951
952# endif // FEAT_TIMERS
953
954# if defined(STARTUPTIME) || defined(PROTO)
955static struct timeval prev_timeval;
956
957# ifdef MSWIN
958/*
959 * Windows doesn't have gettimeofday(), although it does have struct timeval.
960 */
961 static int
962gettimeofday(struct timeval *tv, char *dummy UNUSED)
963{
964 long t = clock();
965 tv->tv_sec = t / CLOCKS_PER_SEC;
966 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
967 return 0;
968}
969# endif
970
971/*
972 * Save the previous time before doing something that could nest.
973 * set "*tv_rel" to the time elapsed so far.
974 */
975 void
976time_push(void *tv_rel, void *tv_start)
977{
978 *((struct timeval *)tv_rel) = prev_timeval;
979 gettimeofday(&prev_timeval, NULL);
980 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
981 - ((struct timeval *)tv_rel)->tv_usec;
982 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
983 - ((struct timeval *)tv_rel)->tv_sec;
984 if (((struct timeval *)tv_rel)->tv_usec < 0)
985 {
986 ((struct timeval *)tv_rel)->tv_usec += 1000000;
987 --((struct timeval *)tv_rel)->tv_sec;
988 }
989 *(struct timeval *)tv_start = prev_timeval;
990}
991
992/*
993 * Compute the previous time after doing something that could nest.
994 * Subtract "*tp" from prev_timeval;
995 * Note: The arguments are (void *) to avoid trouble with systems that don't
996 * have struct timeval.
997 */
998 void
999time_pop(
1000 void *tp) // actually (struct timeval *)
1001{
1002 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
1003 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
1004 if (prev_timeval.tv_usec < 0)
1005 {
1006 prev_timeval.tv_usec += 1000000;
1007 --prev_timeval.tv_sec;
1008 }
1009}
1010
1011 static void
1012time_diff(struct timeval *then, struct timeval *now)
1013{
1014 long usec;
1015 long msec;
1016
1017 usec = now->tv_usec - then->tv_usec;
1018 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
1019 usec = usec % 1000L;
1020 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
1021}
1022
1023 void
1024time_msg(
1025 char *mesg,
1026 void *tv_start) // only for do_source: start time; actually
1027 // (struct timeval *)
1028{
1029 static struct timeval start;
1030 struct timeval now;
1031
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001032 if (time_fd == NULL)
1033 return;
1034
1035 if (strstr(mesg, "STARTING") != NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001036 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001037 gettimeofday(&start, NULL);
1038 prev_timeval = start;
1039 fprintf(time_fd, "\n\ntimes in msec\n");
1040 fprintf(time_fd, " clock self+sourced self: sourced script\n");
1041 fprintf(time_fd, " clock elapsed: other lines\n\n");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001042 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001043 gettimeofday(&now, NULL);
1044 time_diff(&start, &now);
1045 if (((struct timeval *)tv_start) != NULL)
1046 {
1047 fprintf(time_fd, " ");
1048 time_diff(((struct timeval *)tv_start), &now);
1049 }
1050 fprintf(time_fd, " ");
1051 time_diff(&prev_timeval, &now);
1052 prev_timeval = now;
1053 fprintf(time_fd, ": %s\n", mesg);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001054}
1055# endif // STARTUPTIME
1056#endif // FEAT_EVAL
1057
1058#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1059/*
1060 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1061 * Returns -1 when encountering EOF.
1062 */
1063 time_T
1064get8ctime(FILE *fd)
1065{
1066 int c;
1067 time_T n = 0;
1068 int i;
1069
1070 for (i = 0; i < 8; ++i)
1071 {
1072 c = getc(fd);
1073 if (c == EOF) return -1;
1074 n = (n << 8) + c;
1075 }
1076 return n;
1077}
1078
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001079/*
1080 * Write time_T to file "fd" in 8 bytes.
1081 * Returns FAIL when the write failed.
1082 */
1083 int
1084put_time(FILE *fd, time_T the_time)
1085{
1086 char_u buf[8];
1087
1088 time_to_bytes(the_time, buf);
1089 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1090}
1091
1092/*
1093 * Write time_T to "buf[8]".
1094 */
1095 void
1096time_to_bytes(time_T the_time, char_u *buf)
1097{
1098 int c;
1099 int i;
1100 int bi = 0;
1101 time_T wtime = the_time;
1102
1103 // time_T can be up to 8 bytes in size, more than long_u, thus we
1104 // can't use put_bytes() here.
1105 // Another problem is that ">>" may do an arithmetic shift that keeps the
1106 // sign. This happens for large values of wtime. A cast to long_u may
1107 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1108 // it's safe to assume that long_u is 4 bytes or more and when using 8
1109 // bytes the top bit won't be set.
1110 for (i = 7; i >= 0; --i)
1111 {
1112 if (i + 1 > (int)sizeof(time_T))
1113 // ">>" doesn't work well when shifting more bits than avail
1114 buf[bi++] = 0;
1115 else
1116 {
K.Takatac351dc12022-01-24 11:24:08 +00001117# if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001118 c = (int)(wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001119# else
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001120 c = (int)((long_u)wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001121# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001122 buf[bi++] = c;
1123 }
1124 }
1125}
1126
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001127#endif
1128
1129/*
1130 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1131 */
1132 void
1133add_time(char_u *buf, size_t buflen, time_t tt)
1134{
1135#ifdef HAVE_STRFTIME
1136 struct tm tmval;
1137 struct tm *curtime;
Bram Moolenaara2e4e0f2022-10-15 16:29:03 +01001138 size_t n;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001139
1140 if (vim_time() - tt >= 100)
1141 {
1142 curtime = vim_localtime(&tt, &tmval);
1143 if (vim_time() - tt < (60L * 60L * 12L))
1144 // within 12 hours
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001145 n = strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001146 else
1147 // longer ago
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001148 n = strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1149 if (n == 0)
1150 buf[0] = NUL;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001151 }
1152 else
1153#endif
1154 {
1155 long seconds = (long)(vim_time() - tt);
1156
1157 vim_snprintf((char *)buf, buflen,
1158 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1159 seconds);
1160 }
1161}