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 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 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 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 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 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 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 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 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 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 * 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 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 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 save_called_emsg = called_emsg; 524 int save_must_redraw = must_redraw; 525 int save_trylevel = trylevel; 526 int save_did_throw = did_throw; 527 int save_need_rethrow = need_rethrow; 528 int save_ex_pressedreturn = get_pressedreturn(); 529 int save_may_garbage_collect = may_garbage_collect; 530 except_T *save_current_exception = current_exception; 531 vimvars_save_T vvsave; 532 533 // Create a scope for running the timer callback, ignoring most of 534 // the current scope, such as being inside a try/catch. 535 timer_busy = timer_busy > 0 || vgetc_busy > 0; 536 vgetc_busy = 0; 537 called_emsg = 0; 538 did_emsg = FALSE; 539 did_uncaught_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 (did_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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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