www.pudn.com > vim53src.zip > message.c


/* vi:set ts=8 sts=4 sw=4: 
 * 
 * VIM - Vi IMproved	by Bram Moolenaar 
 * 
 * Do ":help uganda"  in Vim to read copying and usage conditions. 
 * Do ":help credits" in Vim to see a list of people who contributed. 
 */ 
 
/* 
 * message.c: functions for displaying messages on the command line 
 */ 
 
#define MESSAGE_FILE		/* don't include prototype for smsg() */ 
 
#include "vim.h" 
 
#ifdef __QNX__ 
# include  
#endif 
 
static void msg_home_replace_attr __ARGS((char_u *fname, int attr)); 
static int  msg_use_printf __ARGS((void)); 
static void msg_screen_putchar __ARGS((int c, int attr)); 
static int  msg_check_screen __ARGS((void)); 
static void redir_write __ARGS((char_u *s)); 
#ifdef CON_DIALOG 
static char_u *msg_show_console_dialog __ARGS((char_u *message, char_u *buttons, int dfltbutton)); 
#endif 
 
/* 
 * msg(s) - displays the string 's' on the status line 
 * When terminal not initialized (yet) mch_errmsg(..) is used. 
 * return TRUE if wait_return not called 
 */ 
    int 
msg(s) 
    char_u	    *s; 
{ 
    return msg_attr(s, 0); 
} 
 
    int 
msg_attr(s, attr) 
    char_u	   *s; 
    int		    attr; 
{ 
    static int	    entered = 0; 
    int		    retval; 
 
    /* 
     * It is possible that displaying a messages causes a problem (e.g., 
     * when redrawing the window), which causes another message, etc..	To 
     * break this loop, limit the recursiveness to 3 levels. 
     */ 
    if (entered >= 3) 
	return TRUE; 
    ++entered; 
 
    msg_start(); 
    msg_outtrans_attr(s, attr); 
    msg_clr_eos(); 
    retval = msg_end(); 
 
    --entered; 
    return retval; 
} 
 
/* 
 * automatic prototype generation does not understand this function 
 */ 
#ifndef PROTO 
# ifndef __QNX__ 
 
int 
#ifdef __BORLANDC__ 
_RTLENTRYF 
#endif 
smsg __ARGS((char_u *, long, long, long, 
			long, long, long, long, long, long, long)); 
int 
#ifdef __BORLANDC__ 
_RTLENTRYF 
#endif 
smsg_attr __ARGS((int, char_u *, long, long, long, 
			long, long, long, long, long, long, long)); 
 
/* VARARGS */ 
    int 
#ifdef __BORLANDC__ 
_RTLENTRYF 
#endif 
smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) 
    char_u	*s; 
    long	a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; 
{ 
    return smsg_attr(0, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); 
} 
 
/* VARARGS */ 
    int 
#ifdef __BORLANDC__ 
_RTLENTRYF 
#endif 
smsg_attr(attr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) 
    int		attr; 
    char_u	*s; 
    long	a1, a2, a3, a4, a5, a6, a7, a8, a9, a10; 
{ 
    sprintf((char *)IObuff, (char *)s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); 
    return msg_attr(IObuff, attr); 
} 
 
# else /* __QNX__ */ 
 
int smsg(char_u *s, ...) 
{ 
    va_list arglist; 
 
    va_start(arglist, s); 
    vsprintf((char *)IObuff, (char *)s, arglist); 
    va_end(arglist); 
    return msg(IObuff); 
} 
 
int smsg_attr(int attr, char_u *s, ...) 
{ 
    va_list arglist; 
 
    va_start(arglist, s); 
    vsprintf((char *)IObuff, (char *)s, arglist); 
    va_end(arglist); 
    return msg_attr(IObuff, attr); 
} 
 
# endif /* __QNX__ */ 
#endif 
 
/* 
 * emsg() - display an error message 
 * 
 * Rings the bell, if appropriate, and calls message() to do the real work 
 * When terminal not initialized (yet) mch_errmsg(..) is used. 
 * 
 * return TRUE if wait_return not called 
 */ 
    int 
emsg(s) 
    char_u	   *s; 
{ 
    char_u	    *Buf; 
    static int	    last_lnum = 0; 
    static char_u   *last_sourcing_name = NULL; 
    int		    attr; 
    int		    other_sourcing_name; 
 
    if (emsg_off)		/* no error messages at the moment */ 
	return TRUE; 
 
    if (global_busy)		/* break :global command */ 
	++global_busy; 
 
    if (p_eb) 
	beep_flush();		/* also includes flush_buffers() */ 
    else 
	flush_buffers(FALSE);	/* flush internal buffers */ 
    did_emsg = TRUE;		/* flag for DoOneCmd() */ 
    emsg_on_display = TRUE;	/* remember there is an error message */ 
    ++msg_scroll;		/* don't overwrite a previous message */ 
    attr = hl_attr(HLF_E);	/* set highlight mode for error messages */ 
    if (msg_scrolled) 
	need_wait_return = TRUE;    /* needed in case emsg() is called after 
				     * wait_return has reset need_wait_return 
				     * and a redraw is expected because 
				     * msg_scrolled is non-zero */ 
 
/* 
 * First output name and line number of source of error message 
 */ 
    if (sourcing_name != NULL) 
    { 
	if (last_sourcing_name != NULL) 
	    other_sourcing_name = STRCMP(sourcing_name, last_sourcing_name); 
	else 
	    other_sourcing_name = TRUE; 
    } 
    else 
	other_sourcing_name = FALSE; 
 
    if (sourcing_name != NULL 
	    && (other_sourcing_name || sourcing_lnum != last_lnum) 
	    && (Buf = alloc(MAXPATHL + 30)) != NULL) 
    { 
	++no_wait_return; 
	if (other_sourcing_name) 
	{ 
	    sprintf((char *)Buf, "Error detected while processing %s:", 
					    sourcing_name); 
	    msg_attr(Buf, attr); 
	} 
	    /* lnum is 0 when executing a command from the command line 
	     * argument, we don't want a line number then */ 
	if (sourcing_lnum != 0) 
	{ 
	    sprintf((char *)Buf, "line %4ld:", sourcing_lnum); 
	    msg_attr(Buf, hl_attr(HLF_N)); 
	} 
	--no_wait_return; 
	last_lnum = sourcing_lnum;  /* only once for each line */ 
	vim_free(Buf); 
    } 
 
    /* remember the last sourcing name printed, also when it's empty */ 
    if (sourcing_name == NULL || other_sourcing_name) 
    { 
	vim_free(last_sourcing_name); 
	if (sourcing_name == NULL) 
	    last_sourcing_name = NULL; 
	else 
	    last_sourcing_name = vim_strsave(sourcing_name); 
    } 
    msg_nowait = FALSE;			/* wait for this msg */ 
 
#ifdef WANT_EVAL 
    set_internal_string_var((char_u *)"errmsg", s); 
#endif 
    return msg_attr(s, attr); 
} 
 
    int 
