blob: f7f614e42741451b9b17709202880d3b1f61a530 [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
177 if (argvars[0].v_type == VAR_UNKNOWN)
178 {
179 // No arguments: get current time.
180 profile_start(&res);
181 }
182 else if (argvars[1].v_type == VAR_UNKNOWN)
183 {
184 if (list2proftime(&argvars[0], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200185 {
186 if (in_vim9script())
187 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100188 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200189 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100190 profile_end(&res);
191 }
192 else
193 {
194 // Two arguments: compute the difference.
195 if (list2proftime(&argvars[0], &start) == FAIL
196 || list2proftime(&argvars[1], &res) == FAIL)
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200197 {
198 if (in_vim9script())
199 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100200 return;
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200201 }
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100202 profile_sub(&res, &start);
203 }
204
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100205# ifdef MSWIN
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200206 n1 = res.HighPart;
207 n2 = res.LowPart;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100208# else
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200209 n1 = res.tv_sec;
210 n2 = res.tv_usec;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100211# endif
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200212 list_append_number(rettv->vval.v_list, (varnumber_T)n1);
213 list_append_number(rettv->vval.v_list, (varnumber_T)n2);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100214# endif
215}
216
217# ifdef FEAT_FLOAT
218/*
219 * "reltimefloat()" function
220 */
221 void
222f_reltimefloat(typval_T *argvars UNUSED, typval_T *rettv)
223{
224# ifdef FEAT_RELTIME
225 proftime_T tm;
226# endif
227
228 rettv->v_type = VAR_FLOAT;
229 rettv->vval.v_float = 0;
230# ifdef FEAT_RELTIME
231 if (list2proftime(&argvars[0], &tm) == OK)
232 rettv->vval.v_float = profile_float(&tm);
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200233 else if (in_vim9script())
234 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100235# endif
236}
237# endif
238
239/*
240 * "reltimestr()" function
241 */
242 void
243f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv)
244{
245# ifdef FEAT_RELTIME
246 proftime_T tm;
247# endif
248
249 rettv->v_type = VAR_STRING;
250 rettv->vval.v_string = NULL;
251# ifdef FEAT_RELTIME
252 if (list2proftime(&argvars[0], &tm) == OK)
253 rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
Bram Moolenaarc816a2c2021-07-14 21:00:41 +0200254 else if (in_vim9script())
255 emsg(_(e_invarg));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100256# endif
257}
258
259# if defined(HAVE_STRFTIME) || defined(PROTO)
260/*
261 * "strftime({format}[, {time}])" function
262 */
263 void
264f_strftime(typval_T *argvars, typval_T *rettv)
265{
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100266 struct tm tmval;
267 struct tm *curtime;
268 time_t seconds;
269 char_u *p;
270
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200271 if (in_vim9script()
272 && (check_for_string_arg(argvars, 0) == FAIL
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200273 || check_for_opt_number_arg(argvars, 1) == FAIL))
Yegappan Lakshmanan1a71d312021-07-15 12:49:58 +0200274 return;
275
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100276 rettv->v_type = VAR_STRING;
277
278 p = tv_get_string(&argvars[0]);
279 if (argvars[1].v_type == VAR_UNKNOWN)
280 seconds = time(NULL);
281 else
282 seconds = (time_t)tv_get_number(&argvars[1]);
283 curtime = vim_localtime(&seconds, &tmval);
284 // MSVC returns NULL for an invalid value of seconds.
285 if (curtime == NULL)
286 rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)"));
287 else
288 {
K.Takata2c4a1d02021-05-28 15:49:34 +0200289# ifdef MSWIN
290 WCHAR result_buf[256];
291 WCHAR *wp;
292
293 wp = enc_to_utf16(p, NULL);
294 if (wp != NULL)
K.Takataeeec2542021-06-02 13:28:16 +0200295 (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
K.Takata2c4a1d02021-05-28 15:49:34 +0200296 else
297 result_buf[0] = NUL;
298 rettv->vval.v_string = utf16_to_enc(result_buf, NULL);
299 vim_free(wp);
300# else
301 char_u result_buf[256];
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100302 vimconv_T conv;
303 char_u *enc;
304
305 conv.vc_type = CONV_NONE;
306 enc = enc_locale();
307 convert_setup(&conv, p_enc, enc);
308 if (conv.vc_type != CONV_NONE)
309 p = string_convert(&conv, p, NULL);
310 if (p != NULL)
311 (void)strftime((char *)result_buf, sizeof(result_buf),
312 (char *)p, curtime);
313 else
314 result_buf[0] = NUL;
315
316 if (conv.vc_type != CONV_NONE)
317 vim_free(p);
318 convert_setup(&conv, enc, p_enc);
319 if (conv.vc_type != CONV_NONE)
320 rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
321 else
322 rettv->vval.v_string = vim_strsave(result_buf);
323
324 // Release conversion descriptors
325 convert_setup(&conv, NULL, NULL);
326 vim_free(enc);
K.Takata2c4a1d02021-05-28 15:49:34 +0200327# endif
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100328 }
329}
330# endif
331
332# if defined(HAVE_STRPTIME) || defined(PROTO)
333/*
334 * "strptime({format}, {timestring})" function
335 */
336 void
337f_strptime(typval_T *argvars, typval_T *rettv)
338{
339 struct tm tmval;
340 char_u *fmt;
341 char_u *str;
342 vimconv_T conv;
343 char_u *enc;
344
Bram Moolenaara80faa82020-04-12 19:37:17 +0200345 CLEAR_FIELD(tmval);
Bram Moolenaarea1233f2020-06-10 16:54:13 +0200346 tmval.tm_isdst = -1;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100347 fmt = tv_get_string(&argvars[0]);
348 str = tv_get_string(&argvars[1]);
349
350 conv.vc_type = CONV_NONE;
351 enc = enc_locale();
352 convert_setup(&conv, p_enc, enc);
353 if (conv.vc_type != CONV_NONE)
354 fmt = string_convert(&conv, fmt, NULL);
355 if (fmt == NULL
356 || strptime((char *)str, (char *)fmt, &tmval) == NULL
357 || (rettv->vval.v_number = mktime(&tmval)) == -1)
358 rettv->vval.v_number = 0;
359
360 if (conv.vc_type != CONV_NONE)
361 vim_free(fmt);
362 convert_setup(&conv, NULL, NULL);
363 vim_free(enc);
364}
365# endif
366
367# if defined(FEAT_TIMERS) || defined(PROTO)
368static timer_T *first_timer = NULL;
369static long last_timer_id = 0;
370
371/*
372 * Return time left until "due". Negative if past "due".
373 */
374 long
375proftime_time_left(proftime_T *due, proftime_T *now)
376{
377# ifdef MSWIN
378 LARGE_INTEGER fr;
379
380 if (now->QuadPart > due->QuadPart)
381 return 0;
382 QueryPerformanceFrequency(&fr);
383 return (long)(((double)(due->QuadPart - now->QuadPart)
384 / (double)fr.QuadPart) * 1000);
385# else
386 if (now->tv_sec > due->tv_sec)
387 return 0;
388 return (due->tv_sec - now->tv_sec) * 1000
389 + (due->tv_usec - now->tv_usec) / 1000;
390# endif
391}
392
393/*
394 * Insert a timer in the list of timers.
395 */
396 static void
397insert_timer(timer_T *timer)
398{
399 timer->tr_next = first_timer;
400 timer->tr_prev = NULL;
401 if (first_timer != NULL)
402 first_timer->tr_prev = timer;
403 first_timer = timer;
404 did_add_timer = TRUE;
405}
406
407/*
408 * Take a timer out of the list of timers.
409 */
410 static void
411remove_timer(timer_T *timer)
412{
413 if (timer->tr_prev == NULL)
414 first_timer = timer->tr_next;
415 else
416 timer->tr_prev->tr_next = timer->tr_next;
417 if (timer->tr_next != NULL)
418 timer->tr_next->tr_prev = timer->tr_prev;
419}
420
421 static void
422free_timer(timer_T *timer)
423{
424 free_callback(&timer->tr_callback);
425 vim_free(timer);
426}
427
428/*
429 * Create a timer and return it. NULL if out of memory.
430 * Caller should set the callback.
431 */
432 timer_T *
433create_timer(long msec, int repeat)
434{
435 timer_T *timer = ALLOC_CLEAR_ONE(timer_T);
436 long prev_id = last_timer_id;
437
438 if (timer == NULL)
439 return NULL;
440 if (++last_timer_id <= prev_id)
441 // Overflow! Might cause duplicates...
442 last_timer_id = 0;
443 timer->tr_id = last_timer_id;
444 insert_timer(timer);
445 if (repeat != 0)
446 timer->tr_repeat = repeat - 1;
447 timer->tr_interval = msec;
448
449 profile_setlimit(msec, &timer->tr_due);
450 return timer;
451}
452
453/*
454 * Invoke the callback of "timer".
455 */
456 static void
457timer_callback(timer_T *timer)
458{
459 typval_T rettv;
460 typval_T argv[2];
461
462 argv[0].v_type = VAR_NUMBER;
463 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
464 argv[1].v_type = VAR_UNKNOWN;
465
466 call_callback(&timer->tr_callback, -1, &rettv, 1, argv);
467 clear_tv(&rettv);
468}
469
470/*
471 * Call timers that are due.
472 * Return the time in msec until the next timer is due.
473 * Returns -1 if there are no pending timers.
474 */
475 long
476check_due_timer(void)
477{
478 timer_T *timer;
479 timer_T *timer_next;
480 long this_due;
481 long next_due = -1;
482 proftime_T now;
483 int did_one = FALSE;
484 int need_update_screen = FALSE;
485 long current_id = last_timer_id;
486
487 // Don't run any timers while exiting or dealing with an error.
488 if (exiting || aborting())
489 return next_due;
490
491 profile_start(&now);
492 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
493 {
494 timer_next = timer->tr_next;
495
496 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
497 continue;
498 this_due = proftime_time_left(&timer->tr_due, &now);
499 if (this_due <= 1)
500 {
501 // Save and restore a lot of flags, because the timer fires while
502 // waiting for a character, which might be halfway a command.
503 int save_timer_busy = timer_busy;
504 int save_vgetc_busy = vgetc_busy;
505 int save_did_emsg = did_emsg;
506 int save_called_emsg = called_emsg;
507 int save_must_redraw = must_redraw;
508 int save_trylevel = trylevel;
509 int save_did_throw = did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100510 int save_need_rethrow = need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100511 int save_ex_pressedreturn = get_pressedreturn();
512 int save_may_garbage_collect = may_garbage_collect;
513 except_T *save_current_exception = current_exception;
514 vimvars_save_T vvsave;
515
516 // Create a scope for running the timer callback, ignoring most of
517 // the current scope, such as being inside a try/catch.
518 timer_busy = timer_busy > 0 || vgetc_busy > 0;
519 vgetc_busy = 0;
520 called_emsg = 0;
521 did_emsg = FALSE;
522 did_uncaught_emsg = FALSE;
523 must_redraw = 0;
524 trylevel = 0;
525 did_throw = FALSE;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100526 need_rethrow = FALSE;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100527 current_exception = NULL;
528 may_garbage_collect = FALSE;
529 save_vimvars(&vvsave);
530
Bram Moolenaar22286892020-11-05 20:50:51 +0100531 // Invoke the callback.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100532 timer->tr_firing = TRUE;
533 timer_callback(timer);
534 timer->tr_firing = FALSE;
535
Bram Moolenaar22286892020-11-05 20:50:51 +0100536 // Restore stuff.
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100537 timer_next = timer->tr_next;
538 did_one = TRUE;
539 timer_busy = save_timer_busy;
540 vgetc_busy = save_vgetc_busy;
541 if (did_uncaught_emsg)
542 ++timer->tr_emsg_count;
543 did_emsg = save_did_emsg;
544 called_emsg = save_called_emsg;
545 trylevel = save_trylevel;
546 did_throw = save_did_throw;
Bram Moolenaara0f7f732021-01-20 22:22:49 +0100547 need_rethrow = save_need_rethrow;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100548 current_exception = save_current_exception;
549 restore_vimvars(&vvsave);
550 if (must_redraw != 0)
551 need_update_screen = TRUE;
552 must_redraw = must_redraw > save_must_redraw
553 ? must_redraw : save_must_redraw;
554 set_pressedreturn(save_ex_pressedreturn);
555 may_garbage_collect = save_may_garbage_collect;
556
557 // Only fire the timer again if it repeats and stop_timer() wasn't
558 // called while inside the callback (tr_id == -1).
559 if (timer->tr_repeat != 0 && timer->tr_id != -1
560 && timer->tr_emsg_count < 3)
561 {
562 profile_setlimit(timer->tr_interval, &timer->tr_due);
563 this_due = proftime_time_left(&timer->tr_due, &now);
564 if (this_due < 1)
565 this_due = 1;
566 if (timer->tr_repeat > 0)
567 --timer->tr_repeat;
568 }
569 else
570 {
571 this_due = -1;
572 remove_timer(timer);
573 free_timer(timer);
574 }
575 }
576 if (this_due > 0 && (next_due == -1 || next_due > this_due))
577 next_due = this_due;
578 }
579
580 if (did_one)
581 redraw_after_callback(need_update_screen);
582
583#ifdef FEAT_BEVAL_TERM
584 if (bevalexpr_due_set)
585 {
586 this_due = proftime_time_left(&bevalexpr_due, &now);
587 if (this_due <= 1)
588 {
589 bevalexpr_due_set = FALSE;
590 if (balloonEval == NULL)
591 {
592 balloonEval = ALLOC_CLEAR_ONE(BalloonEval);
593 balloonEvalForTerm = TRUE;
594 }
595 if (balloonEval != NULL)
596 {
597 general_beval_cb(balloonEval, 0);
598 setcursor();
599 out_flush();
600 }
601 }
602 else if (next_due == -1 || next_due > this_due)
603 next_due = this_due;
604 }
605#endif
606#ifdef FEAT_TERMINAL
607 // Some terminal windows may need their buffer updated.
608 next_due = term_check_timers(next_due, &now);
609#endif
610
611 return current_id != last_timer_id ? 1 : next_due;
612}
613
614/*
615 * Find a timer by ID. Returns NULL if not found;
616 */
617 static timer_T *
618find_timer(long id)
619{
620 timer_T *timer;
621
622 if (id >= 0)
623 {
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200624 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100625 if (timer->tr_id == id)
626 return timer;
627 }
628 return NULL;
629}
630
631
632/*
633 * Stop a timer and delete it.
634 */
635 void
636stop_timer(timer_T *timer)
637{
638 if (timer->tr_firing)
639 // Free the timer after the callback returns.
640 timer->tr_id = -1;
641 else
642 {
643 remove_timer(timer);
644 free_timer(timer);
645 }
646}
647
648 static void
649stop_all_timers(void)
650{
651 timer_T *timer;
652 timer_T *timer_next;
653
654 for (timer = first_timer; timer != NULL; timer = timer_next)
655 {
656 timer_next = timer->tr_next;
657 stop_timer(timer);
658 }
659}
660
661 static void
662add_timer_info(typval_T *rettv, timer_T *timer)
663{
664 list_T *list = rettv->vval.v_list;
665 dict_T *dict = dict_alloc();
666 dictitem_T *di;
667 long remaining;
668 proftime_T now;
669
670 if (dict == NULL)
671 return;
672 list_append_dict(list, dict);
673
674 dict_add_number(dict, "id", timer->tr_id);
675 dict_add_number(dict, "time", (long)timer->tr_interval);
676
677 profile_start(&now);
678 remaining = proftime_time_left(&timer->tr_due, &now);
679 dict_add_number(dict, "remaining", (long)remaining);
680
681 dict_add_number(dict, "repeat",
682 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1));
683 dict_add_number(dict, "paused", (long)(timer->tr_paused));
684
685 di = dictitem_alloc((char_u *)"callback");
686 if (di != NULL)
687 {
688 if (dict_add(dict, di) == FAIL)
689 vim_free(di);
690 else
691 put_callback(&timer->tr_callback, &di->di_tv);
692 }
693}
694
695 static void
696add_timer_info_all(typval_T *rettv)
697{
698 timer_T *timer;
699
Bram Moolenaar00d253e2020-04-06 22:13:01 +0200700 FOR_ALL_TIMERS(timer)
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100701 if (timer->tr_id != -1)
702 add_timer_info(rettv, timer);
703}
704
705/*
706 * Mark references in partials of timers.
707 */
708 int
709set_ref_in_timer(int copyID)
710{
711 int abort = FALSE;
712 timer_T *timer;
713 typval_T tv;
714
715 for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
716 {
717 if (timer->tr_callback.cb_partial != NULL)
718 {
719 tv.v_type = VAR_PARTIAL;
720 tv.vval.v_partial = timer->tr_callback.cb_partial;
721 }
722 else
723 {
724 tv.v_type = VAR_FUNC;
725 tv.vval.v_string = timer->tr_callback.cb_name;
726 }
727 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
728 }
729 return abort;
730}
731
732# if defined(EXITFREE) || defined(PROTO)
733 void
734timer_free_all()
735{
736 timer_T *timer;
737
738 while (first_timer != NULL)
739 {
740 timer = first_timer;
741 remove_timer(timer);
742 free_timer(timer);
743 }
744}
745# endif
746
747/*
748 * "timer_info([timer])" function
749 */
750 void
751f_timer_info(typval_T *argvars, typval_T *rettv)
752{
753 timer_T *timer = NULL;
754
755 if (rettv_list_alloc(rettv) != OK)
756 return;
757 if (argvars[0].v_type != VAR_UNKNOWN)
758 {
759 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200760 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100761 else
762 {
763 timer = find_timer((int)tv_get_number(&argvars[0]));
764 if (timer != NULL)
765 add_timer_info(rettv, timer);
766 }
767 }
768 else
769 add_timer_info_all(rettv);
770}
771
772/*
773 * "timer_pause(timer, paused)" function
774 */
775 void
776f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
777{
778 timer_T *timer = NULL;
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200779
780 if (in_vim9script()
781 && (check_for_number_arg(argvars, 0) == FAIL
782 || check_for_bool_arg(argvars, 1) == FAIL))
783 return;
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100784
785 if (argvars[0].v_type != VAR_NUMBER)
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200786 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100787 else
788 {
Yegappan Lakshmanan83494b42021-07-20 17:51:51 +0200789 int paused = (int)tv_get_bool(&argvars[1]);
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100790 timer = find_timer((int)tv_get_number(&argvars[0]));
791 if (timer != NULL)
792 timer->tr_paused = paused;
793 }
794}
795
796/*
797 * "timer_start(time, callback [, options])" function
798 */
799 void
800f_timer_start(typval_T *argvars, typval_T *rettv)
801{
802 long msec = (long)tv_get_number(&argvars[0]);
803 timer_T *timer;
804 int repeat = 0;
805 callback_T callback;
806 dict_T *dict;
807
808 rettv->vval.v_number = -1;
809 if (check_secure())
810 return;
811 if (argvars[2].v_type != VAR_UNKNOWN)
812 {
813 if (argvars[2].v_type != VAR_DICT
814 || (dict = argvars[2].vval.v_dict) == NULL)
815 {
816 semsg(_(e_invarg2), tv_get_string(&argvars[2]));
817 return;
818 }
819 if (dict_find(dict, (char_u *)"repeat", -1) != NULL)
820 repeat = dict_get_number(dict, (char_u *)"repeat");
821 }
822
823 callback = get_callback(&argvars[1]);
824 if (callback.cb_name == NULL)
825 return;
826
827 timer = create_timer(msec, repeat);
828 if (timer == NULL)
829 free_callback(&callback);
830 else
831 {
832 set_callback(&timer->tr_callback, &callback);
833 rettv->vval.v_number = (varnumber_T)timer->tr_id;
834 }
835}
836
837/*
838 * "timer_stop(timer)" function
839 */
840 void
841f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
842{
843 timer_T *timer;
844
845 if (argvars[0].v_type != VAR_NUMBER)
846 {
Bram Moolenaare29a27f2021-07-20 21:07:36 +0200847 emsg(_(e_number_expected));
Bram Moolenaar0a8fed62020-02-14 13:22:17 +0100848 return;
849 }
850 timer = find_timer((int)tv_get_number(&argvars[0]));
851 if (timer != NULL)
852 stop_timer(timer);
853}
854
855/*
856 * "timer_stopall()" function
857 */
858 void
859f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
860{
861 stop_all_timers();
862}
863
864# endif // FEAT_TIMERS
865
866# if defined(STARTUPTIME) || defined(PROTO)
867static struct timeval prev_timeval;
868
869# ifdef MSWIN
870/*
871 * Windows doesn't have gettimeofday(), although it does have struct timeval.
872 */
873 static int
874gettimeofday(struct timeval *tv, char *dummy UNUSED)
875{
876 long t = clock();
877 tv->tv_sec = t / CLOCKS_PER_SEC;
878 tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
879 return 0;
880}
881# endif
882
883/*
884 * Save the previous time before doing something that could nest.
885 * set "*tv_rel" to the time elapsed so far.
886 */
887 void
888time_push(void *tv_rel, void *tv_start)
889{
890 *((struct timeval *)tv_rel) = prev_timeval;
891 gettimeofday(&prev_timeval, NULL);
892 ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
893 - ((struct timeval *)tv_rel)->tv_usec;
894 ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
895 - ((struct timeval *)tv_rel)->tv_sec;
896 if (((struct timeval *)tv_rel)->tv_usec < 0)
897 {
898 ((struct timeval *)tv_rel)->tv_usec += 1000000;
899 --((struct timeval *)tv_rel)->tv_sec;
900 }
901 *(struct timeval *)tv_start = prev_timeval;
902}
903
904/*
905 * Compute the previous time after doing something that could nest.
906 * Subtract "*tp" from prev_timeval;
907 * Note: The arguments are (void *) to avoid trouble with systems that don't
908 * have struct timeval.
909 */
910 void
911time_pop(
912 void *tp) // actually (struct timeval *)
913{
914 prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
915 prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
916 if (prev_timeval.tv_usec < 0)
917 {
918 prev_timeval.tv_usec += 1000000;
919 --prev_timeval.tv_sec;
920 }
921}
922
923 static void
924time_diff(struct timeval *then, struct timeval *now)
925{
926 long usec;
927 long msec;
928
929 usec = now->tv_usec - then->tv_usec;
930 msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
931 usec = usec % 1000L;
932 fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
933}
934
935 void
936time_msg(
937 char *mesg,
938 void *tv_start) // only for do_source: start time; actually
939 // (struct timeval *)
940{
941 static struct timeval start;
942 struct timeval now;
943
944 if (time_fd != NULL)
945 {
946 if (strstr(mesg, "STARTING") != NULL)
947 {
948 gettimeofday(&start, NULL);
949 prev_timeval = start;
950 fprintf(time_fd, "\n\ntimes in msec\n");
951 fprintf(time_fd, " clock self+sourced self: sourced script\n");
952 fprintf(time_fd, " clock elapsed: other lines\n\n");
953 }
954 gettimeofday(&now, NULL);
955 time_diff(&start, &now);
956 if (((struct timeval *)tv_start) != NULL)
957 {
958 fprintf(time_fd, " ");
959 time_diff(((struct timeval *)tv_start), &now);
960 }
961 fprintf(time_fd, " ");
962 time_diff(&prev_timeval, &now);
963 prev_timeval = now;
964 fprintf(time_fd, ": %s\n", mesg);
965 }
966}
967# endif // STARTUPTIME
968#endif // FEAT_EVAL
969
970#if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
971/*
972 * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
973 * Returns -1 when encountering EOF.
974 */
975 time_T
976get8ctime(FILE *fd)
977{
978 int c;
979 time_T n = 0;
980 int i;
981
982 for (i = 0; i < 8; ++i)
983 {
984 c = getc(fd);
985 if (c == EOF) return -1;
986 n = (n << 8) + c;
987 }
988 return n;
989}
990
991#ifdef _MSC_VER
992# if (_MSC_VER <= 1200)
993// This line is required for VC6 without the service pack. Also see the
994// matching #pragma below.
995 # pragma optimize("", off)
996# endif
997#endif
998
999/*
1000 * Write time_T to file "fd" in 8 bytes.
1001 * Returns FAIL when the write failed.
1002 */
1003 int
1004put_time(FILE *fd, time_T the_time)
1005{
1006 char_u buf[8];
1007
1008 time_to_bytes(the_time, buf);
1009 return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1010}
1011
1012/*
1013 * Write time_T to "buf[8]".
1014 */
1015 void
1016time_to_bytes(time_T the_time, char_u *buf)
1017{
1018 int c;
1019 int i;
1020 int bi = 0;
1021 time_T wtime = the_time;
1022
1023 // time_T can be up to 8 bytes in size, more than long_u, thus we
1024 // can't use put_bytes() here.
1025 // Another problem is that ">>" may do an arithmetic shift that keeps the
1026 // sign. This happens for large values of wtime. A cast to long_u may
1027 // truncate if time_T is 8 bytes. So only use a cast when it is 4 bytes,
1028 // it's safe to assume that long_u is 4 bytes or more and when using 8
1029 // bytes the top bit won't be set.
1030 for (i = 7; i >= 0; --i)
1031 {
1032 if (i + 1 > (int)sizeof(time_T))
1033 // ">>" doesn't work well when shifting more bits than avail
1034 buf[bi++] = 0;
1035 else
1036 {
1037#if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
1038 c = (int)(wtime >> (i * 8));
1039#else
1040 c = (int)((long_u)wtime >> (i * 8));
1041#endif
1042 buf[bi++] = c;
1043 }
1044 }
1045}
1046
1047#ifdef _MSC_VER
1048# if (_MSC_VER <= 1200)
1049 # pragma optimize("", on)
1050# endif
1051#endif
1052
1053#endif
1054
1055/*
1056 * Put timestamp "tt" in "buf[buflen]" in a nice format.
1057 */
1058 void
1059add_time(char_u *buf, size_t buflen, time_t tt)
1060{
1061#ifdef HAVE_STRFTIME
1062 struct tm tmval;
1063 struct tm *curtime;
1064
1065 if (vim_time() - tt >= 100)
1066 {
1067 curtime = vim_localtime(&tt, &tmval);
1068 if (vim_time() - tt < (60L * 60L * 12L))
1069 // within 12 hours
1070 (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
1071 else
1072 // longer ago
1073 (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1074 }
1075 else
1076#endif
1077 {
1078 long seconds = (long)(vim_time() - tt);
1079
1080 vim_snprintf((char *)buf, buflen,
1081 NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1082 seconds);
1083 }
1084}