blob: bcc7eef1396092745bfeff49f3729e7035a714bb [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{
Ernie Rael076de792023-03-16 21:43:15 +0000261 static char buf[50];
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100262# ifdef FEAT_RELTIME
263 proftime_T tm;
264# endif
265
266 rettv->v_type = VAR_STRING;
267 rettv->vval.v_string = NULL;
268# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200269 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
270 return;
271
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100272 if (list2proftime(&argvars[0], &tm) == OK)
Ernie Rael076de792023-03-16 21:43:15 +0000273 {
274# ifdef MSWIN
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100275 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
Ernie Rael076de792023-03-16 21:43:15 +0000276# else
277 long usec = tm.tv_fsec / (TV_FSEC_SEC / 1000000);
278 sprintf(buf, "%3ld.%06ld", (long)tm.tv_sec, usec);
279 rettv->vval.v_string = vim_strsave((char_u *)buf);
280# endif
281 }
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200282 else if (in_vim9script())
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000283 emsg(_(e_invalid_argument));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100284# endif
285}
286
287# if defined(HAVE_STRFTIME) || defined(PROTO)
288/*
289 * "strftime({format}[, {time}])" function
290 */
291 void
292f_strftime(typval_T *argvars, typval_T *rettv)
293{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100294 struct tm tmval;
295 struct tm *curtime;
296 time_t seconds;
297 char_u *p;
298
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200299 if (in_vim9script()
300 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200301 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200302 return;
303
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100304 rettv->v_type = VAR_STRING;
305
306 p = tv_get_string(&argvars[0]);
307 if (argvars[1].v_type == VAR_UNKNOWN)
308 seconds = time(NULL);
309 else
310 seconds = (time_t)tv_get_number(&argvars[1]);
311 curtime = vim_localtime(&seconds, &tmval);
312 // MSVC returns NULL for an invalid value of seconds.
313 if (curtime == NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100314 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000315 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
316 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100317 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000318
319# ifdef MSWIN
320 WCHAR result_buf[256];
321 WCHAR *wp;
322
323 wp = enc_to_utf16(p, NULL);
324 if (wp != NULL)
325 (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
326 else
327 result_buf[0] = NUL;
328 rettv->vval.v_string = utf16_to_enc(result_buf, NULL);
329 vim_free(wp);
330# else
331 char_u result_buf[256];
332 vimconv_T conv;
333 char_u *enc;
334
335 conv.vc_type = CONV_NONE;
336 enc = enc_locale();
337 convert_setup(&conv, p_enc, enc);
338 if (conv.vc_type != CONV_NONE)
339 p = string_convert(&conv, p, NULL);
340 if (p == NULL || strftime((char *)result_buf, sizeof(result_buf),
341 (char *)p, curtime) == 0)
342 result_buf[0] = NUL;
343
344 if (conv.vc_type != CONV_NONE)
345 vim_free(p);
346 convert_setup(&conv, enc, p_enc);
347 if (conv.vc_type != CONV_NONE)
348 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
349 else
350 rettv->vval.v_string = vim_strsave(result_buf);
351
352 // Release conversion descriptors
353 convert_setup(&conv, NULL, NULL);
354 vim_free(enc);
355# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100356}
357# endif
358
359# if defined(HAVE_STRPTIME) || defined(PROTO)
360/*
361 * "strptime({format}, {timestring})" function
362 */
363 void
364f_strptime(typval_T *argvars, typval_T *rettv)
365{
366 struct tm tmval;
367 char_u *fmt;
368 char_u *str;
369 vimconv_T conv;
370 char_u *enc;
371
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200372 if (in_vim9script()
373 && (check_for_string_arg(argvars, 0) == FAIL
374 || check_for_string_arg(argvars, 1) == FAIL))
375 return;
376
Bram Moolenaara80faa82020-04-12 19:37:17 +0200377 CLEAR_FIELD(tmval);
Bram Moolenaarea1233f2020-06-10 16:54:13 +0200378 tmval.tm_isdst = -1;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100379 fmt = tv_get_string(&argvars[0]);
380 str = tv_get_string(&argvars[1]);
381
382 conv.vc_type = CONV_NONE;
383 enc = enc_locale();
384 convert_setup(&conv, p_enc, enc);
385 if (conv.vc_type != CONV_NONE)
386 fmt = string_convert(&conv, fmt, NULL);
387 if (fmt == NULL
388 || strptime((char *)str, (char *)fmt, &tmval) == NULL
389 || (rettv->vval.v_number = mktime(&tmval)) == -1)
390 rettv->vval.v_number = 0;
391
392 if (conv.vc_type != CONV_NONE)
393 vim_free(fmt);
394 convert_setup(&conv, NULL, NULL);
395 vim_free(enc);
396}
397# endif
398
399# if defined(FEAT_TIMERS) || defined(PROTO)
400static timer_T *first_timer = NULL;
401static long last_timer_id = 0;
402
403/*
Ernie Rael076de792023-03-16 21:43:15 +0000404 * Return time left, in "msec", until "due". Negative if past "due".
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100405 */
406 long
407proftime_time_left(proftime_T *due, proftime_T *now)
408{
409# ifdef MSWIN
410 LARGE_INTEGER fr;
411
412 if (now->QuadPart > due->QuadPart)
413 return 0;
414 QueryPerformanceFrequency(&fr);
415 return (long)(((double)(due->QuadPart - now->QuadPart)
416 / (double)fr.QuadPart) * 1000);
417# else
418 if (now->tv_sec > due->tv_sec)
419 return 0;
420 return (due->tv_sec - now->tv_sec) * 1000
Ernie Rael076de792023-03-16 21:43:15 +0000421 + (due->tv_fsec - now->tv_fsec) / (TV_FSEC_SEC / 1000);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100422# endif
423}
424
425/*
426 * Insert a timer in the list of timers.
427 */
428 static void
429insert_timer(timer_T *timer)
430{
431 timer->tr_next = first_timer;
432 timer->tr_prev = NULL;
433 if (first_timer != NULL)
434 first_timer->tr_prev = timer;
435 first_timer = timer;
436 did_add_timer = TRUE;
437}
438
439/*
440 * Take a timer out of the list of timers.
441 */
442 static void
443remove_timer(timer_T *timer)
444{
445 if (timer->tr_prev == NULL)
446 first_timer = timer->tr_next;
447 else
448 timer->tr_prev->tr_next = timer->tr_next;
449 if (timer->tr_next != NULL)
450 timer->tr_next->tr_prev = timer->tr_prev;
451}
452
453 static void
454free_timer(timer_T *timer)
455{
456 free_callback(&timer->tr_callback);
457 vim_free(timer);
458}
459
460/*
461 * Create a timer and return it. NULL if out of memory.
462 * Caller should set the callback.
463 */
464 timer_T *
465create_timer(long msec, int repeat)
466{
467 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
468 long prev_id = last_timer_id;
469
470 if (timer == NULL)
471 return NULL;
472 if (++last_timer_id <= prev_id)
473 // Overflow! Might cause duplicates...
474 last_timer_id = 0;
475 timer->tr_id = last_timer_id;
476 insert_timer(timer);
477 if (repeat != 0)
478 timer->tr_repeat = repeat - 1;
479 timer->tr_interval = msec;
480
Bram Moolenaar9198de32022-08-27 21:30:03 +0100481 timer_start(timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100482 return timer;
483}
484
485/*
Bram Moolenaar9198de32022-08-27 21:30:03 +0100486 * (Re)start a timer.
487 */
488 void
489timer_start(timer_T *timer)
490{
491 profile_setlimit(timer->tr_interval, &timer->tr_due);
492 timer->tr_paused = FALSE;
493}
494
495/*
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100496 * Invoke the callback of "timer".
497 */
498 static void
499timer_callback(timer_T *timer)
500{
501 typval_T rettv;
502 typval_T argv[2];
503
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000504#ifdef FEAT_EVAL
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100505 if (ch_log_active())
506 {
507 callback_T *cb = &timer->tr_callback;
508
509 ch_log(NULL, "invoking timer callback %s",
510 cb->cb_partial != NULL ? cb->cb_partial->pt_name : cb->cb_name);
511 }
512#endif
513
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100514 argv[0].v_type = VAR_NUMBER;
515 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
516 argv[1].v_type = VAR_UNKNOWN;
517
Bram Moolenaar745b9382022-01-27 13:55:35 +0000518 rettv.v_type = VAR_UNKNOWN;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100519 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
520 clear_tv(&rettv);
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100521
Bram Moolenaar4c5678f2022-11-30 18:12:19 +0000522#ifdef FEAT_EVAL
Bram Moolenaar9b334d52022-05-06 14:59:04 +0100523 ch_log(NULL, "timer callback finished");
524#endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100525}
526
527/*
528 * Call timers that are due.
529 * Return the time in msec until the next timer is due.
530 * Returns -1 if there are no pending timers.
531 */
532 long
533check_due_timer(void)
534{
535 timer_T *timer;
536 timer_T *timer_next;
537 long this_due;
538 long next_due = -1;
539 proftime_T now;
540 int did_one = FALSE;
541 int need_update_screen = FALSE;
542 long current_id = last_timer_id;
543
Bram Moolenaar48d0ac72022-01-07 20:40:08 +0000544 // Don't run any timers while exiting, dealing with an error or at the
545 // debug prompt.
546 if (exiting || aborting() || debug_mode)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100547 return next_due;
548
549 profile_start(&now);
550 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
551 {
552 timer_next = timer->tr_next;
553
554 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
555 continue;
556 this_due = proftime_time_left(&timer->tr_due, &now);
557 if (this_due <= 1)
558 {
559 // Save and restore a lot of flags, because the timer fires while
560 // waiting for a character, which might be halfway a command.
561 int save_timer_busy = timer_busy;
562 int save_vgetc_busy = vgetc_busy;
563 int save_did_emsg = did_emsg;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200564 int prev_uncaught_emsg = uncaught_emsg;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100565 int save_called_emsg = called_emsg;
566 int save_must_redraw = must_redraw;
567 int save_trylevel = trylevel;
568 int save_did_throw = did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100569 int save_need_rethrow = need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100570 int save_ex_pressedreturn = get_pressedreturn();
571 int save_may_garbage_collect = may_garbage_collect;
572 except_T *save_current_exception = current_exception;
573 vimvars_save_T vvsave;
574
575 // Create a scope for running the timer callback, ignoring most of
576 // the current scope, such as being inside a try/catch.
577 timer_busy = timer_busy > 0 || vgetc_busy > 0;
578 vgetc_busy = 0;
579 called_emsg = 0;
580 did_emsg = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100581 must_redraw = 0;
582 trylevel = 0;
583 did_throw = FALSE;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100584 need_rethrow = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100585 current_exception = NULL;
586 may_garbage_collect = FALSE;
587 save_vimvars(&vvsave);
588
Bram Moolenaar22286892020-11-05 20:50:51 +0100589 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100590 timer->tr_firing = TRUE;
591 timer_callback(timer);
592 timer->tr_firing = FALSE;
593
Bram Moolenaar22286892020-11-05 20:50:51 +0100594 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100595 timer_next = timer->tr_next;
596 did_one = TRUE;
597 timer_busy = save_timer_busy;
598 vgetc_busy = save_vgetc_busy;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200599 if (uncaught_emsg > prev_uncaught_emsg)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100600 ++timer->tr_emsg_count;
601 did_emsg = save_did_emsg;
602 called_emsg = save_called_emsg;
603 trylevel = save_trylevel;
604 did_throw = save_did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100605 need_rethrow = save_need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100606 current_exception = save_current_exception;
607 restore_vimvars(&vvsave);
608 if (must_redraw != 0)
609 need_update_screen = TRUE;
610 must_redraw = must_redraw > save_must_redraw
611 ? must_redraw : save_must_redraw;
612 set_pressedreturn(save_ex_pressedreturn);
613 may_garbage_collect = save_may_garbage_collect;
614
615 // Only fire the timer again if it repeats and stop_timer() wasn't
616 // called while inside the callback (tr_id == -1).
617 if (timer->tr_repeat != 0 && timer->tr_id != -1
618 && timer->tr_emsg_count < 3)
619 {
620 profile_setlimit(timer->tr_interval, &timer->tr_due);
621 this_due = proftime_time_left(&timer->tr_due, &now);
622 if (this_due < 1)
623 this_due = 1;
624 if (timer->tr_repeat > 0)
625 --timer->tr_repeat;
626 }
627 else
628 {
629 this_due = -1;
Bram Moolenaar9198de32022-08-27 21:30:03 +0100630 if (timer->tr_keep)
631 timer->tr_paused = TRUE;
632 else
633 {
634 remove_timer(timer);
635 free_timer(timer);
636 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100637 }
638 }
639 if (this_due > 0 && (next_due == -1 || next_due > this_due))
640 next_due = this_due;
641 }
642
643 if (did_one)
Bram Moolenaare5050712021-12-09 10:51:05 +0000644 redraw_after_callback(need_update_screen, FALSE);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100645
646#ifdef FEAT_BEVAL_TERM
647 if (bevalexpr_due_set)
648 {
649 this_due = proftime_time_left(&bevalexpr_due, &now);
650 if (this_due <= 1)
651 {
652 bevalexpr_due_set = FALSE;
653 if (balloonEval == NULL)
654 {
655 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
656 balloonEvalForTerm = TRUE;
657 }
658 if (balloonEval != NULL)
659 {
660 general_beval_cb(balloonEval, 0);
661 setcursor();
662 out_flush();
663 }
664 }
665 else if (next_due == -1 || next_due > this_due)
666 next_due = this_due;
667 }
668#endif
669#ifdef FEAT_TERMINAL
670 // Some terminal windows may need their buffer updated.
671 next_due = term_check_timers(next_due, &now);
672#endif
673
674 return current_id != last_timer_id ? 1 : next_due;
675}
676
677/*
678 * Find a timer by ID. Returns NULL if not found;
679 */
680 static timer_T *
681find_timer(long id)
682{
683 timer_T *timer;
684
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000685 if (id < 0)
686 return NULL;
687
688 FOR_ALL_TIMERS(timer)
689 if (timer->tr_id == id)
690 return timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100691 return NULL;
692}
693
694
695/*
696 * Stop a timer and delete it.
697 */
698 void
699stop_timer(timer_T *timer)
700{
701 if (timer->tr_firing)
702 // Free the timer after the callback returns.
703 timer->tr_id = -1;
704 else
705 {
706 remove_timer(timer);
707 free_timer(timer);
708 }
709}
710
711 static void
712stop_all_timers(void)
713{
714 timer_T *timer;
715 timer_T *timer_next;
716
717 for (timer = first_timer; timer != NULL; timer = timer_next)
718 {
719 timer_next = timer->tr_next;
720 stop_timer(timer);
721 }
722}
723
724 static void
725add_timer_info(typval_T *rettv, timer_T *timer)
726{
727 list_T *list = rettv->vval.v_list;
728 dict_T *dict = dict_alloc();
729 dictitem_T *di;
730 long remaining;
731 proftime_T now;
732
733 if (dict == NULL)
734 return;
735 list_append_dict(list, dict);
736
737 dict_add_number(dict, "id", timer->tr_id);
738 dict_add_number(dict, "time", (long)timer->tr_interval);
739
740 profile_start(&now);
741 remaining = proftime_time_left(&timer->tr_due, &now);
742 dict_add_number(dict, "remaining", (long)remaining);
743
744 dict_add_number(dict, "repeat",
Bram Moolenaar95b2dd02021-12-09 18:42:57 +0000745 (long)(timer->tr_repeat < 0 ? -1
746 : timer->tr_repeat + (timer->tr_firing ? 0 : 1)));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100747 dict_add_number(dict, "paused", (long)(timer->tr_paused));
748
749 di = dictitem_alloc((char_u *)"callback");
750 if (di != NULL)
751 {
752 if (dict_add(dict, di) == FAIL)
753 vim_free(di);
754 else
755 put_callback(&timer->tr_callback, &di->di_tv);
756 }
757}
758
759 static void
760add_timer_info_all(typval_T *rettv)
761{
762 timer_T *timer;
763
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200764 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100765 if (timer->tr_id != -1)
766 add_timer_info(rettv, timer);
767}
768
769/*
770 * Mark references in partials of timers.
771 */
772 int
773set_ref_in_timer(int copyID)
774{
775 int abort = FALSE;
776 timer_T *timer;
777 typval_T tv;
778
779 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
780 {
781 if (timer->tr_callback.cb_partial != NULL)
782 {
783 tv.v_type = VAR_PARTIAL;
784 tv.vval.v_partial = timer->tr_callback.cb_partial;
785 }
786 else
787 {
788 tv.v_type = VAR_FUNC;
789 tv.vval.v_string = timer->tr_callback.cb_name;
790 }
791 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
792 }
793 return abort;
794}
795
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100796/*
797 * Return TRUE if "timer" exists in the list of timers.
798 */
799 int
800timer_valid(timer_T *timer)
801{
802 if (timer == NULL)
803 return FALSE;
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000804
805 timer_T *t;
806 FOR_ALL_TIMERS(t)
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100807 if (t == timer)
808 return TRUE;
809 return FALSE;
810}
811
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100812# if defined(EXITFREE) || defined(PROTO)
813 void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000814timer_free_all(void)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100815{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100816 while (first_timer != NULL)
817 {
Bram Moolenaarcf3d0ea2022-10-07 11:20:29 +0100818 timer_T *timer = first_timer;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100819 remove_timer(timer);
820 free_timer(timer);
821 }
822}
823# endif
824
825/*
826 * "timer_info([timer])" function
827 */
828 void
829f_timer_info(typval_T *argvars, typval_T *rettv)
830{
831 timer_T *timer = NULL;
832
Bram Moolenaar93a10962022-06-16 11:42:09 +0100833 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100834 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200835
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100836 if (check_for_opt_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200837 return;
838
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100839 if (argvars[0].v_type != VAR_UNKNOWN)
840 {
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100841 timer = find_timer((int)tv_get_number(&argvars[0]));
842 if (timer != NULL)
843 add_timer_info(rettv, timer);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100844 }
845 else
846 add_timer_info_all(rettv);
847}
848
849/*
850 * "timer_pause(timer, paused)" function
851 */
852 void
853f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
854{
855 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200856
857 if (in_vim9script()
858 && (check_for_number_arg(argvars, 0) == FAIL
859 || check_for_bool_arg(argvars, 1) == FAIL))
860 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100861
862 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100863 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000864 emsg(_(e_number_expected));
865 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100866 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000867
868 int paused = (int)tv_get_bool(&argvars[1]);
869
870 timer = find_timer((int)tv_get_number(&argvars[0]));
871 if (timer != NULL)
872 timer->tr_paused = paused;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100873}
874
875/*
876 * "timer_start(time, callback [, options])" function
877 */
878 void
879f_timer_start(typval_T *argvars, typval_T *rettv)
880{
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200881 long msec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100882 timer_T *timer;
883 int repeat = 0;
884 callback_T callback;
885 dict_T *dict;
886
887 rettv->vval.v_number = -1;
888 if (check_secure())
889 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200890
891 if (in_vim9script()
892 && (check_for_number_arg(argvars, 0) == FAIL
893 || check_for_opt_dict_arg(argvars, 2) == FAIL))
894 return;
895
896 msec = (long)tv_get_number(&argvars[0]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100897 if (argvars[2].v_type != VAR_UNKNOWN)
898 {
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100899 if (check_for_nonnull_dict_arg(argvars, 2) == FAIL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100900 return;
Yegappan Lakshmanan04c4c572022-08-30 19:48:24 +0100901
902 dict = argvars[2].vval.v_dict;
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100903 if (dict_has_key(dict, "repeat"))
Bram Moolenaard61efa52022-07-23 09:52:04 +0100904 repeat = dict_get_number(dict, "repeat");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100905 }
906
907 callback = get_callback(&argvars[1]);
908 if (callback.cb_name == NULL)
909 return;
Bram Moolenaar745b9382022-01-27 13:55:35 +0000910 if (in_vim9script() && *callback.cb_name == NUL)
911 {
912 // empty callback is not useful for a timer
913 emsg(_(e_invalid_callback_argument));
914 free_callback(&callback);
915 return;
916 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100917
918 timer = create_timer(msec, repeat);
919 if (timer == NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100920 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000921 free_callback(&callback);
922 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100923 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +0000924 set_callback(&timer->tr_callback, &callback);
925 if (callback.cb_free_name)
926 vim_free(callback.cb_name);
927 rettv->vval.v_number = (varnumber_T)timer->tr_id;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100928}
929
930/*
931 * "timer_stop(timer)" function
932 */
933 void
934f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
935{
936 timer_T *timer;
937
Yegappan Lakshmanan8deb2b32022-09-02 15:15:27 +0100938 if (check_for_number_arg(argvars, 0) == FAIL)
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200939 return;
940
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100941 timer = find_timer((int)tv_get_number(&argvars[0]));
942 if (timer != NULL)
943 stop_timer(timer);
944}
945
946/*
947 * "timer_stopall()" function
948 */
949 void
950f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
951{
952 stop_all_timers();
953}
954
955# endif // FEAT_TIMERS
956
957# if defined(STARTUPTIME) || defined(PROTO)
958static struct timeval prev_timeval;
959
960# ifdef MSWIN
961/*
962 * Windows doesn't have gettimeofday(), although it does have struct timeval.
963 */
964 static int
965gettimeofday(struct timeval *tv, char *dummy UNUSED)
966{
967 long t = clock();
968 tv->tv_sec = t / CLOCKS_PER_SEC;
969 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
970 return 0;
971}
972# endif
973
974/*
975 * Save the previous time before doing something that could nest.
976 * set "*tv_rel" to the time elapsed so far.
977 */
978 void
979time_push(void *tv_rel, void *tv_start)
980{
981 *((struct timeval *)tv_rel) = prev_timeval;
982 gettimeofday(&prev_timeval, NULL);
983 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
984 - ((struct timeval *)tv_rel)->tv_usec;
985 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
986 - ((struct timeval *)tv_rel)->tv_sec;
987 if (((struct timeval *)tv_rel)->tv_usec < 0)
988 {
989 ((struct timeval *)tv_rel)->tv_usec += 1000000;
990 --((struct timeval *)tv_rel)->tv_sec;
991 }
992 *(struct timeval *)tv_start = prev_timeval;
993}
994
995/*
996 * Compute the previous time after doing something that could nest.
997 * Subtract "*tp" from prev_timeval;
998 * Note: The arguments are (void *) to avoid trouble with systems that don't
999 * have struct timeval.
1000 */
1001 void
1002time_pop(
1003 void *tp) // actually (struct timeval *)
1004{
1005 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
1006 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
1007 if (prev_timeval.tv_usec < 0)
1008 {
1009 prev_timeval.tv_usec += 1000000;
1010 --prev_timeval.tv_sec;
1011 }
1012}
1013
1014 static void
1015time_diff(struct timeval *then, struct timeval *now)
1016{
1017 long usec;
1018 long msec;
1019
1020 usec = now->tv_usec - then->tv_usec;
1021 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
1022 usec = usec % 1000L;
1023 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
1024}
1025
1026 void
1027time_msg(
1028 char *mesg,
1029 void *tv_start) // only for do_source: start time; actually
1030 // (struct timeval *)
1031{
1032 static struct timeval start;
1033 struct timeval now;
1034
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001035 if (time_fd == NULL)
1036 return;
1037
1038 if (strstr(mesg, "STARTING") != NULL)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001039 {
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001040 gettimeofday(&start, NULL);
1041 prev_timeval = start;
1042 fprintf(time_fd, "\n\ntimes in msec\n");
1043 fprintf(time_fd, " clock self+sourced self: sourced script\n");
1044 fprintf(time_fd, " clock elapsed: other lines\n\n");
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001045 }
Yegappan Lakshmanan032713f2023-01-25 21:05:38 +00001046 gettimeofday(&now, NULL);
1047 time_diff(&start, &now);
1048 if (((struct timeval *)tv_start) != NULL)
1049 {
1050 fprintf(time_fd, " ");
1051 time_diff(((struct timeval *)tv_start), &now);
1052 }
1053 fprintf(time_fd, " ");
1054 time_diff(&prev_timeval, &now);
1055 prev_timeval = now;
1056 fprintf(time_fd, ": %s\n", mesg);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001057}
1058# endif // STARTUPTIME
1059#endif // FEAT_EVAL
1060
1061#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1062/*
1063 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1064 * Returns -1 when encountering EOF.
1065 */
1066 time_T
1067get8ctime(FILE *fd)
1068{
1069 int c;
1070 time_T n = 0;
1071 int i;
1072
1073 for (i = 0; i < 8; ++i)
1074 {
1075 c = getc(fd);
1076 if (c == EOF) return -1;
1077 n = (n << 8) + c;
1078 }
1079 return n;
1080}
1081
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001082/*
1083 * Write time_T to file "fd" in 8 bytes.
1084 * Returns FAIL when the write failed.
1085 */
1086 int
1087put_time(FILE *fd, time_T the_time)
1088{
1089 char_u buf[8];
1090
1091 time_to_bytes(the_time, buf);
1092 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1093}
1094
1095/*
1096 * Write time_T to "buf[8]".
1097 */
1098 void
1099time_to_bytes(time_T the_time, char_u *buf)
1100{
1101 int c;
1102 int i;
1103 int bi = 0;
1104 time_T wtime = the_time;
1105
1106 // time_T can be up to 8 bytes in size, more than long_u, thus we
1107 // can't use put_bytes() here.
1108 // Another problem is that ">>" may do an arithmetic shift that keeps the
1109 // sign. This happens for large values of wtime. A cast to long_u may
1110 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1111 // it's safe to assume that long_u is 4 bytes or more and when using 8
1112 // bytes the top bit won't be set.
1113 for (i = 7; i >= 0; --i)
1114 {
1115 if (i + 1 > (int)sizeof(time_T))
1116 // ">>" doesn't work well when shifting more bits than avail
1117 buf[bi++] = 0;
1118 else
1119 {
K.Takatac351dc12022-01-24 11:24:08 +00001120# if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001121 c = (int)(wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001122# else
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001123 c = (int)((long_u)wtime >> (i * 8));
K.Takatac351dc12022-01-24 11:24:08 +00001124# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001125 buf[bi++] = c;
1126 }
1127 }
1128}
1129
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001130#endif
1131
1132/*
1133 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1134 */
1135 void
1136add_time(char_u *buf, size_t buflen, time_t tt)
1137{
1138#ifdef HAVE_STRFTIME
1139 struct tm tmval;
1140 struct tm *curtime;
Bram Moolenaara2e4e0f2022-10-15 16:29:03 +01001141 size_t n;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001142
1143 if (vim_time() - tt >= 100)
1144 {
1145 curtime = vim_localtime(&tt, &tmval);
1146 if (vim_time() - tt < (60L * 60L * 12L))
1147 // within 12 hours
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001148 n = strftime((char *)buf, buflen, "%H:%M:%S", curtime);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001149 else
1150 // longer ago
Dominique Pelle84d14cc2022-10-12 13:30:25 +01001151 n = strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1152 if (n == 0)
1153 buf[0] = NUL;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001154 }
1155 else
1156#endif
1157 {
1158 long seconds = (long)(vim_time() - tt);
1159
1160 vim_snprintf((char *)buf, buflen,
1161 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1162 seconds);
1163 }
1164}