emsg2(s, a1) 
    char_u *s, *a1; 
{ 
    if (emsg_off)		/* no error messages at the moment */ 
	return TRUE; 
 
    /* Check for NULL strings (just in case) */ 
    if (a1 == NULL) 
	a1 = (char_u *)"[NULL]"; 
    /* Check for very long strings (can happen with ":help ^A") */ 
    if (STRLEN(s) + STRLEN(a1) >= (size_t)IOSIZE) 
	a1 = (char_u *)"[string too long]"; 
    sprintf((char *)IObuff, (char *)s, (char *)a1); 
    return emsg(IObuff); 
} 
 
    int 
emsgn(s, n) 
    char_u *s; 
    long    n; 
{ 
    if (emsg_off)		/* no error messages at the moment */ 
	return TRUE; 
    sprintf((char *)IObuff, (char *)s, n); 
    return emsg(IObuff); 
} 
 
/* 
 * Like msg(), but truncate to a single line if p_shm contains 't', or when 
 * "force" is TRUE. 
 * Careful: The string may be changed! 
 * Returns a pointer to the printed message, if wait_return() not called. 
 */ 
    char_u * 
msg_trunc_attr(s, force, attr) 
    char_u	*s; 
    int		force; 
    int		attr; 
{ 
    int		n; 
 
    if ((force || shortmess(SHM_TRUNC)) && (n = (int)STRLEN(s) - 
		    (int)(Rows - cmdline_row - 1) * Columns - sc_col + 1) > 0) 
    { 
	s += n; 
	*s = '<'; 
    } 
    if (msg_attr(s, attr)) 
	return s; 
    return NULL; 
} 
 
/* 
 * wait for the user to hit a key (normally a return) 
 * if 'redraw' is TRUE, clear and redraw the screen 
 * if 'redraw' is FALSE, just redraw the screen 
 * if 'redraw' is -1, don't redraw at all 
 */ 
    void 
wait_return(redraw) 
    int	    redraw; 
{ 
    int		    c; 
    int		    oldState; 
    int		    tmpState; 
 
    if (redraw == TRUE) 
	must_redraw = CLEAR; 
 
/* 
 * With the global command (and some others) we only need one return at the 
 * end. Adjust cmdline_row to avoid the next message overwriting the last one. 
 * When inside vgetc(), we can't wait for a typed character at all. 
 */ 
    if (vgetc_busy) 
	return; 
    if (no_wait_return) 
    { 
	need_wait_return = TRUE; 
	if (!exmode_active) 
	    cmdline_row = msg_row; 
	return; 
    } 
 
    redir_off = TRUE;		    /* don't redirect this message */ 
    oldState = State; 
    if (quit_more) 
    { 
	c = CR;			    /* just pretend CR was hit */ 
	quit_more = FALSE; 
	got_int = FALSE; 
    } 
    else if (exmode_active) 
    { 
	MSG_PUTS(" ");	  /* make sure the cursor is on the right line */ 
	c = CR;			    /* no need for a return in ex mode */ 
	got_int = FALSE; 
    } 
    else 
    { 
	State = HITRETURN; 
#ifdef USE_MOUSE 
	setmouse(); 
#endif 
	if (msg_didout)		    /* start on a new line */ 
	    msg_putchar('\n'); 
	if (got_int) 
	    MSG_PUTS("Interrupt: "); 
 
#ifdef ORG_HITRETURN 
	MSG_PUTS_ATTR("Press RETURN to continue", hl_attr(HLF_R)); 
	do { 
	    c = vgetc(); 
	} while (vim_strchr((char_u *)"\r\n: ", c) == NULL); 
	if (c == ':')			/* this can vi too (but not always!) */ 
	    stuffcharReadbuff(c); 
#else 
	MSG_PUTS_ATTR("Press RETURN or enter command to continue", 
							      hl_attr(HLF_R)); 
	if (!msg_use_printf()) 
	    msg_clr_eos(); 
	do 
	{ 
	    c = vgetc(); 
	    if (!global_busy) 
		got_int = FALSE; 
	} while (c == Ctrl('C') 
#ifdef USE_GUI 
				|| c == K_SCROLLBAR || c == K_HORIZ_SCROLLBAR 
#endif 
#ifdef USE_MOUSE 
				|| c == K_LEFTDRAG   || c == K_LEFTRELEASE 
				|| c == K_MIDDLEDRAG || c == K_MIDDLERELEASE 
				|| c == K_RIGHTDRAG  || c == K_RIGHTRELEASE 
				|| c == K_IGNORE     || 
				(!mouse_has(MOUSE_RETURN) && 
				     (c == K_LEFTMOUSE || 
				      c == K_MIDDLEMOUSE || 
				      c == K_RIGHTMOUSE)) 
#endif 
				); 
	ui_breakcheck(); 
#ifdef USE_MOUSE 
	/* 
	 * Avoid that the mouse-up event causes visual mode to start. 
	 */ 
	if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE) 
	    jump_to_mouse(MOUSE_SETPOS, NULL); 
	else 
#endif 
	    if (vim_strchr((char_u *)"\r\n ", c) == NULL) 
	{ 
	    stuffcharReadbuff(c); 
	    do_redraw = TRUE;	    /* need a redraw even though there is 
				       something in the stuff buffer */ 
	} 
#endif 
    } 
    redir_off = FALSE; 
 
    /* 
     * If the user hits ':', '?' or '/' we get a command line from the next 
     * line. 
     */ 
    if (c == ':' || c == '?' || c == '/') 
    { 
	if (!exmode_active) 
	    cmdline_row = msg_row; 
	skip_redraw = TRUE;	    /* skip redraw once */ 
	do_redraw = FALSE; 
    } 
 
