blob: ff796498973eef28aa59567425ef42dbe9ca7e0f [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* 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#ifdef HAVE_STDARG_H
19# include <stdarg.h>
20#endif
21
22static void reset_last_sourcing __ARGS((void));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +000023static int other_sourcing_name __ARGS((void));
24static char_u *get_emsg_source __ARGS((void));
25static char_u *get_emsg_lnum __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +000026static void add_msg_hist __ARGS((char_u *s, int len, int attr));
27static void hit_return_msg __ARGS((void));
28static void msg_home_replace_attr __ARGS((char_u *fname, int attr));
29#ifdef FEAT_MBYTE
30static char_u *screen_puts_mbyte __ARGS((char_u *s, int l, int attr));
31#endif
32static void msg_puts_attr_len __ARGS((char_u *str, int maxlen, int attr));
33static void t_puts __ARGS((int t_col, char_u *t_s, char_u *s, int attr));
34static void msg_screen_putchar __ARGS((int c, int attr));
35static int msg_check_screen __ARGS((void));
36static void redir_write __ARGS((char_u *s, int maxlen));
Bram Moolenaar8b044b32005-05-31 22:05:58 +000037static void verbose_write __ARGS((char_u *s, int maxlen));
Bram Moolenaar071d4272004-06-13 20:20:40 +000038#ifdef FEAT_CON_DIALOG
39static char_u *msg_show_console_dialog __ARGS((char_u *message, char_u *buttons, int dfltbutton));
40static int confirm_msg_used = FALSE; /* displaying confirm_msg */
41static char_u *confirm_msg = NULL; /* ":confirm" message */
42static char_u *confirm_msg_tail; /* tail of confirm_msg */
43#endif
44
45struct msg_hist
46{
47 struct msg_hist *next;
48 char_u *msg;
49 int attr;
50};
51
52static struct msg_hist *first_msg_hist = NULL;
53static struct msg_hist *last_msg_hist = NULL;
54static int msg_hist_len = 0;
55static int msg_hist_off = FALSE; /* don't add messages to history */
56
57/*
58 * When writing messages to the screen, there are many different situations.
59 * A number of variables is used to remember the current state:
60 * msg_didany TRUE when messages were written since the last time the
61 * user reacted to a prompt.
62 * Reset: After hitting a key for the hit-return prompt,
63 * hitting <CR> for the command line or input().
64 * Set: When any message is written to the screen.
65 * msg_didout TRUE when something was written to the current line.
66 * Reset: When advancing to the next line, when the current
67 * text can be overwritten.
68 * Set: When any message is written to the screen.
69 * msg_nowait No extra delay for the last drawn message.
70 * Used in normal_cmd() before the mode message is drawn.
71 * emsg_on_display There was an error message recently. Indicates that there
72 * should be a delay before redrawing.
73 * msg_scroll The next message should not overwrite the current one.
74 * msg_scrolled How many lines the screen has been scrolled (because of
75 * messages). Used in update_screen() to scroll the screen
76 * back. Incremented each time the screen scrolls a line.
77 * msg_scrolled_ign TRUE when msg_scrolled is non-zero and msg_puts_attr()
78 * writes something without scrolling should not make
79 * need_wait_return to be set. This is a hack to make ":ts"
80 * work without an extra prompt.
81 * lines_left Number of lines available for messages before the
82 * more-prompt is to be given.
83 * need_wait_return TRUE when the hit-return prompt is needed.
84 * Reset: After giving the hit-return prompt, when the user
85 * has answered some other prompt.
86 * Set: When the ruler or typeahead display is overwritten,
87 * scrolling the screen for some message.
88 * keep_msg Message to be displayed after redrawing the screen, in
89 * main_loop().
90 * This is an allocated string or NULL when not used.
91 */
92
93/*
94 * msg(s) - displays the string 's' on the status line
95 * When terminal not initialized (yet) mch_errmsg(..) is used.
96 * return TRUE if wait_return not called
97 */
98 int
99msg(s)
100 char_u *s;
101{
102 return msg_attr_keep(s, 0, FALSE);
103}
104
Bram Moolenaar8b044b32005-05-31 22:05:58 +0000105#if defined(FEAT_EVAL) || defined(FEAT_X11) || defined(USE_XSMP) \
106 || defined(PROTO)
107/*
108 * Like msg() but keep it silent when 'verbosefile' is set.
109 */
110 int
111verb_msg(s)
112 char_u *s;
113{
114 int n;
115
116 verbose_enter();
117 n = msg_attr_keep(s, 0, FALSE);
118 verbose_leave();
119
120 return n;
121}
122#endif
123
Bram Moolenaar071d4272004-06-13 20:20:40 +0000124 int
125msg_attr(s, attr)
126 char_u *s;
127 int attr;
128{
129 return msg_attr_keep(s, attr, FALSE);
130}
131
132 int
133msg_attr_keep(s, attr, keep)
134 char_u *s;
135 int attr;
136 int keep; /* TRUE: set keep_msg if it doesn't scroll */
137{
138 static int entered = 0;
139 int retval;
140 char_u *buf = NULL;
141
142#ifdef FEAT_EVAL
143 if (attr == 0)
144 set_vim_var_string(VV_STATUSMSG, s, -1);
145#endif
146
147 /*
148 * It is possible that displaying a messages causes a problem (e.g.,
149 * when redrawing the window), which causes another message, etc.. To
150 * break this loop, limit the recursiveness to 3 levels.
151 */
152 if (entered >= 3)
153 return TRUE;
154 ++entered;
155
156 /* Add message to history (unless it's a repeated kept message or a
157 * truncated message) */
158 if (s != keep_msg
159 || (*s != '<'
160 && last_msg_hist != NULL
161 && last_msg_hist->msg != NULL
162 && STRCMP(s, last_msg_hist->msg)))
163 add_msg_hist(s, -1, attr);
164
165 /* When displaying keep_msg, don't let msg_start() free it, caller must do
166 * that. */
167 if (s == keep_msg)
168 keep_msg = NULL;
169
170 /* Truncate the message if needed. */
171 buf = msg_strtrunc(s);
172 if (buf != NULL)
173 s = buf;
174
175 msg_start();
176 msg_outtrans_attr(s, attr);
177 msg_clr_eos();
178 retval = msg_end();
179
180 if (keep && retval && vim_strsize(s) < (int)(Rows - cmdline_row - 1)
181 * Columns + sc_col)
182 {
183 set_keep_msg(s);
184 keep_msg_attr = 0;
185 }
186
187 vim_free(buf);
188 --entered;
189 return retval;
190}
191
192/*
193 * Truncate a string such that it can be printed without causing a scroll.
194 * Returns an allocated string or NULL when no truncating is done.
195 */
196 char_u *
197msg_strtrunc(s)
198 char_u *s;
199{
200 char_u *buf = NULL;
201 int len;
202 int room;
203
204 /* May truncate message to avoid a hit-return prompt */
205 if (!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL)
Bram Moolenaar8b044b32005-05-31 22:05:58 +0000206 && !exmode_active && msg_silent == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207 {
208 len = vim_strsize(s);
209 room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
210 if (len > room && room > 0)
211 {
212#ifdef FEAT_MBYTE
213 if (enc_utf8)
214 /* may have up to 18 bytes per cell (6 per char, up to two
215 * composing chars) */
216 buf = alloc((room + 2) * 18);
217 else if (enc_dbcs == DBCS_JPNU)
218 /* may have up to 2 bytes per cell for euc-jp */
219 buf = alloc((room + 2) * 2);
220 else
221#endif
222 buf = alloc(room + 2);
223 if (buf != NULL)
224 trunc_string(s, buf, room);
225 }
226 }
227 return buf;
228}
229
230/*
231 * Truncate a string "s" to "buf" with cell width "room".
232 * "s" and "buf" may be equal.
233 */
234 void
235trunc_string(s, buf, room)
236 char_u *s;
237 char_u *buf;
238 int room;
239{
240 int half;
241 int len;
242 int e;
243 int i;
244 int n;
245
246 room -= 3;
247 half = room / 2;
248 len = 0;
249
250 /* First part: Start of the string. */
251 for (e = 0; len < half; ++e)
252 {
253 if (s[e] == NUL)
254 {
255 /* text fits without truncating! */
256 buf[e] = NUL;
257 return;
258 }
259 n = ptr2cells(s + e);
260 if (len + n >= half)
261 break;
262 len += n;
263 buf[e] = s[e];
264#ifdef FEAT_MBYTE
265 if (has_mbyte)
266 for (n = (*mb_ptr2len_check)(s + e); --n > 0; )
267 {
268 ++e;
269 buf[e] = s[e];
270 }
271#endif
272 }
273
274 /* Last part: End of the string. */
275 i = e;
276#ifdef FEAT_MBYTE
277 if (enc_dbcs != 0)
278 {
279 /* For DBCS going backwards in a string is slow, but
280 * computing the cell width isn't too slow: go forward
281 * until the rest fits. */
282 n = vim_strsize(s + i);
283 while (len + n > room)
284 {
285 n -= ptr2cells(s + i);
286 i += (*mb_ptr2len_check)(s + i);
287 }
288 }
289 else if (enc_utf8)
290 {
291 /* For UTF-8 we can go backwards easily. */
292 i = (int)STRLEN(s);
293 for (;;)
294 {
295 half = i - (*mb_head_off)(s, s + i - 1) - 1;
296 n = ptr2cells(s + half);
297 if (len + n > room)
298 break;
299 len += n;
300 i = half;
301 }
302 }
303 else
304#endif
305 {
306 for (i = (int)STRLEN(s); len + (n = ptr2cells(s + i - 1)) <= room; --i)
307 len += n;
308 }
309
310 /* Set the middle and copy the last part. */
311 mch_memmove(buf + e, "...", (size_t)3);
312 mch_memmove(buf + e + 3, s + i, STRLEN(s + i) + 1);
313}
314
315/*
316 * Automatic prototype generation does not understand this function.
317 * Note: Caller of smgs() and smsg_attr() must check the resulting string is
318 * shorter than IOSIZE!!!
319 */
320#ifndef PROTO
321# ifndef HAVE_STDARG_H
322
323int
324#ifdef __BORLANDC__
325_RTLENTRYF
326#endif
327smsg __ARGS((char_u *, long, long, long,
328 long, long, long, long, long, long, long));
329int
330#ifdef __BORLANDC__
331_RTLENTRYF
332#endif
333smsg_attr __ARGS((int, char_u *, long, long, long,
334 long, long, long, long, long, long, long));
335
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000336int vim_snprintf __ARGS((char *, size_t, char *, long, long, long,
337 long, long, long, long, long, long, long));
338
339/*
340 * smsg(str, arg, ...) is like using sprintf(buf, str, arg, ...) and then
341 * calling msg(buf).
342 * The buffer used is IObuff, the message is truncated at IOSIZE.
343 */
344
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345/* VARARGS */
346 int
347#ifdef __BORLANDC__
348_RTLENTRYF
349#endif
350smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
351 char_u *s;
352 long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
353{
354 return smsg_attr(0, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
355}
356
357/* VARARGS */
358 int
359#ifdef __BORLANDC__
360_RTLENTRYF
361#endif
362smsg_attr(attr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
363 int attr;
364 char_u *s;
365 long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
366{
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000367 vim_snprintf((char *)IObuff, IOSIZE, (char *)s,
368 a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369 return msg_attr(IObuff, attr);
370}
371
372# else /* HAVE_STDARG_H */
373
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000374int vim_snprintf(char *str, size_t str_m, char *fmt, ...);
375static int vim_vsnprintf(char *str, size_t str_m, char *fmt, va_list ap);
376
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 int
378#ifdef __BORLANDC__
379_RTLENTRYF
380#endif
381smsg(char_u *s, ...)
382{
383 va_list arglist;
384
385 va_start(arglist, s);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000386 vim_vsnprintf((char *)IObuff, IOSIZE, (char *)s, arglist);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387 va_end(arglist);
388 return msg(IObuff);
389}
390
391 int
392#ifdef __BORLANDC__
393_RTLENTRYF
394#endif
395smsg_attr(int attr, char_u *s, ...)
396{
397 va_list arglist;
398
399 va_start(arglist, s);
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000400 vim_vsnprintf((char *)IObuff, IOSIZE, (char *)s, arglist);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000401 va_end(arglist);
402 return msg_attr(IObuff, attr);
403}
404
405# endif /* HAVE_STDARG_H */
406#endif
407
408/*
409 * Remember the last sourcing name/lnum used in an error message, so that it
410 * isn't printed each time when it didn't change.
411 */
412static int last_sourcing_lnum = 0;
413static char_u *last_sourcing_name = NULL;
414
415/*
416 * Reset the last used sourcing name/lnum. Makes sure it is displayed again
417 * for the next error message;
418 */
419 static void
420reset_last_sourcing()
421{
422 vim_free(last_sourcing_name);
423 last_sourcing_name = NULL;
424 last_sourcing_lnum = 0;
425}
426
427/*
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000428 * Return TRUE if "sourcing_name" differs from "last_sourcing_name".
429 */
430 static int
431other_sourcing_name()
432{
433 if (sourcing_name != NULL)
434 {
435 if (last_sourcing_name != NULL)
436 return STRCMP(sourcing_name, last_sourcing_name) != 0;
437 return TRUE;
438 }
439 return FALSE;
440}
441
442/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443 * Get the message about the source, as used for an error message.
444 * Returns an allocated string with room for one more character.
445 * Returns NULL when no message is to be given.
446 */
447 static char_u *
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000448get_emsg_source()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449{
450 char_u *Buf, *p;
451
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000452 if (sourcing_name != NULL && other_sourcing_name())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000453 {
454 p = (char_u *)_("Error detected while processing %s:");
455 Buf = alloc((unsigned)(STRLEN(sourcing_name) + STRLEN(p)));
456 if (Buf != NULL)
457 sprintf((char *)Buf, (char *)p, sourcing_name);
458 return Buf;
459 }
460 return NULL;
461}
462
463/*
464 * Get the message about the source lnum, as used for an error message.
465 * Returns an allocated string with room for one more character.
466 * Returns NULL when no message is to be given.
467 */
468 static char_u *
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000469get_emsg_lnum()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470{
471 char_u *Buf, *p;
472
473 /* lnum is 0 when executing a command from the command line
474 * argument, we don't want a line number then */
475 if (sourcing_name != NULL
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000476 && (other_sourcing_name() || sourcing_lnum != last_sourcing_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 && sourcing_lnum != 0)
478 {
479 p = (char_u *)_("line %4ld:");
480 Buf = alloc((unsigned)(STRLEN(p) + 20));
481 if (Buf != NULL)
482 sprintf((char *)Buf, (char *)p, (long)sourcing_lnum);
483 return Buf;
484 }
485 return NULL;
486}
487
488/*
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000489 * Display name and line number for the source of an error.
490 * Remember the file name and line number, so that for the next error the info
491 * is only displayed if it changed.
492 */
493 void
494msg_source(attr)
495 int attr;
496{
497 char_u *p;
498
499 ++no_wait_return;
500 p = get_emsg_source();
501 if (p != NULL)
502 {
503 msg_attr(p, attr);
504 vim_free(p);
505 }
506 p = get_emsg_lnum();
507 if (p != NULL)
508 {
509 msg_attr(p, hl_attr(HLF_N));
510 vim_free(p);
511 last_sourcing_lnum = sourcing_lnum; /* only once for each line */
512 }
513
514 /* remember the last sourcing name printed, also when it's empty */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000515 if (sourcing_name == NULL || other_sourcing_name())
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000516 {
517 vim_free(last_sourcing_name);
518 if (sourcing_name == NULL)
519 last_sourcing_name = NULL;
520 else
521 last_sourcing_name = vim_strsave(sourcing_name);
522 }
523 --no_wait_return;
524}
525
526/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527 * emsg() - display an error message
528 *
529 * Rings the bell, if appropriate, and calls message() to do the real work
530 * When terminal not initialized (yet) mch_errmsg(..) is used.
531 *
532 * return TRUE if wait_return not called
533 */
534 int
535emsg(s)
536 char_u *s;
537{
538 int attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539 char_u *p;
540#ifdef FEAT_EVAL
541 int ignore = FALSE;
542 int severe;
543#endif
544
545 called_emsg = TRUE;
Bram Moolenaardf177f62005-02-22 08:39:57 +0000546 ex_exitval = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000547
548 /*
Bram Moolenaar1d817d02005-01-16 21:56:27 +0000549 * If "emsg_severe" is TRUE: When an error exception is to be thrown,
550 * prefer this message over previous messages for the same command.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000551 */
552#ifdef FEAT_EVAL
553 severe = emsg_severe;
554 emsg_severe = FALSE;
555#endif
556
557 /*
558 * If "emsg_off" is set: no error messages at the moment.
559 * If 'debug' is set: do error message anyway, but without side effects.
560 * If "emsg_skip" is set: never do error messages.
561 */
Bram Moolenaardf177f62005-02-22 08:39:57 +0000562 if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000563#ifdef FEAT_EVAL
564 || emsg_skip > 0
565#endif
566 )
567 return TRUE;
568
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 if (!emsg_off)
570 {
571#ifdef FEAT_EVAL
572 /*
573 * Cause a throw of an error exception if appropriate. Don't display
574 * the error message in this case. (If no matching catch clause will
575 * be found, the message will be displayed later on.) "ignore" is set
576 * when the message should be ignored completely (used for the
577 * interrupt message).
578 */
579 if (cause_errthrow(s, severe, &ignore) == TRUE)
580 {
581 if (!ignore)
582 did_emsg = TRUE;
583 return TRUE;
584 }
585
586 /* set "v:errmsg", also when using ":silent! cmd" */
587 set_vim_var_string(VV_ERRMSG, s, -1);
588#endif
589
590 /*
591 * When using ":silent! cmd" ignore error messsages.
592 * But do write it to the redirection file.
593 */
594 if (emsg_silent != 0)
595 {
596 msg_start();
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000597 p = get_emsg_source();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598 if (p != NULL)
599 {
600 STRCAT(p, "\n");
601 redir_write(p, -1);
602 vim_free(p);
603 }
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000604 p = get_emsg_lnum();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000605 if (p != NULL)
606 {
607 STRCAT(p, "\n");
608 redir_write(p, -1);
609 vim_free(p);
610 }
611 redir_write(s, -1);
612 return TRUE;
613 }
614
615 /* Reset msg_silent, an error causes messages to be switched back on. */
616 msg_silent = 0;
617 cmd_silent = FALSE;
618
619 if (global_busy) /* break :global command */
620 ++global_busy;
621
622 if (p_eb)
623 beep_flush(); /* also includes flush_buffers() */
624 else
625 flush_buffers(FALSE); /* flush internal buffers */
626 did_emsg = TRUE; /* flag for DoOneCmd() */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000627 }
628
629 emsg_on_display = TRUE; /* remember there is an error message */
630 ++msg_scroll; /* don't overwrite a previous message */
631 attr = hl_attr(HLF_E); /* set highlight mode for error messages */
632 if (msg_scrolled)
633 need_wait_return = TRUE; /* needed in case emsg() is called after
634 * wait_return has reset need_wait_return
635 * and a redraw is expected because
636 * msg_scrolled is non-zero */
637
638 /*
639 * Display name and line number for the source of the error.
640 */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000641 msg_source(attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642
643 /*
644 * Display the error message itself.
645 */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +0000646 msg_nowait = FALSE; /* wait for this msg */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 return msg_attr(s, attr);
648}
649
650/*
651 * Print an error message with one "%s" and one string argument.
652 */
653 int
654emsg2(s, a1)
655 char_u *s, *a1;
656{
657 return emsg3(s, a1, NULL);
658}
659
660/*
661 * Print an error message with one or two "%s" and one or two string arguments.
662 */
663 int
664emsg3(s, a1, a2)
665 char_u *s, *a1, *a2;
666{
Bram Moolenaardf177f62005-02-22 08:39:57 +0000667 if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000668#ifdef FEAT_EVAL
669 || emsg_skip > 0
670#endif
671 )
672 return TRUE; /* no error messages at the moment */
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000673 vim_snprintf((char *)IObuff, IOSIZE, (char *)s, (char *)a1, (char *)a2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000674 return emsg(IObuff);
675}
676
677/*
678 * Print an error message with one "%ld" and one long int argument.
679 */
680 int
681emsgn(s, n)
682 char_u *s;
683 long n;
684{
Bram Moolenaardf177f62005-02-22 08:39:57 +0000685 if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000686#ifdef FEAT_EVAL
687 || emsg_skip > 0
688#endif
689 )
690 return TRUE; /* no error messages at the moment */
Bram Moolenaar9c13b352005-05-19 20:53:52 +0000691 vim_snprintf((char *)IObuff, IOSIZE, (char *)s, n);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692 return emsg(IObuff);
693}
694
Bram Moolenaardf177f62005-02-22 08:39:57 +0000695 void
696emsg_invreg(name)
697 int name;
698{
699 EMSG2(_("E354: Invalid register name: '%s'"), transchar(name));
700}
701
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702/*
703 * Like msg(), but truncate to a single line if p_shm contains 't', or when
704 * "force" is TRUE. This truncates in another way as for normal messages.
705 * Careful: The string may be changed by msg_may_trunc()!
706 * Returns a pointer to the printed message, if wait_return() not called.
707 */
708 char_u *
709msg_trunc_attr(s, force, attr)
710 char_u *s;
711 int force;
712 int attr;
713{
714 int n;
715
716 /* Add message to history before truncating */
717 add_msg_hist(s, -1, attr);
718
719 s = msg_may_trunc(force, s);
720
721 msg_hist_off = TRUE;
722 n = msg_attr(s, attr);
723 msg_hist_off = FALSE;
724
725 if (n)
726 return s;
727 return NULL;
728}
729
730/*
731 * Check if message "s" should be truncated at the start (for filenames).
732 * Return a pointer to where the truncated message starts.
733 * Note: May change the message by replacing a character with '<'.
734 */
735 char_u *
736msg_may_trunc(force, s)
737 int force;
738 char_u *s;
739{
740 int n;
741 int room;
742
743 room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
744 if ((force || (shortmess(SHM_TRUNC) && !exmode_active))
745 && (n = (int)STRLEN(s) - room) > 0)
746 {
747#ifdef FEAT_MBYTE
748 if (has_mbyte)
749 {
750 int size = vim_strsize(s);
751
752 for (n = 0; size >= room; )
753 {
754 size -= (*mb_ptr2cells)(s + n);
755 n += (*mb_ptr2len_check)(s + n);
756 }
757 --n;
758 }
759#endif
760 s += n;
761 *s = '<';
762 }
763 return s;
764}
765
766 static void
767add_msg_hist(s, len, attr)
768 char_u *s;
769 int len; /* -1 for undetermined length */
770 int attr;
771{
772 struct msg_hist *p;
773
774 if (msg_hist_off || msg_silent != 0)
775 return;
776
777 /* Don't let the message history get too big */
778 while (msg_hist_len > 20)
779 {
780 p = first_msg_hist;
781 first_msg_hist = p->next;
782 vim_free(p->msg);
783 vim_free(p);
784 --msg_hist_len;
785 }
786 /* allocate an entry and add the message at the end of the history */
787 p = (struct msg_hist *)alloc((int)sizeof(struct msg_hist));
788 if (p != NULL)
789 {
790 if (len < 0)
791 len = (int)STRLEN(s);
792 /* remove leading and trailing newlines */
793 while (len > 0 && *s == '\n')
794 {
795 ++s;
796 --len;
797 }
798 while (len > 0 && s[len - 1] == '\n')
799 --len;
800 p->msg = vim_strnsave(s, len);
801 p->next = NULL;
802 p->attr = attr;
803 if (last_msg_hist != NULL)
804 last_msg_hist->next = p;
805 last_msg_hist = p;
806 if (first_msg_hist == NULL)
807 first_msg_hist = last_msg_hist;
808 ++msg_hist_len;
809 }
810}
811
812/*
813 * ":messages" command.
814 */
815/*ARGSUSED*/
816 void
817ex_messages(eap)
818 exarg_T *eap;
819{
820 struct msg_hist *p;
821 char_u *s;
822
823 msg_hist_off = TRUE;
824
825 s = mch_getenv((char_u *)"LANG");
826 if (s != NULL && *s != NUL)
827 msg_attr((char_u *)
828 _("Messages maintainer: Bram Moolenaar <Bram@vim.org>"),
829 hl_attr(HLF_T));
830
831 for (p = first_msg_hist; p != NULL; p = p->next)
832 if (p->msg != NULL)
833 msg_attr(p->msg, p->attr);
834
835 msg_hist_off = FALSE;
836}
837
Bram Moolenaar7171abe2004-10-11 10:06:20 +0000838#if defined(FEAT_CON_DIALOG) || defined(FIND_REPLACE_DIALOG) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000839/*
840 * Call this after prompting the user. This will avoid a hit-return message
841 * and a delay.
842 */
Bram Moolenaar7171abe2004-10-11 10:06:20 +0000843 void
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844msg_end_prompt()
845{
846 need_wait_return = FALSE;
847 emsg_on_display = FALSE;
848 cmdline_row = msg_row;
849 msg_col = 0;
850 msg_clr_eos();
851}
852#endif
853
854/*
855 * wait for the user to hit a key (normally a return)
856 * if 'redraw' is TRUE, clear and redraw the screen
857 * if 'redraw' is FALSE, just redraw the screen
858 * if 'redraw' is -1, don't redraw at all
859 */
860 void
861wait_return(redraw)
862 int redraw;
863{
864 int c;
865 int oldState;
866 int tmpState;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000867 int had_got_int;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000868
869 if (redraw == TRUE)
870 must_redraw = CLEAR;
871
872 /* If using ":silent cmd", don't wait for a return. Also don't set
873 * need_wait_return to do it later. */
874 if (msg_silent != 0)
875 return;
876
877/*
878 * With the global command (and some others) we only need one return at the
879 * end. Adjust cmdline_row to avoid the next message overwriting the last one.
880 * When inside vgetc(), we can't wait for a typed character at all.
881 */
882 if (vgetc_busy)
883 return;
884 if (no_wait_return)
885 {
886 need_wait_return = TRUE;
887 if (!exmode_active)
888 cmdline_row = msg_row;
889 return;
890 }
891
892 redir_off = TRUE; /* don't redirect this message */
893 oldState = State;
894 if (quit_more)
895 {
896 c = CAR; /* just pretend CR was hit */
897 quit_more = FALSE;
898 got_int = FALSE;
899 }
900 else if (exmode_active)
901 {
902 MSG_PUTS(" "); /* make sure the cursor is on the right line */
903 c = CAR; /* no need for a return in ex mode */
904 got_int = FALSE;
905 }
906 else
907 {
908 /* Make sure the hit-return prompt is on screen when 'guioptions' was
909 * just changed. */
910 screenalloc(FALSE);
911
912 State = HITRETURN;
913#ifdef FEAT_MOUSE
914 setmouse();
915#endif
916#ifdef USE_ON_FLY_SCROLL
917 dont_scroll = TRUE; /* disallow scrolling here */
918#endif
919 hit_return_msg();
920
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921 do
922 {
923 /* Remember "got_int", if it is set vgetc() probably returns a
924 * CTRL-C, but we need to loop then. */
925 had_got_int = got_int;
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000926
927 /* Don't do mappings here, we put the character back in the
928 * typeahead buffer. */
929 ++no_mapping;
930 ++allow_keys;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000931 c = safe_vgetc();
Bram Moolenaar4317d9b2005-03-18 20:25:31 +0000932 if (had_got_int && !global_busy)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000933 got_int = FALSE;
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000934 --no_mapping;
935 --allow_keys;
936
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937#ifdef FEAT_CLIPBOARD
938 /* Strange way to allow copying (yanking) a modeless selection at
939 * the hit-enter prompt. Use CTRL-Y, because the same is used in
940 * Cmdline-mode and it's harmless when there is no selection. */
941 if (c == Ctrl_Y && clip_star.state == SELECT_DONE)
942 {
943 clip_copy_modeless_selection(TRUE);
944 c = K_IGNORE;
945 }
946#endif
947 } while ((had_got_int && c == Ctrl_C)
948 || c == K_IGNORE
949#ifdef FEAT_GUI
950 || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR
951#endif
952#ifdef FEAT_MOUSE
953 || c == K_LEFTDRAG || c == K_LEFTRELEASE
954 || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
955 || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
956 || c == K_MOUSEDOWN || c == K_MOUSEUP
957 || (!mouse_has(MOUSE_RETURN)
958 && mouse_row < msg_row
959 && (c == K_LEFTMOUSE
960 || c == K_MIDDLEMOUSE
961 || c == K_RIGHTMOUSE
962 || c == K_X1MOUSE
963 || c == K_X2MOUSE))
964#endif
965 );
966 ui_breakcheck();
967#ifdef FEAT_MOUSE
968 /*
969 * Avoid that the mouse-up event causes visual mode to start.
970 */
971 if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
972 || c == K_X1MOUSE || c == K_X2MOUSE)
973 (void)jump_to_mouse(MOUSE_SETPOS, NULL, 0);
974 else
975#endif
976 if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C)
977 {
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000978 char_u buf[2];
979
980 /* Put the character back in the typeahead buffer. Don't use the
981 * stuff buffer, because lmaps wouldn't work. */
982 buf[0] = c;
983 buf[1] = NUL;
984 ins_typebuf(buf, REMAP_YES, 0, !KeyTyped, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000985 do_redraw = TRUE; /* need a redraw even though there is
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000986 typeahead */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000987 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988 }
989 redir_off = FALSE;
990
991 /*
992 * If the user hits ':', '?' or '/' we get a command line from the next
993 * line.
994 */
995 if (c == ':' || c == '?' || c == '/')
996 {
997 if (!exmode_active)
998 cmdline_row = msg_row;
999 skip_redraw = TRUE; /* skip redraw once */
1000 do_redraw = FALSE;
1001 }
1002
1003 /*
1004 * If the window size changed set_shellsize() will redraw the screen.
1005 * Otherwise the screen is only redrawn if 'redraw' is set and no ':'
1006 * typed.
1007 */
1008 tmpState = State;
1009 State = oldState; /* restore State before set_shellsize */
1010#ifdef FEAT_MOUSE
1011 setmouse();
1012#endif
1013 msg_check();
1014
1015#if defined(UNIX) || defined(VMS)
1016 /*
1017 * When switching screens, we need to output an extra newline on exit.
1018 */
1019 if (swapping_screen() && !termcap_active)
1020 newline_on_exit = TRUE;
1021#endif
1022
1023 need_wait_return = FALSE;
1024 did_wait_return = TRUE;
1025 emsg_on_display = FALSE; /* can delete error message now */
1026 lines_left = -1; /* reset lines_left at next msg_start() */
1027 reset_last_sourcing();
1028 if (keep_msg != NULL && vim_strsize(keep_msg) >=
1029 (Rows - cmdline_row - 1) * Columns + sc_col)
1030 {
1031 vim_free(keep_msg);
1032 keep_msg = NULL; /* don't redisplay message, it's too long */
1033 }
1034
1035 if (tmpState == SETWSIZE) /* got resize event while in vgetc() */
1036 {
1037 starttermcap(); /* start termcap before redrawing */
1038 shell_resized();
1039 }
1040 else if (!skip_redraw
1041 && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1)))
1042 {
1043 starttermcap(); /* start termcap before redrawing */
1044 redraw_later(VALID);
1045 }
1046}
1047
1048/*
1049 * Write the hit-return prompt.
1050 */
1051 static void
1052hit_return_msg()
1053{
1054 if (msg_didout) /* start on a new line */
1055 msg_putchar('\n');
1056 if (got_int)
1057 MSG_PUTS(_("Interrupt: "));
1058
Bram Moolenaar071d4272004-06-13 20:20:40 +00001059 MSG_PUTS_ATTR(_("Hit ENTER or type command to continue"), hl_attr(HLF_R));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001060 if (!msg_use_printf())
1061 msg_clr_eos();
1062}
1063
1064/*
1065 * Set "keep_msg" to "s". Free the old value and check for NULL pointer.
1066 */
1067 void
1068set_keep_msg(s)
1069 char_u *s;
1070{
1071 vim_free(keep_msg);
1072 if (s != NULL && msg_silent == 0)
1073 keep_msg = vim_strsave(s);
1074 else
1075 keep_msg = NULL;
Bram Moolenaaraab21c32005-01-25 21:46:35 +00001076 keep_msg_more = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001077}
1078
1079/*
1080 * Prepare for outputting characters in the command line.
1081 */
1082 void
1083msg_start()
1084{
1085 int did_return = FALSE;
1086
1087 vim_free(keep_msg);
1088 keep_msg = NULL; /* don't display old message now */
1089 if (!msg_scroll && full_screen) /* overwrite last message */
1090 {
1091 msg_row = cmdline_row;
1092 msg_col =
1093#ifdef FEAT_RIGHTLEFT
1094 cmdmsg_rl ? Columns - 1 :
1095#endif
1096 0;
1097 }
1098 else if (msg_didout) /* start message on next line */
1099 {
1100 msg_putchar('\n');
1101 did_return = TRUE;
1102 if (exmode_active != EXMODE_NORMAL)
1103 cmdline_row = msg_row;
1104 }
1105 if (!msg_didany || lines_left < 0)
1106 msg_starthere();
1107 if (msg_silent == 0)
1108 {
1109 msg_didout = FALSE; /* no output on current line yet */
1110 cursor_off();
1111 }
1112
1113 /* when redirecting, may need to start a new line. */
1114 if (!did_return)
1115 redir_write((char_u *)"\n", -1);
1116}
1117
1118/*
1119 * Note that the current msg position is where messages start.
1120 */
1121 void
1122msg_starthere()
1123{
1124 lines_left = cmdline_row;
1125 msg_didany = FALSE;
1126}
1127
1128 void
1129msg_putchar(c)
1130 int c;
1131{
1132 msg_putchar_attr(c, 0);
1133}
1134
1135 void
1136msg_putchar_attr(c, attr)
1137 int c;
1138 int attr;
1139{
1140#ifdef FEAT_MBYTE
1141 char_u buf[MB_MAXBYTES + 1];
1142#else
1143 char_u buf[4];
1144#endif
1145
1146 if (IS_SPECIAL(c))
1147 {
1148 buf[0] = K_SPECIAL;
1149 buf[1] = K_SECOND(c);
1150 buf[2] = K_THIRD(c);
1151 buf[3] = NUL;
1152 }
1153 else
1154 {
1155#ifdef FEAT_MBYTE
1156 buf[(*mb_char2bytes)(c, buf)] = NUL;
1157#else
1158 buf[0] = c;
1159 buf[1] = NUL;
1160#endif
1161 }
1162 msg_puts_attr(buf, attr);
1163}
1164
1165 void
1166msg_outnum(n)
1167 long n;
1168{
1169 char_u buf[20];
1170
1171 sprintf((char *)buf, "%ld", n);
1172 msg_puts(buf);
1173}
1174
1175 void
1176msg_home_replace(fname)
1177 char_u *fname;
1178{
1179 msg_home_replace_attr(fname, 0);
1180}
1181
1182#if defined(FEAT_FIND_ID) || defined(PROTO)
1183 void
1184msg_home_replace_hl(fname)
1185 char_u *fname;
1186{
1187 msg_home_replace_attr(fname, hl_attr(HLF_D));
1188}
1189#endif
1190
1191 static void
1192msg_home_replace_attr(fname, attr)
1193 char_u *fname;
1194 int attr;
1195{
1196 char_u *name;
1197
1198 name = home_replace_save(NULL, fname);
1199 if (name != NULL)
1200 msg_outtrans_attr(name, attr);
1201 vim_free(name);
1202}
1203
1204/*
1205 * Output 'len' characters in 'str' (including NULs) with translation
1206 * if 'len' is -1, output upto a NUL character.
1207 * Use attributes 'attr'.
1208 * Return the number of characters it takes on the screen.
1209 */
1210 int
1211msg_outtrans(str)
1212 char_u *str;
1213{
1214 return msg_outtrans_attr(str, 0);
1215}
1216
1217 int
1218msg_outtrans_attr(str, attr)
1219 char_u *str;
1220 int attr;
1221{
1222 return msg_outtrans_len_attr(str, (int)STRLEN(str), attr);
1223}
1224
1225 int
1226msg_outtrans_len(str, len)
1227 char_u *str;
1228 int len;
1229{
1230 return msg_outtrans_len_attr(str, len, 0);
1231}
1232
1233/*
1234 * Output one character at "p". Return pointer to the next character.
1235 * Handles multi-byte characters.
1236 */
1237 char_u *
1238msg_outtrans_one(p, attr)
1239 char_u *p;
1240 int attr;
1241{
1242#ifdef FEAT_MBYTE
1243 int l;
1244
1245 if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
1246 {
1247 msg_outtrans_len_attr(p, l, attr);
1248 return p + l;
1249 }
1250#endif
1251 msg_puts_attr(transchar_byte(*p), attr);
1252 return p + 1;
1253}
1254
1255 int
1256msg_outtrans_len_attr(msgstr, len, attr)
1257 char_u *msgstr;
1258 int len;
1259 int attr;
1260{
1261 int retval = 0;
1262 char_u *str = msgstr;
1263 char_u *plain_start = msgstr;
1264 char_u *s;
1265#ifdef FEAT_MBYTE
1266 int mb_l;
1267 int c;
1268#endif
1269
1270 /* if MSG_HIST flag set, add message to history */
1271 if (attr & MSG_HIST)
1272 {
1273 add_msg_hist(str, len, attr);
1274 attr &= ~MSG_HIST;
1275 }
1276
1277#ifdef FEAT_MBYTE
1278 /* If the string starts with a composing character first draw a space on
1279 * which the composing char can be drawn. */
1280 if (enc_utf8 && utf_iscomposing(utf_ptr2char(msgstr)))
1281 msg_puts_attr((char_u *)" ", attr);
1282#endif
1283
1284 /*
1285 * Go over the string. Special characters are translated and printed.
1286 * Normal characters are printed several at a time.
1287 */
1288 while (--len >= 0)
1289 {
1290#ifdef FEAT_MBYTE
1291 if (enc_utf8)
1292 /* Don't include composing chars after the end. */
1293 mb_l = utfc_ptr2len_check_len(str, len + 1);
1294 else if (has_mbyte)
1295 mb_l = (*mb_ptr2len_check)(str);
1296 else
1297 mb_l = 1;
1298 if (has_mbyte && mb_l > 1)
1299 {
1300 c = (*mb_ptr2char)(str);
1301 if (vim_isprintc(c))
1302 /* printable multi-byte char: count the cells. */
1303 retval += (*mb_ptr2cells)(str);
1304 else
1305 {
1306 /* unprintable multi-byte char: print the printable chars so
1307 * far and the translation of the unprintable char. */
1308 if (str > plain_start)
1309 msg_puts_attr_len(plain_start, (int)(str - plain_start),
1310 attr);
1311 plain_start = str + mb_l;
1312 msg_puts_attr(transchar(c), attr == 0 ? hl_attr(HLF_8) : attr);
1313 retval += char2cells(c);
1314 }
1315 len -= mb_l - 1;
1316 str += mb_l;
1317 }
1318 else
1319#endif
1320 {
1321 s = transchar_byte(*str);
1322 if (s[1] != NUL)
1323 {
1324 /* unprintable char: print the printable chars so far and the
1325 * translation of the unprintable char. */
1326 if (str > plain_start)
1327 msg_puts_attr_len(plain_start, (int)(str - plain_start),
1328 attr);
1329 plain_start = str + 1;
1330 msg_puts_attr(s, attr == 0 ? hl_attr(HLF_8) : attr);
1331 }
1332 retval += ptr2cells(str);
1333 ++str;
1334 }
1335 }
1336
1337 if (str > plain_start)
1338 /* print the printable chars at the end */
1339 msg_puts_attr_len(plain_start, (int)(str - plain_start), attr);
1340
1341 return retval;
1342}
1343
1344#if defined(FEAT_QUICKFIX) || defined(PROTO)
1345 void
1346msg_make(arg)
1347 char_u *arg;
1348{
1349 int i;
1350 static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB";
1351
1352 arg = skipwhite(arg);
1353 for (i = 5; *arg && i >= 0; --i)
1354 if (*arg++ != str[i])
1355 break;
1356 if (i < 0)
1357 {
1358 msg_putchar('\n');
1359 for (i = 0; rs[i]; ++i)
1360 msg_putchar(rs[i] - 3);
1361 }
1362}
1363#endif
1364
1365/*
1366 * Output the string 'str' upto a NUL character.
1367 * Return the number of characters it takes on the screen.
1368 *
1369 * If K_SPECIAL is encountered, then it is taken in conjunction with the
1370 * following character and shown as <F1>, <S-Up> etc. Any other character
1371 * which is not printable shown in <> form.
1372 * If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>.
1373 * If a character is displayed in one of these special ways, is also
1374 * highlighted (its highlight name is '8' in the p_hl variable).
1375 * Otherwise characters are not highlighted.
1376 * This function is used to show mappings, where we want to see how to type
1377 * the character/string -- webb
1378 */
1379 int
1380msg_outtrans_special(strstart, from)
1381 char_u *strstart;
1382 int from; /* TRUE for lhs of a mapping */
1383{
1384 char_u *str = strstart;
1385 int retval = 0;
1386 char_u *string;
1387 int attr;
1388 int len;
1389
1390 attr = hl_attr(HLF_8);
1391 while (*str != NUL)
1392 {
1393 /* Leading and trailing spaces need to be displayed in <> form. */
1394 if ((str == strstart || str[1] == NUL) && *str == ' ')
1395 {
1396 string = (char_u *)"<Space>";
1397 ++str;
1398 }
1399 else
1400 string = str2special(&str, from);
1401 len = vim_strsize(string);
1402 /* Highlight special keys */
1403 msg_puts_attr(string, len > 1
1404#ifdef FEAT_MBYTE
1405 && (*mb_ptr2len_check)(string) <= 1
1406#endif
1407 ? attr : 0);
1408 retval += len;
1409 }
1410 return retval;
1411}
1412
1413/*
1414 * Return the printable string for the key codes at "*sp".
1415 * Used for translating the lhs or rhs of a mapping to printable chars.
1416 * Advances "sp" to the next code.
1417 */
1418 char_u *
1419str2special(sp, from)
1420 char_u **sp;
1421 int from; /* TRUE for lhs of mapping */
1422{
1423 int c;
1424 static char_u buf[7];
1425 char_u *str = *sp;
1426 int modifiers = 0;
1427 int special = FALSE;
1428
1429#ifdef FEAT_MBYTE
1430 if (has_mbyte)
1431 {
1432 char_u *p;
1433
1434 /* Try to un-escape a multi-byte character. Return the un-escaped
1435 * string if it is a multi-byte character. */
1436 p = mb_unescape(sp);
1437 if (p != NULL)
1438 return p;
1439 }
1440#endif
1441
1442 c = *str;
1443 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1444 {
1445 if (str[1] == KS_MODIFIER)
1446 {
1447 modifiers = str[2];
1448 str += 3;
1449 c = *str;
1450 }
1451 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1452 {
1453 c = TO_SPECIAL(str[1], str[2]);
1454 str += 2;
1455 if (c == K_ZERO) /* display <Nul> as ^@ */
1456 c = NUL;
1457 }
1458 if (IS_SPECIAL(c) || modifiers) /* special key */
1459 special = TRUE;
1460 }
1461 *sp = str + 1;
1462
1463#ifdef FEAT_MBYTE
1464 /* For multi-byte characters check for an illegal byte. */
1465 if (has_mbyte && MB_BYTE2LEN(*str) > (*mb_ptr2len_check)(str))
1466 {
1467 transchar_nonprint(buf, c);
1468 return buf;
1469 }
1470#endif
1471
1472 /* Make unprintable characters in <> form, also <M-Space> and <Tab>.
1473 * Use <Space> only for lhs of a mapping. */
1474 if (special || char2cells(c) > 1 || (from && c == ' '))
1475 return get_special_key_name(c, modifiers);
1476 buf[0] = c;
1477 buf[1] = NUL;
1478 return buf;
1479}
1480
1481/*
1482 * Translate a key sequence into special key names.
1483 */
1484 void
1485str2specialbuf(sp, buf, len)
1486 char_u *sp;
1487 char_u *buf;
1488 int len;
1489{
1490 char_u *s;
1491
1492 *buf = NUL;
1493 while (*sp)
1494 {
1495 s = str2special(&sp, FALSE);
1496 if ((int)(STRLEN(s) + STRLEN(buf)) < len)
1497 STRCAT(buf, s);
1498 }
1499}
1500
1501/*
1502 * print line for :print or :list command
1503 */
1504 void
Bram Moolenaardf177f62005-02-22 08:39:57 +00001505msg_prt_line(s, list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 char_u *s;
Bram Moolenaardf177f62005-02-22 08:39:57 +00001507 int list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508{
1509 int c;
1510 int col = 0;
1511 int n_extra = 0;
1512 int c_extra = 0;
1513 char_u *p_extra = NULL; /* init to make SASC shut up */
1514 int n;
1515 int attr= 0;
1516 char_u *trail = NULL;
1517#ifdef FEAT_MBYTE
1518 int l;
1519 char_u buf[MB_MAXBYTES + 1];
1520#endif
1521
Bram Moolenaardf177f62005-02-22 08:39:57 +00001522 if (curwin->w_p_list)
1523 list = TRUE;
1524
Bram Moolenaar071d4272004-06-13 20:20:40 +00001525 /* find start of trailing whitespace */
Bram Moolenaardf177f62005-02-22 08:39:57 +00001526 if (list && lcs_trail)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 {
1528 trail = s + STRLEN(s);
1529 while (trail > s && vim_iswhite(trail[-1]))
1530 --trail;
1531 }
1532
1533 /* output a space for an empty line, otherwise the line will be
1534 * overwritten */
Bram Moolenaardf177f62005-02-22 08:39:57 +00001535 if (*s == NUL && !(list && lcs_eol != NUL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001536 msg_putchar(' ');
1537
1538 for (;;)
1539 {
1540 if (n_extra)
1541 {
1542 --n_extra;
1543 if (c_extra)
1544 c = c_extra;
1545 else
1546 c = *p_extra++;
1547 }
1548#ifdef FEAT_MBYTE
1549 else if (has_mbyte && (l = (*mb_ptr2len_check)(s)) > 1)
1550 {
1551 col += (*mb_ptr2cells)(s);
1552 mch_memmove(buf, s, (size_t)l);
1553 buf[l] = NUL;
1554 msg_puts_attr(buf, attr);
1555 s += l;
1556 continue;
1557 }
1558#endif
1559 else
1560 {
1561 attr = 0;
1562 c = *s++;
Bram Moolenaardf177f62005-02-22 08:39:57 +00001563 if (c == TAB && (!list || lcs_tab1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001564 {
1565 /* tab amount depends on current column */
1566 n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
Bram Moolenaardf177f62005-02-22 08:39:57 +00001567 if (!list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001568 {
1569 c = ' ';
1570 c_extra = ' ';
1571 }
1572 else
1573 {
1574 c = lcs_tab1;
1575 c_extra = lcs_tab2;
1576 attr = hl_attr(HLF_8);
1577 }
1578 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00001579 else if (c == NUL && list && lcs_eol != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001580 {
1581 p_extra = (char_u *)"";
1582 c_extra = NUL;
1583 n_extra = 1;
1584 c = lcs_eol;
1585 attr = hl_attr(HLF_AT);
1586 --s;
1587 }
1588 else if (c != NUL && (n = byte2cells(c)) > 1)
1589 {
1590 n_extra = n - 1;
1591 p_extra = transchar_byte(c);
1592 c_extra = NUL;
1593 c = *p_extra++;
1594 }
1595 else if (c == ' ' && trail != NULL && s > trail)
1596 {
1597 c = lcs_trail;
1598 attr = hl_attr(HLF_8);
1599 }
1600 }
1601
1602 if (c == NUL)
1603 break;
1604
1605 msg_putchar_attr(c, attr);
1606 col++;
1607 }
1608 msg_clr_eos();
1609}
1610
1611#ifdef FEAT_MBYTE
1612/*
1613 * Use screen_puts() to output one multi-byte character.
1614 * Return the pointer "s" advanced to the next character.
1615 */
1616 static char_u *
1617screen_puts_mbyte(s, l, attr)
1618 char_u *s;
1619 int l;
1620 int attr;
1621{
1622 int cw;
1623
1624 msg_didout = TRUE; /* remember that line is not empty */
1625 cw = (*mb_ptr2cells)(s);
1626 if (cw > 1 && (
1627#ifdef FEAT_RIGHTLEFT
1628 cmdmsg_rl ? msg_col <= 1 :
1629#endif
1630 msg_col == Columns - 1))
1631 {
1632 /* Doesn't fit, print a highlighted '>' to fill it up. */
1633 msg_screen_putchar('>', hl_attr(HLF_AT));
1634 return s;
1635 }
1636
1637 screen_puts_len(s, l, msg_row, msg_col, attr);
1638#ifdef FEAT_RIGHTLEFT
1639 if (cmdmsg_rl)
1640 {
1641 msg_col -= cw;
1642 if (msg_col == 0)
1643 {
1644 msg_col = Columns;
1645 ++msg_row;
1646 }
1647 }
1648 else
1649#endif
1650 {
1651 msg_col += cw;
1652 if (msg_col >= Columns)
1653 {
1654 msg_col = 0;
1655 ++msg_row;
1656 }
1657 }
1658 return s + l;
1659}
1660#endif
1661
1662/*
1663 * Output a string to the screen at position msg_row, msg_col.
1664 * Update msg_row and msg_col for the next message.
1665 */
1666 void
1667msg_puts(s)
1668 char_u *s;
1669{
1670 msg_puts_attr(s, 0);
1671}
1672
1673 void
1674msg_puts_title(s)
1675 char_u *s;
1676{
1677 msg_puts_attr(s, hl_attr(HLF_T));
1678}
1679
1680#if defined(FEAT_CSCOPE) || defined(PROTO)
1681/*
1682 * if printing a string will exceed the screen width, print "..." in the
1683 * middle.
1684 */
1685 void
1686msg_puts_long(longstr)
1687 char_u *longstr;
1688{
1689 msg_puts_long_len_attr(longstr, (int)strlen((char *)longstr), 0);
1690}
1691#endif
1692
1693/*
1694 * Show a message in such a way that it always fits in the line. Cut out a
1695 * part in the middle and replace it with "..." when necessary.
1696 * Does not handle multi-byte characters!
1697 */
1698 void
1699msg_puts_long_attr(longstr, attr)
1700 char_u *longstr;
1701 int attr;
1702{
1703 msg_puts_long_len_attr(longstr, (int)strlen((char *)longstr), attr);
1704}
1705
1706 void
1707msg_puts_long_len_attr(longstr, len, attr)
1708 char_u *longstr;
1709 int len;
1710 int attr;
1711{
1712 int slen = len;
1713 int room;
1714
1715 room = Columns - msg_col;
1716 if (len > room && room >= 20)
1717 {
1718 slen = (room - 3) / 2;
1719 msg_outtrans_len_attr(longstr, slen, attr);
1720 msg_puts_attr((char_u *)"...", hl_attr(HLF_8));
1721 }
1722 msg_outtrans_len_attr(longstr + len - slen, slen, attr);
1723}
1724
1725/*
1726 * Basic function for writing a message with highlight attributes.
1727 */
1728 void
1729msg_puts_attr(s, attr)
1730 char_u *s;
1731 int attr;
1732{
1733 msg_puts_attr_len(s, -1, attr);
1734}
1735
1736/*
1737 * Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes).
1738 * When "maxlen" is -1 there is no maximum length.
1739 * When "maxlen" is >= 0 the message is not put in the history.
1740 */
1741 static void
1742msg_puts_attr_len(str, maxlen, attr)
1743 char_u *str;
1744 int maxlen;
1745 int attr;
1746{
1747 int oldState;
1748 char_u *s = str;
1749 char_u *p;
1750 char_u buf[4];
1751 char_u *t_s = str; /* string from "t_s" to "s" is still todo */
1752 int t_col = 0; /* screen cells todo, 0 when "t_s" not used */
1753#ifdef FEAT_MBYTE
1754 int l;
1755 int cw;
1756#endif
1757 int c;
1758
1759 /*
1760 * If redirection is on, also write to the redirection file.
1761 */
1762 redir_write(s, maxlen);
1763
1764 /*
1765 * Don't print anything when using ":silent cmd".
1766 */
1767 if (msg_silent != 0)
1768 return;
1769
1770 /* if MSG_HIST flag set, add message to history */
1771 if ((attr & MSG_HIST) && maxlen < 0)
1772 {
1773 add_msg_hist(s, -1, attr);
1774 attr &= ~MSG_HIST;
1775 }
1776
1777 /*
1778 * When writing something to the screen after it has scrolled, requires a
1779 * wait-return prompt later. Needed when scrolling, resetting
1780 * need_wait_return after some prompt, and then outputting something
1781 * without scrolling
1782 */
1783 if (msg_scrolled && !msg_scrolled_ign)
1784 need_wait_return = TRUE;
1785 msg_didany = TRUE; /* remember that something was outputted */
1786
1787 /*
1788 * If there is no valid screen, use fprintf so we can see error messages.
1789 * If termcap is not active, we may be writing in an alternate console
1790 * window, cursor positioning may not work correctly (window size may be
1791 * different, e.g. for Win32 console) or we just don't know where the
1792 * cursor is.
1793 */
1794 if (msg_use_printf())
1795 {
1796#ifdef WIN3264
1797 if (!(silent_mode && p_verbose == 0))
1798 mch_settmode(TMODE_COOK); /* handle '\r' and '\n' correctly */
1799#endif
1800 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
1801 {
1802 if (!(silent_mode && p_verbose == 0))
1803 {
1804 p = &buf[0];
1805 /* NL --> CR NL translation (for Unix, not for "--version") */
1806 /* NL --> CR translation (for Mac) */
1807 if (*s == '\n' && !info_message)
1808 *p++ = '\r';
1809#if defined(USE_CR) && !defined(MACOS_X_UNIX)
1810 else
1811#endif
1812 *p++ = *s;
1813 *p = '\0';
1814 if (info_message) /* informative message, not an error */
1815 mch_msg((char *)buf);
1816 else
1817 mch_errmsg((char *)buf);
1818 }
1819
1820 /* primitive way to compute the current column */
1821#ifdef FEAT_RIGHTLEFT
1822 if (cmdmsg_rl)
1823 {
1824 if (*s == '\r' || *s == '\n')
1825 msg_col = Columns - 1;
1826 else
1827 --msg_col;
1828 }
1829 else
1830#endif
1831 {
1832 if (*s == '\r' || *s == '\n')
1833 msg_col = 0;
1834 else
1835 ++msg_col;
1836 }
1837 ++s;
1838 }
1839 msg_didout = TRUE; /* assume that line is not empty */
1840
1841#ifdef WIN3264
1842 if (!(silent_mode && p_verbose == 0))
1843 mch_settmode(TMODE_RAW);
1844#endif
1845 return;
1846 }
1847
1848 did_wait_return = FALSE;
1849 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
1850 {
1851 /*
1852 * The screen is scrolled up when:
1853 * - When outputting a newline in the last row
1854 * - when outputting a character in the last column of the last row
1855 * (some terminals scroll automatically, some don't. To avoid
1856 * problems we scroll ourselves)
1857 */
1858 if (msg_row >= Rows - 1
1859 && (*s == '\n'
1860 || (
1861#ifdef FEAT_RIGHTLEFT
1862 cmdmsg_rl
1863 ? (
1864 msg_col <= 1
1865 || (*s == TAB && msg_col <= 7)
1866# ifdef FEAT_MBYTE
1867 || (has_mbyte && (*mb_ptr2cells)(s) > 1 && msg_col <= 2)
1868# endif
1869 )
1870 :
1871#endif
1872 (msg_col + t_col >= Columns - 1
1873 || (*s == TAB && msg_col + t_col >= ((Columns - 1) & ~7))
1874# ifdef FEAT_MBYTE
1875 || (has_mbyte && (*mb_ptr2cells)(s) > 1
1876 && msg_col + t_col >= Columns - 2)
1877# endif
1878 ))))
1879 {
1880 if (t_col > 0)
1881 {
1882 /* output postponed text */
1883 t_puts(t_col, t_s, s, attr);
1884 t_col = 0;
1885 }
1886
1887 /* When no more prompt an no more room, truncate here */
1888 if (msg_no_more && lines_left == 0)
1889 break;
1890#ifdef FEAT_GUI
1891 /* Remove the cursor before scrolling, ScreenLines[] is going to
1892 * become invalid. */
1893 if (gui.in_use)
1894 gui_undraw_cursor();
1895#endif
1896 /* scrolling up always works */
1897 screen_del_lines(0, 0, 1, (int)Rows, TRUE, NULL);
1898
1899 if (!can_clear((char_u *)" "))
1900 {
1901 /* Scrolling up doesn't result in the right background. Set
1902 * the background here. It's not efficient, but avoids that
1903 * we have to do it all over the code. */
1904 screen_fill((int)Rows - 1, (int)Rows, 0,
1905 (int)Columns, ' ', ' ', 0);
1906
1907 /* Also clear the last char of the last but one line if it was
1908 * not cleared before to avoid a scroll-up. */
1909 if (ScreenAttrs[LineOffset[Rows - 2] + Columns - 1]
1910 == (sattr_T)-1)
1911 screen_fill((int)Rows - 2, (int)Rows - 1,
1912 (int)Columns - 1, (int)Columns, ' ', ' ', 0);
1913 }
1914
1915 msg_row = Rows - 2;
1916 if (msg_col >= Columns) /* can happen after screen resize */
1917 msg_col = Columns - 1;
1918
1919 ++msg_scrolled;
1920 need_wait_return = TRUE; /* may need wait_return in main() */
1921 if (must_redraw < VALID)
1922 must_redraw = VALID;
1923 redraw_cmdline = TRUE;
1924 if (cmdline_row > 0 && !exmode_active)
1925 --cmdline_row;
1926
1927 /*
1928 * if screen is completely filled wait for a character
1929 */
1930 if (p_more && --lines_left == 0 && State != HITRETURN
1931 && !msg_no_more && !exmode_active)
1932 {
1933 oldState = State;
1934 State = ASKMORE;
1935#ifdef FEAT_MOUSE
1936 setmouse();
1937#endif
1938 msg_moremsg(FALSE);
1939 for (;;)
1940 {
1941 /*
1942 * Get a typed character directly from the user.
1943 */
1944 c = get_keystroke();
1945
1946#if defined(FEAT_MENU) && defined(FEAT_GUI)
1947 if (c == K_MENU)
1948 {
1949 int idx = get_menu_index(current_menu, ASKMORE);
1950
1951 /* Used a menu. If it starts with CTRL-Y, it must
1952 * be a "Copy" for the clipboard. Otherwise
1953 * assume that we end */
1954 if (idx == MENU_INDEX_INVALID)
1955 continue;
1956 c = *current_menu->strings[idx];
1957 if (c != NUL && current_menu->strings[idx][1] != NUL)
1958 ins_typebuf(current_menu->strings[idx] + 1,
1959 current_menu->noremap[idx], 0, TRUE,
1960 current_menu->silent[idx]);
1961 }
1962#endif
1963
1964 switch (c)
1965 {
1966 case BS:
1967 case 'k':
1968 case K_UP:
1969 if (!more_back_used)
1970 {
1971 msg_moremsg(TRUE);
1972 continue;
1973 }
1974 more_back = 1;
1975 lines_left = 1;
1976 break;
1977 case CAR: /* one extra line */
1978 case NL:
1979 case 'j':
1980 case K_DOWN:
1981 lines_left = 1;
1982 break;
1983 case ':': /* start new command line */
1984#ifdef FEAT_CON_DIALOG
1985 if (!confirm_msg_used)
1986#endif
1987 {
1988 /* Since got_int is set all typeahead will be
1989 * flushed, but we want to keep this ':', remember
1990 * that in a special way. */
1991 typeahead_noflush(':');
1992 cmdline_row = Rows - 1; /* put ':' on this line */
1993 skip_redraw = TRUE; /* skip redraw once */
1994 need_wait_return = FALSE; /* don't wait in main() */
1995 }
1996 /*FALLTHROUGH*/
1997 case 'q': /* quit */
1998 case Ctrl_C:
1999 case ESC:
2000#ifdef FEAT_CON_DIALOG
2001 if (confirm_msg_used)
2002 {
2003 /* Jump to the choices of the dialog. */
2004 s = confirm_msg_tail;
2005 lines_left = Rows - 1;
2006 }
2007 else
2008#endif
2009 {
2010 got_int = TRUE;
2011 quit_more = TRUE;
2012 }
2013 break;
2014 case 'u': /* Up half a page */
2015 case K_PAGEUP:
2016 if (!more_back_used)
2017 {
2018 msg_moremsg(TRUE);
2019 continue;
2020 }
2021 more_back = Rows / 2;
2022 /*FALLTHROUGH*/
2023 case 'd': /* Down half a page */
2024 lines_left = Rows / 2;
2025 break;
2026 case 'b': /* one page back */
2027 if (!more_back_used)
2028 {
2029 msg_moremsg(TRUE);
2030 continue;
2031 }
2032 more_back = Rows - 1;
2033 /*FALLTHROUGH*/
2034 case ' ': /* one extra page */
2035 case K_PAGEDOWN:
2036 case K_LEFTMOUSE:
2037 lines_left = Rows - 1;
2038 break;
2039
2040#ifdef FEAT_CLIPBOARD
2041 case Ctrl_Y:
2042 /* Strange way to allow copying (yanking) a modeless
2043 * selection at the more prompt. Use CTRL-Y,
2044 * because the same is used in Cmdline-mode and at the
2045 * hit-enter prompt. However, scrolling one line up
2046 * might be expected... */
2047 if (clip_star.state == SELECT_DONE)
2048 clip_copy_modeless_selection(TRUE);
2049 continue;
2050#endif
2051 default: /* no valid response */
2052 msg_moremsg(TRUE);
2053 continue;
2054 }
2055 break;
2056 }
2057
2058 /* clear the --more-- message */
2059 screen_fill((int)Rows - 1, (int)Rows,
2060 0, (int)Columns, ' ', ' ', 0);
2061 State = oldState;
2062#ifdef FEAT_MOUSE
2063 setmouse();
2064#endif
2065 if (quit_more)
2066 {
2067 msg_row = Rows - 1;
2068 msg_col = 0;
2069 return; /* the string is not displayed! */
2070 }
2071#ifdef FEAT_RIGHTLEFT
2072 if (cmdmsg_rl)
2073 msg_col = Columns - 1;
2074#endif
2075 }
2076 }
2077
2078 if (t_col > 0
2079 && (vim_strchr((char_u *)"\n\r\b\t", *s) != NULL
2080 || *s == BELL
2081 || msg_col + t_col >= Columns
2082#ifdef FEAT_MBYTE
2083 || (has_mbyte && (*mb_ptr2cells)(s) > 1
2084 && msg_col + t_col >= Columns - 1)
2085#endif
2086 ))
2087 {
2088 /* output any postponed text */
2089 t_puts(t_col, t_s, s, attr);
2090 t_col = 0;
2091 }
2092
2093 if (*s == '\n') /* go to next line */
2094 {
2095 msg_didout = FALSE; /* remember that line is empty */
2096 msg_col = 0;
2097 if (++msg_row >= Rows) /* safety check */
2098 msg_row = Rows - 1;
2099 }
2100 else if (*s == '\r') /* go to column 0 */
2101 {
2102 msg_col = 0;
2103 }
2104 else if (*s == '\b') /* go to previous char */
2105 {
2106 if (msg_col)
2107 --msg_col;
2108 }
2109 else if (*s == TAB) /* translate into spaces */
2110 {
2111 do
2112 msg_screen_putchar(' ', attr);
2113 while (msg_col & 7);
2114 }
2115 else if (*s == BELL) /* beep (from ":sh") */
2116 vim_beep();
2117 else
2118 {
2119#ifdef FEAT_MBYTE
2120 if (has_mbyte)
2121 {
2122 cw = (*mb_ptr2cells)(s);
2123 if (enc_utf8 && maxlen >= 0)
2124 /* avoid including composing chars after the end */
2125 l = utfc_ptr2len_check_len(s, (int)((str + maxlen) - s));
2126 else
2127 l = (*mb_ptr2len_check)(s);
2128 }
2129 else
2130 {
2131 cw = 1;
2132 l = 1;
2133 }
2134#endif
2135 /* When drawing from right to left or when a double-wide character
2136 * doesn't fit, draw a single character here. Otherwise collect
2137 * characters and draw them all at once later. */
2138#if defined(FEAT_RIGHTLEFT) || defined(FEAT_MBYTE)
2139 if (
2140# ifdef FEAT_RIGHTLEFT
2141 cmdmsg_rl
2142# ifdef FEAT_MBYTE
2143 ||
2144# endif
2145# endif
2146# ifdef FEAT_MBYTE
2147 (cw > 1 && msg_col + t_col >= Columns - 1)
2148# endif
2149 )
2150 {
2151# ifdef FEAT_MBYTE
2152 if (l > 1)
2153 s = screen_puts_mbyte(s, l, attr) - 1;
2154 else
2155# endif
2156 msg_screen_putchar(*s, attr);
2157 }
2158 else
2159#endif
2160 {
2161 /* postpone this character until later */
2162 if (t_col == 0)
2163 t_s = s;
2164#ifdef FEAT_MBYTE
2165 t_col += cw;
2166 s += l - 1;
2167#else
2168 ++t_col;
2169#endif
2170 }
2171 }
2172 ++s;
2173 }
2174
2175 /* output any postponed text */
2176 if (t_col > 0)
2177 t_puts(t_col, t_s, s, attr);
2178
2179 msg_check();
2180}
2181
2182/*
2183 * Output any postponed text for msg_puts_attr_len().
2184 */
2185 static void
2186t_puts(t_col, t_s, s, attr)
2187 int t_col;
2188 char_u *t_s;
2189 char_u *s;
2190 int attr;
2191{
2192 /* output postponed text */
2193 msg_didout = TRUE; /* remember that line is not empty */
2194 screen_puts_len(t_s, (int)(s - t_s), msg_row, msg_col, attr);
2195 msg_col += t_col;
2196#ifdef FEAT_MBYTE
2197 /* If the string starts with a composing character don't increment the
2198 * column position for it. */
2199 if (enc_utf8 && utf_iscomposing(utf_ptr2char(t_s)))
2200 --msg_col;
2201#endif
2202 if (msg_col >= Columns)
2203 {
2204 msg_col = 0;
2205 ++msg_row;
2206 }
2207}
2208
2209
2210/*
2211 * Returns TRUE when messages should be printed with mch_errmsg().
2212 * This is used when there is no valid screen, so we can see error messages.
2213 * If termcap is not active, we may be writing in an alternate console
2214 * window, cursor positioning may not work correctly (window size may be
2215 * different, e.g. for Win32 console) or we just don't know where the
2216 * cursor is.
2217 */
2218 int
2219msg_use_printf()
2220{
2221 return (!msg_check_screen()
2222#if defined(WIN3264) && !defined(FEAT_GUI_MSWIN)
2223 || !termcap_active
2224#endif
2225 || (swapping_screen() && !termcap_active)
2226 );
2227}
2228
2229#if defined(USE_MCH_ERRMSG) || defined(PROTO)
2230
2231#ifdef mch_errmsg
2232# undef mch_errmsg
2233#endif
2234#ifdef mch_msg
2235# undef mch_msg
2236#endif
2237
2238/*
2239 * Give an error message. To be used when the screen hasn't been initialized
2240 * yet. When stderr can't be used, collect error messages until the GUI has
2241 * started and they can be displayed in a message box.
2242 */
2243 void
2244mch_errmsg(str)
2245 char *str;
2246{
2247 int len;
2248
2249#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI)
2250 /* On Unix use stderr if it's a tty.
2251 * When not going to start the GUI also use stderr.
2252 * On Mac, when started from Finder, stderr is the console. */
2253 if (
2254# ifdef UNIX
2255# ifdef MACOS_X_UNIX
2256 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0)
2257# else
2258 isatty(2)
2259# endif
2260# ifdef FEAT_GUI
2261 ||
2262# endif
2263# endif
2264# ifdef FEAT_GUI
2265 !(gui.in_use || gui.starting)
2266# endif
2267 )
2268 {
2269 fprintf(stderr, "%s", str);
2270 return;
2271 }
2272#endif
2273
2274 /* avoid a delay for a message that isn't there */
2275 emsg_on_display = FALSE;
2276
2277 len = (int)STRLEN(str) + 1;
2278 if (error_ga.ga_growsize == 0)
2279 {
2280 error_ga.ga_growsize = 80;
2281 error_ga.ga_itemsize = 1;
2282 }
2283 if (ga_grow(&error_ga, len) == OK)
2284 {
2285 mch_memmove((char_u *)error_ga.ga_data + error_ga.ga_len,
2286 (char_u *)str, len);
2287#ifdef UNIX
2288 /* remove CR characters, they are displayed */
2289 {
2290 char_u *p;
2291
2292 p = (char_u *)error_ga.ga_data + error_ga.ga_len;
2293 for (;;)
2294 {
2295 p = vim_strchr(p, '\r');
2296 if (p == NULL)
2297 break;
2298 *p = ' ';
2299 }
2300 }
2301#endif
2302 --len; /* don't count the NUL at the end */
2303 error_ga.ga_len += len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002304 }
2305}
2306
2307/*
2308 * Give a message. To be used when the screen hasn't been initialized yet.
2309 * When there is no tty, collect messages until the GUI has started and they
2310 * can be displayed in a message box.
2311 */
2312 void
2313mch_msg(str)
2314 char *str;
2315{
2316#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI)
2317 /* On Unix use stdout if we have a tty. This allows "vim -h | more" and
2318 * uses mch_errmsg() when started from the desktop.
2319 * When not going to start the GUI also use stdout.
2320 * On Mac, when started from Finder, stderr is the console. */
2321 if (
2322# ifdef UNIX
2323# ifdef MACOS_X_UNIX
2324 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0)
2325# else
2326 isatty(2)
2327# endif
2328# ifdef FEAT_GUI
2329 ||
2330# endif
2331# endif
2332# ifdef FEAT_GUI
2333 !(gui.in_use || gui.starting)
2334# endif
2335 )
2336 {
2337 printf("%s", str);
2338 return;
2339 }
2340# endif
2341 mch_errmsg(str);
2342}
2343#endif /* USE_MCH_ERRMSG */
2344
2345/*
2346 * Put a character on the screen at the current message position and advance
2347 * to the next position. Only for printable ASCII!
2348 */
2349 static void
2350msg_screen_putchar(c, attr)
2351 int c;
2352 int attr;
2353{
2354 msg_didout = TRUE; /* remember that line is not empty */
2355 screen_putchar(c, msg_row, msg_col, attr);
2356#ifdef FEAT_RIGHTLEFT
2357 if (cmdmsg_rl)
2358 {
2359 if (--msg_col == 0)
2360 {
2361 msg_col = Columns;
2362 ++msg_row;
2363 }
2364 }
2365 else
2366#endif
2367 {
2368 if (++msg_col >= Columns)
2369 {
2370 msg_col = 0;
2371 ++msg_row;
2372 }
2373 }
2374}
2375
2376 void
2377msg_moremsg(full)
2378 int full;
2379{
2380 int attr;
2381
2382 attr = hl_attr(HLF_M);
2383 screen_puts((char_u *)_("-- More --"), (int)Rows - 1, 0, attr);
2384 if (full)
2385 screen_puts(more_back_used
2386 ? (char_u *)_(" (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)")
2387 : (char_u *)_(" (RET: line, SPACE: page, d: half page, q: quit)"),
2388 (int)Rows - 1, 10, attr);
2389}
2390
2391/*
2392 * Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or
2393 * exmode_active.
2394 */
2395 void
2396repeat_message()
2397{
2398 if (State == ASKMORE)
2399 {
2400 msg_moremsg(TRUE); /* display --more-- message again */
2401 msg_row = Rows - 1;
2402 }
2403#ifdef FEAT_CON_DIALOG
2404 else if (State == CONFIRM)
2405 {
2406 display_confirm_msg(); /* display ":confirm" message again */
2407 msg_row = Rows - 1;
2408 }
2409#endif
2410 else if (State == EXTERNCMD)
2411 {
2412 windgoto(msg_row, msg_col); /* put cursor back */
2413 }
2414 else if (State == HITRETURN || State == SETWSIZE)
2415 {
2416 hit_return_msg();
2417 msg_row = Rows - 1;
2418 }
2419}
2420
2421/*
2422 * msg_check_screen - check if the screen is initialized.
2423 * Also check msg_row and msg_col, if they are too big it may cause a crash.
2424 * While starting the GUI the terminal codes will be set for the GUI, but the
2425 * output goes to the terminal. Don't use the terminal codes then.
2426 */
2427 static int
2428msg_check_screen()
2429{
2430 if (!full_screen || !screen_valid(FALSE))
2431 return FALSE;
2432
2433 if (msg_row >= Rows)
2434 msg_row = Rows - 1;
2435 if (msg_col >= Columns)
2436 msg_col = Columns - 1;
2437 return TRUE;
2438}
2439
2440/*
2441 * Clear from current message position to end of screen.
2442 * Skip this when ":silent" was used, no need to clear for redirection.
2443 */
2444 void
2445msg_clr_eos()
2446{
2447 if (msg_silent == 0)
2448 msg_clr_eos_force();
2449}
2450
2451/*
2452 * Clear from current message position to end of screen.
2453 * Note: msg_col is not updated, so we remember the end of the message
2454 * for msg_check().
2455 */
2456 void
2457msg_clr_eos_force()
2458{
2459 if (msg_use_printf())
2460 {
2461 if (full_screen) /* only when termcap codes are valid */
2462 {
2463 if (*T_CD)
2464 out_str(T_CD); /* clear to end of display */
2465 else if (*T_CE)
2466 out_str(T_CE); /* clear to end of line */
2467 }
2468 }
2469 else
2470 {
2471#ifdef FEAT_RIGHTLEFT
2472 if (cmdmsg_rl)
2473 {
2474 screen_fill(msg_row, msg_row + 1, 0, msg_col + 1, ' ', ' ', 0);
2475 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
2476 }
2477 else
2478#endif
2479 {
2480 screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns,
2481 ' ', ' ', 0);
2482 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
2483 }
2484 }
2485}
2486
2487/*
2488 * Clear the command line.
2489 */
2490 void
2491msg_clr_cmdline()
2492{
2493 msg_row = cmdline_row;
2494 msg_col = 0;
2495 msg_clr_eos_force();
2496}
2497
2498/*
2499 * end putting a message on the screen
2500 * call wait_return if the message does not fit in the available space
2501 * return TRUE if wait_return not called.
2502 */
2503 int
2504msg_end()
2505{
2506 /*
2507 * if the string is larger than the window,
2508 * or the ruler option is set and we run into it,
2509 * we have to redraw the window.
2510 * Do not do this if we are abandoning the file or editing the command line.
2511 */
2512 if (!exiting && need_wait_return && !(State & CMDLINE))
2513 {
2514 wait_return(FALSE);
2515 return FALSE;
2516 }
2517 out_flush();
2518 return TRUE;
2519}
2520
2521/*
2522 * If the written message runs into the shown command or ruler, we have to
2523 * wait for hit-return and redraw the window later.
2524 */
2525 void
2526msg_check()
2527{
2528 if (msg_row == Rows - 1 && msg_col >= sc_col)
2529 {
2530 need_wait_return = TRUE;
2531 redraw_cmdline = TRUE;
2532 }
2533}
2534
2535/*
2536 * May write a string to the redirection file.
2537 * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes.
2538 */
2539 static void
2540redir_write(str, maxlen)
2541 char_u *str;
2542 int maxlen;
2543{
2544 char_u *s = str;
2545 static int cur_col = 0;
2546
Bram Moolenaar8b044b32005-05-31 22:05:58 +00002547 /* Don't do anything for displaying prompts and the like. */
2548 if (redir_off)
2549 return;
2550
2551 /*
2552 * If 'verbosefile' is set write message in that file.
2553 * Must come before the rest because of updating "msg_col".
2554 */
2555 if (*p_vfile != NUL)
2556 verbose_write(s, maxlen);
2557
2558 if (redir_fd != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559#ifdef FEAT_EVAL
Bram Moolenaardf177f62005-02-22 08:39:57 +00002560 || redir_reg || redir_vname
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561#endif
Bram Moolenaar8b044b32005-05-31 22:05:58 +00002562 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002563 {
2564 /* If the string doesn't start with CR or NL, go to msg_col */
2565 if (*s != '\n' && *s != '\r')
2566 {
2567 while (cur_col < msg_col)
2568 {
2569#ifdef FEAT_EVAL
2570 if (redir_reg)
2571 write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE);
Bram Moolenaardf177f62005-02-22 08:39:57 +00002572 else if (redir_vname)
2573 var_redir_str((char_u *)" ", -1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002574 else if (redir_fd)
2575#endif
2576 fputs(" ", redir_fd);
2577 ++cur_col;
2578 }
2579 }
2580
2581#ifdef FEAT_EVAL
2582 if (redir_reg)
2583 write_reg_contents(redir_reg, s, maxlen, TRUE);
Bram Moolenaardf177f62005-02-22 08:39:57 +00002584 if (redir_vname)
2585 var_redir_str(s, maxlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002586#endif
2587
2588 /* Adjust the current column */
2589 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
2590 {
2591#ifdef FEAT_EVAL
Bram Moolenaardf177f62005-02-22 08:39:57 +00002592 if (!redir_reg && !redir_vname && redir_fd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002593#endif
2594 putc(*s, redir_fd);
2595 if (*s == '\r' || *s == '\n')
2596 cur_col = 0;
2597 else if (*s == '\t')
2598 cur_col += (8 - cur_col % 8);
2599 else
2600 ++cur_col;
2601 ++s;
2602 }
2603
2604 if (msg_silent != 0) /* should update msg_col */
2605 msg_col = cur_col;
2606 }
2607}
2608
2609/*
Bram Moolenaar8b044b32005-05-31 22:05:58 +00002610 * Before giving verbose messsage.
2611 * Must always be called paired with verbose_leave()!
2612 */
2613 void
2614verbose_enter()
2615{
2616 if (*p_vfile != NUL)
2617 ++msg_silent;
2618}
2619
2620/*
2621 * After giving verbose message.
2622 * Must always be called paired with verbose_enter()!
2623 */
2624 void
2625verbose_leave()
2626{
2627 if (*p_vfile != NUL)
2628 if (--msg_silent < 0)
2629 msg_silent = 0;
2630}
2631
2632/*
2633 * Like verbose_enter() and set msg_scroll when displaying the message.
2634 */
2635 void
2636verbose_enter_scroll()
2637{
2638 if (*p_vfile != NUL)
2639 ++msg_silent;
2640 else
2641 /* always scroll up, don't overwrite */
2642 msg_scroll = TRUE;
2643}
2644
2645/*
2646 * Like verbose_leave() and set cmdline_row when displaying the message.
2647 */
2648 void
2649verbose_leave_scroll()
2650{
2651 if (*p_vfile != NUL)
2652 {
2653 if (--msg_silent < 0)
2654 msg_silent = 0;
2655 }
2656 else
2657 cmdline_row = msg_row;
2658}
2659
2660static FILE *verbose_fd = NULL;
2661static int verbose_did_open = FALSE;
2662
2663/*
2664 * Called when 'verbosefile' is set: stop writing to the file.
2665 */
2666 void
2667verbose_stop()
2668{
2669 if (verbose_fd != NULL)
2670 {
2671 fclose(verbose_fd);
2672 verbose_fd = NULL;
2673 }
2674 verbose_did_open = FALSE;
2675}
2676
2677/*
2678 * Open the file 'verbosefile'.
2679 * Return FAIL or OK.
2680 */
2681 int
2682verbose_open()
2683{
2684 if (verbose_fd == NULL && !verbose_did_open)
2685 {
2686 /* Only give the error message once. */
2687 verbose_did_open = TRUE;
2688
2689 verbose_fd = fopen((char *)p_vfile, "a");
2690 if (verbose_fd == NULL)
2691 {
2692 EMSG2(_(e_notopen), p_vfile);
2693 return FAIL;
2694 }
2695 }
2696 return OK;
2697}
2698
2699/*
2700 * Write a string to 'verbosefile'.
2701 * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes.
2702 */
2703 static void
2704verbose_write(str, maxlen)
2705 char_u *str;
2706 int maxlen;
2707{
2708 char_u *s = str;
2709 static int cur_col = 0;
2710
2711 /* Open the file when called the first time. */
2712 if (verbose_fd == NULL)
2713 verbose_open();
2714
2715 if (verbose_fd != NULL)
2716 {
2717 /* If the string doesn't start with CR or NL, go to msg_col */
2718 if (*s != '\n' && *s != '\r')
2719 {
2720 while (cur_col < msg_col)
2721 {
2722 fputs(" ", verbose_fd);
2723 ++cur_col;
2724 }
2725 }
2726
2727 /* Adjust the current column */
2728 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
2729 {
2730 putc(*s, verbose_fd);
2731 if (*s == '\r' || *s == '\n')
2732 cur_col = 0;
2733 else if (*s == '\t')
2734 cur_col += (8 - cur_col % 8);
2735 else
2736 ++cur_col;
2737 ++s;
2738 }
2739 }
2740}
2741
2742/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002743 * Give a warning message (for searching).
2744 * Use 'w' highlighting and may repeat the message after redrawing
2745 */
2746 void
2747give_warning(message, hl)
2748 char_u *message;
2749 int hl;
2750{
2751 /* Don't do this for ":silent". */
2752 if (msg_silent != 0)
2753 return;
2754
2755 /* Don't want a hit-enter prompt here. */
2756 ++no_wait_return;
Bram Moolenaared203462004-06-16 11:19:22 +00002757
Bram Moolenaar071d4272004-06-13 20:20:40 +00002758#ifdef FEAT_EVAL
2759 set_vim_var_string(VV_WARNINGMSG, message, -1);
2760#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002761 vim_free(keep_msg);
2762 keep_msg = NULL;
2763 if (hl)
2764 keep_msg_attr = hl_attr(HLF_W);
2765 else
2766 keep_msg_attr = 0;
2767 if (msg_attr(message, keep_msg_attr) && msg_scrolled == 0)
2768 set_keep_msg(message);
2769 msg_didout = FALSE; /* overwrite this message */
2770 msg_nowait = TRUE; /* don't wait for this message */
2771 msg_col = 0;
Bram Moolenaared203462004-06-16 11:19:22 +00002772
Bram Moolenaar071d4272004-06-13 20:20:40 +00002773 --no_wait_return;
2774}
2775
2776/*
2777 * Advance msg cursor to column "col".
2778 */
2779 void
2780msg_advance(col)
2781 int col;
2782{
2783 if (msg_silent != 0) /* nothing to advance to */
2784 {
2785 msg_col = col; /* for redirection, may fill it up later */
2786 return;
2787 }
2788 if (col >= Columns) /* not enough room */
2789 col = Columns - 1;
2790 while (msg_col < col)
2791 msg_putchar(' ');
2792}
2793
2794#if defined(FEAT_CON_DIALOG) || defined(PROTO)
2795/*
2796 * Used for "confirm()" function, and the :confirm command prefix.
2797 * Versions which haven't got flexible dialogs yet, and console
2798 * versions, get this generic handler which uses the command line.
2799 *
2800 * type = one of:
2801 * VIM_QUESTION, VIM_INFO, VIM_WARNING, VIM_ERROR or VIM_GENERIC
2802 * title = title string (can be NULL for default)
2803 * (neither used in console dialogs at the moment)
2804 *
2805 * Format of the "buttons" string:
2806 * "Button1Name\nButton2Name\nButton3Name"
2807 * The first button should normally be the default/accept
2808 * The second button should be the 'Cancel' button
2809 * Other buttons- use your imagination!
2810 * A '&' in a button name becomes a shortcut, so each '&' should be before a
2811 * different letter.
2812 */
2813/* ARGSUSED */
2814 int
2815do_dialog(type, title, message, buttons, dfltbutton, textfield)
2816 int type;
2817 char_u *title;
2818 char_u *message;
2819 char_u *buttons;
2820 int dfltbutton;
2821 char_u *textfield; /* IObuff for inputdialog(), NULL otherwise */
2822{
2823 int oldState;
2824 int retval = 0;
2825 char_u *hotkeys;
2826 int c;
2827 int i;
2828
2829#ifndef NO_CONSOLE
2830 /* Don't output anything in silent mode ("ex -s") */
2831 if (silent_mode)
2832 return dfltbutton; /* return default option */
2833#endif
2834
2835#ifdef FEAT_GUI_DIALOG
2836 /* When GUI is running and 'c' not in 'guioptions', use the GUI dialog */
2837 if (gui.in_use && vim_strchr(p_go, GO_CONDIALOG) == NULL)
2838 {
2839 c = gui_mch_dialog(type, title, message, buttons, dfltbutton,
2840 textfield);
2841 msg_end_prompt();
2842
2843 /* Flush output to avoid that further messages and redrawing is done
2844 * in the wrong order. */
2845 out_flush();
2846 gui_mch_update();
2847
2848 return c;
2849 }
2850#endif
2851
2852 oldState = State;
2853 State = CONFIRM;
2854#ifdef FEAT_MOUSE
2855 setmouse();
2856#endif
2857
2858 /*
2859 * Since we wait for a keypress, don't make the
2860 * user press RETURN as well afterwards.
2861 */
2862 ++no_wait_return;
2863 hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
2864
2865 if (hotkeys != NULL)
2866 {
2867 for (;;)
2868 {
2869 /* Get a typed character directly from the user. */
2870 c = get_keystroke();
2871 switch (c)
2872 {
2873 case CAR: /* User accepts default option */
2874 case NL:
2875 retval = dfltbutton;
2876 break;
2877 case Ctrl_C: /* User aborts/cancels */
2878 case ESC:
2879 retval = 0;
2880 break;
2881 default: /* Could be a hotkey? */
2882 if (c < 0) /* special keys are ignored here */
2883 continue;
2884 /* Make the character lowercase, as chars in "hotkeys" are. */
2885 c = MB_TOLOWER(c);
2886 retval = 1;
2887 for (i = 0; hotkeys[i]; ++i)
2888 {
2889#ifdef FEAT_MBYTE
2890 if (has_mbyte)
2891 {
2892 if ((*mb_ptr2char)(hotkeys + i) == c)
2893 break;
2894 i += (*mb_ptr2len_check)(hotkeys + i) - 1;
2895 }
2896 else
2897#endif
2898 if (hotkeys[i] == c)
2899 break;
2900 ++retval;
2901 }
2902 if (hotkeys[i])
2903 break;
2904 /* No hotkey match, so keep waiting */
2905 continue;
2906 }
2907 break;
2908 }
2909
2910 vim_free(hotkeys);
2911 }
2912
2913 State = oldState;
2914#ifdef FEAT_MOUSE
2915 setmouse();
2916#endif
2917 --no_wait_return;
2918 msg_end_prompt();
2919
2920 return retval;
2921}
2922
2923static int copy_char __ARGS((char_u *from, char_u *to, int lowercase));
2924
2925/*
2926 * Copy one character from "*from" to "*to", taking care of multi-byte
2927 * characters. Return the length of the character in bytes.
2928 */
2929 static int
2930copy_char(from, to, lowercase)
2931 char_u *from;
2932 char_u *to;
2933 int lowercase; /* make character lower case */
2934{
2935#ifdef FEAT_MBYTE
2936 int len;
2937 int c;
2938
2939 if (has_mbyte)
2940 {
2941 if (lowercase)
2942 {
2943 c = MB_TOLOWER((*mb_ptr2char)(from));
2944 return (*mb_char2bytes)(c, to);
2945 }
2946 else
2947 {
2948 len = (*mb_ptr2len_check)(from);
2949 mch_memmove(to, from, (size_t)len);
2950 return len;
2951 }
2952 }
2953 else
2954#endif
2955 {
2956 if (lowercase)
2957 *to = (char_u)TOLOWER_LOC(*from);
2958 else
2959 *to = *from;
2960 return 1;
2961 }
2962}
2963
2964/*
2965 * Format the dialog string, and display it at the bottom of
2966 * the screen. Return a string of hotkey chars (if defined) for
2967 * each 'button'. If a button has no hotkey defined, the first character of
2968 * the button is used.
2969 * The hotkeys can be multi-byte characters, but without combining chars.
2970 *
2971 * Returns an allocated string with hotkeys, or NULL for error.
2972 */
2973 static char_u *
2974msg_show_console_dialog(message, buttons, dfltbutton)
2975 char_u *message;
2976 char_u *buttons;
2977 int dfltbutton;
2978{
2979 int len = 0;
2980#ifdef FEAT_MBYTE
2981# define HOTK_LEN (has_mbyte ? MB_MAXBYTES : 1)
2982#else
2983# define HOTK_LEN 1
2984#endif
2985 int lenhotkey = HOTK_LEN; /* count first button */
2986 char_u *hotk = NULL;
2987 char_u *msgp = NULL;
2988 char_u *hotkp = NULL;
2989 char_u *r;
2990 int copy;
2991#define HAS_HOTKEY_LEN 30
2992 char_u has_hotkey[HAS_HOTKEY_LEN];
2993 int first_hotkey = FALSE; /* first char of button is hotkey */
2994 int idx;
2995
2996 has_hotkey[0] = FALSE;
2997
2998 /*
2999 * First loop: compute the size of memory to allocate.
3000 * Second loop: copy to the allocated memory.
3001 */
3002 for (copy = 0; copy <= 1; ++copy)
3003 {
3004 r = buttons;
3005 idx = 0;
3006 while (*r)
3007 {
3008 if (*r == DLG_BUTTON_SEP)
3009 {
3010 if (copy)
3011 {
3012 *msgp++ = ',';
3013 *msgp++ = ' '; /* '\n' -> ', ' */
3014
3015 /* advance to next hotkey and set default hotkey */
3016#ifdef FEAT_MBYTE
3017 if (has_mbyte)
3018 hotkp += (*mb_ptr2len_check)(hotkp);
3019 else
3020#endif
3021 ++hotkp;
3022 (void)copy_char(r + 1, hotkp, TRUE);
3023 if (dfltbutton)
3024 --dfltbutton;
3025
3026 /* If no hotkey is specified first char is used. */
3027 if (idx < HAS_HOTKEY_LEN - 1 && !has_hotkey[++idx])
3028 first_hotkey = TRUE;
3029 }
3030 else
3031 {
3032 len += 3; /* '\n' -> ', '; 'x' -> '(x)' */
3033 lenhotkey += HOTK_LEN; /* each button needs a hotkey */
3034 if (idx < HAS_HOTKEY_LEN - 1)
3035 has_hotkey[++idx] = FALSE;
3036 }
3037 }
3038 else if (*r == DLG_HOTKEY_CHAR || first_hotkey)
3039 {
3040 if (*r == DLG_HOTKEY_CHAR)
3041 ++r;
3042 first_hotkey = FALSE;
3043 if (copy)
3044 {
3045 if (*r == DLG_HOTKEY_CHAR) /* '&&a' -> '&a' */
3046 *msgp++ = *r;
3047 else
3048 {
3049 /* '&a' -> '[a]' */
3050 *msgp++ = (dfltbutton == 1) ? '[' : '(';
3051 msgp += copy_char(r, msgp, FALSE);
3052 *msgp++ = (dfltbutton == 1) ? ']' : ')';
3053
3054 /* redefine hotkey */
3055 (void)copy_char(r, hotkp, TRUE);
3056 }
3057 }
3058 else
3059 {
3060 ++len; /* '&a' -> '[a]' */
3061 if (idx < HAS_HOTKEY_LEN - 1)
3062 has_hotkey[idx] = TRUE;
3063 }
3064 }
3065 else
3066 {
3067 /* everything else copy literally */
3068 if (copy)
3069 msgp += copy_char(r, msgp, FALSE);
3070 }
3071
3072 /* advance to the next character */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003073 mb_ptr_adv(r);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074 }
3075
3076 if (copy)
3077 {
3078 *msgp++ = ':';
3079 *msgp++ = ' ';
3080 *msgp = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003081 mb_ptr_adv(hotkp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082 *hotkp = NUL;
3083 }
3084 else
3085 {
3086 len += STRLEN(message)
3087 + 2 /* for the NL's */
3088 + STRLEN(buttons)
3089 + 3; /* for the ": " and NUL */
3090 lenhotkey++; /* for the NUL */
3091
3092 /* If no hotkey is specified first char is used. */
3093 if (!has_hotkey[0])
3094 {
3095 first_hotkey = TRUE;
3096 len += 2; /* "x" -> "[x]" */
3097 }
3098
3099 /*
3100 * Now allocate and load the strings
3101 */
3102 vim_free(confirm_msg);
3103 confirm_msg = alloc(len);
3104 if (confirm_msg == NULL)
3105 return NULL;
3106 *confirm_msg = NUL;
3107 hotk = alloc(lenhotkey);
3108 if (hotk == NULL)
3109 return NULL;
3110
3111 *confirm_msg = '\n';
3112 STRCPY(confirm_msg + 1, message);
3113
3114 msgp = confirm_msg + 1 + STRLEN(message);
3115 hotkp = hotk;
3116
3117 /* define first default hotkey */
3118 (void)copy_char(buttons, hotkp, TRUE);
3119
3120 /* Remember where the choices start, displaying starts here when
3121 * "hotkp" typed at the more prompt. */
3122 confirm_msg_tail = msgp;
3123 *msgp++ = '\n';
3124 }
3125 }
3126
3127 display_confirm_msg();
3128 return hotk;
3129}
3130
3131/*
3132 * Display the ":confirm" message. Also called when screen resized.
3133 */
3134 void
3135display_confirm_msg()
3136{
3137 /* avoid that 'q' at the more prompt truncates the message here */
3138 ++confirm_msg_used;
3139 if (confirm_msg != NULL)
3140 msg_puts_attr(confirm_msg, hl_attr(HLF_M));
3141 --confirm_msg_used;
3142}
3143
3144#endif /* FEAT_CON_DIALOG */
3145
3146#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
3147
3148 int
3149vim_dialog_yesno(type, title, message, dflt)
3150 int type;
3151 char_u *title;
3152 char_u *message;
3153 int dflt;
3154{
3155 if (do_dialog(type,
3156 title == NULL ? (char_u *)_("Question") : title,
3157 message,
3158 (char_u *)_("&Yes\n&No"), dflt, NULL) == 1)
3159 return VIM_YES;
3160 return VIM_NO;
3161}
3162
3163 int
3164vim_dialog_yesnocancel(type, title, message, dflt)
3165 int type;
3166 char_u *title;
3167 char_u *message;
3168 int dflt;
3169{
3170 switch (do_dialog(type,
3171 title == NULL ? (char_u *)_("Question") : title,
3172 message,
3173 (char_u *)_("&Yes\n&No\n&Cancel"), dflt, NULL))
3174 {
3175 case 1: return VIM_YES;
3176 case 2: return VIM_NO;
3177 }
3178 return VIM_CANCEL;
3179}
3180
3181 int
3182vim_dialog_yesnoallcancel(type, title, message, dflt)
3183 int type;
3184 char_u *title;
3185 char_u *message;
3186 int dflt;
3187{
3188 switch (do_dialog(type,
3189 title == NULL ? (char_u *)"Question" : title,
3190 message,
3191 (char_u *)_("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"),
3192 dflt, NULL))
3193 {
3194 case 1: return VIM_YES;
3195 case 2: return VIM_NO;
3196 case 3: return VIM_ALL;
3197 case 4: return VIM_DISCARDALL;
3198 }
3199 return VIM_CANCEL;
3200}
3201
3202#endif /* FEAT_GUI_DIALOG || FEAT_CON_DIALOG */
3203
3204#if defined(FEAT_BROWSE) || defined(PROTO)
3205/*
3206 * Generic browse function. Calls gui_mch_browse() when possible.
3207 * Later this may pop-up a non-GUI file selector (external command?).
3208 */
3209 char_u *
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003210do_browse(flags, title, dflt, ext, initdir, filter, buf)
3211 int flags; /* BROWSE_SAVE and BROWSE_DIR */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003212 char_u *title; /* title for the window */
3213 char_u *dflt; /* default file name (may include directory) */
3214 char_u *ext; /* extension added */
3215 char_u *initdir; /* initial directory, NULL for current dir or
3216 when using path from "dflt" */
3217 char_u *filter; /* file name filter */
3218 buf_T *buf; /* buffer to read/write for */
3219{
3220 char_u *fname;
3221 static char_u *last_dir = NULL; /* last used directory */
3222 char_u *tofree = NULL;
3223 int save_browse = cmdmod.browse;
3224
3225 /* Must turn off browse to avoid that autocommands will get the
3226 * flag too! */
3227 cmdmod.browse = FALSE;
3228
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003229 if (title == NULL || *title == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003230 {
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003231 if (flags & BROWSE_DIR)
3232 title = (char_u *)_("Select Directory dialog");
3233 else if (flags & BROWSE_SAVE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003234 title = (char_u *)_("Save File dialog");
3235 else
3236 title = (char_u *)_("Open File dialog");
3237 }
3238
3239 /* When no directory specified, use default file name, default dir, buffer
3240 * dir, last dir or current dir */
3241 if ((initdir == NULL || *initdir == NUL) && dflt != NULL && *dflt != NUL)
3242 {
3243 if (mch_isdir(dflt)) /* default file name is a directory */
3244 {
3245 initdir = dflt;
3246 dflt = NULL;
3247 }
3248 else if (gettail(dflt) != dflt) /* default file name includes a path */
3249 {
3250 tofree = vim_strsave(dflt);
3251 if (tofree != NULL)
3252 {
3253 initdir = tofree;
3254 *gettail(initdir) = NUL;
3255 dflt = gettail(dflt);
3256 }
3257 }
3258 }
3259
3260 if (initdir == NULL || *initdir == NUL)
3261 {
3262 /* When 'browsedir' is a directory, use it */
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003263 if (STRCMP(p_bsdir, "last") != 0
3264 && STRCMP(p_bsdir, "buffer") != 0
3265 && STRCMP(p_bsdir, "current") != 0
3266 && mch_isdir(p_bsdir))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 initdir = p_bsdir;
3268 /* When saving or 'browsedir' is "buffer", use buffer fname */
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003269 else if (((flags & BROWSE_SAVE) || *p_bsdir == 'b')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270 && buf != NULL && buf->b_ffname != NULL)
3271 {
3272 if (dflt == NULL || *dflt == NUL)
3273 dflt = gettail(curbuf->b_ffname);
3274 tofree = vim_strsave(curbuf->b_ffname);
3275 if (tofree != NULL)
3276 {
3277 initdir = tofree;
3278 *gettail(initdir) = NUL;
3279 }
3280 }
3281 /* When 'browsedir' is "last", use dir from last browse */
3282 else if (*p_bsdir == 'l')
3283 initdir = last_dir;
3284 /* When 'browsedir is "current", use current directory. This is the
3285 * default already, leave initdir empty. */
3286 }
3287
3288# ifdef FEAT_GUI
3289 if (gui.in_use) /* when this changes, also adjust f_has()! */
3290 {
3291 if (filter == NULL
3292# ifdef FEAT_EVAL
3293 && (filter = get_var_value((char_u *)"b:browsefilter")) == NULL
3294 && (filter = get_var_value((char_u *)"g:browsefilter")) == NULL
3295# endif
3296 )
3297 filter = BROWSE_FILTER_DEFAULT;
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003298 if (flags & BROWSE_DIR)
3299 {
3300# if defined(HAVE_GTK2) || defined(WIN3264)
3301 /* For systems that have a directory dialog. */
3302 fname = gui_mch_browsedir(title, initdir);
3303# else
3304 /* Generic solution for selecting a directory: select a file and
3305 * remove the file name. */
3306 fname = gui_mch_browse(0, title, dflt, ext, initdir, (char_u *)"");
3307# endif
3308# if !defined(HAVE_GTK2)
3309 /* Win32 adds a dummy file name, others return an arbitrary file
3310 * name. GTK+ 2 returns only the directory, */
3311 if (fname != NULL && *fname != NUL && !mch_isdir(fname))
3312 {
3313 /* Remove the file name. */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003314 char_u *tail = gettail_sep(fname);
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003315
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003316 if (tail == fname)
3317 *tail++ = '.'; /* use current dir */
3318 *tail = NUL;
3319 }
3320# endif
3321 }
3322 else
3323 fname = gui_mch_browse(flags & BROWSE_SAVE,
3324 title, dflt, ext, initdir, filter);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003325
3326 /* We hang around in the dialog for a while, the user might do some
3327 * things to our files. The Win32 dialog allows deleting or renaming
3328 * a file, check timestamps. */
3329 need_check_timestamps = TRUE;
3330 did_check_timestamps = FALSE;
3331 }
3332 else
3333# endif
3334 {
3335 /* TODO: non-GUI file selector here */
3336 EMSG(_("E338: Sorry, no file browser in console mode"));
3337 fname = NULL;
3338 }
3339
3340 /* keep the directory for next time */
3341 if (fname != NULL)
3342 {
3343 vim_free(last_dir);
3344 last_dir = vim_strsave(fname);
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003345 if (last_dir != NULL && !(flags & BROWSE_DIR))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 {
3347 *gettail(last_dir) = NUL;
3348 if (*last_dir == NUL)
3349 {
3350 /* filename only returned, must be in current dir */
3351 vim_free(last_dir);
3352 last_dir = alloc(MAXPATHL);
3353 if (last_dir != NULL)
3354 mch_dirname(last_dir, MAXPATHL);
3355 }
3356 }
3357 }
3358
3359 vim_free(tofree);
3360 cmdmod.browse = save_browse;
3361
3362 return fname;
3363}
3364#endif
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003365
3366/*
3367 * This code was included to provide a portable vsnprintf() and snprintf().
3368 * Some systems may provide their own, but we always use these for
3369 * consistency.
3370 *
3371 * This code is based on snprintf.c - a portable implementation of snprintf
3372 * by Mark Martinec <mark.martinec@ijs.si>, Version 2.2, 2000-10-06.
Bram Moolenaar8b044b32005-05-31 22:05:58 +00003373 * Included with permission. It was heavely modified to fit in Vim.
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003374 * The original code, including useful comments, can be found here:
3375 * http://www.ijs.si/software/snprintf/
3376 *
3377 * This snprintf() only supports the following conversion specifiers:
3378 * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
3379 * with flags: '-', '+', ' ', '0' and '#'.
3380 * An asterisk is supported for field width as well as precision.
3381 *
3382 * Length modifiers 'h' (short int) and 'l' (long int) are supported.
3383 * 'll' (long long int) is not supported.
3384 *
3385 * It is permitted for str_m to be zero, and it is permitted to specify NULL
3386 * pointer for resulting string argument if str_m is zero (as per ISO C99).
3387 *
3388 * The return value is the number of characters which would be generated
3389 * for the given input, excluding the trailing null. If this value
3390 * is greater or equal to str_m, not all characters from the result
3391 * have been stored in str, output bytes beyond the (str_m-1) -th character
3392 * are discarded. If str_m is greater than zero it is guaranteed
3393 * the resulting string will be null-terminated.
3394 */
3395
3396/*
3397 * When va_list is not supported we only define vim_snprintf().
3398 */
3399
3400/* When generating prototypes all of this is skipped, cproto doesn't
3401 * understand this. */
3402#ifndef PROTO
3403# ifdef HAVE_STDARG_H
3404 int
3405vim_snprintf(char *str, size_t str_m, char *fmt, ...)
3406{
3407 va_list ap;
3408 int str_l;
3409
3410 va_start(ap, fmt);
3411 str_l = vim_vsnprintf(str, str_m, fmt, ap);
3412 va_end(ap);
3413 return str_l;
3414}
3415
3416 static int
3417vim_vsnprintf(str, str_m, fmt, ap)
3418# else
3419 /* clumsy way to work around missing va_list */
3420# define get_a_arg(i) (i == 1 ? a1 : i == 2 ? a2 : i == 3 ? a3 : i == 4 ? a4 : i == 5 ? a5 : i == 6 ? a6 : i == 7 ? a7 : i == 8 ? a8 : i == 9 ? a9 : a10)
3421
3422/* VARARGS */
3423 int
3424#ifdef __BORLANDC__
3425_RTLENTRYF
3426#endif
3427vim_snprintf(str, str_m, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
3428# endif
3429 char *str;
3430 size_t str_m;
3431 char *fmt;
3432# ifdef HAVE_STDARG_H
3433 va_list ap;
3434# else
3435 long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
3436# endif
3437{
3438 size_t str_l = 0;
3439 char *p = fmt;
3440# ifndef HAVE_STDARG_H
3441 int arg_idx = 1;
3442# endif
3443
3444 if (p == NULL)
3445 p = "";
3446 while (*p != NUL)
3447 {
3448 if (*p != '%')
3449 {
3450 char *q = strchr(p + 1, '%');
3451 size_t n = (q == NULL) ? STRLEN(p) : (q - p);
3452
3453 if (str_l < str_m)
3454 {
3455 size_t avail = str_m - str_l;
3456
3457 mch_memmove(str + str_l, p, n > avail ? avail : n);
3458 }
3459 p += n;
3460 str_l += n;
3461 }
3462 else
3463 {
3464 size_t min_field_width = 0, precision = 0;
3465 int zero_padding = 0, precision_specified = 0, justify_left = 0;
3466 int alternate_form = 0, force_sign = 0;
3467
3468 /* If both the ' ' and '+' flags appear, the ' ' flag should be
3469 * ignored. */
3470 int space_for_positive = 1;
3471
3472 /* allowed values: \0, h, l, L */
3473 char length_modifier = '\0';
3474
3475 /* temporary buffer for simple numeric->string conversion */
3476 char tmp[32];
3477
3478 /* string address in case of string argument */
3479 char *str_arg;
3480
3481 /* natural field width of arg without padding and sign */
3482 size_t str_arg_l;
3483
3484 /* unsigned char argument value - only defined for c conversion.
3485 * N.B. standard explicitly states the char argument for the c
3486 * conversion is unsigned */
3487 unsigned char uchar_arg;
3488
3489 /* number of zeros to be inserted for numeric conversions as
3490 * required by the precision or minimal field width */
3491 size_t number_of_zeros_to_pad = 0;
3492
3493 /* index into tmp where zero padding is to be inserted */
3494 size_t zero_padding_insertion_ind = 0;
3495
3496 /* current conversion specifier character */
3497 char fmt_spec = '\0';
3498
3499 str_arg = NULL;
3500 p++; /* skip '%' */
3501
3502 /* parse flags */
3503 while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
3504 || *p == '#' || *p == '\'')
3505 {
3506 switch (*p)
3507 {
3508 case '0': zero_padding = 1; break;
3509 case '-': justify_left = 1; break;
3510 case '+': force_sign = 1; space_for_positive = 0; break;
3511 case ' ': force_sign = 1;
3512 /* If both the ' ' and '+' flags appear, the ' '
3513 * flag should be ignored */
3514 break;
3515 case '#': alternate_form = 1; break;
3516 case '\'': break;
3517 }
3518 p++;
3519 }
3520 /* If the '0' and '-' flags both appear, the '0' flag should be
3521 * ignored. */
3522
3523 /* parse field width */
3524 if (*p == '*')
3525 {
3526 int j;
3527
3528 p++;
3529#ifndef HAVE_STDARG_H
3530 j = get_a_arg(arg_idx);
3531 ++arg_idx;
3532#else
3533 j = va_arg(ap, int);
3534#endif
3535 if (j >= 0)
3536 min_field_width = j;
3537 else
3538 {
3539 min_field_width = -j;
3540 justify_left = 1;
3541 }
3542 }
3543 else if (VIM_ISDIGIT((int)(*p)))
3544 {
3545 /* size_t could be wider than unsigned int; make sure we treat
3546 * argument like common implementations do */
3547 unsigned int uj = *p++ - '0';
3548
3549 while (VIM_ISDIGIT((int)(*p)))
3550 uj = 10 * uj + (unsigned int)(*p++ - '0');
3551 min_field_width = uj;
3552 }
3553
3554 /* parse precision */
3555 if (*p == '.')
3556 {
3557 p++;
3558 precision_specified = 1;
3559 if (*p == '*')
3560 {
3561 int j;
3562
3563#ifndef HAVE_STDARG_H
3564 j = get_a_arg(arg_idx);
3565 ++arg_idx;
3566#else
3567 j = va_arg(ap, int);
3568#endif
3569 p++;
3570 if (j >= 0)
3571 precision = j;
3572 else
3573 {
3574 precision_specified = 0;
3575 precision = 0;
3576 }
3577 }
3578 else if (VIM_ISDIGIT((int)(*p)))
3579 {
3580 /* size_t could be wider than unsigned int; make sure we
3581 * treat argument like common implementations do */
3582 unsigned int uj = *p++ - '0';
3583
3584 while (VIM_ISDIGIT((int)(*p)))
3585 uj = 10 * uj + (unsigned int)(*p++ - '0');
3586 precision = uj;
3587 }
3588 }
3589
3590 /* parse 'h', 'l' and 'll' length modifiers */
3591 if (*p == 'h' || *p == 'l')
3592 {
3593 length_modifier = *p;
3594 p++;
3595 if (length_modifier == 'l' && *p == 'l')
3596 {
3597 /* double l = long long */
3598 length_modifier = 'l'; /* treat it as a single 'l' */
3599 p++;
3600 }
3601 }
3602 fmt_spec = *p;
3603
3604 /* common synonyms: */
3605 switch (fmt_spec)
3606 {
3607 case 'i': fmt_spec = 'd'; break;
3608 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
3609 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
3610 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
3611 default: break;
3612 }
3613
3614 /* get parameter value, do initial processing */
3615 switch (fmt_spec)
3616 {
3617 /* '%' and 'c' behave similar to 's' regarding flags and field
3618 * widths */
3619 case '%':
3620 case 'c':
3621 case 's':
3622 length_modifier = '\0';
3623 zero_padding = 0; /* turn zero padding off for string
3624 conversions */
3625 str_arg_l = 1;
3626 switch (fmt_spec)
3627 {
3628 case '%':
3629 str_arg = p;
3630 break;
3631
3632 case 'c':
3633 {
3634 int j;
3635#ifndef HAVE_STDARG_H
3636 j = get_a_arg(arg_idx);
3637 ++arg_idx;
3638#else
3639 j = va_arg(ap, int);
3640#endif
3641 /* standard demands unsigned char */
3642 uchar_arg = (unsigned char)j;
3643 str_arg = (char *)&uchar_arg;
3644 break;
3645 }
3646
3647 case 's':
3648#ifndef HAVE_STDARG_H
3649 str_arg = (char *)get_a_arg(arg_idx);
3650 ++arg_idx;
3651#else
3652 str_arg = va_arg(ap, char *);
3653#endif
3654 if (str_arg == NULL)
3655 {
3656 str_arg = "[NULL]";
3657 str_arg_l = 6;
3658 }
3659 /* make sure not to address string beyond the specified
3660 * precision !!! */
3661 else if (!precision_specified)
3662 str_arg_l = strlen(str_arg);
3663 /* truncate string if necessary as requested by precision */
3664 else if (precision == 0)
3665 str_arg_l = 0;
3666 else
3667 {
3668 /* memchr on HP does not like n > 2^31 !!! */
3669 char *q = memchr(str_arg, '\0',
3670 precision <= 0x7fffffff ? precision
3671 : 0x7fffffff);
3672 str_arg_l = (q == NULL) ? precision : q - str_arg;
3673 }
3674 break;
3675
3676 default:
3677 break;
3678 }
3679 break;
3680
3681 case 'd': case 'u': case 'o': case 'x': case 'X': case 'p':
3682 {
3683 /* NOTE: the u, o, x, X and p conversion specifiers
3684 * imply the value is unsigned; d implies a signed
3685 * value */
3686
3687 /* 0 if numeric argument is zero (or if pointer is
3688 * NULL for 'p'), +1 if greater than zero (or nonzero
3689 * for unsigned arguments), -1 if negative (unsigned
3690 * argument is never negative) */
3691 int arg_sign = 0;
3692
3693 /* only defined for length modifier h, or for no
3694 * length modifiers */
3695 int int_arg = 0;
3696 unsigned int uint_arg = 0;
3697
3698 /* only defined for length modifier l */
3699 long int long_arg = 0;
3700 unsigned long int ulong_arg = 0;
3701
3702 /* pointer argument value -only defined for p
3703 * conversion */
3704 void *ptr_arg = NULL;
3705
3706 if (fmt_spec == 'p')
3707 {
3708 length_modifier = '\0';
3709#ifndef HAVE_STDARG_H
3710 ptr_arg = (void *)get_a_arg(arg_idx);
3711 ++arg_idx;
3712#else
3713 ptr_arg = va_arg(ap, void *);
3714#endif
3715 if (ptr_arg != NULL)
3716 arg_sign = 1;
3717 }
3718 else if (fmt_spec == 'd')
3719 {
3720 /* signed */
3721 switch (length_modifier)
3722 {
3723 case '\0':
3724 case 'h':
3725 /* It is non-portable to specify a second argument
3726 * of char or short to va_arg, because arguments
3727 * seen by the called function are not char or
3728 * short. C converts char and short arguments to
3729 * int before passing them to a function. */
3730#ifndef HAVE_STDARG_H
3731 int_arg = get_a_arg(arg_idx);
3732 ++arg_idx;
3733#else
3734 int_arg = va_arg(ap, int);
3735#endif
3736 if (int_arg > 0)
3737 arg_sign = 1;
3738 else if (int_arg < 0)
3739 arg_sign = -1;
3740 break;
3741 case 'l':
3742#ifndef HAVE_STDARG_H
3743 long_arg = get_a_arg(arg_idx);
3744 ++arg_idx;
3745#else
3746 long_arg = va_arg(ap, long int);
3747#endif
3748 if (long_arg > 0)
3749 arg_sign = 1;
3750 else if (long_arg < 0)
3751 arg_sign = -1;
3752 break;
3753 }
3754 }
3755 else
3756 {
3757 /* unsigned */
3758 switch (length_modifier)
3759 {
3760 case '\0':
3761 case 'h':
3762#ifndef HAVE_STDARG_H
3763 uint_arg = get_a_arg(arg_idx);
3764 ++arg_idx;
3765#else
3766 uint_arg = va_arg(ap, unsigned int);
3767#endif
3768 if (uint_arg != 0)
3769 arg_sign = 1;
3770 break;
3771 case 'l':
3772#ifndef HAVE_STDARG_H
3773 ulong_arg = get_a_arg(arg_idx);
3774 ++arg_idx;
3775#else
3776 ulong_arg = va_arg(ap, unsigned long int);
3777#endif
3778 if (ulong_arg != 0)
3779 arg_sign = 1;
3780 break;
3781 }
3782 }
3783
3784 str_arg = tmp;
3785 str_arg_l = 0;
3786
3787 /* NOTE:
3788 * For d, i, u, o, x, and X conversions, if precision is
3789 * specified, the '0' flag should be ignored. This is so
3790 * with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
3791 * FreeBSD, NetBSD; but not with Perl.
3792 */
3793 if (precision_specified)
3794 zero_padding = 0;
3795 if (fmt_spec == 'd')
3796 {
3797 if (force_sign && arg_sign >= 0)
3798 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
3799 /* leave negative numbers for sprintf to handle, to
3800 * avoid handling tricky cases like (short int)-32768 */
3801 }
3802 else if (alternate_form)
3803 {
3804 if (arg_sign != 0
3805 && (fmt_spec == 'x' || fmt_spec == 'X') )
3806 {
3807 tmp[str_arg_l++] = '0';
3808 tmp[str_arg_l++] = fmt_spec;
3809 }
3810 /* alternate form should have no effect for p
3811 * conversion, but ... */
3812 }
3813
3814 zero_padding_insertion_ind = str_arg_l;
3815 if (!precision_specified)
3816 precision = 1; /* default precision is 1 */
3817 if (precision == 0 && arg_sign == 0)
3818 {
3819 /* When zero value is formatted with an explicit
3820 * precision 0, the resulting formatted string is
3821 * empty (d, i, u, o, x, X, p). */
3822 }
3823 else
3824 {
3825 char f[5];
3826 int f_l = 0;
3827
3828 /* construct a simple format string for sprintf */
3829 f[f_l++] = '%';
3830 if (!length_modifier)
3831 ;
3832 else if (length_modifier == '2')
3833 {
3834 f[f_l++] = 'l';
3835 f[f_l++] = 'l';
3836 }
3837 else
3838 f[f_l++] = length_modifier;
3839 f[f_l++] = fmt_spec;
3840 f[f_l++] = '\0';
3841
3842 if (fmt_spec == 'p')
3843 str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
3844 else if (fmt_spec == 'd')
3845 {
3846 /* signed */
3847 switch (length_modifier)
3848 {
3849 case '\0':
3850 case 'h': str_arg_l += sprintf(
3851 tmp + str_arg_l, f, int_arg);
3852 break;
3853 case 'l': str_arg_l += sprintf(
3854 tmp + str_arg_l, f, long_arg);
3855 break;
3856 }
3857 }
3858 else
3859 {
3860 /* unsigned */
3861 switch (length_modifier)
3862 {
3863 case '\0':
3864 case 'h': str_arg_l += sprintf(
3865 tmp + str_arg_l, f, uint_arg);
3866 break;
3867 case 'l': str_arg_l += sprintf(
3868 tmp + str_arg_l, f, ulong_arg);
3869 break;
3870 }
3871 }
3872
3873 /* include the optional minus sign and possible
3874 * "0x" in the region before the zero padding
3875 * insertion point */
3876 if (zero_padding_insertion_ind < str_arg_l
3877 && tmp[zero_padding_insertion_ind] == '-')
3878 zero_padding_insertion_ind++;
3879 if (zero_padding_insertion_ind + 1 < str_arg_l
3880 && tmp[zero_padding_insertion_ind] == '0'
3881 && (tmp[zero_padding_insertion_ind + 1] == 'x'
3882 || tmp[zero_padding_insertion_ind + 1] == 'X'))
3883 zero_padding_insertion_ind += 2;
3884 }
3885
3886 {
3887 size_t num_of_digits = str_arg_l
3888 - zero_padding_insertion_ind;
3889
3890 if (alternate_form && fmt_spec == 'o'
3891 /* unless zero is already the first
3892 * character */
3893 && !(zero_padding_insertion_ind < str_arg_l
3894 && tmp[zero_padding_insertion_ind] == '0'))
3895 {
3896 /* assure leading zero for alternate-form
3897 * octal numbers */
3898 if (!precision_specified
3899 || precision < num_of_digits + 1)
3900 {
3901 /* precision is increased to force the
3902 * first character to be zero, except if a
3903 * zero value is formatted with an
3904 * explicit precision of zero */
3905 precision = num_of_digits + 1;
3906 precision_specified = 1;
3907 }
3908 }
3909 /* zero padding to specified precision? */
3910 if (num_of_digits < precision)
3911 number_of_zeros_to_pad = precision - num_of_digits;
3912 }
3913 /* zero padding to specified minimal field width? */
3914 if (!justify_left && zero_padding)
3915 {
3916 int n = min_field_width - (str_arg_l
3917 + number_of_zeros_to_pad);
3918 if (n > 0)
3919 number_of_zeros_to_pad += n;
3920 }
3921 break;
3922 }
3923
3924 default:
3925 /* unrecognized conversion specifier, keep format string
3926 * as-is */
3927 zero_padding = 0; /* turn zero padding off for non-numeric
3928 convers. */
3929 justify_left = 1;
3930 min_field_width = 0; /* reset flags */
3931
3932 /* discard the unrecognized conversion, just keep *
3933 * the unrecognized conversion character */
3934 str_arg = p;
3935 str_arg_l = 0;
3936 if (*p != NUL)
3937 str_arg_l++; /* include invalid conversion specifier
3938 unchanged if not at end-of-string */
3939 break;
3940 }
3941
3942 if (*p != NUL)
3943 p++; /* step over the just processed conversion specifier */
3944
3945 /* insert padding to the left as requested by min_field_width;
3946 * this does not include the zero padding in case of numerical
3947 * conversions*/
3948 if (!justify_left)
3949 {
3950 /* left padding with blank or zero */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003951 int pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003952
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003953 if (pn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003954 {
3955 if (str_l < str_m)
3956 {
3957 size_t avail = str_m - str_l;
3958
3959 vim_memset(str + str_l, zero_padding ? '0' : ' ',
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003960 (size_t)pn > avail ? avail : pn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003961 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003962 str_l += pn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003963 }
3964 }
3965
3966 /* zero padding as requested by the precision or by the minimal
3967 * field width for numeric conversions required? */
3968 if (number_of_zeros_to_pad <= 0)
3969 {
3970 /* will not copy first part of numeric right now, *
3971 * force it to be copied later in its entirety */
3972 zero_padding_insertion_ind = 0;
3973 }
3974 else
3975 {
3976 /* insert first part of numerics (sign or '0x') before zero
3977 * padding */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003978 int zn = zero_padding_insertion_ind;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003979
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003980 if (zn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003981 {
3982 if (str_l < str_m)
3983 {
3984 size_t avail = str_m - str_l;
3985
3986 mch_memmove(str + str_l, str_arg,
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003987 (size_t)zn > avail ? avail : zn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003988 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003989 str_l += zn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003990 }
3991
3992 /* insert zero padding as requested by the precision or min
3993 * field width */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003994 zn = number_of_zeros_to_pad;
3995 if (zn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003996 {
3997 if (str_l < str_m)
3998 {
3999 size_t avail = str_m-str_l;
4000
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004001 vim_memset(str + str_l, '0',
4002 (size_t)zn > avail ? avail : zn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004003 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004004 str_l += zn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004005 }
4006 }
4007
4008 /* insert formatted string
4009 * (or as-is conversion specifier for unknown conversions) */
4010 {
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004011 int sn = str_arg_l - zero_padding_insertion_ind;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004012
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004013 if (sn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004014 {
4015 if (str_l < str_m)
4016 {
4017 size_t avail = str_m - str_l;
4018
4019 mch_memmove(str + str_l,
4020 str_arg + zero_padding_insertion_ind,
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004021 (size_t)sn > avail ? avail : sn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004022 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004023 str_l += sn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004024 }
4025 }
4026
4027 /* insert right padding */
4028 if (justify_left)
4029 {
4030 /* right blank padding to the field width */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004031 int pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004032
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004033 if (pn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004034 {
4035 if (str_l < str_m)
4036 {
4037 size_t avail = str_m - str_l;
4038
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004039 vim_memset(str + str_l, ' ',
4040 (size_t)pn > avail ? avail : pn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004041 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004042 str_l += pn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004043 }
4044 }
4045 }
4046 }
4047
4048 if (str_m > 0)
4049 {
4050 /* make sure the string is null-terminated even at the expense of
4051 * overwriting the last character (shouldn't happen, but just in case)
4052 * */
4053 str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
4054 }
4055
4056 /* Return the number of characters formatted (excluding trailing null
4057 * character), that is, the number of characters that would have been
4058 * written to the buffer if it were large enough. */
4059 return (int)str_l;
4060}
4061
4062#endif /* PROTO */