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