/* 
 * If the window size changed set_winsize() will redraw the screen. 
 * Otherwise the screen is only redrawn if 'redraw' is set and no ':' typed. 
 */ 
    tmpState = State; 
    State = oldState;		    /* restore State before set_winsize */ 
#ifdef USE_MOUSE 
    setmouse(); 
#endif 
    msg_check(); 
 
    /* 
     * When switching screens, we need to output an extra newline on exit. 
     */ 
#ifdef UNIX 
    if (swapping_screen() && !termcap_active) 
	newline_on_exit = TRUE; 
#endif 
 
    need_wait_return = FALSE; 
    emsg_on_display = FALSE;	/* can delete error message now */ 
    msg_didany = FALSE;		/* reset lines_left at next msg_start() */ 
    lines_left = -1; 
    if (keep_msg != NULL && linetabsize(keep_msg) >= 
				  (Rows - cmdline_row - 1) * Columns + sc_col) 
	keep_msg = NULL;	    /* don't redisplay message, it's too long */ 
 
    if (tmpState == SETWSIZE)	    /* got resize event while in vgetc() */ 
    { 
	starttermcap();		    /* start termcap before redrawing */ 
	set_winsize(0, 0, FALSE); 
    } 
    else if (!skip_redraw && (redraw == TRUE || (msg_scrolled && redraw != -1))) 
    { 
	starttermcap();		    /* start termcap before redrawing */ 
	update_screen(VALID); 
    } 
 
    dont_wait_return = TRUE;	    /* don't wait again in main() */ 
} 
 
/* 
 * Prepare for outputting characters in the command line. 
 */ 
    void 
msg_start() 
{ 
    int		did_return = FALSE; 
 
    keep_msg = NULL;			    /* don't display old message now */ 
    if (!msg_scroll && full_screen)	    /* overwrite last message */ 
    { 
	msg_row = cmdline_row; 
	msg_col = 0; 
    } 
    else if (msg_didout)		    /* start message on next line */ 
    { 
	msg_putchar('\n'); 
	did_return = TRUE; 
	if (!exmode_active) 
	    cmdline_row = msg_row; 
    } 
    if (!msg_didany) 
	lines_left = cmdline_row; 
    msg_didout = FALSE;			    /* no output on current line yet */ 
    cursor_off(); 
 
    /* when redirecting, may need to start a new line. */ 
    if (!did_return) 
	redir_write((char_u *)"\n"); 
} 
 
    void 
msg_putchar(c) 
    int		c; 
{ 
    msg_putchar_attr(c, 0); 
} 
 
    void 
msg_putchar_attr(c, attr) 
    int		c; 
    int		attr; 
{ 
    char_u	buf[4]; 
 
    if (IS_SPECIAL(c)) 
    { 
	buf[0] = K_SPECIAL; 
	buf[1] = K_SECOND(c); 
	buf[2] = K_THIRD(c); 
	buf[3] = NUL; 
    } 
    else 
    { 
	buf[0] = c; 
	buf[1] = NUL; 
    } 
    msg_puts_attr(buf, attr); 
} 
 
    void 
msg_outnum(n) 
    long	n; 
{ 
    char_u	buf[20]; 
 
    sprintf((char *)buf, "%ld", n); 
    msg_puts(buf); 
} 
 
    void 
msg_home_replace(fname) 
    char_u	*fname; 
{ 
    msg_home_replace_attr(fname, 0); 
} 
 
    void 
msg_home_replace_hl(fname) 
    char_u	*fname; 
{ 
    msg_home_replace_attr(fname, hl_attr(HLF_D)); 
} 
 
    static void 
msg_home_replace_attr(fname, attr) 
    char_u  *fname; 
    int	    attr; 
{ 
    char_u	*name; 
 
    name = home_replace_save(NULL, fname); 
    if (name != NULL) 
	msg_outtrans_attr(name, attr); 
    vim_free(name); 
} 
 
/* 
 * Output 'len' characters in 'str' (including NULs) with translation 
 * if 'len' is -1, output upto a NUL character. 
 * Use attributes 'attr'. 
 * Return the number of characters it takes on the screen. 
 */ 
    int 
msg_outtrans(str) 
    char_u	    *str; 
{ 
    return msg_outtrans_attr(str, 0); 
} 
 
    int 
msg_outtrans_attr(str, attr) 
    char_u	*str; 
    int		attr; 
{ 
    return msg_outtrans_len_attr(str, (int)STRLEN(str), attr); 
} 
 
    int 
msg_outtrans_len(str, len) 
    char_u	*str; 
    int		len; 
{ 
    return msg_outtrans_len_attr(str, len, 0); 
} 
    int 
msg_outtrans_len_attr(str, len, attr) 
    char_u	*str; 
    int		len; 
    int		attr; 
{ 
    int retval = 0; 
 
    while (--len >= 0) 
    { 
	msg_puts_attr(transchar(*str), attr); 
	retval += charsize(*str); 
	++str; 
    } 
    return retval; 
} 
 
    void 
msg_make(arg) 
    char_u  *arg; 
{ 
    int	    i; 
    static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB"; 
 
    arg = skipwhite(arg); 
    for (i = 5; *arg && i >= 0; --i) 
	if (*arg++ != str[i]) 
	    break; 
    if (i < 0) 
    { 
	msg_putchar('\n'); 
	for (i = 0; rs[i]; ++i) 
	    msg_putchar(rs[i] - 3); 
    } 
} 
 
