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