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 } 1042