/* 
 * Output the string 'str' upto a NUL character. 
 * Return the number of characters it takes on the screen. 
 * 
 * If K_SPECIAL is encountered, then it is taken in conjunction with the 
 * following character and shown as ,  etc.  In addition, if 'all' 
 * is TRUE, then any other character which has its 8th bit set is shown as 
 * , where x is the equivalent character without its 8th bit set.	If a 
 * character is displayed in one of these special ways, is also highlighted 
 * (its highlight name is '8' in the p_hl variable). 
 * Otherwise characters are not highlighted. 
 * This function is used to show mappings, where we want to see how to type 
 * the character/string -- webb 
 */ 
    int 
msg_outtrans_special(str, all) 
    char_u	*str; 
    int		all;	/*  etc as well as  etc */ 
{ 
    int	    retval = 0; 
    char_u  *string; 
    int	    c; 
    int	    modifiers; 
    int	    attr; 
 
    attr = hl_attr(HLF_8); 
    for (; *str; ++str) 
    { 
	c = *str; 
	if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) 
	{ 
	    modifiers = 0x0; 
	    if (str[1] == KS_MODIFIER) 
	    { 
		modifiers = str[2]; 
		str += 3; 
		c = *str; 
	    } 
	    if (c == K_SPECIAL) 
	    { 
		c = TO_SPECIAL(str[1], str[2]); 
		str += 2; 
		if (c == K_ZERO)	/* display  as ^@ */ 
		    c = NUL; 
	    } 
	    if (IS_SPECIAL(c) || modifiers)	/* special key */ 
	    { 
		string = get_special_key_name(c, modifiers); 
		msg_puts_attr(string, attr); 
		retval += STRLEN(string); 
		continue; 
	    } 
	} 
	/* output unprintable meta characters, and  */ 
	if (all && (((c & 0x80) && (!vim_isprintc(c) || c == 0xa0)) 
		    || c == ' ')) 
	{ 
	    string = get_special_key_name(c, 0); 
	    msg_puts_attr(string, attr); 
	    retval += STRLEN(string); 
	} 
	else 
	{ 
	    msg_puts(transchar(c)); 
	    retval += charsize(c); 
	} 
    } 
    return retval; 
} 
 
/* 
 * print line for :print or :list command 
 */ 
    void 
msg_prt_line(s) 
    char_u	*s; 
{ 
    int		c; 
    int		col = 0; 
 
    int		n_extra = 0; 
    int		c_extra = 0; 
    char_u	*p_extra = NULL;	    /* init to make SASC shut up */ 
    int		n; 
    int		attr= 0; 
    char_u	*trail = NULL; 
 
    /* find start of trailing whitespace */ 
    if (curwin->w_p_list && lcs_trail) 
    { 
	trail = s + STRLEN(s); 
	while (trail > s && vim_iswhite(trail[-1])) 
	    --trail; 
    } 
 
    /* output a space for an empty line, otherwise the line will be 
     * overwritten */ 
    if (*s == NUL && !curwin->w_p_list) 
	msg_putchar(' '); 
 
    for (;;) 
    { 
	if (n_extra) 
	{ 
	    --n_extra; 
	    if (c_extra) 
		c = c_extra; 
	    else 
		c = *p_extra++; 
	} 
	else 
	{ 
	    attr = 0; 
	    c = *s++; 
	    if (c == TAB && (!curwin->w_p_list || lcs_tab1)) 
	    { 
		/* tab amount depends on current column */ 
		n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1; 
		if (!curwin->w_p_list) 
		{ 
		    c = ' '; 
		    c_extra = ' '; 
		} 
		else 
		{ 
		    c = lcs_tab1; 
		    c_extra = lcs_tab2; 
		    attr = hl_attr(HLF_AT); 
		} 
	    } 
	    else if (c == NUL && curwin->w_p_list && lcs_eol) 
	    { 
		p_extra = (char_u *)""; 
		c_extra = NUL; 
		n_extra = 1; 
		c = lcs_eol; 
		attr = hl_attr(HLF_AT); 
		--s; 
	    } 
	    else if (c != NUL && (n = charsize(c)) > 1) 
	    { 
		n_extra = n - 1; 
		p_extra = transchar(c); 
		c_extra = NUL; 
		c = *p_extra++; 
	    } 
	    else if (c == ' ' && trail != NULL && s > trail) 
	    { 
		c = lcs_trail; 
		attr = hl_attr(HLF_AT); 
	    } 
	} 
 
	if (c == NUL) 
	    break; 
 
	msg_putchar_attr(c, attr); 
	col++; 
    } 
    msg_clr_eos(); 
} 
 
/* 
 * Output a string to the screen at position msg_row, msg_col. 
 * Update msg_row and msg_col for the next message. 
 */ 
    void 
msg_puts(s) 
    char_u	*s; 
{ 
    msg_puts_attr(s, 0); 
} 
 
    void 
msg_puts_title(s) 
    char_u	*s; 
{ 
    msg_puts_attr(s, hl_attr(HLF_T)); 
} 
 
/* 
 * if printing a string will exceed the screen width, print "..." in the 
 * middle. 
 */ 
    void 
msg_puts_long(longstr) 
    char_u	*longstr; 
{ 
    msg_puts_long_len(longstr, (int)strlen((char *)longstr)); 
} 
 
    void 
msg_puts_long_attr(longstr, attr) 
    char_u	*longstr; 
    int		attr; 
{ 
    msg_puts_long_len_attr(longstr, (int)strlen((char *)longstr), attr); 
} 
 
    void 
msg_puts_long_len(longstr, len) 
    char_u	*longstr; 
    int		len; 
{ 
    msg_puts_long_len_attr(longstr, len, 0); 
} 
 
    void 
msg_puts_long_len_attr(longstr, len, attr) 
    char_u	*longstr; 
    int		len; 
    int		attr; 
{ 
    int		slen = len; 
    int		room; 
 
    room = Columns - msg_col; 
    if (len > room && room >= 20) 
    { 
	slen = (room - 3) / 2; 
	msg_outtrans_len_attr(longstr, slen, attr); 
	msg_puts_attr((char_u *)"...", hl_attr(HLF_AT)); 
    } 
    msg_outtrans_len_attr(longstr + len - slen, slen, attr); 
} 
 
    void 
