1 /* vi:set ts=8 sts=4 sw=4: 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 * message.c: functions for displaying messages on the command line 12 */ 13 14 #define MESSAGE_FILE /* don't include prototype for smsg() */ 15 16 #include "vim.h" 17 18 #if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) 19 # include <math.h> 20 #endif 21 22 static int other_sourcing_name __ARGS((void)); 23 static char_u *get_emsg_source __ARGS((void)); 24 static char_u *get_emsg_lnum __ARGS((void)); 25 static void add_msg_hist __ARGS((char_u *s, int len, int attr)); 26 static void hit_return_msg __ARGS((void)); 27 static void msg_home_replace_attr __ARGS((char_u *fname, int attr)); 28 #ifdef FEAT_MBYTE 29 static char_u *screen_puts_mbyte __ARGS((char_u *s, int l, int attr)); 30 #endif 31 static void msg_puts_attr_len __ARGS((char_u *str, int maxlen, int attr)); 32 static void msg_puts_display __ARGS((char_u *str, int maxlen, int attr, int recurse)); 33 static void msg_scroll_up __ARGS((void)); 34 static void inc_msg_scrolled __ARGS((void)); 35 static void store_sb_text __ARGS((char_u **sb_str, char_u *s, int attr, int *sb_col, int finish)); 36 static void t_puts __ARGS((int *t_col, char_u *t_s, char_u *s, int attr)); 37 static void msg_puts_printf __ARGS((char_u *str, int maxlen)); 38 static int do_more_prompt __ARGS((int typed_char)); 39 static void msg_screen_putchar __ARGS((int c, int attr)); 40 static int msg_check_screen __ARGS((void)); 41 static void redir_write __ARGS((char_u *s, int maxlen)); 42 static void verbose_write __ARGS((char_u *s, int maxlen)); 43 #ifdef FEAT_CON_DIALOG 44 static char_u *msg_show_console_dialog __ARGS((char_u *message, char_u *buttons, int dfltbutton)); 45 static int confirm_msg_used = FALSE; /* displaying confirm_msg */ 46 static char_u *confirm_msg = NULL; /* ":confirm" message */ 47 static char_u *confirm_msg_tail; /* tail of confirm_msg */ 48 #endif 49 50 struct msg_hist 51 { 52 struct msg_hist *next; 53 char_u *msg; 54 int attr; 55 }; 56 57 static struct msg_hist *first_msg_hist = NULL; 58 static struct msg_hist *last_msg_hist = NULL; 59 static int msg_hist_len = 0; 60 61 /* 62 * When writing messages to the screen, there are many different situations. 63 * A number of variables is used to remember the current state: 64 * msg_didany TRUE when messages were written since the last time the 65 * user reacted to a prompt. 66 * Reset: After hitting a key for the hit-return prompt, 67 * hitting <CR> for the command line or input(). 68 * Set: When any message is written to the screen. 69 * msg_didout TRUE when something was written to the current line. 70 * Reset: When advancing to the next line, when the current 71 * text can be overwritten. 72 * Set: When any message is written to the screen. 73 * msg_nowait No extra delay for the last drawn message. 74 * Used in normal_cmd() before the mode message is drawn. 75 * emsg_on_display There was an error message recently. Indicates that there 76 * should be a delay before redrawing. 77 * msg_scroll The next message should not overwrite the current one. 78 * msg_scrolled How many lines the screen has been scrolled (because of 79 * messages). Used in update_screen() to scroll the screen 80 * back. Incremented each time the screen scrolls a line. 81 * msg_scrolled_ign TRUE when msg_scrolled is non-zero and msg_puts_attr() 82 * writes something without scrolling should not make 83 * need_wait_return to be set. This is a hack to make ":ts" 84 * work without an extra prompt. 85 * lines_left Number of lines available for messages before the 86 * more-prompt is to be given. 87 * need_wait_return TRUE when the hit-return prompt is needed. 88 * Reset: After giving the hit-return prompt, when the user 89 * has answered some other prompt. 90 * Set: When the ruler or typeahead display is overwritten, 91 * scrolling the screen for some message. 92 * keep_msg Message to be displayed after redrawing the screen, in 93 * main_loop(). 94 * This is an allocated string or NULL when not used. 95 */ 96 97 /* 98 * msg(s) - displays the string 's' on the status line 99 * When terminal not initialized (yet) mch_errmsg(..) is used. 100 * return TRUE if wait_return not called 101 */ 102 int 103 msg(s) 104 char_u *s; 105 { 106 return msg_attr_keep(s, 0, FALSE); 107 } 108 109 #if defined(FEAT_EVAL) || defined(FEAT_X11) || defined(USE_XSMP) \ 110 || defined(FEAT_GUI_GTK) || defined(PROTO) 111 /* 112 * Like msg() but keep it silent when 'verbosefile' is set. 113 */ 114 int 115 verb_msg(s) 116 char_u *s; 117 { 118 int n; 119 120 verbose_enter(); 121 n = msg_attr_keep(s, 0, FALSE); 122 verbose_leave(); 123 124 return n; 125 } 126 #endif 127 128 int 129 msg_attr(s, attr) 130 char_u *s; 131 int attr; 132 { 133 return msg_attr_keep(s, attr, FALSE); 134 } 135 136 int 137 msg_attr_keep(s, attr, keep) 138 char_u *s; 139 int attr; 140 int keep; /* TRUE: set keep_msg if it doesn't scroll */ 141 { 142 static int entered = 0; 143 int retval; 144 char_u *buf = NULL; 145 146 #ifdef FEAT_EVAL 147 if (attr == 0) 148 set_vim_var_string(VV_STATUSMSG, s, -1); 149 #endif 150 151 /* 152 * It is possible that displaying a messages causes a problem (e.g., 153 * when redrawing the window), which causes another message, etc.. To 154 * break this loop, limit the recursiveness to 3 levels. 155 */ 156 if (entered >= 3) 157 return TRUE; 158 ++entered; 159 160 /* Add message to history (unless it's a repeated kept message or a 161 * truncated message) */ 162 if (s != keep_msg 163 || (*s != '<' 164 && last_msg_hist != NULL 165 && last_msg_hist->msg != NULL 166 && STRCMP(s, last_msg_hist->msg))) 167 add_msg_hist(s, -1, attr); 168 169 /* When displaying keep_msg, don't let msg_start() free it, caller must do 170 * that. */ 171 if (s == keep_msg) 172 keep_msg = NULL; 173 174 /* Truncate the message if needed. */ 175 msg_start(); 176 buf = msg_strtrunc(s, FALSE); 177 if (buf != NULL) 178 s = buf; 179 180 msg_outtrans_attr(s, attr); 181 msg_clr_eos(); 182 retval = msg_end(); 183 184 if (keep && retval && vim_strsize(s) < (int)(Rows - cmdline_row - 1) 185 * Columns + sc_col) 186 set_keep_msg(s, 0); 187 188 vim_free(buf); 189 --entered; 190 return retval; 191 } 192 193 /* 194 * Truncate a string such that it can be printed without causing a scroll. 195 * Returns an allocated string or NULL when no truncating is done. 196 */ 197 char_u * 198 msg_strtrunc(s, force) 199 char_u *s; 200 int force; /* always truncate */ 201 { 202 char_u *buf = NULL; 203 int len; 204 int room; 205 206 /* May truncate message to avoid a hit-return prompt */ 207 if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL) 208 && !exmode_active && msg_silent == 0) || force) 209 { 210 len = vim_strsize(s); 211 if (msg_scrolled != 0) 212 /* Use all the columns. */ 213 room = (int)(Rows - msg_row) * Columns - 1; 214 else 215 /* Use up to 'showcmd' column. */ 216 room = (int)(Rows - msg_row - 1) * Columns + sc_col - 1; 217 if (len > room && room > 0) 218 { 219 #ifdef FEAT_MBYTE 220 if (enc_utf8) 221 /* may have up to 18 bytes per cell (6 per char, up to two 222 * composing chars) */ 223 buf = alloc((room + 2) * 18); 224 else if (enc_dbcs == DBCS_JPNU) 225 /* may have up to 2 bytes per cell for euc-jp */ 226 buf = alloc((room + 2) * 2); 227 else 228 #endif 229 buf = alloc(room + 2); 230 if (buf != NULL) 231 trunc_string(s, buf, room); 232 } 233 } 234 return buf; 235 } 236 237 /* 238 * Truncate a string "s" to "buf" with cell width "room". 239 * "s" and "buf" may be equal. 240 */ 241 void 242 trunc_string(s, buf, room) 243 char_u *s; 244 char_u *buf; 245 int room; 246 { 247 int half; 248 int len; 249 int e; 250 int i; 251 int n; 252 253 room -= 3; 254 half = room / 2; 255 len = 0; 256 257 /* First part: Start of the string. */ 258 for (e = 0; len < half; ++e) 259 { 260 if (s[e] == NUL) 261 { 262 /* text fits without truncating! */ 263 buf[e] = NUL; 264 return; 265 } 266 n = ptr2cells(s + e); 267 if (len + n >= half) 268 break; 269 len += n; 270 buf[e] = s[e]; 271 #ifdef FEAT_MBYTE 272 if (has_mbyte) 273 for (n = (*mb_ptr2len)(s + e); --n > 0; ) 274 { 275 ++e; 276 buf[e] = s[e]; 277 } 278 #endif 279 } 280 281 /* Last part: End of the string. */ 282 i = e; 283 #ifdef FEAT_MBYTE 284 if (enc_dbcs != 0) 285 { 286 /* For DBCS going backwards in a string is slow, but 287 * computing the cell width isn't too slow: go forward 288 * until the rest fits. */ 289 n = vim_strsize(s + i); 290 while (len + n > room) 291 { 292 n -= ptr2cells(s + i); 293 i += (*mb_ptr2len)(s + i); 294 } 295 } 296 else if (enc_utf8) 297 { 298 /* For UTF-8 we can go backwards easily. */ 299 half = i = (int)STRLEN(s); 300 for (;;) 301 { 302 do 303 half = half - (*mb_head_off)(s, s + half - 1) - 1; 304 while (utf_iscomposing(utf_ptr2char(s + half)) && half > 0); 305 n = ptr2cells(s + half); 306 if (len + n > room) 307 break; 308 len += n; 309 i = half; 310 } 311 } 312 else 313 #endif 314 { 315 for (i = (int)STRLEN(s); len + (n = ptr2cells(s + i - 1)) <= room; --i) 316 len += n; 317 } 318 319 /* Set the middle and copy the last part. */ 320 mch_memmove(buf + e, "...", (size_t)3); 321 STRMOVE(buf + e + 3, s + i); 322 } 323 324 /* 325 * Automatic prototype generation does not understand this function. 326 * Note: Caller of smgs() and smsg_attr() must check the resulting string is 327 * shorter than IOSIZE!!! 328 */ 329 #ifndef PROTO 330 # ifndef HAVE_STDARG_H 331 332 int 333 #ifdef __BORLANDC__ 334 _RTLENTRYF 335 #endif 336 smsg __ARGS((char_u *, long, long, long, 337 long, long, long, long, long, long, long)); 338 int 339 #ifdef __BORLANDC__ 340 _RTLENTRYF 341 #endif 342 smsg_attr __ARGS((int, char_u *, long, long, long, 343 long, long, long, long, long, long, long)); 344 345 int vim_snprintf __ARGS((char *, size_t, char *, long, long, long, 346 long, long, long, long, long, long, long)); 347 348 /* 349 * smsg(str, arg, ...) is like using sprintf(buf, str, arg, ...) and then 350 * calling msg(buf). 351 * The buffer used is IObuff, the message is truncated at IOSIZE. 352 */ 353 354 /* VARARGS */ 355 int 356 #ifdef __BORLANDC__ 357 _RTLENTRYF 358 #endif 359 smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) 360 char_u *s; 361 long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; 362 { 363 return smsg_attr(0, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); 364 } 365 366 /* VARARGS */ 367 int 368 #ifdef __BORLANDC__ 369 _RTLENTRYF 370 #endif 371 smsg_attr(attr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) 372 int attr; 373 char_u *s; 374 long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; 375 { 376 vim_snprintf((char *)IObuff, IOSIZE, (char *)s, 377 a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); 378 return msg_attr(IObuff, attr); 379 } 380 381 # else /* HAVE_STDARG_H */ 382 383 int vim_snprintf(char *str, size_t str_m, char *fmt, ...); 384 385 int 386 #ifdef __BORLANDC__ 387 _RTLENTRYF 388 #endif 389 smsg(char_u *s, ...) 390 { 391 va_list arglist; 392 393 va_start(arglist, s); 394 vim_vsnprintf((char *)IObuff, IOSIZE, (char *)s, arglist, NULL); 395 va_end(arglist); 396 return msg(IObuff); 397 } 398 399 int 400 #ifdef __BORLANDC__ 401 _RTLENTRYF 402 #endif 403 smsg_attr(int attr, char_u *s, ...) 404 { 405 va_list arglist; 406 407 va_start(arglist, s); 408 vim_vsnprintf((char *)IObuff, IOSIZE, (char *)s, arglist, NULL); 409 va_end(arglist); 410 return msg_attr(IObuff, attr); 411 } 412 413 # endif /* HAVE_STDARG_H */ 414 #endif 415 416 /* 417 * Remember the last sourcing name/lnum used in an error message, so that it 418 * isn't printed each time when it didn't change. 419 */ 420 static int last_sourcing_lnum = 0; 421 static char_u *last_sourcing_name = NULL; 422 423 /* 424 * Reset the last used sourcing name/lnum. Makes sure it is displayed again 425 * for the next error message; 426 */ 427 void 428 reset_last_sourcing() 429 { 430 vim_free(last_sourcing_name); 431 last_sourcing_name = NULL; 432 last_sourcing_lnum = 0; 433 } 434 435 /* 436 * Return TRUE if "sourcing_name" differs from "last_sourcing_name". 437 */ 438 static int 439 other_sourcing_name() 440 { 441 if (sourcing_name != NULL) 442 { 443 if (last_sourcing_name != NULL) 444 return STRCMP(sourcing_name, last_sourcing_name) != 0; 445 return TRUE; 446 } 447 return FALSE; 448 } 449 450 /* 451 * Get the message about the source, as used for an error message. 452 * Returns an allocated string with room for one more character. 453 * Returns NULL when no message is to be given. 454 */ 455 static char_u * 456 get_emsg_source() 457 { 458 char_u *Buf, *p; 459 460 if (sourcing_name != NULL && other_sourcing_name()) 461 { 462 p = (char_u *)_("Error detected while processing %s:"); 463 Buf = alloc((unsigned)(STRLEN(sourcing_name) + STRLEN(p))); 464 if (Buf != NULL) 465 sprintf((char *)Buf, (char *)p, sourcing_name); 466 return Buf; 467 } 468 return NULL; 469 } 470 471 /* 472 * Get the message about the source lnum, as used for an error message. 473 * Returns an allocated string with room for one more character. 474 * Returns NULL when no message is to be given. 475 */ 476 static char_u * 477 get_emsg_lnum() 478 { 479 char_u *Buf, *p; 480 481 /* lnum is 0 when executing a command from the command line 482 * argument, we don't want a line number then */ 483 if (sourcing_name != NULL 484 && (other_sourcing_name() || sourcing_lnum != last_sourcing_lnum) 485 && sourcing_lnum != 0) 486 { 487 p = (char_u *)_("line %4ld:"); 488 Buf = alloc((unsigned)(STRLEN(p) + 20)); 489 if (Buf != NULL) 490 sprintf((char *)Buf, (char *)p, (long)sourcing_lnum); 491 return Buf; 492 } 493 return NULL; 494 } 495 496 /* 497 * Display name and line number for the source of an error. 498 * Remember the file name and line number, so that for the next error the info 499 * is only displayed if it changed. 500 */ 501 void 502 msg_source(attr) 503 int attr; 504 { 505 char_u *p; 506 507 ++no_wait_return; 508 p = get_emsg_source(); 509 if (p != NULL) 510 { 511 msg_attr(p, attr); 512 vim_free(p); 513 } 514 p = get_emsg_lnum(); 515 if (p != NULL) 516 { 517 msg_attr(p, hl_attr(HLF_N)); 518 vim_free(p); 519 last_sourcing_lnum = sourcing_lnum; /* only once for each line */ 520 } 521 522 /* remember the last sourcing name printed, also when it's empty */ 523 if (sourcing_name == NULL || other_sourcing_name()) 524 { 525 vim_free(last_sourcing_name); 526 if (sourcing_name == NULL) 527 last_sourcing_name = NULL; 528 else 529 last_sourcing_name = vim_strsave(sourcing_name); 530 } 531 --no_wait_return; 532 } 533 534 /* 535 * Return TRUE if not giving error messages right now: 536 * If "emsg_off" is set: no error messages at the moment. 537 * If "msg" is in 'debug': do error message but without side effects. 538 * If "emsg_skip" is set: never do error messages. 539 */ 540 int 541 emsg_not_now() 542 { 543 if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL 544 && vim_strchr(p_debug, 't') == NULL) 545 #ifdef FEAT_EVAL 546 || emsg_skip > 0 547 #endif 548 ) 549 return TRUE; 550 return FALSE; 551 } 552 553 /* 554 * emsg() - display an error message 555 * 556 * Rings the bell, if appropriate, and calls message() to do the real work 557 * When terminal not initialized (yet) mch_errmsg(..) is used. 558 * 559 * return TRUE if wait_return not called 560 */ 561 int 562 emsg(s) 563 char_u *s; 564 { 565 int attr; 566 char_u *p; 567 #ifdef FEAT_EVAL 568 int ignore = FALSE; 569 int severe; 570 #endif 571 572 called_emsg = TRUE; 573 ex_exitval = 1; 574 575 /* 576 * If "emsg_severe" is TRUE: When an error exception is to be thrown, 577 * prefer this message over previous messages for the same command. 578 */ 579 #ifdef FEAT_EVAL 580 severe = emsg_severe; 581 emsg_severe = FALSE; 582 #endif 583 584 /* Skip this if not giving error messages at the moment. */ 585 if (emsg_not_now()) 586 return TRUE; 587 588 if (!emsg_off || vim_strchr(p_debug, 't') != NULL) 589 { 590 #ifdef FEAT_EVAL 591 /* 592 * Cause a throw of an error exception if appropriate. Don't display 593 * the error message in this case. (If no matching catch clause will 594 * be found, the message will be displayed later on.) "ignore" is set 595 * when the message should be ignored completely (used for the 596 * interrupt message). 597 */ 598 if (cause_errthrow(s, severe, &ignore) == TRUE) 599 { 600 if (!ignore) 601 did_emsg = TRUE; 602 return TRUE; 603 } 604 605 /* set "v:errmsg", also when using ":silent! cmd" */ 606 set_vim_var_string(VV_ERRMSG, s, -1); 607 #endif 608 609 /* 610 * When using ":silent! cmd" ignore error messages. 611 * But do write it to the redirection file. 612 */ 613 if (emsg_silent != 0) 614 { 615 msg_start(); 616 p = get_emsg_source(); 617 if (p != NULL) 618 { 619 STRCAT(p, "\n"); 620 redir_write(p, -1); 621 vim_free(p); 622 } 623 p = get_emsg_lnum(); 624 if (p != NULL) 625 { 626 STRCAT(p, "\n"); 627 redir_write(p, -1); 628 vim_free(p); 629 } 630 redir_write(s, -1); 631 return TRUE; 632 } 633 634 /* Reset msg_silent, an error causes messages to be switched back on. */ 635 msg_silent = 0; 636 cmd_silent = FALSE; 637 638 if (global_busy) /* break :global command */ 639 ++global_busy; 640 641 if (p_eb) 642 beep_flush(); /* also includes flush_buffers() */ 643 else 644 flush_buffers(FALSE); /* flush internal buffers */ 645 did_emsg = TRUE; /* flag for DoOneCmd() */ 646 } 647 648 emsg_on_display = TRUE; /* remember there is an error message */ 649 ++msg_scroll; /* don't overwrite a previous message */ 650 attr = hl_attr(HLF_E); /* set highlight mode for error messages */ 651 if (msg_scrolled != 0) 652 need_wait_return = TRUE; /* needed in case emsg() is called after 653 * wait_return has reset need_wait_return 654 * and a redraw is expected because 655 * msg_scrolled is non-zero */ 656 657 /* 658 * Display name and line number for the source of the error. 659 */ 660 msg_source(attr); 661 662 /* 663 * Display the error message itself. 664 */ 665 msg_nowait = FALSE; /* wait for this msg */ 666 return msg_attr(s, attr); 667 } 668 669 /* 670 * Print an error message with one "%s" and one string argument. 671 */ 672 int 673 emsg2(s, a1) 674 char_u *s, *a1; 675 { 676 return emsg3(s, a1, NULL); 677 } 678 679 /* emsg3() and emsgn() are in misc2.c to avoid warnings for the prototypes. */ 680 681 void 682 emsg_invreg(name) 683 int name; 684 { 685 EMSG2(_("E354: Invalid register name: '%s'"), transchar(name)); 686 } 687 688 /* 689 * Like msg(), but truncate to a single line if p_shm contains 't', or when 690 * "force" is TRUE. This truncates in another way as for normal messages. 691 * Careful: The string may be changed by msg_may_trunc()! 692 * Returns a pointer to the printed message, if wait_return() not called. 693 */ 694 char_u * 695 msg_trunc_attr(s, force, attr) 696 char_u *s; 697 int force; 698 int attr; 699 { 700 int n; 701 702 /* Add message to history before truncating */ 703 add_msg_hist(s, -1, attr); 704 705 s = msg_may_trunc(force, s); 706 707 msg_hist_off = TRUE; 708 n = msg_attr(s, attr); 709 msg_hist_off = FALSE; 710 711 if (n) 712 return s; 713 return NULL; 714 } 715 716 /* 717 * Check if message "s" should be truncated at the start (for filenames). 718 * Return a pointer to where the truncated message starts. 719 * Note: May change the message by replacing a character with '<'. 720 */ 721 char_u * 722 msg_may_trunc(force, s) 723 int force; 724 char_u *s; 725 { 726 int n; 727 int room; 728 729 room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1; 730 if ((force || (shortmess(SHM_TRUNC) && !exmode_active)) 731 && (n = (int)STRLEN(s) - room) > 0) 732 { 733 #ifdef FEAT_MBYTE 734 if (has_mbyte) 735 { 736 int size = vim_strsize(s); 737 738 /* There may be room anyway when there are multibyte chars. */ 739 if (size <= room) 740 return s; 741 742 for (n = 0; size >= room; ) 743 { 744 size -= (*mb_ptr2cells)(s + n); 745 n += (*mb_ptr2len)(s + n); 746 } 747 --n; 748 } 749 #endif 750 s += n; 751 *s = '<'; 752 } 753 return s; 754 } 755 756 static void 757 add_msg_hist(s, len, attr) 758 char_u *s; 759 int len; /* -1 for undetermined length */ 760 int attr; 761 { 762 struct msg_hist *p; 763 764 if (msg_hist_off || msg_silent != 0) 765 return; 766 767 /* Don't let the message history get too big */ 768 while (msg_hist_len > MAX_MSG_HIST_LEN) 769 (void)delete_first_msg(); 770 771 /* allocate an entry and add the message at the end of the history */ 772 p = (struct msg_hist *)alloc((int)sizeof(struct msg_hist)); 773 if (p != NULL) 774 { 775 if (len < 0) 776 len = (int)STRLEN(s); 777 /* remove leading and trailing newlines */ 778 while (len > 0 && *s == '\n') 779 { 780 ++s; 781 --len; 782 } 783 while (len > 0 && s[len - 1] == '\n') 784 --len; 785 p->msg = vim_strnsave(s, len); 786 p->next = NULL; 787 p->attr = attr; 788 if (last_msg_hist != NULL) 789 last_msg_hist->next = p; 790 last_msg_hist = p; 791 if (first_msg_hist == NULL) 792 first_msg_hist = last_msg_hist; 793 ++msg_hist_len; 794 } 795 } 796 797 /* 798 * Delete the first (oldest) message from the history. 799 * Returns FAIL if there are no messages. 800 */ 801 int 802 delete_first_msg() 803 { 804 struct msg_hist *p; 805 806 if (msg_hist_len <= 0) 807 return FAIL; 808 p = first_msg_hist; 809 first_msg_hist = p->next; 810 if (first_msg_hist == NULL) 811 last_msg_hist = NULL; /* history is empty */ 812 vim_free(p->msg); 813 vim_free(p); 814 --msg_hist_len; 815 return OK; 816 } 817 818 /* 819 * ":messages" command. 820 */ 821 void 822 ex_messages(eap) 823 exarg_T *eap UNUSED; 824 { 825 struct msg_hist *p; 826 char_u *s; 827 828 msg_hist_off = TRUE; 829 830 s = mch_getenv((char_u *)"LANG"); 831 if (s != NULL && *s != NUL) 832 msg_attr((char_u *) 833 _("Messages maintainer: Bram Moolenaar <[email protected]>"), 834 hl_attr(HLF_T)); 835 836 for (p = first_msg_hist; p != NULL && !got_int; p = p->next) 837 if (p->msg != NULL) 838 msg_attr(p->msg, p->attr); 839 840 msg_hist_off = FALSE; 841 } 842 843 #if defined(FEAT_CON_DIALOG) || defined(FIND_REPLACE_DIALOG) || defined(PROTO) 844 /* 845 * Call this after prompting the user. This will avoid a hit-return message 846 * and a delay. 847 */ 848 void 849 msg_end_prompt() 850 { 851 need_wait_return = FALSE; 852 emsg_on_display = FALSE; 853 cmdline_row = msg_row; 854 msg_col = 0; 855 msg_clr_eos(); 856 } 857 #endif 858 859 /* 860 * wait for the user to hit a key (normally a return) 861 * if 'redraw' is TRUE, clear and redraw the screen 862 * if 'redraw' is FALSE, just redraw the screen 863 * if 'redraw' is -1, don't redraw at all 864 */ 865 void 866 wait_return(redraw) 867 int redraw; 868 { 869 int c; 870 int oldState; 871 int tmpState; 872 int had_got_int; 873 874 if (redraw == TRUE) 875 must_redraw = CLEAR; 876 877 /* If using ":silent cmd", don't wait for a return. Also don't set 878 * need_wait_return to do it later. */ 879 if (msg_silent != 0) 880 return; 881 882 /* 883 * With the global command (and some others) we only need one return at the 884 * end. Adjust cmdline_row to avoid the next message overwriting the last one. 885 * When inside vgetc(), we can't wait for a typed character at all. 886 */ 887 if (vgetc_busy > 0) 888 return; 889 if (no_wait_return) 890 { 891 need_wait_return = TRUE; 892 if (!exmode_active) 893 cmdline_row = msg_row; 894 return; 895 } 896 897 redir_off = TRUE; /* don't redirect this message */ 898 oldState = State; 899 if (quit_more) 900 { 901 c = CAR; /* just pretend CR was hit */ 902 quit_more = FALSE; 903 got_int = FALSE; 904 } 905 else if (exmode_active) 906 { 907 MSG_PUTS(" "); /* make sure the cursor is on the right line */ 908 c = CAR; /* no need for a return in ex mode */ 909 got_int = FALSE; 910 } 911 else 912 { 913 /* Make sure the hit-return prompt is on screen when 'guioptions' was 914 * just changed. */ 915 screenalloc(FALSE); 916 917 State = HITRETURN; 918 #ifdef FEAT_MOUSE 919 setmouse(); 920 #endif 921 #ifdef USE_ON_FLY_SCROLL 922 dont_scroll = TRUE; /* disallow scrolling here */ 923 #endif 924 hit_return_msg(); 925 926 do 927 { 928 /* Remember "got_int", if it is set vgetc() probably returns a 929 * CTRL-C, but we need to loop then. */ 930 had_got_int = got_int; 931 932 /* Don't do mappings here, we put the character back in the 933 * typeahead buffer. */ 934 ++no_mapping; 935 ++allow_keys; 936 c = safe_vgetc(); 937 if (had_got_int && !global_busy) 938 got_int = FALSE; 939 --no_mapping; 940 --allow_keys; 941 942 #ifdef FEAT_CLIPBOARD 943 /* Strange way to allow copying (yanking) a modeless selection at 944 * the hit-enter prompt. Use CTRL-Y, because the same is used in 945 * Cmdline-mode and it's harmless when there is no selection. */ 946 if (c == Ctrl_Y && clip_star.state == SELECT_DONE) 947 { 948 clip_copy_modeless_selection(TRUE); 949 c = K_IGNORE; 950 } 951 #endif 952 953 /* 954 * Allow scrolling back in the messages. 955 * Also accept scroll-down commands when messages fill the screen, 956 * to avoid that typing one 'j' too many makes the messages 957 * disappear. 958 */ 959 if (p_more && !p_cp) 960 { 961 if (c == 'b' || c == 'k' || c == 'u' || c == 'g' || c == K_UP) 962 { 963 /* scroll back to show older messages */ 964 do_more_prompt(c); 965 if (quit_more) 966 { 967 c = CAR; /* just pretend CR was hit */ 968 quit_more = FALSE; 969 got_int = FALSE; 970 } 971 else 972 { 973 c = K_IGNORE; 974 hit_return_msg(); 975 } 976 } 977 else if (msg_scrolled > Rows - 2 978 && (c == 'j' || c == K_DOWN || c == 'd' || c == 'f')) 979 c = K_IGNORE; 980 } 981 } while ((had_got_int && c == Ctrl_C) 982 || c == K_IGNORE 983 #ifdef FEAT_GUI 984 || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR 985 #endif 986 #ifdef FEAT_MOUSE 987 || c == K_LEFTDRAG || c == K_LEFTRELEASE 988 || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE 989 || c == K_RIGHTDRAG || c == K_RIGHTRELEASE 990 || c == K_MOUSEDOWN || c == K_MOUSEUP 991 || (!mouse_has(MOUSE_RETURN) 992 && mouse_row < msg_row 993 && (c == K_LEFTMOUSE 994 || c == K_MIDDLEMOUSE 995 || c == K_RIGHTMOUSE 996 || c == K_X1MOUSE 997 || c == K_X2MOUSE)) 998 #endif 999 ); 1000 ui_breakcheck(); 1001 #ifdef FEAT_MOUSE 1002 /* 1003 * Avoid that the mouse-up event causes visual mode to start. 1004 */ 1005 if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE 1006 || c == K_X1MOUSE || c == K_X2MOUSE) 1007 (void)jump_to_mouse(MOUSE_SETPOS, NULL, 0); 1008 else 1009 #endif 1010 if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C) 1011 { 1012 /* Put the character back in the typeahead buffer. Don't use the 1013 * stuff buffer, because lmaps wouldn't work. */ 1014 ins_char_typebuf(c); 1015 do_redraw = TRUE; /* need a redraw even though there is 1016 typeahead */ 1017 } 1018 } 1019 redir_off = FALSE; 1020 1021 /* 1022 * If the user hits ':', '?' or '/' we get a command line from the next 1023 * line. 1024 */ 1025 if (c == ':' || c == '?' || c == '/') 1026 { 1027 if (!exmode_active) 1028 cmdline_row = msg_row; 1029 skip_redraw = TRUE; /* skip redraw once */ 1030 do_redraw = FALSE; 1031 } 1032 1033 /* 1034 * If the window size changed set_shellsize() will redraw the screen. 1035 * Otherwise the screen is only redrawn if 'redraw' is set and no ':' 1036 * typed. 1037 */ 1038 tmpState = State; 1039 State = oldState; /* restore State before set_shellsize */ 1040 #ifdef FEAT_MOUSE 1041 setmouse(); 1042 #endif 1043 msg_check(); 1044 1045 #if defined(UNIX) || defined(VMS) 1046 /* 1047 * When switching screens, we need to output an extra newline on exit. 1048 */ 1049 if (swapping_screen() && !termcap_active) 1050 newline_on_exit = TRUE; 1051 #endif 1052 1053 need_wait_return = FALSE; 1054 did_wait_return = TRUE; 1055 emsg_on_display = FALSE; /* can delete error message now */ 1056 lines_left = -1; /* reset lines_left at next msg_start() */ 1057 reset_last_sourcing(); 1058 if (keep_msg != NULL && vim_strsize(keep_msg) >= 1059 (Rows - cmdline_row - 1) * Columns + sc_col) 1060 { 1061 vim_free(keep_msg); 1062 keep_msg = NULL; /* don't redisplay message, it's too long */ 1063 } 1064 1065 if (tmpState == SETWSIZE) /* got resize event while in vgetc() */ 1066 { 1067 starttermcap(); /* start termcap before redrawing */ 1068 shell_resized(); 1069 } 1070 else if (!skip_redraw 1071 && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1))) 1072 { 1073 starttermcap(); /* start termcap before redrawing */ 1074 redraw_later(VALID); 1075 } 1076 } 1077 1078 /* 1079 * Write the hit-return prompt. 1080 */ 1081 static void 1082 hit_return_msg() 1083 { 1084 int save_p_more = p_more; 1085 1086 p_more = FALSE; /* don't want see this message when scrolling back */ 1087 if (msg_didout) /* start on a new line */ 1088 msg_putchar('\n'); 1089 if (got_int) 1090 MSG_PUTS(_("Interrupt: ")); 1091 1092 MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), hl_attr(HLF_R)); 1093 if (!msg_use_printf()) 1094 msg_clr_eos(); 1095 p_more = save_p_more; 1096 } 1097 1098 /* 1099 * Set "keep_msg" to "s". Free the old value and check for NULL pointer. 1100 */ 1101 void 1102 set_keep_msg(s, attr) 1103 char_u *s; 1104 int attr; 1105 { 1106 vim_free(keep_msg); 1107 if (s != NULL && msg_silent == 0) 1108 keep_msg = vim_strsave(s); 1109 else 1110 keep_msg = NULL; 1111 keep_msg_more = FALSE; 1112 keep_msg_attr = attr; 1113 } 1114 1115 #if defined(FEAT_TERMRESPONSE) || defined(PROTO) 1116 /* 1117 * If there currently is a message being displayed, set "keep_msg" to it, so 1118 * that it will be displayed again after redraw. 1119 */ 1120 void 1121 set_keep_msg_from_hist() 1122 { 1123 if (keep_msg == NULL && last_msg_hist != NULL && msg_scrolled == 0 1124 && (State & NORMAL)) 1125 set_keep_msg(last_msg_hist->msg, last_msg_hist->attr); 1126 } 1127 #endif 1128 1129 /* 1130 * Prepare for outputting characters in the command line. 1131 */ 1132 void 1133 msg_start() 1134 { 1135 int did_return = FALSE; 1136 1137 vim_free(keep_msg); 1138 keep_msg = NULL; /* don't display old message now */ 1139 1140 #ifdef FEAT_EVAL 1141 if (need_clr_eos) 1142 { 1143 /* Halfway an ":echo" command and getting an (error) message: clear 1144 * any text from the command. */ 1145 need_clr_eos = FALSE; 1146 msg_clr_eos(); 1147 } 1148 #endif 1149 1150 if (!msg_scroll && full_screen) /* overwrite last message */ 1151 { 1152 msg_row = cmdline_row; 1153 msg_col = 1154 #ifdef FEAT_RIGHTLEFT 1155 cmdmsg_rl ? Columns - 1 : 1156 #endif 1157 0; 1158 } 1159 else if (msg_didout) /* start message on next line */ 1160 { 1161 msg_putchar('\n'); 1162 did_return = TRUE; 1163 if (exmode_active != EXMODE_NORMAL) 1164 cmdline_row = msg_row; 1165 } 1166 if (!msg_didany || lines_left < 0) 1167 msg_starthere(); 1168 if (msg_silent == 0) 1169 { 1170 msg_didout = FALSE; /* no output on current line yet */ 1171 cursor_off(); 1172 } 1173 1174 /* when redirecting, may need to start a new line. */ 1175 if (!did_return) 1176 redir_write((char_u *)"\n", -1); 1177 } 1178 1179 /* 1180 * Note that the current msg position is where messages start. 1181 */ 1182 void 1183 msg_starthere() 1184 { 1185 lines_left = cmdline_row; 1186 msg_didany = FALSE; 1187 } 1188 1189 void 1190 msg_putchar(c) 1191 int c; 1192 { 1193 msg_putchar_attr(c, 0); 1194 } 1195 1196 void 1197 msg_putchar_attr(c, attr) 1198 int c; 1199 int attr; 1200 { 1201 #ifdef FEAT_MBYTE 1202 char_u buf[MB_MAXBYTES + 1]; 1203 #else 1204 char_u buf[4]; 1205 #endif 1206 1207 if (IS_SPECIAL(c)) 1208 { 1209 buf[0] = K_SPECIAL; 1210 buf[1] = K_SECOND(c); 1211 buf[2] = K_THIRD(c); 1212 buf[3] = NUL; 1213 } 1214 else 1215 { 1216 #ifdef FEAT_MBYTE 1217 buf[(*mb_char2bytes)(c, buf)] = NUL; 1218 #else 1219 buf[0] = c; 1220 buf[1] = NUL; 1221 #endif 1222 } 1223 msg_puts_attr(buf, attr); 1224 } 1225 1226 void 1227 msg_outnum(n) 1228 long n; 1229 { 1230 char_u buf[20]; 1231 1232 sprintf((char *)buf, "%ld", n); 1233 msg_puts(buf); 1234 } 1235 1236 void 1237 msg_home_replace(fname) 1238 char_u *fname; 1239 { 1240 msg_home_replace_attr(fname, 0); 1241 } 1242 1243 #if defined(FEAT_FIND_ID) || defined(PROTO) 1244 void 1245 msg_home_replace_hl(fname) 1246 char_u *fname; 1247 { 1248 msg_home_replace_attr(fname, hl_attr(HLF_D)); 1249 } 1250 #endif 1251 1252 static void 1253 msg_home_replace_attr(fname, attr) 1254 char_u *fname; 1255 int attr; 1256 { 1257 char_u *name; 1258 1259 name = home_replace_save(NULL, fname); 1260 if (name != NULL) 1261 msg_outtrans_attr(name, attr); 1262 vim_free(name); 1263 } 1264 1265 /* 1266 * Output 'len' characters in 'str' (including NULs) with translation 1267 * if 'len' is -1, output upto a NUL character. 1268 * Use attributes 'attr'. 1269 * Return the number of characters it takes on the screen. 1270 */ 1271 int 1272 msg_outtrans(str) 1273 char_u *str; 1274 { 1275 return msg_outtrans_attr(str, 0); 1276 } 1277 1278 int 1279 msg_outtrans_attr(str, attr) 1280 char_u *str; 1281 int attr; 1282 { 1283 return msg_outtrans_len_attr(str, (int)STRLEN(str), attr); 1284 } 1285 1286 int 1287 msg_outtrans_len(str, len) 1288 char_u *str; 1289 int len; 1290 { 1291 return msg_outtrans_len_attr(str, len, 0); 1292 } 1293 1294 /* 1295 * Output one character at "p". Return pointer to the next character. 1296 * Handles multi-byte characters. 1297 */ 1298 char_u * 1299 msg_outtrans_one(p, attr) 1300 char_u *p; 1301 int attr; 1302 { 1303 #ifdef FEAT_MBYTE 1304 int l; 1305 1306 if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) 1307 { 1308 msg_outtrans_len_attr(p, l, attr); 1309 return p + l; 1310 } 1311 #endif 1312 msg_puts_attr(transchar_byte(*p), attr); 1313 return p + 1; 1314 } 1315 1316 int 1317 msg_outtrans_len_attr(msgstr, len, attr) 1318 char_u *msgstr; 1319 int len; 1320 int attr; 1321 { 1322 int retval = 0; 1323 char_u *str = msgstr; 1324 char_u *plain_start = msgstr; 1325 char_u *s; 1326 #ifdef FEAT_MBYTE 1327 int mb_l; 1328 int c; 1329 #endif 1330 1331 /* if MSG_HIST flag set, add message to history */ 1332 if (attr & MSG_HIST) 1333 { 1334 add_msg_hist(str, len, attr); 1335 attr &= ~MSG_HIST; 1336 } 1337 1338 #ifdef FEAT_MBYTE 1339 /* If the string starts with a composing character first draw a space on 1340 * which the composing char can be drawn. */ 1341 if (enc_utf8 && utf_iscomposing(utf_ptr2char(msgstr))) 1342 msg_puts_attr((char_u *)" ", attr); 1343 #endif 1344 1345 /* 1346 * Go over the string. Special characters are translated and printed. 1347 * Normal characters are printed several at a time. 1348 */ 1349 while (--len >= 0) 1350 { 1351 #ifdef FEAT_MBYTE 1352 if (enc_utf8) 1353 /* Don't include composing chars after the end. */ 1354 mb_l = utfc_ptr2len_len(str, len + 1); 1355 else if (has_mbyte) 1356 mb_l = (*mb_ptr2len)(str); 1357 else 1358 mb_l = 1; 1359 if (has_mbyte && mb_l > 1) 1360 { 1361 c = (*mb_ptr2char)(str); 1362 if (vim_isprintc(c)) 1363 /* printable multi-byte char: count the cells. */ 1364 retval += (*mb_ptr2cells)(str); 1365 else 1366 { 1367 /* unprintable multi-byte char: print the printable chars so 1368 * far and the translation of the unprintable char. */ 1369 if (str > plain_start) 1370 msg_puts_attr_len(plain_start, (int)(str - plain_start), 1371 attr); 1372 plain_start = str + mb_l; 1373 msg_puts_attr(transchar(c), attr == 0 ? hl_attr(HLF_8) : attr); 1374 retval += char2cells(c); 1375 } 1376 len -= mb_l - 1; 1377 str += mb_l; 1378 } 1379 else 1380 #endif 1381 { 1382 s = transchar_byte(*str); 1383 if (s[1] != NUL) 1384 { 1385 /* unprintable char: print the printable chars so far and the 1386 * translation of the unprintable char. */ 1387 if (str > plain_start) 1388 msg_puts_attr_len(plain_start, (int)(str - plain_start), 1389 attr); 1390 plain_start = str + 1; 1391 msg_puts_attr(s, attr == 0 ? hl_attr(HLF_8) : attr); 1392 retval += (int)STRLEN(s); 1393 } 1394 else 1395 ++retval; 1396 ++str; 1397 } 1398 } 1399 1400 if (str > plain_start) 1401 /* print the printable chars at the end */ 1402 msg_puts_attr_len(plain_start, (int)(str - plain_start), attr); 1403 1404 return retval; 1405 } 1406 1407 #if defined(FEAT_QUICKFIX) || defined(PROTO) 1408 void 1409 msg_make(arg) 1410 char_u *arg; 1411 { 1412 int i; 1413 static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB"; 1414 1415 arg = skipwhite(arg); 1416 for (i = 5; *arg && i >= 0; --i) 1417 if (*arg++ != str[i]) 1418 break; 1419 if (i < 0) 1420 { 1421 msg_putchar('\n'); 1422 for (i = 0; rs[i]; ++i) 1423 msg_putchar(rs[i] - 3); 1424 } 1425 } 1426 #endif 1427 1428 /* 1429 * Output the string 'str' upto a NUL character. 1430 * Return the number of characters it takes on the screen. 1431 * 1432 * If K_SPECIAL is encountered, then it is taken in conjunction with the 1433 * following character and shown as <F1>, <S-Up> etc. Any other character 1434 * which is not printable shown in <> form. 1435 * If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>. 1436 * If a character is displayed in one of these special ways, is also 1437 * highlighted (its highlight name is '8' in the p_hl variable). 1438 * Otherwise characters are not highlighted. 1439 * This function is used to show mappings, where we want to see how to type 1440 * the character/string -- webb 1441 */ 1442 int 1443 msg_outtrans_special(strstart, from) 1444 char_u *strstart; 1445 int from; /* TRUE for lhs of a mapping */ 1446 { 1447 char_u *str = strstart; 1448 int retval = 0; 1449 char_u *string; 1450 int attr; 1451 int len; 1452 1453 attr = hl_attr(HLF_8); 1454 while (*str != NUL) 1455 { 1456 /* Leading and trailing spaces need to be displayed in <> form. */ 1457 if ((str == strstart || str[1] == NUL) && *str == ' ') 1458 { 1459 string = (char_u *)"<Space>"; 1460 ++str; 1461 } 1462 else 1463 string = str2special(&str, from); 1464 len = vim_strsize(string); 1465 /* Highlight special keys */ 1466 msg_puts_attr(string, len > 1 1467 #ifdef FEAT_MBYTE 1468 && (*mb_ptr2len)(string) <= 1 1469 #endif 1470 ? attr : 0); 1471 retval += len; 1472 } 1473 return retval; 1474 } 1475 1476 /* 1477 * Return the printable string for the key codes at "*sp". 1478 * Used for translating the lhs or rhs of a mapping to printable chars. 1479 * Advances "sp" to the next code. 1480 */ 1481 char_u * 1482 str2special(sp, from) 1483 char_u **sp; 1484 int from; /* TRUE for lhs of mapping */ 1485 { 1486 int c; 1487 static char_u buf[7]; 1488 char_u *str = *sp; 1489 int modifiers = 0; 1490 int special = FALSE; 1491 1492 #ifdef FEAT_MBYTE 1493 if (has_mbyte) 1494 { 1495 char_u *p; 1496 1497 /* Try to un-escape a multi-byte character. Return the un-escaped 1498 * string if it is a multi-byte character. */ 1499 p = mb_unescape(sp); 1500 if (p != NULL) 1501 return p; 1502 } 1503 #endif 1504 1505 c = *str; 1506 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) 1507 { 1508 if (str[1] == KS_MODIFIER) 1509 { 1510 modifiers = str[2]; 1511 str += 3; 1512 c = *str; 1513 } 1514 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) 1515 { 1516 c = TO_SPECIAL(str[1], str[2]); 1517 str += 2; 1518 if (c == K_ZERO) /* display <Nul> as ^@ */ 1519 c = NUL; 1520 } 1521 if (IS_SPECIAL(c) || modifiers) /* special key */ 1522 special = TRUE; 1523 } 1524 *sp = str + 1; 1525 1526 #ifdef FEAT_MBYTE 1527 /* For multi-byte characters check for an illegal byte. */ 1528 if (has_mbyte && MB_BYTE2LEN(*str) > (*mb_ptr2len)(str)) 1529 { 1530 transchar_nonprint(buf, c); 1531 return buf; 1532 } 1533 #endif 1534 1535 /* Make unprintable characters in <> form, also <M-Space> and <Tab>. 1536 * Use <Space> only for lhs of a mapping. */ 1537 if (special || char2cells(c) > 1 || (from && c == ' ')) 1538 return get_special_key_name(c, modifiers); 1539 buf[0] = c; 1540 buf[1] = NUL; 1541 return buf; 1542 } 1543 1544 /* 1545 * Translate a key sequence into special key names. 1546 */ 1547 void 1548 str2specialbuf(sp, buf, len) 1549 char_u *sp; 1550 char_u *buf; 1551 int len; 1552 { 1553 char_u *s; 1554 1555 *buf = NUL; 1556 while (*sp) 1557 { 1558 s = str2special(&sp, FALSE); 1559 if ((int)(STRLEN(s) + STRLEN(buf)) < len) 1560 STRCAT(buf, s); 1561 } 1562 } 1563 1564 /* 1565 * print line for :print or :list command 1566 */ 1567 void 1568 msg_prt_line(s, list) 1569 char_u *s; 1570 int list; 1571 { 1572 int c; 1573 int col = 0; 1574 int n_extra = 0; 1575 int c_extra = 0; 1576 char_u *p_extra = NULL; /* init to make SASC shut up */ 1577 int n; 1578 int attr = 0; 1579 char_u *trail = NULL; 1580 #ifdef FEAT_MBYTE 1581 int l; 1582 char_u buf[MB_MAXBYTES + 1]; 1583 #endif 1584 1585 if (curwin->w_p_list) 1586 list = TRUE; 1587 1588 /* find start of trailing whitespace */ 1589 if (list && lcs_trail) 1590 { 1591 trail = s + STRLEN(s); 1592 while (trail > s && vim_iswhite(trail[-1])) 1593 --trail; 1594 } 1595 1596 /* output a space for an empty line, otherwise the line will be 1597 * overwritten */ 1598 if (*s == NUL && !(list && lcs_eol != NUL)) 1599 msg_putchar(' '); 1600 1601 while (!got_int) 1602 { 1603 if (n_extra > 0) 1604 { 1605 --n_extra; 1606 if (c_extra) 1607 c = c_extra; 1608 else 1609 c = *p_extra++; 1610 } 1611 #ifdef FEAT_MBYTE 1612 else if (has_mbyte && (l = (*mb_ptr2len)(s)) > 1) 1613 { 1614 col += (*mb_ptr2cells)(s); 1615 mch_memmove(buf, s, (size_t)l); 1616 buf[l] = NUL; 1617 msg_puts(buf); 1618 s += l; 1619 continue; 1620 } 1621 #endif 1622 else 1623 { 1624 attr = 0; 1625 c = *s++; 1626 if (c == TAB && (!list || lcs_tab1)) 1627 { 1628 /* tab amount depends on current column */ 1629 n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1; 1630 if (!list) 1631 { 1632 c = ' '; 1633 c_extra = ' '; 1634 } 1635 else 1636 { 1637 c = lcs_tab1; 1638 c_extra = lcs_tab2; 1639 attr = hl_attr(HLF_8); 1640 } 1641 } 1642 else if (c == NUL && list && lcs_eol != NUL) 1643 { 1644 p_extra = (char_u *)""; 1645 c_extra = NUL; 1646 n_extra = 1; 1647 c = lcs_eol; 1648 attr = hl_attr(HLF_AT); 1649 --s; 1650 } 1651 else if (c != NUL && (n = byte2cells(c)) > 1) 1652 { 1653 n_extra = n - 1; 1654 p_extra = transchar_byte(c); 1655 c_extra = NUL; 1656 c = *p_extra++; 1657 /* Use special coloring to be able to distinguish <hex> from 1658 * the same in plain text. */ 1659 attr = hl_attr(HLF_8); 1660 } 1661 else if (c == ' ' && trail != NULL && s > trail) 1662 { 1663 c = lcs_trail; 1664 attr = hl_attr(HLF_8); 1665 } 1666 } 1667 1668 if (c == NUL) 1669 break; 1670 1671 msg_putchar_attr(c, attr); 1672 col++; 1673 } 1674 msg_clr_eos(); 1675 } 1676 1677 #ifdef FEAT_MBYTE 1678 /* 1679 * Use screen_puts() to output one multi-byte character. 1680 * Return the pointer "s" advanced to the next character. 1681 */ 1682 static char_u * 1683 screen_puts_mbyte(s, l, attr) 1684 char_u *s; 1685 int l; 1686 int attr; 1687 { 1688 int cw; 1689 1690 msg_didout = TRUE; /* remember that line is not empty */ 1691 cw = (*mb_ptr2cells)(s); 1692 if (cw > 1 && ( 1693 #ifdef FEAT_RIGHTLEFT 1694 cmdmsg_rl ? msg_col <= 1 : 1695 #endif 1696 msg_col == Columns - 1)) 1697 { 1698 /* Doesn't fit, print a highlighted '>' to fill it up. */ 1699 msg_screen_putchar('>', hl_attr(HLF_AT)); 1700 return s; 1701 } 1702 1703 screen_puts_len(s, l, msg_row, msg_col, attr); 1704 #ifdef FEAT_RIGHTLEFT 1705 if (cmdmsg_rl) 1706 { 1707 msg_col -= cw; 1708 if (msg_col == 0) 1709 { 1710 msg_col = Columns; 1711 ++msg_row; 1712 } 1713 } 1714 else 1715 #endif 1716 { 1717 msg_col += cw; 1718 if (msg_col >= Columns) 1719 { 1720 msg_col = 0; 1721 ++msg_row; 1722 } 1723 } 1724 return s + l; 1725 } 1726 #endif 1727 1728 /* 1729 * Output a string to the screen at position msg_row, msg_col. 1730 * Update msg_row and msg_col for the next message. 1731 */ 1732 void 1733 msg_puts(s) 1734 char_u *s; 1735 { 1736 msg_puts_attr(s, 0); 1737 } 1738 1739 void 1740 msg_puts_title(s) 1741 char_u *s; 1742 { 1743 msg_puts_attr(s, hl_attr(HLF_T)); 1744 } 1745 1746 /* 1747 * Show a message in such a way that it always fits in the line. Cut out a 1748 * part in the middle and replace it with "..." when necessary. 1749 * Does not handle multi-byte characters! 1750 */ 1751 void 1752 msg_puts_long_attr(longstr, attr) 1753 char_u *longstr; 1754 int attr; 1755 { 1756 msg_puts_long_len_attr(longstr, (int)STRLEN(longstr), attr); 1757 } 1758 1759 void 1760 msg_puts_long_len_attr(longstr, len, attr) 1761 char_u *longstr; 1762 int len; 1763 int attr; 1764 { 1765 int slen = len; 1766 int room; 1767 1768 room = Columns - msg_col; 1769 if (len > room && room >= 20) 1770 { 1771 slen = (room - 3) / 2; 1772 msg_outtrans_len_attr(longstr, slen, attr); 1773 msg_puts_attr((char_u *)"...", hl_attr(HLF_8)); 1774 } 1775 msg_outtrans_len_attr(longstr + len - slen, slen, attr); 1776 } 1777 1778 /* 1779 * Basic function for writing a message with highlight attributes. 1780 */ 1781 void 1782 msg_puts_attr(s, attr) 1783 char_u *s; 1784 int attr; 1785 { 1786 msg_puts_attr_len(s, -1, attr); 1787 } 1788 1789 /* 1790 * Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). 1791 * When "maxlen" is -1 there is no maximum length. 1792 * When "maxlen" is >= 0 the message is not put in the history. 1793 */ 1794 static void 1795 msg_puts_attr_len(str, maxlen, attr) 1796 char_u *str; 1797 int maxlen; 1798 int attr; 1799 { 1800 /* 1801 * If redirection is on, also write to the redirection file. 1802 */ 1803 redir_write(str, maxlen); 1804 1805 /* 1806 * Don't print anything when using ":silent cmd". 1807 */ 1808 if (msg_silent != 0) 1809 return; 1810 1811 /* if MSG_HIST flag set, add message to history */ 1812 if ((attr & MSG_HIST) && maxlen < 0) 1813 { 1814 add_msg_hist(str, -1, attr); 1815 attr &= ~MSG_HIST; 1816 } 1817 1818 /* 1819 * When writing something to the screen after it has scrolled, requires a 1820 * wait-return prompt later. Needed when scrolling, resetting 1821 * need_wait_return after some prompt, and then outputting something 1822 * without scrolling 1823 */ 1824 if (msg_scrolled != 0 && !msg_scrolled_ign) 1825 need_wait_return = TRUE; 1826 msg_didany = TRUE; /* remember that something was outputted */ 1827 1828 /* 1829 * If there is no valid screen, use fprintf so we can see error messages. 1830 * If termcap is not active, we may be writing in an alternate console 1831 * window, cursor positioning may not work correctly (window size may be 1832 * different, e.g. for Win32 console) or we just don't know where the 1833 * cursor is. 1834 */ 1835 if (msg_use_printf()) 1836 msg_puts_printf(str, maxlen); 1837 else 1838 msg_puts_display(str, maxlen, attr, FALSE); 1839 } 1840 1841 /* 1842 * The display part of msg_puts_attr_len(). 1843 * May be called recursively to display scroll-back text. 1844 */ 1845 static void 1846 msg_puts_display(str, maxlen, attr, recurse) 1847 char_u *str; 1848 int maxlen; 1849 int attr; 1850 int recurse; 1851 { 1852 char_u *s = str; 1853 char_u *t_s = str; /* string from "t_s" to "s" is still todo */ 1854 int t_col = 0; /* screen cells todo, 0 when "t_s" not used */ 1855 #ifdef FEAT_MBYTE 1856 int l; 1857 int cw; 1858 #endif 1859 char_u *sb_str = str; 1860 int sb_col = msg_col; 1861 int wrap; 1862 int did_last_char; 1863 1864 did_wait_return = FALSE; 1865 while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) 1866 { 1867 /* 1868 * We are at the end of the screen line when: 1869 * - When outputting a newline. 1870 * - When outputting a character in the last column. 1871 */ 1872 if (!recurse && msg_row >= Rows - 1 && (*s == '\n' || ( 1873 #ifdef FEAT_RIGHTLEFT 1874 cmdmsg_rl 1875 ? ( 1876 msg_col <= 1 1877 || (*s == TAB && msg_col <= 7) 1878 # ifdef FEAT_MBYTE 1879 || (has_mbyte && (*mb_ptr2cells)(s) > 1 && msg_col <= 2) 1880 # endif 1881 ) 1882 : 1883 #endif 1884 (msg_col + t_col >= Columns - 1 1885 || (*s == TAB && msg_col + t_col >= ((Columns - 1) & ~7)) 1886 # ifdef FEAT_MBYTE 1887 || (has_mbyte && (*mb_ptr2cells)(s) > 1 1888 && msg_col + t_col >= Columns - 2) 1889 # endif 1890 )))) 1891 { 1892 /* 1893 * The screen is scrolled up when at the last row (some terminals 1894 * scroll automatically, some don't. To avoid problems we scroll 1895 * ourselves). 1896 */ 1897 if (t_col > 0) 1898 /* output postponed text */ 1899 t_puts(&t_col, t_s, s, attr); 1900 1901 /* When no more prompt and no more room, truncate here */ 1902 if (msg_no_more && lines_left == 0) 1903 break; 1904 1905 /* Scroll the screen up one line. */ 1906 msg_scroll_up(); 1907 1908 msg_row = Rows - 2; 1909 if (msg_col >= Columns) /* can happen after screen resize */ 1910 msg_col = Columns - 1; 1911 1912 /* Display char in last column before showing more-prompt. */ 1913 if (*s >= ' ' 1914 #ifdef FEAT_RIGHTLEFT 1915 && !cmdmsg_rl 1916 #endif 1917 ) 1918 { 1919 #ifdef FEAT_MBYTE 1920 if (has_mbyte) 1921 { 1922 if (enc_utf8 && maxlen >= 0) 1923 /* avoid including composing chars after the end */ 1924 l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); 1925 else 1926 l = (*mb_ptr2len)(s); 1927 s = screen_puts_mbyte(s, l, attr); 1928 } 1929 else 1930 #endif 1931 msg_screen_putchar(*s++, attr); 1932 did_last_char = TRUE; 1933 } 1934 else 1935 did_last_char = FALSE; 1936 1937 if (p_more) 1938 /* store text for scrolling back */ 1939 store_sb_text(&sb_str, s, attr, &sb_col, TRUE); 1940 1941 inc_msg_scrolled(); 1942 need_wait_return = TRUE; /* may need wait_return in main() */ 1943 if (must_redraw < VALID) 1944 must_redraw = VALID; 1945 redraw_cmdline = TRUE; 1946 if (cmdline_row > 0 && !exmode_active) 1947 --cmdline_row; 1948 1949 /* 1950 * If screen is completely filled and 'more' is set then wait 1951 * for a character. 1952 */ 1953 if (lines_left > 0) 1954 --lines_left; 1955 if (p_more && lines_left == 0 && State != HITRETURN 1956 && !msg_no_more && !exmode_active) 1957 { 1958 #ifdef FEAT_CON_DIALOG 1959 if (do_more_prompt(NUL)) 1960 s = confirm_msg_tail; 1961 #else 1962 (void)do_more_prompt(NUL); 1963 #endif 1964 if (quit_more) 1965 return; 1966 } 1967 1968 /* When we displayed a char in last column need to check if there 1969 * is still more. */ 1970 if (did_last_char) 1971 continue; 1972 } 1973 1974 wrap = *s == '\n' 1975 || msg_col + t_col >= Columns 1976 #ifdef FEAT_MBYTE 1977 || (has_mbyte && (*mb_ptr2cells)(s) > 1 1978 && msg_col + t_col >= Columns - 1) 1979 #endif 1980 ; 1981 if (t_col > 0 && (wrap || *s == '\r' || *s == '\b' 1982 || *s == '\t' || *s == BELL)) 1983 /* output any postponed text */ 1984 t_puts(&t_col, t_s, s, attr); 1985 1986 if (wrap && p_more && !recurse) 1987 /* store text for scrolling back */ 1988 store_sb_text(&sb_str, s, attr, &sb_col, TRUE); 1989 1990 if (*s == '\n') /* go to next line */ 1991 { 1992 msg_didout = FALSE; /* remember that line is empty */ 1993 #ifdef FEAT_RIGHTLEFT 1994 if (cmdmsg_rl) 1995 msg_col = Columns - 1; 1996 else 1997 #endif 1998 msg_col = 0; 1999 if (++msg_row >= Rows) /* safety check */ 2000 msg_row = Rows - 1; 2001 } 2002 else if (*s == '\r') /* go to column 0 */ 2003 { 2004 msg_col = 0; 2005 } 2006 else if (*s == '\b') /* go to previous char */ 2007 { 2008 if (msg_col) 2009 --msg_col; 2010 } 2011 else if (*s == TAB) /* translate Tab into spaces */ 2012 { 2013 do 2014 msg_screen_putchar(' ', attr); 2015 while (msg_col & 7); 2016 } 2017 else if (*s == BELL) /* beep (from ":sh") */ 2018 vim_beep(); 2019 else 2020 { 2021 #ifdef FEAT_MBYTE 2022 if (has_mbyte) 2023 { 2024 cw = (*mb_ptr2cells)(s); 2025 if (enc_utf8 && maxlen >= 0) 2026 /* avoid including composing chars after the end */ 2027 l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); 2028 else 2029 l = (*mb_ptr2len)(s); 2030 } 2031 else 2032 { 2033 cw = 1; 2034 l = 1; 2035 } 2036 #endif 2037 /* When drawing from right to left or when a double-wide character 2038 * doesn't fit, draw a single character here. Otherwise collect 2039 * characters and draw them all at once later. */ 2040 #if defined(FEAT_RIGHTLEFT) || defined(FEAT_MBYTE) 2041 if ( 2042 # ifdef FEAT_RIGHTLEFT 2043 cmdmsg_rl 2044 # ifdef FEAT_MBYTE 2045 || 2046 # endif 2047 # endif 2048 # ifdef FEAT_MBYTE 2049 (cw > 1 && msg_col + t_col >= Columns - 1) 2050 # endif 2051 ) 2052 { 2053 # ifdef FEAT_MBYTE 2054 if (l > 1) 2055 s = screen_puts_mbyte(s, l, attr) - 1; 2056 else 2057 # endif 2058 msg_screen_putchar(*s, attr); 2059 } 2060 else 2061 #endif 2062 { 2063 /* postpone this character until later */ 2064 if (t_col == 0) 2065 t_s = s; 2066 #ifdef FEAT_MBYTE 2067 t_col += cw; 2068 s += l - 1; 2069 #else 2070 ++t_col; 2071 #endif 2072 } 2073 } 2074 ++s; 2075 } 2076 2077 /* output any postponed text */ 2078 if (t_col > 0) 2079 t_puts(&t_col, t_s, s, attr); 2080 if (p_more && !recurse) 2081 store_sb_text(&sb_str, s, attr, &sb_col, FALSE); 2082 2083 msg_check(); 2084 } 2085 2086 /* 2087 * Scroll the screen up one line for displaying the next message line. 2088 */ 2089 static void 2090 msg_scroll_up() 2091 { 2092 #ifdef FEAT_GUI 2093 /* Remove the cursor before scrolling, ScreenLines[] is going 2094 * to become invalid. */ 2095 if (gui.in_use) 2096 gui_undraw_cursor(); 2097 #endif 2098 /* scrolling up always works */ 2099 screen_del_lines(0, 0, 1, (int)Rows, TRUE, NULL); 2100 2101 if (!can_clear((char_u *)" ")) 2102 { 2103 /* Scrolling up doesn't result in the right background. Set the 2104 * background here. It's not efficient, but avoids that we have to do 2105 * it all over the code. */ 2106 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); 2107 2108 /* Also clear the last char of the last but one line if it was not 2109 * cleared before to avoid a scroll-up. */ 2110 if (ScreenAttrs[LineOffset[Rows - 2] + Columns - 1] == (sattr_T)-1) 2111 screen_fill((int)Rows - 2, (int)Rows - 1, 2112 (int)Columns - 1, (int)Columns, ' ', ' ', 0); 2113 } 2114 } 2115 2116 /* 2117 * Increment "msg_scrolled". 2118 */ 2119 static void 2120 inc_msg_scrolled() 2121 { 2122 #ifdef FEAT_EVAL 2123 if (*get_vim_var_str(VV_SCROLLSTART) == NUL) 2124 { 2125 char_u *p = sourcing_name; 2126 char_u *tofree = NULL; 2127 int len; 2128 2129 /* v:scrollstart is empty, set it to the script/function name and line 2130 * number */ 2131 if (p == NULL) 2132 p = (char_u *)_("Unknown"); 2133 else 2134 { 2135 len = (int)STRLEN(p) + 40; 2136 tofree = alloc(len); 2137 if (tofree != NULL) 2138 { 2139 vim_snprintf((char *)tofree, len, _("%s line %ld"), 2140 p, (long)sourcing_lnum); 2141 p = tofree; 2142 } 2143 } 2144 set_vim_var_string(VV_SCROLLSTART, p, -1); 2145 vim_free(tofree); 2146 } 2147 #endif 2148 ++msg_scrolled; 2149 } 2150 2151 /* 2152 * To be able to scroll back at the "more" and "hit-enter" prompts we need to 2153 * store the displayed text and remember where screen lines start. 2154 */ 2155 typedef struct msgchunk_S msgchunk_T; 2156 struct msgchunk_S 2157 { 2158 msgchunk_T *sb_next; 2159 msgchunk_T *sb_prev; 2160 char sb_eol; /* TRUE when line ends after this text */ 2161 int sb_msg_col; /* column in which text starts */ 2162 int sb_attr; /* text attributes */ 2163 char_u sb_text[1]; /* text to be displayed, actually longer */ 2164 }; 2165 2166 static msgchunk_T *last_msgchunk = NULL; /* last displayed text */ 2167 2168 static msgchunk_T *msg_sb_start __ARGS((msgchunk_T *mps)); 2169 static msgchunk_T *disp_sb_line __ARGS((int row, msgchunk_T *smp)); 2170 2171 static int do_clear_sb_text = FALSE; /* clear text on next msg */ 2172 2173 /* 2174 * Store part of a printed message for displaying when scrolling back. 2175 */ 2176 static void 2177 store_sb_text(sb_str, s, attr, sb_col, finish) 2178 char_u **sb_str; /* start of string */ 2179 char_u *s; /* just after string */ 2180 int attr; 2181 int *sb_col; 2182 int finish; /* line ends */ 2183 { 2184 msgchunk_T *mp; 2185 2186 if (do_clear_sb_text) 2187 { 2188 clear_sb_text(); 2189 do_clear_sb_text = FALSE; 2190 } 2191 2192 if (s > *sb_str) 2193 { 2194 mp = (msgchunk_T *)alloc((int)(sizeof(msgchunk_T) + (s - *sb_str))); 2195 if (mp != NULL) 2196 { 2197 mp->sb_eol = finish; 2198 mp->sb_msg_col = *sb_col; 2199 mp->sb_attr = attr; 2200 vim_strncpy(mp->sb_text, *sb_str, s - *sb_str); 2201 2202 if (last_msgchunk == NULL) 2203 { 2204 last_msgchunk = mp; 2205 mp->sb_prev = NULL; 2206 } 2207 else 2208 { 2209 mp->sb_prev = last_msgchunk; 2210 last_msgchunk->sb_next = mp; 2211 last_msgchunk = mp; 2212 } 2213 mp->sb_next = NULL; 2214 } 2215 } 2216 else if (finish && last_msgchunk != NULL) 2217 last_msgchunk->sb_eol = TRUE; 2218 2219 *sb_str = s; 2220 *sb_col = 0; 2221 } 2222 2223 /* 2224 * Finished showing messages, clear the scroll-back text on the next message. 2225 */ 2226 void 2227 may_clear_sb_text() 2228 { 2229 do_clear_sb_text = TRUE; 2230 } 2231 2232 /* 2233 * Clear any text remembered for scrolling back. 2234 * Called when redrawing the screen. 2235 */ 2236 void 2237 clear_sb_text() 2238 { 2239 msgchunk_T *mp; 2240 2241 while (last_msgchunk != NULL) 2242 { 2243 mp = last_msgchunk->sb_prev; 2244 vim_free(last_msgchunk); 2245 last_msgchunk = mp; 2246 } 2247 } 2248 2249 /* 2250 * "g<" command. 2251 */ 2252 void 2253 show_sb_text() 2254 { 2255 msgchunk_T *mp; 2256 2257 /* Only show something if there is more than one line, otherwise it looks 2258 * weird, typing a command without output results in one line. */ 2259 mp = msg_sb_start(last_msgchunk); 2260 if (mp == NULL || mp->sb_prev == NULL) 2261 vim_beep(); 2262 else 2263 { 2264 do_more_prompt('G'); 2265 wait_return(FALSE); 2266 } 2267 } 2268 2269 /* 2270 * Move to the start of screen line in already displayed text. 2271 */ 2272 static msgchunk_T * 2273 msg_sb_start(mps) 2274 msgchunk_T *mps; 2275 { 2276 msgchunk_T *mp = mps; 2277 2278 while (mp != NULL && mp->sb_prev != NULL && !mp->sb_prev->sb_eol) 2279 mp = mp->sb_prev; 2280 return mp; 2281 } 2282 2283 /* 2284 * Display a screen line from previously displayed text at row "row". 2285 * Returns a pointer to the text for the next line (can be NULL). 2286 */ 2287 static msgchunk_T * 2288 disp_sb_line(row, smp) 2289 int row; 2290 msgchunk_T *smp; 2291 { 2292 msgchunk_T *mp = smp; 2293 char_u *p; 2294 2295 for (;;) 2296 { 2297 msg_row = row; 2298 msg_col = mp->sb_msg_col; 2299 p = mp->sb_text; 2300 if (*p == '\n') /* don't display the line break */ 2301 ++p; 2302 msg_puts_display(p, -1, mp->sb_attr, TRUE); 2303 if (mp->sb_eol || mp->sb_next == NULL) 2304 break; 2305 mp = mp->sb_next; 2306 } 2307 return mp->sb_next; 2308 } 2309 2310 /* 2311 * Output any postponed text for msg_puts_attr_len(). 2312 */ 2313 static void 2314 t_puts(t_col, t_s, s, attr) 2315 int *t_col; 2316 char_u *t_s; 2317 char_u *s; 2318 int attr; 2319 { 2320 /* output postponed text */ 2321 msg_didout = TRUE; /* remember that line is not empty */ 2322 screen_puts_len(t_s, (int)(s - t_s), msg_row, msg_col, attr); 2323 msg_col += *t_col; 2324 *t_col = 0; 2325 #ifdef FEAT_MBYTE 2326 /* If the string starts with a composing character don't increment the 2327 * column position for it. */ 2328 if (enc_utf8 && utf_iscomposing(utf_ptr2char(t_s))) 2329 --msg_col; 2330 #endif 2331 if (msg_col >= Columns) 2332 { 2333 msg_col = 0; 2334 ++msg_row; 2335 } 2336 } 2337 2338 /* 2339 * Returns TRUE when messages should be printed with mch_errmsg(). 2340 * This is used when there is no valid screen, so we can see error messages. 2341 * If termcap is not active, we may be writing in an alternate console 2342 * window, cursor positioning may not work correctly (window size may be 2343 * different, e.g. for Win32 console) or we just don't know where the 2344 * cursor is. 2345 */ 2346 int 2347 msg_use_printf() 2348 { 2349 return (!msg_check_screen() 2350 #if defined(WIN3264) && !defined(FEAT_GUI_MSWIN) 2351 || !termcap_active 2352 #endif 2353 || (swapping_screen() && !termcap_active) 2354 ); 2355 } 2356 2357 /* 2358 * Print a message when there is no valid screen. 2359 */ 2360 static void 2361 msg_puts_printf(str, maxlen) 2362 char_u *str; 2363 int maxlen; 2364 { 2365 char_u *s = str; 2366 char_u buf[4]; 2367 char_u *p; 2368 2369 #ifdef WIN3264 2370 if (!(silent_mode && p_verbose == 0)) 2371 mch_settmode(TMODE_COOK); /* handle '\r' and '\n' correctly */ 2372 #endif 2373 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) 2374 { 2375 if (!(silent_mode && p_verbose == 0)) 2376 { 2377 /* NL --> CR NL translation (for Unix, not for "--version") */ 2378 /* NL --> CR translation (for Mac) */ 2379 p = &buf[0]; 2380 if (*s == '\n' && !info_message) 2381 *p++ = '\r'; 2382 #if defined(USE_CR) && !defined(MACOS_X_UNIX) 2383 else 2384 #endif 2385 *p++ = *s; 2386 *p = '\0'; 2387 if (info_message) /* informative message, not an error */ 2388 mch_msg((char *)buf); 2389 else 2390 mch_errmsg((char *)buf); 2391 } 2392 2393 /* primitive way to compute the current column */ 2394 #ifdef FEAT_RIGHTLEFT 2395 if (cmdmsg_rl) 2396 { 2397 if (*s == '\r' || *s == '\n') 2398 msg_col = Columns - 1; 2399 else 2400 --msg_col; 2401 } 2402 else 2403 #endif 2404 { 2405 if (*s == '\r' || *s == '\n') 2406 msg_col = 0; 2407 else 2408 ++msg_col; 2409 } 2410 ++s; 2411 } 2412 msg_didout = TRUE; /* assume that line is not empty */ 2413 2414 #ifdef WIN3264 2415 if (!(silent_mode && p_verbose == 0)) 2416 mch_settmode(TMODE_RAW); 2417 #endif 2418 } 2419 2420 /* 2421 * Show the more-prompt and handle the user response. 2422 * This takes care of scrolling back and displaying previously displayed text. 2423 * When at hit-enter prompt "typed_char" is the already typed character, 2424 * otherwise it's NUL. 2425 * Returns TRUE when jumping ahead to "confirm_msg_tail". 2426 */ 2427 static int 2428 do_more_prompt(typed_char) 2429 int typed_char; 2430 { 2431 int used_typed_char = typed_char; 2432 int oldState = State; 2433 int c; 2434 #ifdef FEAT_CON_DIALOG 2435 int retval = FALSE; 2436 #endif 2437 int scroll; 2438 msgchunk_T *mp_last = NULL; 2439 msgchunk_T *mp; 2440 int i; 2441 2442 if (typed_char == 'G') 2443 { 2444 /* "g<": Find first line on the last page. */ 2445 mp_last = msg_sb_start(last_msgchunk); 2446 for (i = 0; i < Rows - 2 && mp_last != NULL 2447 && mp_last->sb_prev != NULL; ++i) 2448 mp_last = msg_sb_start(mp_last->sb_prev); 2449 } 2450 2451 State = ASKMORE; 2452 #ifdef FEAT_MOUSE 2453 setmouse(); 2454 #endif 2455 if (typed_char == NUL) 2456 msg_moremsg(FALSE); 2457 for (;;) 2458 { 2459 /* 2460 * Get a typed character directly from the user. 2461 */ 2462 if (used_typed_char != NUL) 2463 { 2464 c = used_typed_char; /* was typed at hit-enter prompt */ 2465 used_typed_char = NUL; 2466 } 2467 else 2468 c = get_keystroke(); 2469 2470 #if defined(FEAT_MENU) && defined(FEAT_GUI) 2471 if (c == K_MENU) 2472 { 2473 int idx = get_menu_index(current_menu, ASKMORE); 2474 2475 /* Used a menu. If it starts with CTRL-Y, it must 2476 * be a "Copy" for the clipboard. Otherwise 2477 * assume that we end */ 2478 if (idx == MENU_INDEX_INVALID) 2479 continue; 2480 c = *current_menu->strings[idx]; 2481 if (c != NUL && current_menu->strings[idx][1] != NUL) 2482 ins_typebuf(current_menu->strings[idx] + 1, 2483 current_menu->noremap[idx], 0, TRUE, 2484 current_menu->silent[idx]); 2485 } 2486 #endif 2487 2488 scroll = 0; 2489 switch (c) 2490 { 2491 case BS: /* scroll one line back */ 2492 case K_BS: 2493 case 'k': 2494 case K_UP: 2495 scroll = -1; 2496 break; 2497 2498 case CAR: /* one extra line */ 2499 case NL: 2500 case 'j': 2501 case K_DOWN: 2502 scroll = 1; 2503 break; 2504 2505 case 'u': /* Up half a page */ 2506 scroll = -(Rows / 2); 2507 break; 2508 2509 case 'd': /* Down half a page */ 2510 scroll = Rows / 2; 2511 break; 2512 2513 case 'b': /* one page back */ 2514 case K_PAGEUP: 2515 scroll = -(Rows - 1); 2516 break; 2517 2518 case ' ': /* one extra page */ 2519 case 'f': 2520 case K_PAGEDOWN: 2521 case K_LEFTMOUSE: 2522 scroll = Rows - 1; 2523 break; 2524 2525 case 'g': /* all the way back to the start */ 2526 scroll = -999999; 2527 break; 2528 2529 case 'G': /* all the way to the end */ 2530 scroll = 999999; 2531 lines_left = 999999; 2532 break; 2533 2534 case ':': /* start new command line */ 2535 #ifdef FEAT_CON_DIALOG 2536 if (!confirm_msg_used) 2537 #endif 2538 { 2539 /* Since got_int is set all typeahead will be flushed, but we 2540 * want to keep this ':', remember that in a special way. */ 2541 typeahead_noflush(':'); 2542 cmdline_row = Rows - 1; /* put ':' on this line */ 2543 skip_redraw = TRUE; /* skip redraw once */ 2544 need_wait_return = FALSE; /* don't wait in main() */ 2545 } 2546 /*FALLTHROUGH*/ 2547 case 'q': /* quit */ 2548 case Ctrl_C: 2549 case ESC: 2550 #ifdef FEAT_CON_DIALOG 2551 if (confirm_msg_used) 2552 { 2553 /* Jump to the choices of the dialog. */ 2554 retval = TRUE; 2555 } 2556 else 2557 #endif 2558 { 2559 got_int = TRUE; 2560 quit_more = TRUE; 2561 } 2562 /* When there is some more output (wrapping line) display that 2563 * without another prompt. */ 2564 lines_left = Rows - 1; 2565 break; 2566 2567 #ifdef FEAT_CLIPBOARD 2568 case Ctrl_Y: 2569 /* Strange way to allow copying (yanking) a modeless 2570 * selection at the more prompt. Use CTRL-Y, 2571 * because the same is used in Cmdline-mode and at the 2572 * hit-enter prompt. However, scrolling one line up 2573 * might be expected... */ 2574 if (clip_star.state == SELECT_DONE) 2575 clip_copy_modeless_selection(TRUE); 2576 continue; 2577 #endif 2578 default: /* no valid response */ 2579 msg_moremsg(TRUE); 2580 continue; 2581 } 2582 2583 if (scroll != 0) 2584 { 2585 if (scroll < 0) 2586 { 2587 /* go to start of last line */ 2588 if (mp_last == NULL) 2589 mp = msg_sb_start(last_msgchunk); 2590 else if (mp_last->sb_prev != NULL) 2591 mp = msg_sb_start(mp_last->sb_prev); 2592 else 2593 mp = NULL; 2594 2595 /* go to start of line at top of the screen */ 2596 for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL; 2597 ++i) 2598 mp = msg_sb_start(mp->sb_prev); 2599 2600 if (mp != NULL && mp->sb_prev != NULL) 2601 { 2602 /* Find line to be displayed at top. */ 2603 for (i = 0; i > scroll; --i) 2604 { 2605 if (mp == NULL || mp->sb_prev == NULL) 2606 break; 2607 mp = msg_sb_start(mp->sb_prev); 2608 if (mp_last == NULL) 2609 mp_last = msg_sb_start(last_msgchunk); 2610 else 2611 mp_last = msg_sb_start(mp_last->sb_prev); 2612 } 2613 2614 if (scroll == -1 && screen_ins_lines(0, 0, 1, 2615 (int)Rows, NULL) == OK) 2616 { 2617 /* display line at top */ 2618 (void)disp_sb_line(0, mp); 2619 } 2620 else 2621 { 2622 /* redisplay all lines */ 2623 screenclear(); 2624 for (i = 0; mp != NULL && i < Rows - 1; ++i) 2625 { 2626 mp = disp_sb_line(i, mp); 2627 ++msg_scrolled; 2628 } 2629 } 2630 scroll = 0; 2631 } 2632 } 2633 else 2634 { 2635 /* First display any text that we scrolled back. */ 2636 while (scroll > 0 && mp_last != NULL) 2637 { 2638 /* scroll up, display line at bottom */ 2639 msg_scroll_up(); 2640 inc_msg_scrolled(); 2641 screen_fill((int)Rows - 2, (int)Rows - 1, 0, 2642 (int)Columns, ' ', ' ', 0); 2643 mp_last = disp_sb_line((int)Rows - 2, mp_last); 2644 --scroll; 2645 } 2646 } 2647 2648 if (scroll <= 0) 2649 { 2650 /* displayed the requested text, more prompt again */ 2651 screen_fill((int)Rows - 1, (int)Rows, 0, 2652 (int)Columns, ' ', ' ', 0); 2653 msg_moremsg(FALSE); 2654 continue; 2655 } 2656 2657 /* display more text, return to caller */ 2658 lines_left = scroll; 2659 } 2660 2661 break; 2662 } 2663 2664 /* clear the --more-- message */ 2665 screen_fill((int)Rows - 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); 2666 State = oldState; 2667 #ifdef FEAT_MOUSE 2668 setmouse(); 2669 #endif 2670 if (quit_more) 2671 { 2672 msg_row = Rows - 1; 2673 msg_col = 0; 2674 } 2675 #ifdef FEAT_RIGHTLEFT 2676 else if (cmdmsg_rl) 2677 msg_col = Columns - 1; 2678 #endif 2679 2680 #ifdef FEAT_CON_DIALOG 2681 return retval; 2682 #else 2683 return FALSE; 2684 #endif 2685 } 2686 2687 #if defined(USE_MCH_ERRMSG) || defined(PROTO) 2688 2689 #ifdef mch_errmsg 2690 # undef mch_errmsg 2691 #endif 2692 #ifdef mch_msg 2693 # undef mch_msg 2694 #endif 2695 2696 /* 2697 * Give an error message. To be used when the screen hasn't been initialized 2698 * yet. When stderr can't be used, collect error messages until the GUI has 2699 * started and they can be displayed in a message box. 2700 */ 2701 void 2702 mch_errmsg(str) 2703 char *str; 2704 { 2705 int len; 2706 2707 #if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI) 2708 /* On Unix use stderr if it's a tty. 2709 * When not going to start the GUI also use stderr. 2710 * On Mac, when started from Finder, stderr is the console. */ 2711 if ( 2712 # ifdef UNIX 2713 # ifdef MACOS_X_UNIX 2714 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0) 2715 # else 2716 isatty(2) 2717 # endif 2718 # ifdef FEAT_GUI 2719 || 2720 # endif 2721 # endif 2722 # ifdef FEAT_GUI 2723 !(gui.in_use || gui.starting) 2724 # endif 2725 ) 2726 { 2727 fprintf(stderr, "%s", str); 2728 return; 2729 } 2730 #endif 2731 2732 /* avoid a delay for a message that isn't there */ 2733 emsg_on_display = FALSE; 2734 2735 len = (int)STRLEN(str) + 1; 2736 if (error_ga.ga_growsize == 0) 2737 { 2738 error_ga.ga_growsize = 80; 2739 error_ga.ga_itemsize = 1; 2740 } 2741 if (ga_grow(&error_ga, len) == OK) 2742 { 2743 mch_memmove((char_u *)error_ga.ga_data + error_ga.ga_len, 2744 (char_u *)str, len); 2745 #ifdef UNIX 2746 /* remove CR characters, they are displayed */ 2747 { 2748 char_u *p; 2749 2750 p = (char_u *)error_ga.ga_data + error_ga.ga_len; 2751 for (;;) 2752 { 2753 p = vim_strchr(p, '\r'); 2754 if (p == NULL) 2755 break; 2756 *p = ' '; 2757 } 2758 } 2759 #endif 2760 --len; /* don't count the NUL at the end */ 2761 error_ga.ga_len += len; 2762 } 2763 } 2764 2765 /* 2766 * Give a message. To be used when the screen hasn't been initialized yet. 2767 * When there is no tty, collect messages until the GUI has started and they 2768 * can be displayed in a message box. 2769 */ 2770 void 2771 mch_msg(str) 2772 char *str; 2773 { 2774 #if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI) 2775 /* On Unix use stdout if we have a tty. This allows "vim -h | more" and 2776 * uses mch_errmsg() when started from the desktop. 2777 * When not going to start the GUI also use stdout. 2778 * On Mac, when started from Finder, stderr is the console. */ 2779 if ( 2780 # ifdef UNIX 2781 # ifdef MACOS_X_UNIX 2782 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0) 2783 # else 2784 isatty(2) 2785 # endif 2786 # ifdef FEAT_GUI 2787 || 2788 # endif 2789 # endif 2790 # ifdef FEAT_GUI 2791 !(gui.in_use || gui.starting) 2792 # endif 2793 ) 2794 { 2795 printf("%s", str); 2796 return; 2797 } 2798 # endif 2799 mch_errmsg(str); 2800 } 2801 #endif /* USE_MCH_ERRMSG */ 2802 2803 /* 2804 * Put a character on the screen at the current message position and advance 2805 * to the next position. Only for printable ASCII! 2806 */ 2807 static void 2808 msg_screen_putchar(c, attr) 2809 int c; 2810 int attr; 2811 { 2812 msg_didout = TRUE; /* remember that line is not empty */ 2813 screen_putchar(c, msg_row, msg_col, attr); 2814 #ifdef FEAT_RIGHTLEFT 2815 if (cmdmsg_rl) 2816 { 2817 if (--msg_col == 0) 2818 { 2819 msg_col = Columns; 2820 ++msg_row; 2821 } 2822 } 2823 else 2824 #endif 2825 { 2826 if (++msg_col >= Columns) 2827 { 2828 msg_col = 0; 2829 ++msg_row; 2830 } 2831 } 2832 } 2833 2834 void 2835 msg_moremsg(full) 2836 int full; 2837 { 2838 int attr; 2839 char_u *s = (char_u *)_("-- More --"); 2840 2841 attr = hl_attr(HLF_M); 2842 screen_puts(s, (int)Rows - 1, 0, attr); 2843 if (full) 2844 screen_puts((char_u *) 2845 _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), 2846 (int)Rows - 1, vim_strsize(s), attr); 2847 } 2848 2849 /* 2850 * Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or 2851 * exmode_active. 2852 */ 2853 void 2854 repeat_message() 2855 { 2856 if (State == ASKMORE) 2857 { 2858 msg_moremsg(TRUE); /* display --more-- message again */ 2859 msg_row = Rows - 1; 2860 } 2861 #ifdef FEAT_CON_DIALOG 2862 else if (State == CONFIRM) 2863 { 2864 display_confirm_msg(); /* display ":confirm" message again */ 2865 msg_row = Rows - 1; 2866 } 2867 #endif 2868 else if (State == EXTERNCMD) 2869 { 2870 windgoto(msg_row, msg_col); /* put cursor back */ 2871 } 2872 else if (State == HITRETURN || State == SETWSIZE) 2873 { 2874 if (msg_row == Rows - 1) 2875 { 2876 /* Avoid drawing the "hit-enter" prompt below the previous one, 2877 * overwrite it. Esp. useful when regaining focus and a 2878 * FocusGained autocmd exists but didn't draw anything. */ 2879 msg_didout = FALSE; 2880 msg_col = 0; 2881 msg_clr_eos(); 2882 } 2883 hit_return_msg(); 2884 msg_row = Rows - 1; 2885 } 2886 } 2887 2888 /* 2889 * msg_check_screen - check if the screen is initialized. 2890 * Also check msg_row and msg_col, if they are too big it may cause a crash. 2891 * While starting the GUI the terminal codes will be set for the GUI, but the 2892 * output goes to the terminal. Don't use the terminal codes then. 2893 */ 2894 static int 2895 msg_check_screen() 2896 { 2897 if (!full_screen || !screen_valid(FALSE)) 2898 return FALSE; 2899 2900 if (msg_row >= Rows) 2901 msg_row = Rows - 1; 2902 if (msg_col >= Columns) 2903 msg_col = Columns - 1; 2904 return TRUE; 2905 } 2906 2907 /* 2908 * Clear from current message position to end of screen. 2909 * Skip this when ":silent" was used, no need to clear for redirection. 2910 */ 2911 void 2912 msg_clr_eos() 2913 { 2914 if (msg_silent == 0) 2915 msg_clr_eos_force(); 2916 } 2917 2918 /* 2919 * Clear from current message position to end of screen. 2920 * Note: msg_col is not updated, so we remember the end of the message 2921 * for msg_check(). 2922 */ 2923 void 2924 msg_clr_eos_force() 2925 { 2926 if (msg_use_printf()) 2927 { 2928 if (full_screen) /* only when termcap codes are valid */ 2929 { 2930 if (*T_CD) 2931 out_str(T_CD); /* clear to end of display */ 2932 else if (*T_CE) 2933 out_str(T_CE); /* clear to end of line */ 2934 } 2935 } 2936 else 2937 { 2938 #ifdef FEAT_RIGHTLEFT 2939 if (cmdmsg_rl) 2940 { 2941 screen_fill(msg_row, msg_row + 1, 0, msg_col + 1, ' ', ' ', 0); 2942 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); 2943 } 2944 else 2945 #endif 2946 { 2947 screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, 2948 ' ', ' ', 0); 2949 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); 2950 } 2951 } 2952 } 2953 2954 /* 2955 * Clear the command line. 2956 */ 2957 void 2958 msg_clr_cmdline() 2959 { 2960 msg_row = cmdline_row; 2961 msg_col = 0; 2962 msg_clr_eos_force(); 2963 } 2964 2965 /* 2966 * end putting a message on the screen 2967 * call wait_return if the message does not fit in the available space 2968 * return TRUE if wait_return not called. 2969 */ 2970 int 2971 msg_end() 2972 { 2973 /* 2974 * if the string is larger than the window, 2975 * or the ruler option is set and we run into it, 2976 * we have to redraw the window. 2977 * Do not do this if we are abandoning the file or editing the command line. 2978 */ 2979 if (!exiting && need_wait_return && !(State & CMDLINE)) 2980 { 2981 wait_return(FALSE); 2982 return FALSE; 2983 } 2984 out_flush(); 2985 return TRUE; 2986 } 2987 2988 /* 2989 * If the written message runs into the shown command or ruler, we have to 2990 * wait for hit-return and redraw the window later. 2991 */ 2992 void 2993 msg_check() 2994 { 2995 if (msg_row == Rows - 1 && msg_col >= sc_col) 2996 { 2997 need_wait_return = TRUE; 2998 redraw_cmdline = TRUE; 2999 } 3000 } 3001 3002 /* 3003 * May write a string to the redirection file. 3004 * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes. 3005 */ 3006 static void 3007 redir_write(str, maxlen) 3008 char_u *str; 3009 int maxlen; 3010 { 3011 char_u *s = str; 3012 static int cur_col = 0; 3013 3014 /* Don't do anything for displaying prompts and the like. */ 3015 if (redir_off) 3016 return; 3017 3018 /* 3019 * If 'verbosefile' is set write message in that file. 3020 * Must come before the rest because of updating "msg_col". 3021 */ 3022 if (*p_vfile != NUL) 3023 verbose_write(s, maxlen); 3024 3025 if (redirecting()) 3026 { 3027 /* If the string doesn't start with CR or NL, go to msg_col */ 3028 if (*s != '\n' && *s != '\r') 3029 { 3030 while (cur_col < msg_col) 3031 { 3032 #ifdef FEAT_EVAL 3033 if (redir_reg) 3034 write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE); 3035 else if (redir_vname) 3036 var_redir_str((char_u *)" ", -1); 3037 else if (redir_fd) 3038 #endif 3039 fputs(" ", redir_fd); 3040 ++cur_col; 3041 } 3042 } 3043 3044 #ifdef FEAT_EVAL 3045 if (redir_reg) 3046 write_reg_contents(redir_reg, s, maxlen, TRUE); 3047 if (redir_vname) 3048 var_redir_str(s, maxlen); 3049 #endif 3050 3051 /* Adjust the current column */ 3052 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) 3053 { 3054 #ifdef FEAT_EVAL 3055 if (!redir_reg && !redir_vname && redir_fd != NULL) 3056 #endif 3057 putc(*s, redir_fd); 3058 if (*s == '\r' || *s == '\n') 3059 cur_col = 0; 3060 else if (*s == '\t') 3061 cur_col += (8 - cur_col % 8); 3062 else 3063 ++cur_col; 3064 ++s; 3065 } 3066 3067 if (msg_silent != 0) /* should update msg_col */ 3068 msg_col = cur_col; 3069 } 3070 } 3071 3072 int 3073 redirecting() 3074 { 3075 return redir_fd != NULL 3076 #ifdef FEAT_EVAL 3077 || redir_reg || redir_vname 3078 #endif 3079 ; 3080 } 3081 3082 /* 3083 * Before giving verbose message. 3084 * Must always be called paired with verbose_leave()! 3085 */ 3086 void 3087 verbose_enter() 3088 { 3089 if (*p_vfile != NUL) 3090 ++msg_silent; 3091 } 3092 3093 /* 3094 * After giving verbose message. 3095 * Must always be called paired with verbose_enter()! 3096 */ 3097 void 3098 verbose_leave() 3099 { 3100 if (*p_vfile != NUL) 3101 if (--msg_silent < 0) 3102 msg_silent = 0; 3103 } 3104 3105 /* 3106 * Like verbose_enter() and set msg_scroll when displaying the message. 3107 */ 3108 void 3109 verbose_enter_scroll() 3110 { 3111 if (*p_vfile != NUL) 3112 ++msg_silent; 3113 else 3114 /* always scroll up, don't overwrite */ 3115 msg_scroll = TRUE; 3116 } 3117 3118 /* 3119 * Like verbose_leave() and set cmdline_row when displaying the message. 3120 */ 3121 void 3122 verbose_leave_scroll() 3123 { 3124 if (*p_vfile != NUL) 3125 { 3126 if (--msg_silent < 0) 3127 msg_silent = 0; 3128 } 3129 else 3130 cmdline_row = msg_row; 3131 } 3132 3133 static FILE *verbose_fd = NULL; 3134 static int verbose_did_open = FALSE; 3135 3136 /* 3137 * Called when 'verbosefile' is set: stop writing to the file. 3138 */ 3139 void 3140 verbose_stop() 3141 { 3142 if (verbose_fd != NULL) 3143 { 3144 fclose(verbose_fd); 3145 verbose_fd = NULL; 3146 } 3147 verbose_did_open = FALSE; 3148 } 3149 3150 /* 3151 * Open the file 'verbosefile'. 3152 * Return FAIL or OK. 3153 */ 3154 int 3155 verbose_open() 3156 { 3157 if (verbose_fd == NULL && !verbose_did_open) 3158 { 3159 /* Only give the error message once. */ 3160 verbose_did_open = TRUE; 3161 3162 verbose_fd = mch_fopen((char *)p_vfile, "a"); 3163 if (verbose_fd == NULL) 3164 { 3165 EMSG2(_(e_notopen), p_vfile); 3166 return FAIL; 3167 } 3168 } 3169 return OK; 3170 } 3171 3172 /* 3173 * Write a string to 'verbosefile'. 3174 * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes. 3175 */ 3176 static void 3177 verbose_write(str, maxlen) 3178 char_u *str; 3179 int maxlen; 3180 { 3181 char_u *s = str; 3182 static int cur_col = 0; 3183 3184 /* Open the file when called the first time. */ 3185 if (verbose_fd == NULL) 3186 verbose_open(); 3187 3188 if (verbose_fd != NULL) 3189 { 3190 /* If the string doesn't start with CR or NL, go to msg_col */ 3191 if (*s != '\n' && *s != '\r') 3192 { 3193 while (cur_col < msg_col) 3194 { 3195 fputs(" ", verbose_fd); 3196 ++cur_col; 3197 } 3198 } 3199 3200 /* Adjust the current column */ 3201 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) 3202 { 3203 putc(*s, verbose_fd); 3204 if (*s == '\r' || *s == '\n') 3205 cur_col = 0; 3206 else if (*s == '\t') 3207 cur_col += (8 - cur_col % 8); 3208 else 3209 ++cur_col; 3210 ++s; 3211 } 3212 } 3213 } 3214 3215 /* 3216 * Give a warning message (for searching). 3217 * Use 'w' highlighting and may repeat the message after redrawing 3218 */ 3219 void 3220 give_warning(message, hl) 3221 char_u *message; 3222 int hl; 3223 { 3224 /* Don't do this for ":silent". */ 3225 if (msg_silent != 0) 3226 return; 3227 3228 /* Don't want a hit-enter prompt here. */ 3229 ++no_wait_return; 3230 3231 #ifdef FEAT_EVAL 3232 set_vim_var_string(VV_WARNINGMSG, message, -1); 3233 #endif 3234 vim_free(keep_msg); 3235 keep_msg = NULL; 3236 if (hl) 3237 keep_msg_attr = hl_attr(HLF_W); 3238 else 3239 keep_msg_attr = 0; 3240 if (msg_attr(message, keep_msg_attr) && msg_scrolled == 0) 3241 set_keep_msg(message, keep_msg_attr); 3242 msg_didout = FALSE; /* overwrite this message */ 3243 msg_nowait = TRUE; /* don't wait for this message */ 3244 msg_col = 0; 3245 3246 --no_wait_return; 3247 } 3248 3249 /* 3250 * Advance msg cursor to column "col". 3251 */ 3252 void 3253 msg_advance(col) 3254 int col; 3255 { 3256 if (msg_silent != 0) /* nothing to advance to */ 3257 { 3258 msg_col = col; /* for redirection, may fill it up later */ 3259 return; 3260 } 3261 if (col >= Columns) /* not enough room */ 3262 col = Columns - 1; 3263 #ifdef FEAT_RIGHTLEFT 3264 if (cmdmsg_rl) 3265 while (msg_col > Columns - col) 3266 msg_putchar(' '); 3267 else 3268 #endif 3269 while (msg_col < col) 3270 msg_putchar(' '); 3271 } 3272 3273 #if defined(FEAT_CON_DIALOG) || defined(PROTO) 3274 /* 3275 * Used for "confirm()" function, and the :confirm command prefix. 3276 * Versions which haven't got flexible dialogs yet, and console 3277 * versions, get this generic handler which uses the command line. 3278 * 3279 * type = one of: 3280 * VIM_QUESTION, VIM_INFO, VIM_WARNING, VIM_ERROR or VIM_GENERIC 3281 * title = title string (can be NULL for default) 3282 * (neither used in console dialogs at the moment) 3283 * 3284 * Format of the "buttons" string: 3285 * "Button1Name\nButton2Name\nButton3Name" 3286 * The first button should normally be the default/accept 3287 * The second button should be the 'Cancel' button 3288 * Other buttons- use your imagination! 3289 * A '&' in a button name becomes a shortcut, so each '&' should be before a 3290 * different letter. 3291 */ 3292 int 3293 do_dialog(type, title, message, buttons, dfltbutton, textfield) 3294 int type UNUSED; 3295 char_u *title UNUSED; 3296 char_u *message; 3297 char_u *buttons; 3298 int dfltbutton; 3299 char_u *textfield UNUSED; /* IObuff for inputdialog(), NULL 3300 otherwise */ 3301 { 3302 int oldState; 3303 int retval = 0; 3304 char_u *hotkeys; 3305 int c; 3306 int i; 3307 3308 #ifndef NO_CONSOLE 3309 /* Don't output anything in silent mode ("ex -s") */ 3310 if (silent_mode) 3311 return dfltbutton; /* return default option */ 3312 #endif 3313 3314 #ifdef FEAT_GUI_DIALOG 3315 /* When GUI is running and 'c' not in 'guioptions', use the GUI dialog */ 3316 if (gui.in_use && vim_strchr(p_go, GO_CONDIALOG) == NULL) 3317 { 3318 c = gui_mch_dialog(type, title, message, buttons, dfltbutton, 3319 textfield); 3320 /* avoid a hit-enter prompt without clearing the cmdline */ 3321 need_wait_return = FALSE; 3322 emsg_on_display = FALSE; 3323 cmdline_row = msg_row; 3324 3325 /* Flush output to avoid that further messages and redrawing is done 3326 * in the wrong order. */ 3327 out_flush(); 3328 gui_mch_update(); 3329 3330 return c; 3331 } 3332 #endif 3333 3334 oldState = State; 3335 State = CONFIRM; 3336 #ifdef FEAT_MOUSE 3337 setmouse(); 3338 #endif 3339 3340 /* 3341 * Since we wait for a keypress, don't make the 3342 * user press RETURN as well afterwards. 3343 */ 3344 ++no_wait_return; 3345 hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); 3346 3347 if (hotkeys != NULL) 3348 { 3349 for (;;) 3350 { 3351 /* Get a typed character directly from the user. */ 3352 c = get_keystroke(); 3353 switch (c) 3354 { 3355 case CAR: /* User accepts default option */ 3356 case NL: 3357 retval = dfltbutton; 3358 break; 3359 case Ctrl_C: /* User aborts/cancels */ 3360 case ESC: 3361 retval = 0; 3362 break; 3363 default: /* Could be a hotkey? */ 3364 if (c < 0) /* special keys are ignored here */ 3365 continue; 3366 /* Make the character lowercase, as chars in "hotkeys" are. */ 3367 c = MB_TOLOWER(c); 3368 retval = 1; 3369 for (i = 0; hotkeys[i]; ++i) 3370 { 3371 #ifdef FEAT_MBYTE 3372 if (has_mbyte) 3373 { 3374 if ((*mb_ptr2char)(hotkeys + i) == c) 3375 break; 3376 i += (*mb_ptr2len)(hotkeys + i) - 1; 3377 } 3378 else 3379 #endif 3380 if (hotkeys[i] == c) 3381 break; 3382 ++retval; 3383 } 3384 if (hotkeys[i]) 3385 break; 3386 /* No hotkey match, so keep waiting */ 3387 continue; 3388 } 3389 break; 3390 } 3391 3392 vim_free(hotkeys); 3393 } 3394 3395 State = oldState; 3396 #ifdef FEAT_MOUSE 3397 setmouse(); 3398 #endif 3399 --no_wait_return; 3400 msg_end_prompt(); 3401 3402 return retval; 3403 } 3404 3405 static int copy_char __ARGS((char_u *from, char_u *to, int lowercase)); 3406 3407 /* 3408 * Copy one character from "*from" to "*to", taking care of multi-byte 3409 * characters. Return the length of the character in bytes. 3410 */ 3411 static int 3412 copy_char(from, to, lowercase) 3413 char_u *from; 3414 char_u *to; 3415 int lowercase; /* make character lower case */ 3416 { 3417 #ifdef FEAT_MBYTE 3418 int len; 3419 int c; 3420 3421 if (has_mbyte) 3422 { 3423 if (lowercase) 3424 { 3425 c = MB_TOLOWER((*mb_ptr2char)(from)); 3426 return (*mb_char2bytes)(c, to); 3427 } 3428 else 3429 { 3430 len = (*mb_ptr2len)(from); 3431 mch_memmove(to, from, (size_t)len); 3432 return len; 3433 } 3434 } 3435 else 3436 #endif 3437 { 3438 if (lowercase) 3439 *to = (char_u)TOLOWER_LOC(*from); 3440 else 3441 *to = *from; 3442 return 1; 3443 } 3444 } 3445 3446 /* 3447 * Format the dialog string, and display it at the bottom of 3448 * the screen. Return a string of hotkey chars (if defined) for 3449 * each 'button'. If a button has no hotkey defined, the first character of 3450 * the button is used. 3451 * The hotkeys can be multi-byte characters, but without combining chars. 3452 * 3453 * Returns an allocated string with hotkeys, or NULL for error. 3454 */ 3455 static char_u * 3456 msg_show_console_dialog(message, buttons, dfltbutton) 3457 char_u *message; 3458 char_u *buttons; 3459 int dfltbutton; 3460 { 3461 int len = 0; 3462 #ifdef FEAT_MBYTE 3463 # define HOTK_LEN (has_mbyte ? MB_MAXBYTES : 1) 3464 #else 3465 # define HOTK_LEN 1 3466 #endif 3467 int lenhotkey = HOTK_LEN; /* count first button */ 3468 char_u *hotk = NULL; 3469 char_u *msgp = NULL; 3470 char_u *hotkp = NULL; 3471 char_u *r; 3472 int copy; 3473 #define HAS_HOTKEY_LEN 30 3474 char_u has_hotkey[HAS_HOTKEY_LEN]; 3475 int first_hotkey = FALSE; /* first char of button is hotkey */ 3476 int idx; 3477 3478 has_hotkey[0] = FALSE; 3479 3480 /* 3481 * First loop: compute the size of memory to allocate. 3482 * Second loop: copy to the allocated memory. 3483 */ 3484 for (copy = 0; copy <= 1; ++copy) 3485 { 3486 r = buttons; 3487 idx = 0; 3488 while (*r) 3489 { 3490 if (*r == DLG_BUTTON_SEP) 3491 { 3492 if (copy) 3493 { 3494 *msgp++ = ','; 3495 *msgp++ = ' '; /* '\n' -> ', ' */ 3496 3497 /* advance to next hotkey and set default hotkey */ 3498 #ifdef FEAT_MBYTE 3499 if (has_mbyte) 3500 hotkp += STRLEN(hotkp); 3501 else 3502 #endif 3503 ++hotkp; 3504 hotkp[copy_char(r + 1, hotkp, TRUE)] = NUL; 3505 if (dfltbutton) 3506 --dfltbutton; 3507 3508 /* If no hotkey is specified first char is used. */ 3509 if (idx < HAS_HOTKEY_LEN - 1 && !has_hotkey[++idx]) 3510 first_hotkey = TRUE; 3511 } 3512 else 3513 { 3514 len += 3; /* '\n' -> ', '; 'x' -> '(x)' */ 3515 lenhotkey += HOTK_LEN; /* each button needs a hotkey */ 3516 if (idx < HAS_HOTKEY_LEN - 1) 3517 has_hotkey[++idx] = FALSE; 3518 } 3519 } 3520 else if (*r == DLG_HOTKEY_CHAR || first_hotkey) 3521 { 3522 if (*r == DLG_HOTKEY_CHAR) 3523 ++r; 3524 first_hotkey = FALSE; 3525 if (copy) 3526 { 3527 if (*r == DLG_HOTKEY_CHAR) /* '&&a' -> '&a' */ 3528 *msgp++ = *r; 3529 else 3530 { 3531 /* '&a' -> '[a]' */ 3532 *msgp++ = (dfltbutton == 1) ? '[' : '('; 3533 msgp += copy_char(r, msgp, FALSE); 3534 *msgp++ = (dfltbutton == 1) ? ']' : ')'; 3535 3536 /* redefine hotkey */ 3537 hotkp[copy_char(r, hotkp, TRUE)] = NUL; 3538 } 3539 } 3540 else 3541 { 3542 ++len; /* '&a' -> '[a]' */ 3543 if (idx < HAS_HOTKEY_LEN - 1) 3544 has_hotkey[idx] = TRUE; 3545 } 3546 } 3547 else 3548 { 3549 /* everything else copy literally */ 3550 if (copy) 3551 msgp += copy_char(r, msgp, FALSE); 3552 } 3553 3554 /* advance to the next character */ 3555 mb_ptr_adv(r); 3556 } 3557 3558 if (copy) 3559 { 3560 *msgp++ = ':'; 3561 *msgp++ = ' '; 3562 *msgp = NUL; 3563 } 3564 else 3565 { 3566 len += (int)(STRLEN(message) 3567 + 2 /* for the NL's */ 3568 + STRLEN(buttons) 3569 + 3); /* for the ": " and NUL */ 3570 lenhotkey++; /* for the NUL */ 3571 3572 /* If no hotkey is specified first char is used. */ 3573 if (!has_hotkey[0]) 3574 { 3575 first_hotkey = TRUE; 3576 len += 2; /* "x" -> "[x]" */ 3577 } 3578 3579 /* 3580 * Now allocate and load the strings 3581 */ 3582 vim_free(confirm_msg); 3583 confirm_msg = alloc(len); 3584 if (confirm_msg == NULL) 3585 return NULL; 3586 *confirm_msg = NUL; 3587 hotk = alloc(lenhotkey); 3588 if (hotk == NULL) 3589 return NULL; 3590 3591 *confirm_msg = '\n'; 3592 STRCPY(confirm_msg + 1, message); 3593 3594 msgp = confirm_msg + 1 + STRLEN(message); 3595 hotkp = hotk; 3596 3597 /* Define first default hotkey. Keep the hotkey string NUL 3598 * terminated to avoid reading past the end. */ 3599 hotkp[copy_char(buttons, hotkp, TRUE)] = NUL; 3600 3601 /* Remember where the choices start, displaying starts here when 3602 * "hotkp" typed at the more prompt. */ 3603 confirm_msg_tail = msgp; 3604 *msgp++ = '\n'; 3605 } 3606 } 3607 3608 display_confirm_msg(); 3609 return hotk; 3610 } 3611 3612 /* 3613 * Display the ":confirm" message. Also called when screen resized. 3614 */ 3615 void 3616 display_confirm_msg() 3617 { 3618 /* avoid that 'q' at the more prompt truncates the message here */ 3619 ++confirm_msg_used; 3620 if (confirm_msg != NULL) 3621 msg_puts_attr(confirm_msg, hl_attr(HLF_M)); 3622 --confirm_msg_used; 3623 } 3624 3625 #endif /* FEAT_CON_DIALOG */ 3626 3627 #if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG) 3628 3629 int 3630 vim_dialog_yesno(type, title, message, dflt) 3631 int type; 3632 char_u *title; 3633 char_u *message; 3634 int dflt; 3635 { 3636 if (do_dialog(type, 3637 title == NULL ? (char_u *)_("Question") : title, 3638 message, 3639 (char_u *)_("&Yes\n&No"), dflt, NULL) == 1) 3640 return VIM_YES; 3641 return VIM_NO; 3642 } 3643 3644 int 3645 vim_dialog_yesnocancel(type, title, message, dflt) 3646 int type; 3647 char_u *title; 3648 char_u *message; 3649 int dflt; 3650 { 3651 switch (do_dialog(type, 3652 title == NULL ? (char_u *)_("Question") : title, 3653 message, 3654 (char_u *)_("&Yes\n&No\n&Cancel"), dflt, NULL)) 3655 { 3656 case 1: return VIM_YES; 3657 case 2: return VIM_NO; 3658 } 3659 return VIM_CANCEL; 3660 } 3661 3662 int 3663 vim_dialog_yesnoallcancel(type, title, message, dflt) 3664 int type; 3665 char_u *title; 3666 char_u *message; 3667 int dflt; 3668 { 3669 switch (do_dialog(type, 3670 title == NULL ? (char_u *)"Question" : title, 3671 message, 3672 (char_u *)_("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"), 3673 dflt, NULL)) 3674 { 3675 case 1: return VIM_YES; 3676 case 2: return VIM_NO; 3677 case 3: return VIM_ALL; 3678 case 4: return VIM_DISCARDALL; 3679 } 3680 return VIM_CANCEL; 3681 } 3682 3683 #endif /* FEAT_GUI_DIALOG || FEAT_CON_DIALOG */ 3684 3685 #if defined(FEAT_BROWSE) || defined(PROTO) 3686 /* 3687 * Generic browse function. Calls gui_mch_browse() when possible. 3688 * Later this may pop-up a non-GUI file selector (external command?). 3689 */ 3690 char_u * 3691 do_browse(flags, title, dflt, ext, initdir, filter, buf) 3692 int flags; /* BROWSE_SAVE and BROWSE_DIR */ 3693 char_u *title; /* title for the window */ 3694 char_u *dflt; /* default file name (may include directory) */ 3695 char_u *ext; /* extension added */ 3696 char_u *initdir; /* initial directory, NULL for current dir or 3697 when using path from "dflt" */ 3698 char_u *filter; /* file name filter */ 3699 buf_T *buf; /* buffer to read/write for */ 3700 { 3701 char_u *fname; 3702 static char_u *last_dir = NULL; /* last used directory */ 3703 char_u *tofree = NULL; 3704 int save_browse = cmdmod.browse; 3705 3706 /* Must turn off browse to avoid that autocommands will get the 3707 * flag too! */ 3708 cmdmod.browse = FALSE; 3709 3710 if (title == NULL || *title == NUL) 3711 { 3712 if (flags & BROWSE_DIR) 3713 title = (char_u *)_("Select Directory dialog"); 3714 else if (flags & BROWSE_SAVE) 3715 title = (char_u *)_("Save File dialog"); 3716 else 3717 title = (char_u *)_("Open File dialog"); 3718 } 3719 3720 /* When no directory specified, use default file name, default dir, buffer 3721 * dir, last dir or current dir */ 3722 if ((initdir == NULL || *initdir == NUL) && dflt != NULL && *dflt != NUL) 3723 { 3724 if (mch_isdir(dflt)) /* default file name is a directory */ 3725 { 3726 initdir = dflt; 3727 dflt = NULL; 3728 } 3729 else if (gettail(dflt) != dflt) /* default file name includes a path */ 3730 { 3731 tofree = vim_strsave(dflt); 3732 if (tofree != NULL) 3733 { 3734 initdir = tofree; 3735 *gettail(initdir) = NUL; 3736 dflt = gettail(dflt); 3737 } 3738 } 3739 } 3740 3741 if (initdir == NULL || *initdir == NUL) 3742 { 3743 /* When 'browsedir' is a directory, use it */ 3744 if (STRCMP(p_bsdir, "last") != 0 3745 && STRCMP(p_bsdir, "buffer") != 0 3746 && STRCMP(p_bsdir, "current") != 0 3747 && mch_isdir(p_bsdir)) 3748 initdir = p_bsdir; 3749 /* When saving or 'browsedir' is "buffer", use buffer fname */ 3750 else if (((flags & BROWSE_SAVE) || *p_bsdir == 'b') 3751 && buf != NULL && buf->b_ffname != NULL) 3752 { 3753 if (dflt == NULL || *dflt == NUL) 3754 dflt = gettail(curbuf->b_ffname); 3755 tofree = vim_strsave(curbuf->b_ffname); 3756 if (tofree != NULL) 3757 { 3758 initdir = tofree; 3759 *gettail(initdir) = NUL; 3760 } 3761 } 3762 /* When 'browsedir' is "last", use dir from last browse */ 3763 else if (*p_bsdir == 'l') 3764 initdir = last_dir; 3765 /* When 'browsedir is "current", use current directory. This is the 3766 * default already, leave initdir empty. */ 3767 } 3768 3769 # ifdef FEAT_GUI 3770 if (gui.in_use) /* when this changes, also adjust f_has()! */ 3771 { 3772 if (filter == NULL 3773 # ifdef FEAT_EVAL 3774 && (filter = get_var_value((char_u *)"b:browsefilter")) == NULL 3775 && (filter = get_var_value((char_u *)"g:browsefilter")) == NULL 3776 # endif 3777 ) 3778 filter = BROWSE_FILTER_DEFAULT; 3779 if (flags & BROWSE_DIR) 3780 { 3781 # if defined(HAVE_GTK2) || defined(WIN3264) 3782 /* For systems that have a directory dialog. */ 3783 fname = gui_mch_browsedir(title, initdir); 3784 # else 3785 /* Generic solution for selecting a directory: select a file and 3786 * remove the file name. */ 3787 fname = gui_mch_browse(0, title, dflt, ext, initdir, (char_u *)""); 3788 # endif 3789 # if !defined(HAVE_GTK2) 3790 /* Win32 adds a dummy file name, others return an arbitrary file 3791 * name. GTK+ 2 returns only the directory, */ 3792 if (fname != NULL && *fname != NUL && !mch_isdir(fname)) 3793 { 3794 /* Remove the file name. */ 3795 char_u *tail = gettail_sep(fname); 3796 3797 if (tail == fname) 3798 *tail++ = '.'; /* use current dir */ 3799 *tail = NUL; 3800 } 3801 # endif 3802 } 3803 else 3804 fname = gui_mch_browse(flags & BROWSE_SAVE, 3805 title, dflt, ext, initdir, filter); 3806 3807 /* We hang around in the dialog for a while, the user might do some 3808 * things to our files. The Win32 dialog allows deleting or renaming 3809 * a file, check timestamps. */ 3810 need_check_timestamps = TRUE; 3811 did_check_timestamps = FALSE; 3812 } 3813 else 3814 # endif 3815 { 3816 /* TODO: non-GUI file selector here */ 3817 EMSG(_("E338: Sorry, no file browser in console mode")); 3818 fname = NULL; 3819 } 3820 3821 /* keep the directory for next time */ 3822 if (fname != NULL) 3823 { 3824 vim_free(last_dir); 3825 last_dir = vim_strsave(fname); 3826 if (last_dir != NULL && !(flags & BROWSE_DIR)) 3827 { 3828 *gettail(last_dir) = NUL; 3829 if (*last_dir == NUL) 3830 { 3831 /* filename only returned, must be in current dir */ 3832 vim_free(last_dir); 3833 last_dir = alloc(MAXPATHL); 3834 if (last_dir != NULL) 3835 mch_dirname(last_dir, MAXPATHL); 3836 } 3837 } 3838 } 3839 3840 vim_free(tofree); 3841 cmdmod.browse = save_browse; 3842 3843 return fname; 3844 } 3845 #endif 3846 3847 #if defined(HAVE_STDARG_H) && defined(FEAT_EVAL) 3848 static char *e_printf = N_("E766: Insufficient arguments for printf()"); 3849 3850 static long tv_nr __ARGS((typval_T *tvs, int *idxp)); 3851 static char *tv_str __ARGS((typval_T *tvs, int *idxp)); 3852 # ifdef FEAT_FLOAT 3853 static double tv_float __ARGS((typval_T *tvs, int *idxp)); 3854 # endif 3855 3856 /* 3857 * Get number argument from "idxp" entry in "tvs". First entry is 1. 3858 */ 3859 static long 3860 tv_nr(tvs, idxp) 3861 typval_T *tvs; 3862 int *idxp; 3863 { 3864 int idx = *idxp - 1; 3865 long n = 0; 3866 int err = FALSE; 3867 3868 if (tvs[idx].v_type == VAR_UNKNOWN) 3869 EMSG(_(e_printf)); 3870 else 3871 { 3872 ++*idxp; 3873 n = get_tv_number_chk(&tvs[idx], &err); 3874 if (err) 3875 n = 0; 3876 } 3877 return n; 3878 } 3879 3880 /* 3881 * Get string argument from "idxp" entry in "tvs". First entry is 1. 3882 * Returns NULL for an error. 3883 */ 3884 static char * 3885 tv_str(tvs, idxp) 3886 typval_T *tvs; 3887 int *idxp; 3888 { 3889 int idx = *idxp - 1; 3890 char *s = NULL; 3891 3892 if (tvs[idx].v_type == VAR_UNKNOWN) 3893 EMSG(_(e_printf)); 3894 else 3895 { 3896 ++*idxp; 3897 s = (char *)get_tv_string_chk(&tvs[idx]); 3898 } 3899 return s; 3900 } 3901 3902 # ifdef FEAT_FLOAT 3903 /* 3904 * Get float argument from "idxp" entry in "tvs". First entry is 1. 3905 */ 3906 static double 3907 tv_float(tvs, idxp) 3908 typval_T *tvs; 3909 int *idxp; 3910 { 3911 int idx = *idxp - 1; 3912 double f = 0; 3913 3914 if (tvs[idx].v_type == VAR_UNKNOWN) 3915 EMSG(_(e_printf)); 3916 else 3917 { 3918 ++*idxp; 3919 if (tvs[idx].v_type == VAR_FLOAT) 3920 f = tvs[idx].vval.v_float; 3921 else if (tvs[idx].v_type == VAR_NUMBER) 3922 f = tvs[idx].vval.v_number; 3923 else 3924 EMSG(_("E807: Expected Float argument for printf()")); 3925 } 3926 return f; 3927 } 3928 # endif 3929 #endif 3930 3931 /* 3932 * This code was included to provide a portable vsnprintf() and snprintf(). 3933 * Some systems may provide their own, but we always use this one for 3934 * consistency. 3935 * 3936 * This code is based on snprintf.c - a portable implementation of snprintf 3937 * by Mark Martinec <[email protected]>, Version 2.2, 2000-10-06. 3938 * Included with permission. It was heavily modified to fit in Vim. 3939 * The original code, including useful comments, can be found here: 3940 * http://www.ijs.si/software/snprintf/ 3941 * 3942 * This snprintf() only supports the following conversion specifiers: 3943 * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below) 3944 * with flags: '-', '+', ' ', '0' and '#'. 3945 * An asterisk is supported for field width as well as precision. 3946 * 3947 * Limited support for floating point was added: 'f', 'e', 'E', 'g', 'G'. 3948 * 3949 * Length modifiers 'h' (short int) and 'l' (long int) are supported. 3950 * 'll' (long long int) is not supported. 3951 * 3952 * The locale is not used, the string is used as a byte string. This is only 3953 * relevant for double-byte encodings where the second byte may be '%'. 3954 * 3955 * It is permitted for "str_m" to be zero, and it is permitted to specify NULL 3956 * pointer for resulting string argument if "str_m" is zero (as per ISO C99). 3957 * 3958 * The return value is the number of characters which would be generated 3959 * for the given input, excluding the trailing null. If this value 3960 * is greater or equal to "str_m", not all characters from the result 3961 * have been stored in str, output bytes beyond the ("str_m"-1) -th character 3962 * are discarded. If "str_m" is greater than zero it is guaranteed 3963 * the resulting string will be null-terminated. 3964 */ 3965 3966 /* 3967 * When va_list is not supported we only define vim_snprintf(). 3968 * 3969 * vim_vsnprintf() can be invoked with either "va_list" or a list of 3970 * "typval_T". When the latter is not used it must be NULL. 3971 */ 3972 3973 /* When generating prototypes all of this is skipped, cproto doesn't 3974 * understand this. */ 3975 #ifndef PROTO 3976 # ifdef HAVE_STDARG_H 3977 int 3978 vim_snprintf(char *str, size_t str_m, char *fmt, ...) 3979 { 3980 va_list ap; 3981 int str_l; 3982 3983 va_start(ap, fmt); 3984 str_l = vim_vsnprintf(str, str_m, fmt, ap, NULL); 3985 va_end(ap); 3986 return str_l; 3987 } 3988 3989 int 3990 vim_vsnprintf(str, str_m, fmt, ap, tvs) 3991 # else 3992 /* clumsy way to work around missing va_list */ 3993 # define get_a_arg(i) (++i, i == 2 ? a1 : i == 3 ? a2 : i == 4 ? a3 : i == 5 ? a4 : i == 6 ? a5 : i == 7 ? a6 : i == 8 ? a7 : i == 9 ? a8 : i == 10 ? a9 : a10) 3994 3995 /* VARARGS */ 3996 int 3997 #ifdef __BORLANDC__ 3998 _RTLENTRYF 3999 #endif 4000 vim_snprintf(str, str_m, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) 4001 # endif 4002 char *str; 4003 size_t str_m; 4004 char *fmt; 4005 # ifdef HAVE_STDARG_H 4006 va_list ap; 4007 typval_T *tvs; 4008 # else 4009 long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; 4010 # endif 4011 { 4012 size_t str_l = 0; 4013 char *p = fmt; 4014 int arg_idx = 1; 4015 4016 if (p == NULL) 4017 p = ""; 4018 while (*p != NUL) 4019 { 4020 if (*p != '%') 4021 { 4022 char *q = strchr(p + 1, '%'); 4023 size_t n = (q == NULL) ? STRLEN(p) : (size_t)(q - p); 4024 4025 /* Copy up to the next '%' or NUL without any changes. */ 4026 if (str_l < str_m) 4027 { 4028 size_t avail = str_m - str_l; 4029 4030 mch_memmove(str + str_l, p, n > avail ? avail : n); 4031 } 4032 p += n; 4033 str_l += n; 4034 } 4035 else 4036 { 4037 size_t min_field_width = 0, precision = 0; 4038 int zero_padding = 0, precision_specified = 0, justify_left = 0; 4039 int alternate_form = 0, force_sign = 0; 4040 4041 /* If both the ' ' and '+' flags appear, the ' ' flag should be 4042 * ignored. */ 4043 int space_for_positive = 1; 4044 4045 /* allowed values: \0, h, l, L */ 4046 char length_modifier = '\0'; 4047 4048 /* temporary buffer for simple numeric->string conversion */ 4049 #ifdef FEAT_FLOAT 4050 # define TMP_LEN 350 /* On my system 1e308 is the biggest number possible. 4051 * That sounds reasonable to use as the maximum 4052 * printable. */ 4053 #else 4054 # define TMP_LEN 32 4055 #endif 4056 char tmp[TMP_LEN]; 4057 4058 /* string address in case of string argument */ 4059 char *str_arg; 4060 4061 /* natural field width of arg without padding and sign */ 4062 size_t str_arg_l; 4063 4064 /* unsigned char argument value - only defined for c conversion. 4065 * N.B. standard explicitly states the char argument for the c 4066 * conversion is unsigned */ 4067 unsigned char uchar_arg; 4068 4069 /* number of zeros to be inserted for numeric conversions as 4070 * required by the precision or minimal field width */ 4071 size_t number_of_zeros_to_pad = 0; 4072 4073 /* index into tmp where zero padding is to be inserted */ 4074 size_t zero_padding_insertion_ind = 0; 4075 4076 /* current conversion specifier character */ 4077 char fmt_spec = '\0'; 4078 4079 str_arg = NULL; 4080 p++; /* skip '%' */ 4081 4082 /* parse flags */ 4083 while (*p == '0' || *p == '-' || *p == '+' || *p == ' ' 4084 || *p == '#' || *p == '\'') 4085 { 4086 switch (*p) 4087 { 4088 case '0': zero_padding = 1; break; 4089 case '-': justify_left = 1; break; 4090 case '+': force_sign = 1; space_for_positive = 0; break; 4091 case ' ': force_sign = 1; 4092 /* If both the ' ' and '+' flags appear, the ' ' 4093 * flag should be ignored */ 4094 break; 4095 case '#': alternate_form = 1; break; 4096 case '\'': break; 4097 } 4098 p++; 4099 } 4100 /* If the '0' and '-' flags both appear, the '0' flag should be 4101 * ignored. */ 4102 4103 /* parse field width */ 4104 if (*p == '*') 4105 { 4106 int j; 4107 4108 p++; 4109 j = 4110 #ifndef HAVE_STDARG_H 4111 get_a_arg(arg_idx); 4112 #else 4113 # if defined(FEAT_EVAL) 4114 tvs != NULL ? tv_nr(tvs, &arg_idx) : 4115 # endif 4116 va_arg(ap, int); 4117 #endif 4118 if (j >= 0) 4119 min_field_width = j; 4120 else 4121 { 4122 min_field_width = -j; 4123 justify_left = 1; 4124 } 4125 } 4126 else if (VIM_ISDIGIT((int)(*p))) 4127 { 4128 /* size_t could be wider than unsigned int; make sure we treat 4129 * argument like common implementations do */ 4130 unsigned int uj = *p++ - '0'; 4131 4132 while (VIM_ISDIGIT((int)(*p))) 4133 uj = 10 * uj + (unsigned int)(*p++ - '0'); 4134 min_field_width = uj; 4135 } 4136 4137 /* parse precision */ 4138 if (*p == '.') 4139 { 4140 p++; 4141 precision_specified = 1; 4142 if (*p == '*') 4143 { 4144 int j; 4145 4146 j = 4147 #ifndef HAVE_STDARG_H 4148 get_a_arg(arg_idx); 4149 #else 4150 # if defined(FEAT_EVAL) 4151 tvs != NULL ? tv_nr(tvs, &arg_idx) : 4152 # endif 4153 va_arg(ap, int); 4154 #endif 4155 p++; 4156 if (j >= 0) 4157 precision = j; 4158 else 4159 { 4160 precision_specified = 0; 4161 precision = 0; 4162 } 4163 } 4164 else if (VIM_ISDIGIT((int)(*p))) 4165 { 4166 /* size_t could be wider than unsigned int; make sure we 4167 * treat argument like common implementations do */ 4168 unsigned int uj = *p++ - '0'; 4169 4170 while (VIM_ISDIGIT((int)(*p))) 4171 uj = 10 * uj + (unsigned int)(*p++ - '0'); 4172 precision = uj; 4173 } 4174 } 4175 4176 /* parse 'h', 'l' and 'll' length modifiers */ 4177 if (*p == 'h' || *p == 'l') 4178 { 4179 length_modifier = *p; 4180 p++; 4181 if (length_modifier == 'l' && *p == 'l') 4182 { 4183 /* double l = long long */ 4184 length_modifier = 'l'; /* treat it as a single 'l' */ 4185 p++; 4186 } 4187 } 4188 fmt_spec = *p; 4189 4190 /* common synonyms: */ 4191 switch (fmt_spec) 4192 { 4193 case 'i': fmt_spec = 'd'; break; 4194 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; 4195 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; 4196 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; 4197 case 'F': fmt_spec = 'f'; break; 4198 default: break; 4199 } 4200 4201 /* get parameter value, do initial processing */ 4202 switch (fmt_spec) 4203 { 4204 /* '%' and 'c' behave similar to 's' regarding flags and field 4205 * widths */ 4206 case '%': 4207 case 'c': 4208 case 's': 4209 length_modifier = '\0'; 4210 str_arg_l = 1; 4211 switch (fmt_spec) 4212 { 4213 case '%': 4214 str_arg = p; 4215 break; 4216 4217 case 'c': 4218 { 4219 int j; 4220 4221 j = 4222 #ifndef HAVE_STDARG_H 4223 get_a_arg(arg_idx); 4224 #else 4225 # if defined(FEAT_EVAL) 4226 tvs != NULL ? tv_nr(tvs, &arg_idx) : 4227 # endif 4228 va_arg(ap, int); 4229 #endif 4230 /* standard demands unsigned char */ 4231 uchar_arg = (unsigned char)j; 4232 str_arg = (char *)&uchar_arg; 4233 break; 4234 } 4235 4236 case 's': 4237 str_arg = 4238 #ifndef HAVE_STDARG_H 4239 (char *)get_a_arg(arg_idx); 4240 #else 4241 # if defined(FEAT_EVAL) 4242 tvs != NULL ? tv_str(tvs, &arg_idx) : 4243 # endif 4244 va_arg(ap, char *); 4245 #endif 4246 if (str_arg == NULL) 4247 { 4248 str_arg = "[NULL]"; 4249 str_arg_l = 6; 4250 } 4251 /* make sure not to address string beyond the specified 4252 * precision !!! */ 4253 else if (!precision_specified) 4254 str_arg_l = strlen(str_arg); 4255 /* truncate string if necessary as requested by precision */ 4256 else if (precision == 0) 4257 str_arg_l = 0; 4258 else 4259 { 4260 /* Don't put the #if inside memchr(), it can be a 4261 * macro. */ 4262 #if SIZEOF_INT <= 2 4263 char *q = memchr(str_arg, '\0', precision); 4264 #else 4265 /* memchr on HP does not like n > 2^31 !!! */ 4266 char *q = memchr(str_arg, '\0', 4267 precision <= (size_t)0x7fffffffL ? precision 4268 : (size_t)0x7fffffffL); 4269 #endif 4270 str_arg_l = (q == NULL) ? precision 4271 : (size_t)(q - str_arg); 4272 } 4273 break; 4274 4275 default: 4276 break; 4277 } 4278 break; 4279 4280 case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': 4281 { 4282 /* NOTE: the u, o, x, X and p conversion specifiers 4283 * imply the value is unsigned; d implies a signed 4284 * value */ 4285 4286 /* 0 if numeric argument is zero (or if pointer is 4287 * NULL for 'p'), +1 if greater than zero (or nonzero 4288 * for unsigned arguments), -1 if negative (unsigned 4289 * argument is never negative) */ 4290 int arg_sign = 0; 4291 4292 /* only defined for length modifier h, or for no 4293 * length modifiers */ 4294 int int_arg = 0; 4295 unsigned int uint_arg = 0; 4296 4297 /* only defined for length modifier l */ 4298 long int long_arg = 0; 4299 unsigned long int ulong_arg = 0; 4300 4301 /* pointer argument value -only defined for p 4302 * conversion */ 4303 void *ptr_arg = NULL; 4304 4305 if (fmt_spec == 'p') 4306 { 4307 length_modifier = '\0'; 4308 ptr_arg = 4309 #ifndef HAVE_STDARG_H 4310 (void *)get_a_arg(arg_idx); 4311 #else 4312 # if defined(FEAT_EVAL) 4313 tvs != NULL ? (void *)tv_str(tvs, &arg_idx) : 4314 # endif 4315 va_arg(ap, void *); 4316 #endif 4317 if (ptr_arg != NULL) 4318 arg_sign = 1; 4319 } 4320 else if (fmt_spec == 'd') 4321 { 4322 /* signed */ 4323 switch (length_modifier) 4324 { 4325 case '\0': 4326 case 'h': 4327 /* char and short arguments are passed as int. */ 4328 int_arg = 4329 #ifndef HAVE_STDARG_H 4330 get_a_arg(arg_idx); 4331 #else 4332 # if defined(FEAT_EVAL) 4333 tvs != NULL ? tv_nr(tvs, &arg_idx) : 4334 # endif 4335 va_arg(ap, int); 4336 #endif 4337 if (int_arg > 0) 4338 arg_sign = 1; 4339 else if (int_arg < 0) 4340 arg_sign = -1; 4341 break; 4342 case 'l': 4343 long_arg = 4344 #ifndef HAVE_STDARG_H 4345 get_a_arg(arg_idx); 4346 #else 4347 # if defined(FEAT_EVAL) 4348 tvs != NULL ? tv_nr(tvs, &arg_idx) : 4349 # endif 4350 va_arg(ap, long int); 4351 #endif 4352 if (long_arg > 0) 4353 arg_sign = 1; 4354 else if (long_arg < 0) 4355 arg_sign = -1; 4356 break; 4357 } 4358 } 4359 else 4360 { 4361 /* unsigned */ 4362 switch (length_modifier) 4363 { 4364 case '\0': 4365 case 'h': 4366 uint_arg = 4367 #ifndef HAVE_STDARG_H 4368 get_a_arg(arg_idx); 4369 #else 4370 # if defined(FEAT_EVAL) 4371 tvs != NULL ? (unsigned) 4372 tv_nr(tvs, &arg_idx) : 4373 # endif 4374 va_arg(ap, unsigned int); 4375 #endif 4376 if (uint_arg != 0) 4377 arg_sign = 1; 4378 break; 4379 case 'l': 4380 ulong_arg = 4381 #ifndef HAVE_STDARG_H 4382 get_a_arg(arg_idx); 4383 #else 4384 # if defined(FEAT_EVAL) 4385 tvs != NULL ? (unsigned long) 4386 tv_nr(tvs, &arg_idx) : 4387 # endif 4388 va_arg(ap, unsigned long int); 4389 #endif 4390 if (ulong_arg != 0) 4391 arg_sign = 1; 4392 break; 4393 } 4394 } 4395 4396 str_arg = tmp; 4397 str_arg_l = 0; 4398 4399 /* NOTE: 4400 * For d, i, u, o, x, and X conversions, if precision is 4401 * specified, the '0' flag should be ignored. This is so 4402 * with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux, 4403 * FreeBSD, NetBSD; but not with Perl. 4404 */ 4405 if (precision_specified) 4406 zero_padding = 0; 4407 if (fmt_spec == 'd') 4408 { 4409 if (force_sign && arg_sign >= 0) 4410 tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; 4411 /* leave negative numbers for sprintf to handle, to 4412 * avoid handling tricky cases like (short int)-32768 */ 4413 } 4414 else if (alternate_form) 4415 { 4416 if (arg_sign != 0 4417 && (fmt_spec == 'x' || fmt_spec == 'X') ) 4418 { 4419 tmp[str_arg_l++] = '0'; 4420 tmp[str_arg_l++] = fmt_spec; 4421 } 4422 /* alternate form should have no effect for p 4423 * conversion, but ... */ 4424 } 4425 4426 zero_padding_insertion_ind = str_arg_l; 4427 if (!precision_specified) 4428 precision = 1; /* default precision is 1 */ 4429 if (precision == 0 && arg_sign == 0) 4430 { 4431 /* When zero value is formatted with an explicit 4432 * precision 0, the resulting formatted string is 4433 * empty (d, i, u, o, x, X, p). */ 4434 } 4435 else 4436 { 4437 char f[5]; 4438 int f_l = 0; 4439 4440 /* construct a simple format string for sprintf */ 4441 f[f_l++] = '%'; 4442 if (!length_modifier) 4443 ; 4444 else if (length_modifier == '2') 4445 { 4446 f[f_l++] = 'l'; 4447 f[f_l++] = 'l'; 4448 } 4449 else 4450 f[f_l++] = length_modifier; 4451 f[f_l++] = fmt_spec; 4452 f[f_l++] = '\0'; 4453 4454 if (fmt_spec == 'p') 4455 str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg); 4456 else if (fmt_spec == 'd') 4457 { 4458 /* signed */ 4459 switch (length_modifier) 4460 { 4461 case '\0': 4462 case 'h': str_arg_l += sprintf( 4463 tmp + str_arg_l, f, int_arg); 4464 break; 4465 case 'l': str_arg_l += sprintf( 4466 tmp + str_arg_l, f, long_arg); 4467 break; 4468 } 4469 } 4470 else 4471 { 4472 /* unsigned */ 4473 switch (length_modifier) 4474 { 4475 case '\0': 4476 case 'h': str_arg_l += sprintf( 4477 tmp + str_arg_l, f, uint_arg); 4478 break; 4479 case 'l': str_arg_l += sprintf( 4480 tmp + str_arg_l, f, ulong_arg); 4481 break; 4482 } 4483 } 4484 4485 /* include the optional minus sign and possible 4486 * "0x" in the region before the zero padding 4487 * insertion point */ 4488 if (zero_padding_insertion_ind < str_arg_l 4489 && tmp[zero_padding_insertion_ind] == '-') 4490 zero_padding_insertion_ind++; 4491 if (zero_padding_insertion_ind + 1 < str_arg_l 4492 && tmp[zero_padding_insertion_ind] == '0' 4493 && (tmp[zero_padding_insertion_ind + 1] == 'x' 4494 || tmp[zero_padding_insertion_ind + 1] == 'X')) 4495 zero_padding_insertion_ind += 2; 4496 } 4497 4498 { 4499 size_t num_of_digits = str_arg_l 4500 - zero_padding_insertion_ind; 4501 4502 if (alternate_form && fmt_spec == 'o' 4503 /* unless zero is already the first 4504 * character */ 4505 && !(zero_padding_insertion_ind < str_arg_l 4506 && tmp[zero_padding_insertion_ind] == '0')) 4507 { 4508 /* assure leading zero for alternate-form 4509 * octal numbers */ 4510 if (!precision_specified 4511 || precision < num_of_digits + 1) 4512 { 4513 /* precision is increased to force the 4514 * first character to be zero, except if a 4515 * zero value is formatted with an 4516 * explicit precision of zero */ 4517 precision = num_of_digits + 1; 4518 precision_specified = 1; 4519 } 4520 } 4521 /* zero padding to specified precision? */ 4522 if (num_of_digits < precision) 4523 number_of_zeros_to_pad = precision - num_of_digits; 4524 } 4525 /* zero padding to specified minimal field width? */ 4526 if (!justify_left && zero_padding) 4527 { 4528 int n = (int)(min_field_width - (str_arg_l 4529 + number_of_zeros_to_pad)); 4530 if (n > 0) 4531 number_of_zeros_to_pad += n; 4532 } 4533 break; 4534 } 4535 4536 #ifdef FEAT_FLOAT 4537 case 'f': 4538 case 'e': 4539 case 'E': 4540 case 'g': 4541 case 'G': 4542 { 4543 /* Floating point. */ 4544 double f; 4545 double abs_f; 4546 char format[40]; 4547 int l; 4548 int remove_trailing_zeroes = FALSE; 4549 4550 f = 4551 # ifndef HAVE_STDARG_H 4552 get_a_arg(arg_idx); 4553 # else 4554 # if defined(FEAT_EVAL) 4555 tvs != NULL ? tv_float(tvs, &arg_idx) : 4556 # endif 4557 va_arg(ap, double); 4558 # endif 4559 abs_f = f < 0 ? -f : f; 4560 4561 if (fmt_spec == 'g' || fmt_spec == 'G') 4562 { 4563 /* Would be nice to use %g directly, but it prints 4564 * "1.0" as "1", we don't want that. */ 4565 if ((abs_f >= 0.001 && abs_f < 10000000.0) 4566 || abs_f == 0.0) 4567 fmt_spec = 'f'; 4568 else 4569 fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; 4570 remove_trailing_zeroes = TRUE; 4571 } 4572 4573 if (fmt_spec == 'f' && 4574 #ifdef VAX 4575 abs_f > 1.0e38 4576 #else 4577 abs_f > 1.0e307 4578 #endif 4579 ) 4580 { 4581 /* Avoid a buffer overflow */ 4582 strcpy(tmp, "inf"); 4583 str_arg_l = 3; 4584 } 4585 else 4586 { 4587 format[0] = '%'; 4588 l = 1; 4589 if (precision_specified) 4590 { 4591 size_t max_prec = TMP_LEN - 10; 4592 4593 /* Make sure we don't get more digits than we 4594 * have room for. */ 4595 if (fmt_spec == 'f' && abs_f > 1.0) 4596 max_prec -= (size_t)log10(abs_f); 4597 if (precision > max_prec) 4598 precision = max_prec; 4599 l += sprintf(format + 1, ".%d", (int)precision); 4600 } 4601 format[l] = fmt_spec; 4602 format[l + 1] = NUL; 4603 str_arg_l = sprintf(tmp, format, f); 4604 4605 if (remove_trailing_zeroes) 4606 { 4607 int i; 4608 char *tp; 4609 4610 /* Using %g or %G: remove superfluous zeroes. */ 4611 if (fmt_spec == 'f') 4612 tp = tmp + str_arg_l - 1; 4613 else 4614 { 4615 tp = (char *)vim_strchr((char_u *)tmp, 4616 fmt_spec == 'e' ? 'e' : 'E'); 4617 if (tp != NULL) 4618 { 4619 /* Remove superfluous '+' and leading 4620 * zeroes from the exponent. */ 4621 if (tp[1] == '+') 4622 { 4623 /* Change "1.0e+07" to "1.0e07" */ 4624 STRMOVE(tp + 1, tp + 2); 4625 --str_arg_l; 4626 } 4627 i = (tp[1] == '-') ? 2 : 1; 4628 while (tp[i] == '0') 4629 { 4630 /* Change "1.0e07" to "1.0e7" */ 4631 STRMOVE(tp + i, tp + i + 1); 4632 --str_arg_l; 4633 } 4634 --tp; 4635 } 4636 } 4637 4638 if (tp != NULL && !precision_specified) 4639 /* Remove trailing zeroes, but keep the one 4640 * just after a dot. */ 4641 while (tp > tmp + 2 && *tp == '0' 4642 && tp[-1] != '.') 4643 { 4644 STRMOVE(tp, tp + 1); 4645 --tp; 4646 --str_arg_l; 4647 } 4648 } 4649 else 4650 { 4651 char *tp; 4652 4653 /* Be consistent: some printf("%e") use 1.0e+12 4654 * and some 1.0e+012. Remove one zero in the last 4655 * case. */ 4656 tp = (char *)vim_strchr((char_u *)tmp, 4657 fmt_spec == 'e' ? 'e' : 'E'); 4658 if (tp != NULL && (tp[1] == '+' || tp[1] == '-') 4659 && tp[2] == '0' 4660 && vim_isdigit(tp[3]) 4661 && vim_isdigit(tp[4])) 4662 { 4663 STRMOVE(tp + 2, tp + 3); 4664 --str_arg_l; 4665 } 4666 } 4667 } 4668 str_arg = tmp; 4669 break; 4670 } 4671 #endif 4672 4673 default: 4674 /* unrecognized conversion specifier, keep format string 4675 * as-is */ 4676 zero_padding = 0; /* turn zero padding off for non-numeric 4677 conversion */ 4678 justify_left = 1; 4679 min_field_width = 0; /* reset flags */ 4680 4681 /* discard the unrecognized conversion, just keep * 4682 * the unrecognized conversion character */ 4683 str_arg = p; 4684 str_arg_l = 0; 4685 if (*p != NUL) 4686 str_arg_l++; /* include invalid conversion specifier 4687 unchanged if not at end-of-string */ 4688 break; 4689 } 4690 4691 if (*p != NUL) 4692 p++; /* step over the just processed conversion specifier */ 4693 4694 /* insert padding to the left as requested by min_field_width; 4695 * this does not include the zero padding in case of numerical 4696 * conversions*/ 4697 if (!justify_left) 4698 { 4699 /* left padding with blank or zero */ 4700 int pn = (int)(min_field_width - (str_arg_l + number_of_zeros_to_pad)); 4701 4702 if (pn > 0) 4703 { 4704 if (str_l < str_m) 4705 { 4706 size_t avail = str_m - str_l; 4707 4708 vim_memset(str + str_l, zero_padding ? '0' : ' ', 4709 (size_t)pn > avail ? avail 4710 : (size_t)pn); 4711 } 4712 str_l += pn; 4713 } 4714 } 4715 4716 /* zero padding as requested by the precision or by the minimal 4717 * field width for numeric conversions required? */ 4718 if (number_of_zeros_to_pad == 0) 4719 { 4720 /* will not copy first part of numeric right now, * 4721 * force it to be copied later in its entirety */ 4722 zero_padding_insertion_ind = 0; 4723 } 4724 else 4725 { 4726 /* insert first part of numerics (sign or '0x') before zero 4727 * padding */ 4728 int zn = (int)zero_padding_insertion_ind; 4729 4730 if (zn > 0) 4731 { 4732 if (str_l < str_m) 4733 { 4734 size_t avail = str_m - str_l; 4735 4736 mch_memmove(str + str_l, str_arg, 4737 (size_t)zn > avail ? avail 4738 : (size_t)zn); 4739 } 4740 str_l += zn; 4741 } 4742 4743 /* insert zero padding as requested by the precision or min 4744 * field width */ 4745 zn = (int)number_of_zeros_to_pad; 4746 if (zn > 0) 4747 { 4748 if (str_l < str_m) 4749 { 4750 size_t avail = str_m-str_l; 4751 4752 vim_memset(str + str_l, '0', 4753 (size_t)zn > avail ? avail 4754 : (size_t)zn); 4755 } 4756 str_l += zn; 4757 } 4758 } 4759 4760 /* insert formatted string 4761 * (or as-is conversion specifier for unknown conversions) */ 4762 { 4763 int sn = (int)(str_arg_l - zero_padding_insertion_ind); 4764 4765 if (sn > 0) 4766 { 4767 if (str_l < str_m) 4768 { 4769 size_t avail = str_m - str_l; 4770 4771 mch_memmove(str + str_l, 4772 str_arg + zero_padding_insertion_ind, 4773 (size_t)sn > avail ? avail : (size_t)sn); 4774 } 4775 str_l += sn; 4776 } 4777 } 4778 4779 /* insert right padding */ 4780 if (justify_left) 4781 { 4782 /* right blank padding to the field width */ 4783 int pn = (int)(min_field_width 4784 - (str_arg_l + number_of_zeros_to_pad)); 4785 4786 if (pn > 0) 4787 { 4788 if (str_l < str_m) 4789 { 4790 size_t avail = str_m - str_l; 4791 4792 vim_memset(str + str_l, ' ', 4793 (size_t)pn > avail ? avail 4794 : (size_t)pn); 4795 } 4796 str_l += pn; 4797 } 4798 } 4799 } 4800 } 4801 4802 if (str_m > 0) 4803 { 4804 /* make sure the string is nul-terminated even at the expense of 4805 * overwriting the last character (shouldn't happen, but just in case) 4806 * */ 4807 str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0'; 4808 } 4809 4810 #ifdef HAVE_STDARG_H 4811 if (tvs != NULL && tvs[arg_idx - 1].v_type != VAR_UNKNOWN) 4812 EMSG(_("E767: Too many arguments to printf()")); 4813 #endif 4814 4815 /* Return the number of characters formatted (excluding trailing nul 4816 * character), that is, the number of characters that would have been 4817 * written to the buffer if it were large enough. */ 4818 return (int)str_l; 4819 } 4820 4821 #endif /* PROTO */ 4822