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