msg_puts_attr(s, attr) 
    char_u	*s; 
    int		attr; 
{ 
    int		oldState; 
    char_u	buf[20]; 
    char_u	*p; 
 
    dont_wait_return = FALSE;	/* may call wait_return() in main() */ 
 
    /* 
     * If redirection is on, also write to the redirection file. 
     */ 
    redir_write(s); 
 
    /* 
     * If there is no valid screen, use fprintf so we can see error messages. 
     * If termcap is not active, we may be writing in an alternate console 
     * window, cursor positioning may not work correctly (window size may be 
     * different, e.g. for WIN32 console) or we just don't know where the 
     * cursor is. 
     */ 
    if (msg_use_printf()) 
    { 
#ifdef WIN32 
	if (!silent_mode) 
	    mch_settmode(TMODE_COOK);	/* handle '\r' and '\n' correctly */ 
#endif 
	while (*s) 
	{ 
	    if (!silent_mode) 
	    { 
		p = &buf[0]; 
		if (*s == '\n')	/* NL --> CR NL translation (for Unix) */ 
		    *p++ = '\r'; 
		*p++ = *s; 
		*p = '\0'; 
		mch_errmsg((char *)buf); 
	    } 
 
	    /* primitive way to compute the current column */ 
	    if (*s == '\r' || *s == '\n') 
		msg_col = 0; 
	    else 
		++msg_col; 
	    ++s; 
	} 
	msg_didout = TRUE;	    /* assume that line is not empty */ 
 
#ifdef WIN32 
	if (!silent_mode) 
	    mch_settmode(TMODE_RAW); 
#endif 
	return; 
    } 
 
    msg_didany = TRUE;		/* remember that something was outputted */ 
    while (*s) 
    { 
	/* 
	 * The screen is scrolled up when: 
	 * - When outputting a newline in the last row 
	 * - when outputting a character in the last column of the last row 
	 *   (some terminals scroll automatically, some don't. To avoid 
	 *   problems we scroll ourselves) 
	 */ 
	if (msg_row >= Rows - 1 && (*s == '\n' || msg_col >= Columns - 1 || 
			      (*s == TAB && msg_col >= ((Columns - 1) & ~7)))) 
	{ 
	    screen_del_lines(0, 0, 1, (int)Rows, TRUE);	/* always works */ 
	    msg_row = Rows - 2; 
	    if (msg_col >= Columns)	/* can happen after screen resize */ 
		msg_col = Columns - 1; 
	    ++msg_scrolled; 
	    need_wait_return = TRUE;	/* may need wait_return in main() */ 
	    if (cmdline_row > 0 && !exmode_active) 
		--cmdline_row; 
	    /* 
	     * if screen is completely filled wait for a character 
	     */ 
	    if (p_more && --lines_left == 0 && State != HITRETURN && 
							       !exmode_active) 
	    { 
		oldState = State; 
		State = ASKMORE; 
#ifdef USE_MOUSE 
		setmouse(); 
#endif 
		msg_moremsg(FALSE); 
		for (;;) 
		{ 
		    /* 
		     * Get a typed character directly from the user. 
		     * Don't use vgetc(), it syncs undo and eats mapped 
		     * characters.  Disadvantage: Special keys and mouse 
		     * cannot be used here, typeahead is ignored. 
		     */ 
		    out_flush(); 
		    (void)ui_inchar(buf, 20, -1L); 
		    switch (buf[0]) 
		    { 
		    case CR:		/* one extra line */ 
		    case NL: 
			lines_left = 1; 
			break; 
		    case ':':		/* start new command line */ 
			stuffcharReadbuff(':'); 
			cmdline_row = Rows - 1;	    /* put ':' on this line */ 
			skip_redraw = TRUE;	    /* skip redraw once */ 
			dont_wait_return = TRUE;    /* don't wait in main() */ 
			/*FALLTHROUGH*/ 
		    case 'q':		/* quit */ 
		    case Ctrl('C'): 
		    case ESC: 
			got_int = TRUE; 
			quit_more = TRUE; 
			break; 
		    case 'd':		/* Down half a page */ 
			lines_left = Rows / 2; 
			break; 
		    case ' ':		/* one extra page */ 
			lines_left = Rows - 1; 
			break; 
		    default:		/* no valid response */ 
#ifdef UNIX 
			if (buf[0] == intr_char) 
			{ 
			    got_int = TRUE; 
			    quit_more = TRUE; 
			    break; 
			} 
#endif 
			msg_moremsg(TRUE); 
			continue; 
		    } 
		    break; 
		} 
		/* clear the --more-- message */ 
		screen_fill((int)Rows - 1, (int)Rows, 
						0, (int)Columns, ' ', ' ', 0); 
		State = oldState; 
#ifdef USE_MOUSE 
		setmouse(); 
#endif 
		if (quit_more) 
		{ 
		    msg_row = Rows - 1; 
		    msg_col = 0; 
		    return;	    /* the string is not displayed! */ 
		} 
	    } 
	} 
	if (*s == '\n')		    /* go to next line */ 
	{ 
	    msg_didout = FALSE;	    /* remember that line is empty */ 
	    msg_col = 0; 
	    if (++msg_row >= Rows)  /* safety check */ 
		msg_row = Rows - 1; 
	} 
	else if (*s == '\r')	    /* go to column 0 */ 
	{ 
	    msg_col = 0; 
	} 
	else if (*s == '\b')	    /* go to previous char */ 
	{ 
	    if (msg_col) 
		--msg_col; 
	} 
	else if (*s == TAB)	    /* translate into spaces */ 
	{ 
	    do 
		msg_screen_putchar(' ', attr); 
	    while (msg_col & 7); 
	} 
	else 
	    msg_screen_putchar(*s, attr); 
	++s; 
    } 
} 
 
/* 
 * Returns TRUE when messages should be printed to stderr. 
 * This is used when there is no valid screen, so we can see error messages. 
 * If termcap is not active, we may be writing in an alternate console 
 * window, cursor positioning may not work correctly (window size may be 
 * different, e.g. for WIN32 console) or we just don't know where the 
 * cursor is. 
 */ 
    static int 
