1 /* 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)
21 static char	tz_cache[64];
22 #endif
23 
24 #define FOR_ALL_TIMERS(t) \
25     for ((t) = first_timer; (t) != NULL; (t) = (t)->tr_next)
26 
27 /*
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 *
vim_localtime(const time_t * timep,struct tm * result UNUSED)38 vim_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
vim_time(void)66 vim_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 *
get_ctime(time_t thetime,int add_newline)83 get_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
f_localtime(typval_T * argvars UNUSED,typval_T * rettv)131 f_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
list2proftime(typval_T * arg,proftime_T * tm)142 list2proftime(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
f_reltime(typval_T * argvars UNUSED,typval_T * rettv UNUSED)167 f_reltime(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
168 {
169 # ifdef FEAT_RELTIME
170     proftime_T	res;
171     proftime_T	start;
172     long	n1, n2;
173 
174     if (rettv_list_alloc(rettv) != OK)
175 	return;
176 
177     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 
183     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)
191 	{
192 	    if (in_vim9script())
193 		emsg(_(e_invarg));
194 	    return;
195 	}
196 	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)
203 	{
204 	    if (in_vim9script())
205 		emsg(_(e_invarg));
206 	    return;
207 	}
208 	profile_sub(&res, &start);
209     }
210 
211 #  ifdef MSWIN
212     n1 = res.HighPart;
213     n2 = res.LowPart;
214 #  else
215     n1 = res.tv_sec;
216     n2 = res.tv_usec;
217 #  endif
218     list_append_number(rettv->vval.v_list, (varnumber_T)n1);
219     list_append_number(rettv->vval.v_list, (varnumber_T)n2);
220 # endif
221 }
222 
223 # ifdef FEAT_FLOAT
224 /*
225  * "reltimefloat()" function
226  */
227     void
f_reltimefloat(typval_T * argvars UNUSED,typval_T * rettv)228 f_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
237     if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
238 	return;
239 
240     if (list2proftime(&argvars[0], &tm) == OK)
241 	rettv->vval.v_float = profile_float(&tm);
242     else if (in_vim9script())
243 	emsg(_(e_invarg));
244 #  endif
245 }
246 # endif
247 
248 /*
249  * "reltimestr()" function
250  */
251     void
f_reltimestr(typval_T * argvars UNUSED,typval_T * rettv)252 f_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
261     if (in_vim9script() && check_for_list_arg(argvars, 0) == FAIL)
262 	return;
263 
264     if (list2proftime(&argvars[0], &tm) == OK)
265 	rettv->vval.v_string = vim_strsave((char_u *)profile_msg(&tm));
266     else if (in_vim9script())
267 	emsg(_(e_invarg));
268 # endif
269 }
270 
271 # if defined(HAVE_STRFTIME) || defined(PROTO)
272 /*
273  * "strftime({format}[, {time}])" function
274  */
275     void
f_strftime(typval_T * argvars,typval_T * rettv)276 f_strftime(typval_T *argvars, typval_T *rettv)
277 {
278     struct tm	tmval;
279     struct tm	*curtime;
280     time_t	seconds;
281     char_u	*p;
282 
283     if (in_vim9script()
284 	    && (check_for_string_arg(argvars, 0) == FAIL
285 		|| check_for_opt_number_arg(argvars, 1) == FAIL))
286 	return;
287 
288     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     {
301 # ifdef MSWIN
302 	WCHAR	    result_buf[256];
303 	WCHAR	    *wp;
304 
305 	wp = enc_to_utf16(p, NULL);
306 	if (wp != NULL)
307 	    (void)wcsftime(result_buf, ARRAY_LENGTH(result_buf), wp, curtime);
308 	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];
314 	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);
339 # endif
340     }
341 }
342 # endif
343 
344 # if defined(HAVE_STRPTIME) || defined(PROTO)
345 /*
346  * "strptime({format}, {timestring})" function
347  */
348     void
f_strptime(typval_T * argvars,typval_T * rettv)349 f_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 
357     if (in_vim9script()
358 	    && (check_for_string_arg(argvars, 0) == FAIL
359 		|| check_for_string_arg(argvars, 1) == FAIL))
360 	return;
361 
362     CLEAR_FIELD(tmval);
363     tmval.tm_isdst = -1;
364     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)
385 static timer_T	*first_timer = NULL;
386 static long	last_timer_id = 0;
387 
388 /*
389  * Return time left until "due".  Negative if past "due".
390  */
391     long
proftime_time_left(proftime_T * due,proftime_T * now)392 proftime_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
insert_timer(timer_T * timer)414 insert_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
remove_timer(timer_T * timer)428 remove_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
free_timer(timer_T * timer)439 free_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 *
create_timer(long msec,int repeat)450 create_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
timer_callback(timer_T * timer)474 timer_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
check_due_timer(void)493 check_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;
523 	    int prev_uncaught_emsg = uncaught_emsg;
524 	    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;
528 	    int save_need_rethrow = need_rethrow;
529 	    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;
540 	    must_redraw = 0;
541 	    trylevel = 0;
542 	    did_throw = FALSE;
543 	    need_rethrow = FALSE;
544 	    current_exception = NULL;
545 	    may_garbage_collect = FALSE;
546 	    save_vimvars(&vvsave);
547 
548 	    // Invoke the callback.
549 	    timer->tr_firing = TRUE;
550 	    timer_callback(timer);
551 	    timer->tr_firing = FALSE;
552 
553 	    // Restore stuff.
554 	    timer_next = timer->tr_next;
555 	    did_one = TRUE;
556 	    timer_busy = save_timer_busy;
557 	    vgetc_busy = save_vgetc_busy;
558 	    if (uncaught_emsg > prev_uncaught_emsg)
559 		++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;
564 	    need_rethrow = save_need_rethrow;
565 	    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)
598 	redraw_after_callback(need_update_screen);
599 
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 *
find_timer(long id)635 find_timer(long id)
636 {
637     timer_T *timer;
638 
639     if (id >= 0)
640     {
641 	FOR_ALL_TIMERS(timer)
642 	    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
stop_timer(timer_T * timer)653 stop_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
stop_all_timers(void)666 stop_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
add_timer_info(typval_T * rettv,timer_T * timer)679 add_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",
699 		    (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1));
700     dict_add_number(dict, "paused", (long)(timer->tr_paused));
701 
702     di = dictitem_alloc((char_u *)"callback");
703     if (di != NULL)
704     {
705 	if (dict_add(dict, di) == FAIL)
706 	    vim_free(di);
707 	else
708 	    put_callback(&timer->tr_callback, &di->di_tv);
709     }
710 }
711 
712     static void
add_timer_info_all(typval_T * rettv)713 add_timer_info_all(typval_T *rettv)
714 {
715     timer_T *timer;
716 
717     FOR_ALL_TIMERS(timer)
718 	if (timer->tr_id != -1)
719 	    add_timer_info(rettv, timer);
720 }
721 
722 /*
723  * Mark references in partials of timers.
724  */
725     int
set_ref_in_timer(int copyID)726 set_ref_in_timer(int copyID)
727 {
728     int		abort = FALSE;
729     timer_T	*timer;
730     typval_T	tv;
731 
732     for (timer = first_timer; !abort && timer != NULL; timer = timer->tr_next)
733     {
734 	if (timer->tr_callback.cb_partial != NULL)
735 	{
736 	    tv.v_type = VAR_PARTIAL;
737 	    tv.vval.v_partial = timer->tr_callback.cb_partial;
738 	}
739 	else
740 	{
741 	    tv.v_type = VAR_FUNC;
742 	    tv.vval.v_string = timer->tr_callback.cb_name;
743 	}
744 	abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
745     }
746     return abort;
747 }
748 
749 # if defined(EXITFREE) || defined(PROTO)
750     void
timer_free_all()751 timer_free_all()
752 {
753     timer_T *timer;
754 
755     while (first_timer != NULL)
756     {
757 	timer = first_timer;
758 	remove_timer(timer);
759 	free_timer(timer);
760     }
761 }
762 # endif
763 
764 /*
765  * "timer_info([timer])" function
766  */
767     void
f_timer_info(typval_T * argvars,typval_T * rettv)768 f_timer_info(typval_T *argvars, typval_T *rettv)
769 {
770     timer_T *timer = NULL;
771 
772     if (rettv_list_alloc(rettv) != OK)
773 	return;
774 
775     if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
776 	return;
777 
778     if (argvars[0].v_type != VAR_UNKNOWN)
779     {
780 	if (argvars[0].v_type != VAR_NUMBER)
781 	    emsg(_(e_number_expected));
782 	else
783 	{
784 	    timer = find_timer((int)tv_get_number(&argvars[0]));
785 	    if (timer != NULL)
786 		add_timer_info(rettv, timer);
787 	}
788     }
789     else
790 	add_timer_info_all(rettv);
791 }
792 
793 /*
794  * "timer_pause(timer, paused)" function
795  */
796     void
f_timer_pause(typval_T * argvars,typval_T * rettv UNUSED)797 f_timer_pause(typval_T *argvars, typval_T *rettv UNUSED)
798 {
799     timer_T	*timer = NULL;
800 
801     if (in_vim9script()
802 	    && (check_for_number_arg(argvars, 0) == FAIL
803 		|| check_for_bool_arg(argvars, 1) == FAIL))
804 	return;
805 
806     if (argvars[0].v_type != VAR_NUMBER)
807 	emsg(_(e_number_expected));
808     else
809     {
810 	int	paused = (int)tv_get_bool(&argvars[1]);
811 	timer = find_timer((int)tv_get_number(&argvars[0]));
812 	if (timer != NULL)
813 	    timer->tr_paused = paused;
814     }
815 }
816 
817 /*
818  * "timer_start(time, callback [, options])" function
819  */
820     void
f_timer_start(typval_T * argvars,typval_T * rettv)821 f_timer_start(typval_T *argvars, typval_T *rettv)
822 {
823     long	msec;
824     timer_T	*timer;
825     int		repeat = 0;
826     callback_T	callback;
827     dict_T	*dict;
828 
829     rettv->vval.v_number = -1;
830     if (check_secure())
831 	return;
832 
833     if (in_vim9script()
834 	    && (check_for_number_arg(argvars, 0) == FAIL
835 		|| check_for_opt_dict_arg(argvars, 2) == FAIL))
836 	return;
837 
838     msec = (long)tv_get_number(&argvars[0]);
839     if (argvars[2].v_type != VAR_UNKNOWN)
840     {
841 	if (argvars[2].v_type != VAR_DICT
842 				   || (dict = argvars[2].vval.v_dict) == NULL)
843 	{
844 	    semsg(_(e_invarg2), tv_get_string(&argvars[2]));
845 	    return;
846 	}
847 	if (dict_find(dict, (char_u *)"repeat", -1) != NULL)
848 	    repeat = dict_get_number(dict, (char_u *)"repeat");
849     }
850 
851     callback = get_callback(&argvars[1]);
852     if (callback.cb_name == NULL)
853 	return;
854 
855     timer = create_timer(msec, repeat);
856     if (timer == NULL)
857 	free_callback(&callback);
858     else
859     {
860 	set_callback(&timer->tr_callback, &callback);
861 	rettv->vval.v_number = (varnumber_T)timer->tr_id;
862     }
863 }
864 
865 /*
866  * "timer_stop(timer)" function
867  */
868     void
f_timer_stop(typval_T * argvars,typval_T * rettv UNUSED)869 f_timer_stop(typval_T *argvars, typval_T *rettv UNUSED)
870 {
871     timer_T *timer;
872 
873     if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
874 	return;
875 
876     if (argvars[0].v_type != VAR_NUMBER)
877     {
878 	emsg(_(e_number_expected));
879 	return;
880     }
881     timer = find_timer((int)tv_get_number(&argvars[0]));
882     if (timer != NULL)
883 	stop_timer(timer);
884 }
885 
886 /*
887  * "timer_stopall()" function
888  */
889     void
f_timer_stopall(typval_T * argvars UNUSED,typval_T * rettv UNUSED)890 f_timer_stopall(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
891 {
892     stop_all_timers();
893 }
894 
895 # endif // FEAT_TIMERS
896 
897 # if defined(STARTUPTIME) || defined(PROTO)
898 static struct timeval	prev_timeval;
899 
900 #  ifdef MSWIN
901 /*
902  * Windows doesn't have gettimeofday(), although it does have struct timeval.
903  */
904     static int
gettimeofday(struct timeval * tv,char * dummy UNUSED)905 gettimeofday(struct timeval *tv, char *dummy UNUSED)
906 {
907     long t = clock();
908     tv->tv_sec = t / CLOCKS_PER_SEC;
909     tv->tv_usec = (t - tv->tv_sec * CLOCKS_PER_SEC) * 1000000 / CLOCKS_PER_SEC;
910     return 0;
911 }
912 #  endif
913 
914 /*
915  * Save the previous time before doing something that could nest.
916  * set "*tv_rel" to the time elapsed so far.
917  */
918     void
time_push(void * tv_rel,void * tv_start)919 time_push(void *tv_rel, void *tv_start)
920 {
921     *((struct timeval *)tv_rel) = prev_timeval;
922     gettimeofday(&prev_timeval, NULL);
923     ((struct timeval *)tv_rel)->tv_usec = prev_timeval.tv_usec
924 					- ((struct timeval *)tv_rel)->tv_usec;
925     ((struct timeval *)tv_rel)->tv_sec = prev_timeval.tv_sec
926 					 - ((struct timeval *)tv_rel)->tv_sec;
927     if (((struct timeval *)tv_rel)->tv_usec < 0)
928     {
929 	((struct timeval *)tv_rel)->tv_usec += 1000000;
930 	--((struct timeval *)tv_rel)->tv_sec;
931     }
932     *(struct timeval *)tv_start = prev_timeval;
933 }
934 
935 /*
936  * Compute the previous time after doing something that could nest.
937  * Subtract "*tp" from prev_timeval;
938  * Note: The arguments are (void *) to avoid trouble with systems that don't
939  * have struct timeval.
940  */
941     void
time_pop(void * tp)942 time_pop(
943     void	*tp)	// actually (struct timeval *)
944 {
945     prev_timeval.tv_usec -= ((struct timeval *)tp)->tv_usec;
946     prev_timeval.tv_sec -= ((struct timeval *)tp)->tv_sec;
947     if (prev_timeval.tv_usec < 0)
948     {
949 	prev_timeval.tv_usec += 1000000;
950 	--prev_timeval.tv_sec;
951     }
952 }
953 
954     static void
time_diff(struct timeval * then,struct timeval * now)955 time_diff(struct timeval *then, struct timeval *now)
956 {
957     long	usec;
958     long	msec;
959 
960     usec = now->tv_usec - then->tv_usec;
961     msec = (now->tv_sec - then->tv_sec) * 1000L + usec / 1000L,
962     usec = usec % 1000L;
963     fprintf(time_fd, "%03ld.%03ld", msec, usec >= 0 ? usec : usec + 1000L);
964 }
965 
966     void
time_msg(char * mesg,void * tv_start)967 time_msg(
968     char	*mesg,
969     void	*tv_start)  // only for do_source: start time; actually
970 			    // (struct timeval *)
971 {
972     static struct timeval	start;
973     struct timeval		now;
974 
975     if (time_fd != NULL)
976     {
977 	if (strstr(mesg, "STARTING") != NULL)
978 	{
979 	    gettimeofday(&start, NULL);
980 	    prev_timeval = start;
981 	    fprintf(time_fd, "\n\ntimes in msec\n");
982 	    fprintf(time_fd, " clock   self+sourced   self:  sourced script\n");
983 	    fprintf(time_fd, " clock   elapsed:              other lines\n\n");
984 	}
985 	gettimeofday(&now, NULL);
986 	time_diff(&start, &now);
987 	if (((struct timeval *)tv_start) != NULL)
988 	{
989 	    fprintf(time_fd, "  ");
990 	    time_diff(((struct timeval *)tv_start), &now);
991 	}
992 	fprintf(time_fd, "  ");
993 	time_diff(&prev_timeval, &now);
994 	prev_timeval = now;
995 	fprintf(time_fd, ": %s\n", mesg);
996     }
997 }
998 # endif	// STARTUPTIME
999 #endif // FEAT_EVAL
1000 
1001 #if defined(FEAT_SPELL) || defined(FEAT_PERSISTENT_UNDO) || defined(PROTO)
1002 /*
1003  * Read 8 bytes from "fd" and turn them into a time_T, MSB first.
1004  * Returns -1 when encountering EOF.
1005  */
1006     time_T
get8ctime(FILE * fd)1007 get8ctime(FILE *fd)
1008 {
1009     int		c;
1010     time_T	n = 0;
1011     int		i;
1012 
1013     for (i = 0; i < 8; ++i)
1014     {
1015 	c = getc(fd);
1016 	if (c == EOF) return -1;
1017 	n = (n << 8) + c;
1018     }
1019     return n;
1020 }
1021 
1022 #ifdef _MSC_VER
1023 # if (_MSC_VER <= 1200)
1024 // This line is required for VC6 without the service pack.  Also see the
1025 // matching #pragma below.
1026  #  pragma optimize("", off)
1027 # endif
1028 #endif
1029 
1030 /*
1031  * Write time_T to file "fd" in 8 bytes.
1032  * Returns FAIL when the write failed.
1033  */
1034     int
put_time(FILE * fd,time_T the_time)1035 put_time(FILE *fd, time_T the_time)
1036 {
1037     char_u	buf[8];
1038 
1039     time_to_bytes(the_time, buf);
1040     return fwrite(buf, (size_t)8, (size_t)1, fd) == 1 ? OK : FAIL;
1041 }
1042 
1043 /*
1044  * Write time_T to "buf[8]".
1045  */
1046     void
time_to_bytes(time_T the_time,char_u * buf)1047 time_to_bytes(time_T the_time, char_u *buf)
1048 {
1049     int		c;
1050     int		i;
1051     int		bi = 0;
1052     time_T	wtime = the_time;
1053 
1054     // time_T can be up to 8 bytes in size, more than long_u, thus we
1055     // can't use put_bytes() here.
1056     // Another problem is that ">>" may do an arithmetic shift that keeps the
1057     // sign.  This happens for large values of wtime.  A cast to long_u may
1058     // truncate if time_T is 8 bytes.  So only use a cast when it is 4 bytes,
1059     // it's safe to assume that long_u is 4 bytes or more and when using 8
1060     // bytes the top bit won't be set.
1061     for (i = 7; i >= 0; --i)
1062     {
1063 	if (i + 1 > (int)sizeof(time_T))
1064 	    // ">>" doesn't work well when shifting more bits than avail
1065 	    buf[bi++] = 0;
1066 	else
1067 	{
1068 #if defined(SIZEOF_TIME_T) && SIZEOF_TIME_T > 4
1069 	    c = (int)(wtime >> (i * 8));
1070 #else
1071 	    c = (int)((long_u)wtime >> (i * 8));
1072 #endif
1073 	    buf[bi++] = c;
1074 	}
1075     }
1076 }
1077 
1078 #ifdef _MSC_VER
1079 # if (_MSC_VER <= 1200)
1080  #  pragma optimize("", on)
1081 # endif
1082 #endif
1083 
1084 #endif
1085 
1086 /*
1087  * Put timestamp "tt" in "buf[buflen]" in a nice format.
1088  */
1089     void
add_time(char_u * buf,size_t buflen,time_t tt)1090 add_time(char_u *buf, size_t buflen, time_t tt)
1091 {
1092 #ifdef HAVE_STRFTIME
1093     struct tm	tmval;
1094     struct tm	*curtime;
1095 
1096     if (vim_time() - tt >= 100)
1097     {
1098 	curtime = vim_localtime(&tt, &tmval);
1099 	if (vim_time() - tt < (60L * 60L * 12L))
1100 	    // within 12 hours
1101 	    (void)strftime((char *)buf, buflen, "%H:%M:%S", curtime);
1102 	else
1103 	    // longer ago
1104 	    (void)strftime((char *)buf, buflen, "%Y/%m/%d %H:%M:%S", curtime);
1105     }
1106     else
1107 #endif
1108     {
1109 	long seconds = (long)(vim_time() - tt);
1110 
1111 	vim_snprintf((char *)buf, buflen,
1112 		NGETTEXT("%ld second ago", "%ld seconds ago", seconds),
1113 		seconds);
1114     }
1115 }
1116