blob: bb4a2028293b1e59e2566789b4a38a7476b7266d [file] [log] [blame]
Bram Moolenaar0a8fed62020-02-14 13:22:17 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * time.c: functions related to time and timers
12 */
13
14#include "vim.h"
15
16/*
17 * Cache of the current timezone name as retrieved from TZ, or an empty string
18 * where unset, up to 64 octets long including trailing null byte.
19 */
20#if defined(HAVE_LOCALTIME_R) && defined(HAVE_TZSET)
21static char tz_cache[64];
22#endif
23
Bram Moolenaar00d253e2020-04-06 22:13:01 +020024#define FOR_ALL_TIMERS(t) \
25 for ((t) = first_timer; (t) != NULL; (t) = (t)->tr_next)
26
Bram Moolenaar0a8fed62020-02-14 13:22:17 +010027/*
28 * Call either localtime(3) or localtime_r(3) from POSIX libc time.h, with the
29 * latter version preferred for reentrancy.
30 *
31 * If we use localtime_r(3) and we have tzset(3) available, check to see if the
32 * environment variable TZ has changed since the last run, and call tzset(3) to
33 * update the global timezone variables if it has. This is because the POSIX
34 * standard doesn't require localtime_r(3) implementations to do that as it
35 * does with localtime(3), and we don't want to call tzset(3) every time.
36 */
37 static struct tm *
38vim_localtime(
39 const time_t *timep, // timestamp for local representation
40 struct tm *result UNUSED) // pointer to caller return buffer
41{
42#ifdef HAVE_LOCALTIME_R
43# ifdef HAVE_TZSET
44 char *tz; // pointer for TZ environment var
45
46 tz = (char *)mch_getenv((char_u *)"TZ");
47 if (tz == NULL)
48 tz = "";
49 if (STRNCMP(tz_cache, tz, sizeof(tz_cache) - 1) != 0)
50 {
51 tzset();
52 vim_strncpy((char_u *)tz_cache, (char_u *)tz, sizeof(tz_cache) - 1);
53 }
54# endif // HAVE_TZSET
55 return localtime_r(timep, result);
56#else
57 return localtime(timep);
58#endif // HAVE_LOCALTIME_R
59}
60
61/*
62 * Return the current time in seconds. Calls time(), unless test_settime()
63 * was used.
64 */
65 time_T
66vim_time(void)
67{
68# ifdef FEAT_EVAL
69 return time_for_testing == 0 ? time(NULL) : time_for_testing;
70# else
71 return time(NULL);
72# endif
73}
74
75/*
76 * Replacement for ctime(), which is not safe to use.
77 * Requires strftime(), otherwise returns "(unknown)".
78 * If "thetime" is invalid returns "(invalid)". Never returns NULL.
79 * When "add_newline" is TRUE add a newline like ctime() does.
80 * Uses a static buffer.
81 */
82 char *
83get_ctime(time_t thetime, int add_newline)
84{
85 static char buf[50];
86#ifdef HAVE_STRFTIME
87 struct tm tmval;
88 struct tm *curtime;
89
90 curtime = vim_localtime(&thetime, &tmval);
91 // MSVC returns NULL for an invalid value of seconds.
92 if (curtime == NULL)
93 vim_strncpy((char_u *)buf, (char_u *)_("(Invalid)"), sizeof(buf) - 1);
94 else
95 {
96 (void)strftime(buf, sizeof(buf) - 1, _("%a %b %d %H:%M:%S %Y"),
97 curtime);
98# ifdef MSWIN
99 if (enc_codepage >= 0 && (int)GetACP() != enc_codepage)
100 {
101 char_u *to_free = NULL;
102 int len;
103
104 acp_to_enc((char_u *)buf, (int)strlen(buf), &to_free, &len);
105 if (to_free != NULL)
106 {
107 STRCPY(buf, to_free);
108 vim_free(to_free);
109 }
110 }
111# endif
112 }
113#else
114 STRCPY(buf, "(unknown)");
115#endif
116 if (add_newline)
117 STRCAT(buf, "\n");
118 return buf;
119}
120
121#if defined(FEAT_EVAL) || defined(PROTO)
122
123#if defined(MACOS_X)
124# include <time.h> // for time_t
125#endif
126
127/*
128 * "localtime()" function
129 */
130 void
131f_localtime(typval_T *argvars UNUSED, typval_T *rettv)
132{
133 rettv->vval.v_number = (varnumber_T)time(NULL);
134}
135
136# if defined(FEAT_RELTIME)
137/*
138 * Convert a List to proftime_T.
139 * Return FAIL when there is something wrong.
140 */
141 static int
142list2proftime(typval_T *arg, proftime_T *tm)
143{
144 long n1, n2;
145 int error = FALSE;
146
147 if (arg->v_type != VAR_LIST || arg->vval.v_list == NULL
148 || arg->vval.v_list->lv_len != 2)
149 return FAIL;
150 n1 = list_find_nr(arg->vval.v_list, 0L, &error);
151 n2 = list_find_nr(arg->vval.v_list, 1L, &error);
152# ifdef MSWIN
153 tm->HighPart = n1;
154 tm->LowPart = n2;
155# else
156 tm->tv_sec = n1;
157 tm->tv_usec = n2;
158# endif
159 return error ? FAIL : OK;
160}
161# endif // FEAT_RELTIME
162
163/*
164 * "reltime()" function
165 */
166 void
167f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
168{
169# ifdef FEAT_RELTIME
170 proftime_T res;
171 proftime_T start;
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200172 long n1, n2;
173
174 if (rettv_list_alloc(rettv) != OK)
175 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100176
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200177 if (in_vim9script()
178 && (check_for_opt_list_arg(argvars, 0) == FAIL
179 || (argvars[0].v_type != VAR_UNKNOWN
180 && check_for_opt_list_arg(argvars, 1) == FAIL)))
181 return;
182
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100183 if (argvars[0].v_type == VAR_UNKNOWN)
184 {
185 // No arguments: get current time.
186 profile_start(&res);
187 }
188 else if (argvars[1].v_type == VAR_UNKNOWN)
189 {
190 if (list2proftime(&argvars[0], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200191 {
192 if (in_vim9script())
193 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100194 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200195 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100196 profile_end(&res);
197 }
198 else
199 {
200 // Two arguments: compute the difference.
201 if (list2proftime(&argvars[0], &start) == FAIL
202 || list2proftime(&argvars[1], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200203 {
204 if (in_vim9script())
205 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100206 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200207 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100208 profile_sub(&res, &start);
209 }
210
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100211# ifdef MSWIN
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200212 n1 = res.HighPart;
213 n2 = res.LowPart;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100214# else
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200215 n1 = res.tv_sec;
216 n2 = res.tv_usec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100217# endif
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200218 list_append_number(rettv->vval.v_list, (varnumber_T)n1);
219 list_append_number(rettv->vval.v_list, (varnumber_T)n2);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100220# endif
221}
222
223# ifdef FEAT_FLOAT
224/*
225 * "reltimefloat()" function
226 */
227 void
228f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv)
229{
230# ifdef FEAT_RELTIME
231 proftime_T tm;
232# endif
233
234 rettv->v_type = VAR_FLOAT;
235 rettv->vval.v_float = 0;
236# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200237 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
238 return;
239
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100240 if (list2proftime(&argvars[0], &tm) == OK)
241 rettv->vval.v_float = profile_float(&tm);
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200242 else if (in_vim9script())
243 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100244# endif
245}
246# endif
247
248/*
249 * "reltimestr()" function
250 */
251 void
252f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
253{
254# ifdef FEAT_RELTIME
255 proftime_T tm;
256# endif
257
258 rettv->v_type = VAR_STRING;
259 rettv->vval.v_string = NULL;
260# ifdef FEAT_RELTIME
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200261 if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
262 return;
263
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100264 if (list2proftime(&argvars[0], &tm) == OK)
265 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200266 else if (in_vim9script())
267 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100268# endif
269}
270
271# if defined(HAVE_STRFTIME) || defined(PROTO)
272/*
273 * "strftime({format}[, {time}])" function
274 */
275 void
276f_strftime(typval_T *argvars, typval_T *rettv)
277{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100278 struct tm tmval;
279 struct tm *curtime;
280 time_t seconds;
281 char_u *p;
282
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200283 if (in_vim9script()
284 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200285 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200286 return;
287
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100288 rettv->v_type = VAR_STRING;
289
290 p = tv_get_string(&argvars[0]);
291 if (argvars[1].v_type == VAR_UNKNOWN)
292 seconds = time(NULL);
293 else
294 seconds = (time_t)tv_get_number(&argvars[1]);
295 curtime = vim_localtime(&seconds, &tmval);
296 // MSVC returns NULL for an invalid value of seconds.
297 if (curtime == NULL)
298 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
299 else
300 {
K.Takata2c4a1d02021-05-28 15:49:34 +0200301# ifdef MSWIN
302 WCHAR result_buf[256];
303 WCHAR *wp;
304
305 wp = enc_to_utf16(p, NULL);
306 if (wp != NULL)
K.Takataeeec2542021-06-02 13:28:16 +0200307 (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
K.Takata2c4a1d02021-05-28 15:49:34 +0200308 else
309 result_buf[0] = NUL;
310 rettv->vval.v_string = utf16_to_enc(result_buf, NULL);
311 vim_free(wp);
312# else
313 char_u result_buf[256];
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100314 vimconv_T conv;
315 char_u *enc;
316
317 conv.vc_type = CONV_NONE;
318 enc = enc_locale();
319 convert_setup(&conv, p_enc, enc);
320 if (conv.vc_type != CONV_NONE)
321 p = string_convert(&conv, p, NULL);
322 if (p != NULL)
323 (void)strftime((char *)result_buf, sizeof(result_buf),
324 (char *)p, curtime);
325 else
326 result_buf[0] = NUL;
327
328 if (conv.vc_type != CONV_NONE)
329 vim_free(p);
330 convert_setup(&conv, enc, p_enc);
331 if (conv.vc_type != CONV_NONE)
332 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
333 else
334 rettv->vval.v_string = vim_strsave(result_buf);
335
336 // Release conversion descriptors
337 convert_setup(&conv, NULL, NULL);
338 vim_free(enc);
K.Takata2c4a1d02021-05-28 15:49:34 +0200339# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100340 }
341}
342# endif
343
344# if defined(HAVE_STRPTIME) || defined(PROTO)
345/*
346 * "strptime({format}, {timestring})" function
347 */
348 void
349f_strptime(typval_T *argvars, typval_T *rettv)
350{
351 struct tm tmval;
352 char_u *fmt;
353 char_u *str;
354 vimconv_T conv;
355 char_u *enc;
356
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200357 if (in_vim9script()
358 && (check_for_string_arg(argvars, 0) == FAIL
359 || check_for_string_arg(argvars, 1) == FAIL))
360 return;
361
Bram Moolenaara80faa82020-04-12 19:37:17 +0200362 CLEAR_FIELD(tmval);
Bram Moolenaarea1233f2020-06-10 16:54:13 +0200363 tmval.tm_isdst = -1;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100364 fmt = tv_get_string(&argvars[0]);
365 str = tv_get_string(&argvars[1]);
366
367 conv.vc_type = CONV_NONE;
368 enc = enc_locale();
369 convert_setup(&conv, p_enc, enc);
370 if (conv.vc_type != CONV_NONE)
371 fmt = string_convert(&conv, fmt, NULL);
372 if (fmt == NULL
373 || strptime((char *)str, (char *)fmt, &tmval) == NULL
374 || (rettv->vval.v_number = mktime(&tmval)) == -1)
375 rettv->vval.v_number = 0;
376
377 if (conv.vc_type != CONV_NONE)
378 vim_free(fmt);
379 convert_setup(&conv, NULL, NULL);
380 vim_free(enc);
381}
382# endif
383
384# if defined(FEAT_TIMERS) || defined(PROTO)
385static timer_T *first_timer = NULL;
386static long last_timer_id = 0;
387
388/*
389 * Return time left until "due". Negative if past "due".
390 */
391 long
392proftime_time_left(proftime_T *due, proftime_T *now)
393{
394# ifdef MSWIN
395 LARGE_INTEGER fr;
396
397 if (now->QuadPart > due->QuadPart)
398 return 0;
399 QueryPerformanceFrequency(&fr);
400 return (long)(((double)(due->QuadPart - now->QuadPart)
401 / (double)fr.QuadPart) * 1000);
402# else
403 if (now->tv_sec > due->tv_sec)
404 return 0;
405 return (due->tv_sec - now->tv_sec) * 1000
406 + (due->tv_usec - now->tv_usec) / 1000;
407# endif
408}
409
410/*
411 * Insert a timer in the list of timers.
412 */
413 static void
414insert_timer(timer_T *timer)
415{
416 timer->tr_next = first_timer;
417 timer->tr_prev = NULL;
418 if (first_timer != NULL)
419 first_timer->tr_prev = timer;
420 first_timer = timer;
421 did_add_timer = TRUE;
422}
423
424/*
425 * Take a timer out of the list of timers.
426 */
427 static void
428remove_timer(timer_T *timer)
429{
430 if (timer->tr_prev == NULL)
431 first_timer = timer->tr_next;
432 else
433 timer->tr_prev->tr_next = timer->tr_next;
434 if (timer->tr_next != NULL)
435 timer->tr_next->tr_prev = timer->tr_prev;
436}
437
438 static void
439free_timer(timer_T *timer)
440{
441 free_callback(&timer->tr_callback);
442 vim_free(timer);
443}
444
445/*
446 * Create a timer and return it. NULL if out of memory.
447 * Caller should set the callback.
448 */
449 timer_T *
450create_timer(long msec, int repeat)
451{
452 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
453 long prev_id = last_timer_id;
454
455 if (timer == NULL)
456 return NULL;
457 if (++last_timer_id <= prev_id)
458 // Overflow! Might cause duplicates...
459 last_timer_id = 0;
460 timer->tr_id = last_timer_id;
461 insert_timer(timer);
462 if (repeat != 0)
463 timer->tr_repeat = repeat - 1;
464 timer->tr_interval = msec;
465
466 profile_setlimit(msec, &timer->tr_due);
467 return timer;
468}
469
470/*
471 * Invoke the callback of "timer".
472 */
473 static void
474timer_callback(timer_T *timer)
475{
476 typval_T rettv;
477 typval_T argv[2];
478
479 argv[0].v_type = VAR_NUMBER;
480 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
481 argv[1].v_type = VAR_UNKNOWN;
482
483 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
484 clear_tv(&rettv);
485}
486
487/*
488 * Call timers that are due.
489 * Return the time in msec until the next timer is due.
490 * Returns -1 if there are no pending timers.
491 */
492 long
493check_due_timer(void)
494{
495 timer_T *timer;
496 timer_T *timer_next;
497 long this_due;
498 long next_due = -1;
499 proftime_T now;
500 int did_one = FALSE;
501 int need_update_screen = FALSE;
502 long current_id = last_timer_id;
503
504 // Don't run any timers while exiting or dealing with an error.
505 if (exiting || aborting())
506 return next_due;
507
508 profile_start(&now);
509 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
510 {
511 timer_next = timer->tr_next;
512
513 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
514 continue;
515 this_due = proftime_time_left(&timer->tr_due, &now);
516 if (this_due <= 1)
517 {
518 // Save and restore a lot of flags, because the timer fires while
519 // waiting for a character, which might be halfway a command.
520 int save_timer_busy = timer_busy;
521 int save_vgetc_busy = vgetc_busy;
522 int save_did_emsg = did_emsg;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200523 int prev_uncaught_emsg = uncaught_emsg;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100524 int save_called_emsg = called_emsg;
525 int save_must_redraw = must_redraw;
526 int save_trylevel = trylevel;
527 int save_did_throw = did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100528 int save_need_rethrow = need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100529 int save_ex_pressedreturn = get_pressedreturn();
530 int save_may_garbage_collect = may_garbage_collect;
531 except_T *save_current_exception = current_exception;
532 vimvars_save_T vvsave;
533
534 // Create a scope for running the timer callback, ignoring most of
535 // the current scope, such as being inside a try/catch.
536 timer_busy = timer_busy > 0 || vgetc_busy > 0;
537 vgetc_busy = 0;
538 called_emsg = 0;
539 did_emsg = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100540 must_redraw = 0;
541 trylevel = 0;
542 did_throw = FALSE;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100543 need_rethrow = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100544 current_exception = NULL;
545 may_garbage_collect = FALSE;
546 save_vimvars(&vvsave);
547
Bram Moolenaar22286892020-11-05 20:50:51 +0100548 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100549 timer->tr_firing = TRUE;
550 timer_callback(timer);
551 timer->tr_firing = FALSE;
552
Bram Moolenaar22286892020-11-05 20:50:51 +0100553 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100554 timer_next = timer->tr_next;
555 did_one = TRUE;
556 timer_busy = save_timer_busy;
557 vgetc_busy = save_vgetc_busy;
Bram Moolenaar88c89c72021-08-14 14:01:05 +0200558 if (uncaught_emsg > prev_uncaught_emsg)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100559 ++timer->tr_emsg_count;
560 did_emsg = save_did_emsg;
561 called_emsg = save_called_emsg;
562 trylevel = save_trylevel;
563 did_throw = save_did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100564 need_rethrow = save_need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100565 current_exception = save_current_exception;
566 restore_vimvars(&vvsave);
567 if (must_redraw != 0)
568 need_update_screen = TRUE;
569 must_redraw = must_redraw > save_must_redraw
570 ? must_redraw : save_must_redraw;
571 set_pressedreturn(save_ex_pressedreturn);
572 may_garbage_collect = save_may_garbage_collect;
573
574 // Only fire the timer again if it repeats and stop_timer() wasn't
575 // called while inside the callback (tr_id == -1).
576 if (timer->tr_repeat != 0 && timer->tr_id != -1
577 && timer->tr_emsg_count < 3)
578 {
579 profile_setlimit(timer->tr_interval, &timer->tr_due);
580 this_due = proftime_time_left(&timer->tr_due, &now);
581 if (this_due < 1)
582 this_due = 1;
583 if (timer->tr_repeat > 0)
584 --timer->tr_repeat;
585 }
586 else
587 {
588 this_due = -1;
589 remove_timer(timer);
590 free_timer(timer);
591 }
592 }
593 if (this_due > 0 && (next_due == -1 || next_due > this_due))
594 next_due = this_due;
595 }
596
597 if (did_one)
Bram Moolenaare5050712021-12-09 10:51:05 +0000598 redraw_after_callback(need_update_screen, FALSE);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100599
600#ifdef FEAT_BEVAL_TERM
601 if (bevalexpr_due_set)
602 {
603 this_due = proftime_time_left(&bevalexpr_due, &now);
604 if (this_due <= 1)
605 {
606 bevalexpr_due_set = FALSE;
607 if (balloonEval == NULL)
608 {
609 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
610 balloonEvalForTerm = TRUE;
611 }
612 if (balloonEval != NULL)
613 {
614 general_beval_cb(balloonEval, 0);
615 setcursor();
616 out_flush();
617 }
618 }
619 else if (next_due == -1 || next_due > this_due)
620 next_due = this_due;
621 }
622#endif
623#ifdef FEAT_TERMINAL
624 // Some terminal windows may need their buffer updated.
625 next_due = term_check_timers(next_due, &now);
626#endif
627
628 return current_id != last_timer_id ? 1 : next_due;
629}
630
631/*
632 * Find a timer by ID. Returns NULL if not found;
633 */
634 static timer_T *
635find_timer(long id)
636{
637 timer_T *timer;
638
639 if (id >= 0)
640 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200641 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100642 if (timer->tr_id == id)
643 return timer;
644 }
645 return NULL;
646}
647
648
649/*
650 * Stop a timer and delete it.
651 */
652 void
653stop_timer(timer_T *timer)
654{
655 if (timer->tr_firing)
656 // Free the timer after the callback returns.
657 timer->tr_id = -1;
658 else
659 {
660 remove_timer(timer);
661 free_timer(timer);
662 }
663}
664
665 static void
666stop_all_timers(void)
667{
668 timer_T *timer;
669 timer_T *timer_next;
670
671 for (timer = first_timer; timer != NULL; timer = timer_next)
672 {
673 timer_next = timer->tr_next;
674 stop_timer(timer);
675 }
676}
677
678 static void
679add_timer_info(typval_T *rettv, timer_T *timer)
680{
681 list_T *list = rettv->vval.v_list;
682 dict_T *dict = dict_alloc();
683 dictitem_T *di;
684 long remaining;
685 proftime_T now;
686
687 if (dict == NULL)
688 return;
689 list_append_dict(list, dict);
690
691 dict_add_number(dict, "id", timer->tr_id);
692 dict_add_number(dict, "time", (long)timer->tr_interval);
693
694 profile_start(&now);
695 remaining = proftime_time_left(&timer->tr_due, &now);
696 dict_add_number(dict, "remaining", (long)remaining);
697
698 dict_add_number(dict, "repeat",
Bram Moolenaar95b2dd02021-12-09 18:42:57 +0000699 (long)(timer->tr_repeat < 0 ? -1
700 : timer->tr_repeat + (timer->tr_firing ? 0 : 1)));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100701 dict_add_number(dict, "paused", (long)(timer->tr_paused));
702
703 di = dictitem_alloc((char_u *)"callback");
704 if (di != NULL)
705 {
706 if (dict_add(dict, di) == FAIL)
707 vim_free(di);
708 else
709 put_callback(&timer->tr_callback, &di->di_tv);
710 }
711}
712
713 static void
714add_timer_info_all(typval_T *rettv)
715{
716 timer_T *timer;
717
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200718 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100719 if (timer->tr_id != -1)
720 add_timer_info(rettv, timer);
721}
722
723/*
724 * Mark references in partials of timers.
725 */
726 int
727set_ref_in_timer(int copyID)
728{
729 int abort = FALSE;
730 timer_T *timer;
731 typval_T tv;
732
733 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
734 {
735 if (timer->tr_callback.cb_partial != NULL)
736 {
737 tv.v_type = VAR_PARTIAL;
738 tv.vval.v_partial = timer->tr_callback.cb_partial;
739 }
740 else
741 {
742 tv.v_type = VAR_FUNC;
743 tv.vval.v_string = timer->tr_callback.cb_name;
744 }
745 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
746 }
747 return abort;
748}
749
750# if defined(EXITFREE) || defined(PROTO)
751 void
752timer_free_all()
753{
754 timer_T *timer;
755
756 while (first_timer != NULL)
757 {
758 timer = first_timer;
759 remove_timer(timer);
760 free_timer(timer);
761 }
762}
763# endif
764
765/*
766 * "timer_info([timer])" function
767 */
768 void
769f_timer_info(typval_T *argvars, typval_T *rettv)
770{
771 timer_T *timer = NULL;
772
773 if (rettv_list_alloc(rettv) != OK)
774 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200775
776 if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
777 return;
778
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100779 if (argvars[0].v_type != VAR_UNKNOWN)
780 {
781 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200782 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100783 else
784 {
785 timer = find_timer((int)tv_get_number(&argvars[0]));
786 if (timer != NULL)
787 add_timer_info(rettv, timer);
788 }
789 }
790 else
791 add_timer_info_all(rettv);
792}
793
794/*
795 * "timer_pause(timer, paused)" function
796 */
797 void
798f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
799{
800 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200801
802 if (in_vim9script()
803 && (check_for_number_arg(argvars, 0) == FAIL
804 || check_for_bool_arg(argvars, 1) == FAIL))
805 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100806
807 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200808 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100809 else
810 {
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200811 int paused = (int)tv_get_bool(&argvars[1]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100812 timer = find_timer((int)tv_get_number(&argvars[0]));
813 if (timer != NULL)
814 timer->tr_paused = paused;
815 }
816}
817
818/*
819 * "timer_start(time, callback [, options])" function
820 */
821 void
822f_timer_start(typval_T *argvars, typval_T *rettv)
823{
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200824 long msec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100825 timer_T *timer;
826 int repeat = 0;
827 callback_T callback;
828 dict_T *dict;
829
830 rettv->vval.v_number = -1;
831 if (check_secure())
832 return;
Yegappan Lakshmanan7973de32021-07-24 16:16:15 +0200833
834 if (in_vim9script()
835 && (check_for_number_arg(argvars, 0) == FAIL
836 || check_for_opt_dict_arg(argvars, 2) == FAIL))
837 return;
838
839 msec = (long)tv_get_number(&argvars[0]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100840 if (argvars[2].v_type != VAR_UNKNOWN)
841 {
842 if (argvars[2].v_type != VAR_DICT
843 || (dict = argvars[2].vval.v_dict) == NULL)
844 {
845 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
846 return;
847 }
848 if (dict_find(dict, (char_u *)"repeat", -1) != NULL)
849 repeat = dict_get_number(dict, (char_u *)"repeat");
850 }
851
852 callback = get_callback(&argvars[1]);
853 if (callback.cb_name == NULL)
854 return;
855
856 timer = create_timer(msec, repeat);
857 if (timer == NULL)
858 free_callback(&callback);
859 else
860 {
861 set_callback(&timer->tr_callback, &callback);
862 rettv->vval.v_number = (varnumber_T)timer->tr_id;
863 }
864}
865
866/*
867 * "timer_stop(timer)" function
868 */
869 void
870f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
871{
872 timer_T *timer;
873
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200874 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
875 return;
876
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100877 if (argvars[0].v_type != VAR_NUMBER)
878 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200879 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100880 return;
881 }
882 timer = find_timer((int)tv_get_number(&argvars[0]));
883 if (timer != NULL)
884 stop_timer(timer);
885}
886
887/*
888 * "timer_stopall()" function
889 */
890 void
891f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
892{
893 stop_all_timers();
894}
895
896# endif // FEAT_TIMERS
897
898# if defined(STARTUPTIME) || defined(PROTO)
899static struct timeval prev_timeval;
900
901# ifdef MSWIN
902/*
903 * Windows doesn't have gettimeofday(), although it does have struct timeval.
904 */
905 static int
906gettimeofday(struct timeval *tv, char *dummy UNUSED)
907{
908 long t = clock();
909 tv->tv_sec = t / CLOCKS_PER_SEC;
910 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
911 return 0;
912}
913# endif
914
915/*
916 * Save the previous time before doing something that could nest.
917 * set "*tv_rel" to the time elapsed so far.
918 */
919 void
920time_push(void *tv_rel, void *tv_start)
921{
922 *((struct timeval *)tv_rel) = prev_timeval;
923 gettimeofday(&prev_timeval, NULL);
924 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
925 - ((struct timeval *)tv_rel)->tv_usec;
926 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
927 - ((struct timeval *)tv_rel)->tv_sec;
928 if (((struct timeval *)tv_rel)->tv_usec < 0)
929 {
930 ((struct timeval *)tv_rel)->tv_usec += 1000000;
931 --((struct timeval *)tv_rel)->tv_sec;
932 }
933 *(struct timeval *)tv_start = prev_timeval;
934}
935
936/*
937 * Compute the previous time after doing something that could nest.
938 * Subtract "*tp" from prev_timeval;
939 * Note: The arguments are (void *) to avoid trouble with systems that don't
940 * have struct timeval.
941 */
942 void
943time_pop(
944 void *tp) // actually (struct timeval *)
945{
946 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
947 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
948 if (prev_timeval.tv_usec < 0)
949 {
950 prev_timeval.tv_usec += 1000000;
951 --prev_timeval.tv_sec;
952 }
953}
954
955 static void
956time_diff(struct timeval *then, struct timeval *now)
957{
958 long usec;
959 long msec;
960
961 usec = now->tv_usec - then->tv_usec;
962 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
963 usec = usec % 1000L;
964 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
965}
966
967 void
968time_msg(
969 char *mesg,
970 void *tv_start) // only for do_source: start time; actually
971 // (struct timeval *)
972{
973 static struct timeval start;
974 struct timeval now;
975
976 if (time_fd != NULL)
977 {
978 if (strstr(mesg, "STARTING") != NULL)
979 {
980 gettimeofday(&start, NULL);
981 prev_timeval = start;
982 fprintf(time_fd, "\n\ntimes in msec\n");
983 fprintf(time_fd, " clock self+sourced self: sourced script\n");
984 fprintf(time_fd, " clock elapsed: other lines\n\n");
985 }
986 gettimeofday(&now, NULL);
987 time_diff(&start, &now);
988 if (((struct timeval *)tv_start) != NULL)
989 {
990 fprintf(time_fd, " ");
991 time_diff(((struct timeval *)tv_start), &now);
992 }
993 fprintf(time_fd, " ");
994 time_diff(&prev_timeval, &now);
995 prev_timeval = now;
996 fprintf(time_fd, ": %s\n", mesg);
997 }
998}
999# endif // STARTUPTIME
1000#endif // FEAT_EVAL
1001
1002#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1003/*
1004 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1005 * Returns -1 when encountering EOF.
1006 */
1007 time_T
1008get8ctime(FILE *fd)
1009{
1010 int c;
1011 time_T n = 0;
1012 int i;
1013
1014 for (i = 0; i < 8; ++i)
1015 {
1016 c = getc(fd);
1017 if (c == EOF) return -1;
1018 n = (n << 8) + c;
1019 }
1020 return n;
1021}
1022
1023#ifdef _MSC_VER
1024# if (_MSC_VER <= 1200)
1025// This line is required for VC6 without the service pack. Also see the
1026// matching #pragma below.
1027 # pragma optimize("", off)
1028# endif
1029#endif
1030
1031/*
1032 * Write time_T to file "fd" in 8 bytes.
1033 * Returns FAIL when the write failed.
1034 */
1035 int
1036put_time(FILE *fd, time_T the_time)
1037{
1038 char_u buf[8];
1039
1040 time_to_bytes(the_time, buf);
1041 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1042}
1043
1044/*
1045 * Write time_T to "buf[8]".
1046 */
1047 void
1048time_to_bytes(time_T the_time, char_u *buf)
1049{
1050 int c;
1051 int i;
1052 int bi = 0;
1053 time_T wtime = the_time;
1054
1055 // time_T can be up to 8 bytes in size, more than long_u, thus we
1056 // can't use put_bytes() here.
1057 // Another problem is that ">>" may do an arithmetic shift that keeps the
1058 // sign. This happens for large values of wtime. A cast to long_u may
1059 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1060 // it's safe to assume that long_u is 4 bytes or more and when using 8
1061 // bytes the top bit won't be set.
1062 for (i = 7; i >= 0; --i)
1063 {
1064 if (i + 1 > (int)sizeof(time_T))
1065 // ">>" doesn't work well when shifting more bits than avail
1066 buf[bi++] = 0;
1067 else
1068 {
1069#if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
1070 c = (int)(wtime >> (i * 8));
1071#else
1072 c = (int)((long_u)wtime >> (i * 8));
1073#endif
1074 buf[bi++] = c;
1075 }
1076 }
1077}
1078
1079#ifdef _MSC_VER
1080# if (_MSC_VER <= 1200)
1081 # pragma optimize("", on)
1082# endif
1083#endif
1084
1085#endif
1086
1087/*
1088 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1089 */
1090 void
1091add_time(char_u *buf, size_t buflen, time_t tt)
1092{
1093#ifdef HAVE_STRFTIME
1094 struct tm tmval;
1095 struct tm *curtime;
1096
1097 if (vim_time() - tt >= 100)
1098 {
1099 curtime = vim_localtime(&tt, &tmval);
1100 if (vim_time() - tt < (60L * 60L * 12L))
1101 // within 12 hours
1102 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
1103 else
1104 // longer ago
1105 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1106 }
1107 else
1108#endif
1109 {
1110 long seconds = (long)(vim_time() - tt);
1111
1112 vim_snprintf((char *)buf, buflen,
1113 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1114 seconds);
1115 }
1116}