msg_use_printf() 
{ 
    return (!msg_check_screen() 
#ifdef WIN32 
	    || !termcap_active 
#endif 
	    || (swapping_screen() && !termcap_active) 
	       ); 
} 
 
    static void 
msg_screen_putchar(c, attr) 
    int	    c; 
    int	    attr; 
{ 
    msg_didout = TRUE;	    /* remember that line is not empty */ 
    screen_putchar(c, msg_row, msg_col, attr); 
    if (++msg_col >= Columns) 
    { 
	msg_col = 0; 
	++msg_row; 
    } 
} 
 
    void 
msg_moremsg(full) 
    int	    full; 
{ 
    int	    attr; 
 
    attr = hl_attr(HLF_M); 
    screen_puts((char_u *)"-- More --", (int)Rows - 1, 0, attr); 
    if (full) 
	screen_puts((char_u *) 
		" (RET: line, SPACE: page, d: half page, q: quit)", 
		(int)Rows - 1, 10, attr); 
} 
 
/* 
 * msg_check_screen - check if the screen is initialized. 
 * Also check msg_row and msg_col, if they are too big it may cause a crash. 
 * While starting the GUI the terminal codes will be set for the GUI, but the 
 * output goes to the terminal.  Don't use the terminal codes then. 
 */ 
    static int 
msg_check_screen() 
{ 
    if (!full_screen || !screen_valid(FALSE)) 
	return FALSE; 
 
    if (msg_row >= Rows) 
	msg_row = Rows - 1; 
    if (msg_col >= Columns) 
	msg_col = Columns - 1; 
    return TRUE; 
} 
 
/* 
 * clear from current message position to end of screen 
 * Note: msg_col is not updated, so we remember the end of the message 
 * for msg_check(). 
 */ 
    void 
msg_clr_eos() 
{ 
    if (!msg_check_screen() 
#ifdef WIN32 
	    || !termcap_active 
#endif 
	    || (swapping_screen() && !termcap_active) 
						) 
    { 
	if (full_screen)	/* only when termcap codes are valid */ 
	{ 
	    if (*T_CD) 
		out_str(T_CD);	/* clear to end of display */ 
	    else if (*T_CE) 
		out_str(T_CE);	/* clear to end of line */ 
	} 
    } 
    else 
    { 
	screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns, ' ', ' ', 0); 
	screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0); 
    } 
} 
 
/* 
 * Clear the command line. 
 */ 
    void 
msg_clr_cmdline() 
{ 
    msg_row = cmdline_row; 
    msg_col = 0; 
    msg_clr_eos(); 
} 
 
/* 
 * end putting a message on the screen 
 * call wait_return if the message does not fit in the available space 
 * return TRUE if wait_return not called. 
 */ 
    int 
msg_end() 
{ 
    /* 
     * if the string is larger than the window, 
     * or the ruler option is set and we run into it, 
     * we have to redraw the window. 
     * Do not do this if we are abandoning the file or editing the command line. 
     */ 
    if (!exiting && msg_check() && State != CMDLINE) 
    { 
	wait_return(FALSE); 
	return FALSE; 
    } 
    out_flush(); 
    return TRUE; 
} 
 
/* 
 * If the written message has caused the screen to scroll up, or if we 
 * run into the shown command or ruler, we have to redraw the window later. 
 */ 
    int 
msg_check() 
{ 
    if (msg_scrolled || (msg_row == Rows - 1 && msg_col >= sc_col)) 
    { 
	redraw_all_later(NOT_VALID); 
	redraw_cmdline = TRUE; 
	return TRUE; 
    } 
    return FALSE; 
} 
 
/* 
 * May write a string to the redirection file. 
 */ 
    static void 
redir_write(s) 
    char_u	*s; 
{ 
    static int	    cur_col = 0; 
 
    if (redir_fd != NULL && !redir_off) 
    { 
	/* If the string doesn't start with CR or NL, go to msg_col */ 
	if (*s != '\n' && *s != '\r') 
	{ 
	    while (cur_col < msg_col) 
	    { 
		fputs(" ", redir_fd); 
		++cur_col; 
	    } 
	} 
 
	fputs((char *)s, redir_fd); 
 
	/* Adjust the current column */ 
	while (*s) 
	{ 
	    if (*s == '\r' || *s == '\n') 
		cur_col = 0; 
	    else if (*s == '\t') 
		cur_col += (8 - cur_col % 8); 
	    else 
		++cur_col; 
	    ++s; 
	} 
    } 
} 
 
/* 
 * Give a warning message (for searching). 
 * Use 'w' highlighting and may repeat the message after redrawing 
 */ 
    void 
give_warning(message, hl) 
    char_u  *message; 
    int	    hl; 
{ 
    keep_msg = NULL; 
    if (hl) 
	keep_msg_attr = hl_attr(HLF_W); 
    else 
	keep_msg_attr = 0; 
    if (msg_attr(message, keep_msg_attr) && !msg_scrolled) 
	keep_msg = message; 
    msg_didout = FALSE;	    /* overwrite this message */ 
    msg_nowait = TRUE;	    /* don't wait for this message */ 
    msg_col = 0; 
} 
 
/* 
 * Advance msg cursor to column "col". 
 */ 
    void 
msg_advance(col) 
    int	    col; 
{ 
    if (col >= Columns)		/* not enough room */ 
	col = Columns - 1; 
    while (msg_col < col) 
	msg_putchar(' '); 
} 
 
#if defined(CON_DIALOG) || defined(PROTO) 
/* 
 * Used for "confirm()" function, and the :confirm command prefix. 
 * Versions which haven't got flexible dialogs yet, and console 
 * versions, get this generic handler which uses the command line. 
 * 
 * type  = one of: 
 *	   VIM_QUESTION, VIM_INFO, VIM_WARNING, VIM_ERROR or VIM_GENERIC 
 * title = title string (can be NULL for default) 
 * (neither used in console dialogs at the moment) 
 * 
 * Format of the "buttons" string: 
 * "Button1Name\nButton2Name\nButton3Name" 
 * The first button should normally be the default/accept 
 * The second button should be the 'Cancel' button 
 * Other buttons- use your imagination! 
 * A '&' in a button name becomes a shortcut, so each '&' should be before a 
 * different letter. 
 */ 
