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