/* ARGSUSED */ 
    int 
do_dialog(type, title, message, buttons, dfltbutton) 
    int		type; 
    char_u	*title; 
    char_u	*message; 
    char_u	*buttons; 
    int		dfltbutton; 
{ 
    int		oldState; 
    char_u	buf[20];	/* for getting keystrokes */ 
    int		retval = 0; 
    char_u	*hotkeys; 
 
#ifndef NO_CONSOLE 
    /* Don't output anything in silent mode ("ex -s") */ 
    if (silent_mode) 
	return dfltbutton;   /* return default option */ 
#endif 
 
#ifdef GUI_DIALOG 
    /* When GUI is running, use the GUI dialog */ 
    if (gui.in_use) 
	return gui_mch_dialog(type, title, message, buttons, dfltbutton); 
#endif 
 
    oldState = State; 
    State = CONFIRM; 
#ifdef USE_MOUSE 
    setmouse(); 
#endif 
 
    /* 
     * Since we wait for a keypress, don't make the 
     * user press RETURN as well afterwards. 
     */ 
    ++no_wait_return; 
    hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); 
 
    if (hotkeys != NULL) 
    { 
	for (;;) 
	{ 
	    /* 
	     * Get a typed character directly from the user. 
	     * Don't use vgetc(), it syncs undo and eats mapped 
	     * characters.  Disadvantage: Special keys and mouse 
	     * cannot be used here, typeahead is ignored. 
	     */ 
	    cursor_on(); 
	    out_flush(); 
	    (void)ui_inchar(buf, 20, -1L); 
	    switch (buf[0]) 
	    { 
	    case CR:		/* User accepts default option */ 
	    case NL: 
		retval = dfltbutton; 
		break; 
	    case Ctrl('C'):		/* User aborts/cancels */ 
	    case ESC: 
		retval = 0; 
		break; 
	    default:		/* Could be a hotkey? */ 
#ifdef UNIX 
		if (buf[0] == intr_char) 
		{ 
		    retval = 0;	/* another way of cancelling */ 
		    break; 
		} 
#endif 
		for (retval = 0; hotkeys[retval]; retval++) 
		{ 
		    if (hotkeys[retval] == TO_LOWER(buf[0])) 
			break; 
		} 
		if (hotkeys[retval]) 
		{ 
		    retval++; 
		    break; 
		} 
		/* No hotkey match, so keep waiting */ 
		continue; 
	    } 
	    break; 
	} 
 
	vim_free(hotkeys); 
    } 
 
    State = oldState; 
#ifdef USE_MOUSE 
    setmouse(); 
#endif 
    --no_wait_return; 
    need_wait_return = FALSE; 
    dont_wait_return = TRUE;	    /* don't wait again in main() */ 
 
    return retval; 
} 
 
char_u	*confirm_msg = NULL;	    /* ":confirm" message */ 
 
/* 
 * Format the dialog string, and display it at the bottom of 
 * the screen. Return a string of hotkey chars (if defined) for 
 * each 'button'. If a button has no hotkey defined, the string 
 * has the buttons first letter. 
 * 
 * Returns allocated array, or NULL for error. 
 * 
 */ 
    static char_u * 
msg_show_console_dialog(message, buttons, dfltbutton) 
    char_u	*message; 
    char_u	*buttons; 
    int		dfltbutton; 
{ 
    int		len = 0; 
    int		lenhotkey = 1;	/*first button*/ 
    char_u	*hotk; 
    char_u	*p; 
    char_u	*q; 
    char_u	*r; 
 
    /* 
     * First compute how long a string we need to allocate for the message. 
     */ 
    r = buttons; 
    while (*r) 
    { 
	if (*r == DLG_BUTTON_SEP) 
	{ 
	    len++;	    /* '\n' -> ', ' */ 
	    lenhotkey++;    /* each button needs a hotkey */ 
	} 
	else if (*r == DLG_HOTKEY_CHAR) 
	{ 
	    len++;	    /* '&a' -> '[a]' */ 
	} 
	r++; 
    } 
 
    len += STRLEN(message) 
	    + 2			/* for the NL's */ 
	    + STRLEN(buttons) 
	    + 3;		/* for the ": " and NUL */ 
 
    lenhotkey++;		/* for the NUL */ 
 
    /* 
     * Now allocate and load the strings 
     */ 
    vim_free(confirm_msg); 
    confirm_msg = alloc(len); 
    if (confirm_msg == NULL) 
	return NULL; 
    *confirm_msg = NUL; 
    hotk = alloc(lenhotkey); 
    if (hotk == NULL) 
	return NULL; 
 
    *confirm_msg = '\n'; 
    STRCPY(confirm_msg + 1, message); 
 
    p = confirm_msg + 1 + STRLEN(message); 
    q = hotk; 
    r = buttons; 
    *q = (char_u)TO_LOWER(*r);	/* define lowercase hotkey */ 
 
    *p++ = '\n'; 
 
    while (*r) 
    { 
	if (*r == DLG_BUTTON_SEP) 
	{ 
	    *p++ = ','; 
	    *p++ = ' ';	    /* '\n' -> ', ' */ 
	    *(++q) = (char_u)TO_LOWER(*(r + 1)); /* next hotkey */ 
	    if (dfltbutton) 
		--dfltbutton; 
	} 
	else if (*r == DLG_HOTKEY_CHAR) 
	{ 
	    r++; 
	    if (*r == DLG_HOTKEY_CHAR)		/* duplicate magic = literal */ 
		*p++ = *r; 
	    else 
	    { 
		/* '&a' -> '[a]' */ 
		*p++ = (dfltbutton == 1) ? '[' : '('; 
		*p++ = *r; 
		*p++ = (dfltbutton == 1) ? ']' : ')'; 
		*q = (char_u)TO_LOWER(*r);	/* define lowercase hotkey */ 
	    } 
	} 
	else 
	{ 
	    *p++ = *r;	    /* everything else copy literally */ 
	} 
	r++; 
    } 
    *p++ = ':'; 
    *p++ = ' '; 
    *p = NUL; 
    *(++q) = NUL; 
 
    display_confirm_msg(); 
    return hotk; 
} 
 
/* 
 * Display the ":confirm" message.  Also called when screen resized. 
 */ 
    void 
display_confirm_msg() 
{ 
    if (confirm_msg != NULL) 
	msg_puts_attr(confirm_msg, hl_attr(HLF_M)); 
} 
 
#endif /* CON_DIALOG */ 
 
#if defined(CON_DIALOG) || defined(GUI_DIALOG) 
 
/* 
 * Various stock dialogs used throughout Vim when :confirm is used. 
 */ 
#if 0	/* not used yet */ 
    void 
vim_dialog_ok(type, title, message) 
    int		type; 
    char_u	*title; 
    char_u	*message; 
{ 
    (void)do_dialog(type, 
			  title == NULL ? (char_u *)"Information" : title, 
			  message, 
			  (char_u *)"&OK", 1); 
} 
#endif 
 
#if 0	/* not used yet */ 
    int 
vim_dialog_okcancel(type, title, message, dflt) 
    int		type; 
    char_u	*title; 
    char_u	*message; 
    int		dflt; 
{ 
    if (do_dialog(type, 
		title == NULL ? (char_u *)"Confirmation" : title, 
		message, 
		(char_u *)"&OK\n&Cancel", dflt) == 1) 
	return VIM_OK; 
    return VIM_CANCEL; 
} 
#endif 
 
    int 
vim_dialog_yesno(type, title, message, dflt) 
    int		type; 
    char_u	*title; 
    char_u	*message; 
    int		dflt; 
{ 
    if (do_dialog(type, 
		title == NULL ? (char_u *)"Question" : title, 
		message, 
		(char_u *)"&Yes\n&No", dflt) == 1) 
	return VIM_YES; 
    return VIM_NO; 
} 
 
    int 
vim_dialog_yesnocancel(type, title, message, dflt) 
    int		type; 
    char_u	*title; 
    char_u	*message; 
    int		dflt; 
{ 
    switch (do_dialog(type, 
		title == NULL ? (char_u *)"Question" : title, 
		message, 
		(char_u *)"&Yes\n&No\n&Cancel", dflt)) 
    { 
	case 1: return VIM_YES; 
	case 2: return VIM_NO; 
    } 
    return VIM_CANCEL; 
} 
 
    int 
vim_dialog_yesnoallcancel(type, title, message, dflt) 
    int		type; 
    char_u	*title; 
    char_u	*message; 
    int		dflt; 
{ 
    switch (do_dialog(type, 
		title == NULL ? (char_u *)"Question" : title, 
		message, 
		(char_u *)"&Yes\n&No\nSave &All\n&Discard All\n&Cancel", dflt)) 
    { 
	case 1: return VIM_YES; 
	case 2: return VIM_NO; 
	case 3: return VIM_ALL; 
	case 4: return VIM_DISCARDALL; 
    } 
    return VIM_CANCEL; 
} 
 
#endif /* GUI_DIALOG || CON_DIALOG */ 
 
#if defined(USE_BROWSE) || defined(PROTO) 
/* 
 * Generic browse function.  Calls gui_mch_browse() when possible. 
 * Later this may pop-up a non-GUI file selector (external command?). 
 */ 
    char_u * 
do_browse(saving, title, dflt, ext, initdir, filter, buf) 
    int		saving;		/* write action */ 
    char_u	*title;		/* title for the window */ 
    char_u	*dflt;		/* default file name */ 
    char_u	*ext;		/* extension added */ 
    char_u	*initdir;	/* initial directory, NULL for current dir */ 
    char_u	*filter;	/* file name filter */ 
    BUF		*buf;		/* buffer to read/write for */ 
{ 
    char_u		*fname; 
    static char_u	*last_dir = NULL;    /* last used directory */ 
    char_u		*tofree = NULL; 
 
 
    /* Must turn off browse straight away, or :so autocommands will get the 
     * flag too!  */ 
    browse = FALSE; 
 
    if (title == NULL) 
    { 
	if (saving) 
	    title = (char_u *)"Save File dialog"; 
	else 
	    title = (char_u *)"Open File dialog"; 
    } 
 
    /* When no directory specified, use buffer dir, last dir or current dir */ 
    if (initdir == NULL || *initdir == NUL) 
    { 
	/* When saving or 'browsedir' is "buffer", use buffer fname */ 
	if ((saving || *p_bsdir == 'b') && buf != NULL && buf->b_ffname != NULL) 
	{ 
	    dflt = gettail(curbuf->b_ffname); 
	    tofree = vim_strsave(curbuf->b_ffname); 
	    if (tofree != NULL) 
	    { 
		initdir = tofree; 
		*gettail(initdir) = NUL; 
	    } 
	} 
	/* When 'browsedir' is "last", use dir from last browse */ 
	else if (*p_bsdir == 'l') 
	    initdir = last_dir; 
	/* When 'browsedir is "current", use current directory.  This is the 
	 * default already, leave initdir empty. */ 
    } 
 
# ifdef USE_GUI 
    if (gui.in_use)		/* when this changes, also adjust f_has()! */ 
    { 
	fname = gui_mch_browse(saving, title, dflt, ext, initdir, filter); 
    } 
    else 
# endif 
    { 
	/* TODO: non-GUI file selector here */ 
	fname = NULL; 
    } 
 
    /* keep the directory for next time */ 
    if (fname != NULL) 
    { 
	vim_free(last_dir); 
	last_dir = vim_strsave(fname); 
	if (last_dir != NULL) 
	{ 
	    *gettail(last_dir) = NUL; 
	    if (*last_dir == NUL) 
	    { 
		/* filename only returned, must be in current dir*/ 
		vim_free(last_dir); 
		last_dir = alloc(MAXPATHL); 
		if (last_dir != NULL) 
		    mch_dirname(last_dir, MAXPATHL); 
	    } 
	} 
    } 
 
    vim_free(tofree); 
 
    return fname; 
} 
#endif