blob: a43cb9287f1c3ce414f4cb8504d541cec3f21aee [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
Bram Moolenaarea424162005-06-16 21:51:00 +0000660/* emsg3() and emsgn() are in misc2.c to avoid warnings for the prototypes. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661
Bram Moolenaardf177f62005-02-22 08:39:57 +0000662 void
663emsg_invreg(name)
664 int name;
665{
666 EMSG2(_("E354: Invalid register name: '%s'"), transchar(name));
667}
668
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669/*
670 * Like msg(), but truncate to a single line if p_shm contains 't', or when
671 * "force" is TRUE. This truncates in another way as for normal messages.
672 * Careful: The string may be changed by msg_may_trunc()!
673 * Returns a pointer to the printed message, if wait_return() not called.
674 */
675 char_u *
676msg_trunc_attr(s, force, attr)
677 char_u *s;
678 int force;
679 int attr;
680{
681 int n;
682
683 /* Add message to history before truncating */
684 add_msg_hist(s, -1, attr);
685
686 s = msg_may_trunc(force, s);
687
688 msg_hist_off = TRUE;
689 n = msg_attr(s, attr);
690 msg_hist_off = FALSE;
691
692 if (n)
693 return s;
694 return NULL;
695}
696
697/*
698 * Check if message "s" should be truncated at the start (for filenames).
699 * Return a pointer to where the truncated message starts.
700 * Note: May change the message by replacing a character with '<'.
701 */
702 char_u *
703msg_may_trunc(force, s)
704 int force;
705 char_u *s;
706{
707 int n;
708 int room;
709
710 room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1;
711 if ((force || (shortmess(SHM_TRUNC) && !exmode_active))
712 && (n = (int)STRLEN(s) - room) > 0)
713 {
714#ifdef FEAT_MBYTE
715 if (has_mbyte)
716 {
717 int size = vim_strsize(s);
718
719 for (n = 0; size >= room; )
720 {
721 size -= (*mb_ptr2cells)(s + n);
722 n += (*mb_ptr2len_check)(s + n);
723 }
724 --n;
725 }
726#endif
727 s += n;
728 *s = '<';
729 }
730 return s;
731}
732
733 static void
734add_msg_hist(s, len, attr)
735 char_u *s;
736 int len; /* -1 for undetermined length */
737 int attr;
738{
739 struct msg_hist *p;
740
741 if (msg_hist_off || msg_silent != 0)
742 return;
743
744 /* Don't let the message history get too big */
745 while (msg_hist_len > 20)
Bram Moolenaar0a5fe212005-06-24 23:01:23 +0000746 (void)delete_first_msg();
747
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748 /* allocate an entry and add the message at the end of the history */
749 p = (struct msg_hist *)alloc((int)sizeof(struct msg_hist));
750 if (p != NULL)
751 {
752 if (len < 0)
753 len = (int)STRLEN(s);
754 /* remove leading and trailing newlines */
755 while (len > 0 && *s == '\n')
756 {
757 ++s;
758 --len;
759 }
760 while (len > 0 && s[len - 1] == '\n')
761 --len;
762 p->msg = vim_strnsave(s, len);
763 p->next = NULL;
764 p->attr = attr;
765 if (last_msg_hist != NULL)
766 last_msg_hist->next = p;
767 last_msg_hist = p;
768 if (first_msg_hist == NULL)
769 first_msg_hist = last_msg_hist;
770 ++msg_hist_len;
771 }
772}
773
774/*
Bram Moolenaar0a5fe212005-06-24 23:01:23 +0000775 * Delete the first (oldest) message from the history.
776 * Returns FAIL if there are no messages.
777 */
778 int
779delete_first_msg()
780{
781 struct msg_hist *p;
782
783 if (msg_hist_len <= 0)
784 return FAIL;
785 p = first_msg_hist;
786 first_msg_hist = p->next;
787 vim_free(p->msg);
788 vim_free(p);
789 --msg_hist_len;
790 return OK;
791}
792
793/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794 * ":messages" command.
795 */
796/*ARGSUSED*/
797 void
798ex_messages(eap)
799 exarg_T *eap;
800{
801 struct msg_hist *p;
802 char_u *s;
803
804 msg_hist_off = TRUE;
805
806 s = mch_getenv((char_u *)"LANG");
807 if (s != NULL && *s != NUL)
808 msg_attr((char_u *)
809 _("Messages maintainer: Bram Moolenaar <Bram@vim.org>"),
810 hl_attr(HLF_T));
811
812 for (p = first_msg_hist; p != NULL; p = p->next)
813 if (p->msg != NULL)
814 msg_attr(p->msg, p->attr);
815
816 msg_hist_off = FALSE;
817}
818
Bram Moolenaar7171abe2004-10-11 10:06:20 +0000819#if defined(FEAT_CON_DIALOG) || defined(FIND_REPLACE_DIALOG) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000820/*
821 * Call this after prompting the user. This will avoid a hit-return message
822 * and a delay.
823 */
Bram Moolenaar7171abe2004-10-11 10:06:20 +0000824 void
Bram Moolenaar071d4272004-06-13 20:20:40 +0000825msg_end_prompt()
826{
827 need_wait_return = FALSE;
828 emsg_on_display = FALSE;
829 cmdline_row = msg_row;
830 msg_col = 0;
831 msg_clr_eos();
832}
833#endif
834
835/*
836 * wait for the user to hit a key (normally a return)
837 * if 'redraw' is TRUE, clear and redraw the screen
838 * if 'redraw' is FALSE, just redraw the screen
839 * if 'redraw' is -1, don't redraw at all
840 */
841 void
842wait_return(redraw)
843 int redraw;
844{
845 int c;
846 int oldState;
847 int tmpState;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000848 int had_got_int;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849
850 if (redraw == TRUE)
851 must_redraw = CLEAR;
852
853 /* If using ":silent cmd", don't wait for a return. Also don't set
854 * need_wait_return to do it later. */
855 if (msg_silent != 0)
856 return;
857
858/*
859 * With the global command (and some others) we only need one return at the
860 * end. Adjust cmdline_row to avoid the next message overwriting the last one.
861 * When inside vgetc(), we can't wait for a typed character at all.
862 */
863 if (vgetc_busy)
864 return;
865 if (no_wait_return)
866 {
867 need_wait_return = TRUE;
868 if (!exmode_active)
869 cmdline_row = msg_row;
870 return;
871 }
872
873 redir_off = TRUE; /* don't redirect this message */
874 oldState = State;
875 if (quit_more)
876 {
877 c = CAR; /* just pretend CR was hit */
878 quit_more = FALSE;
879 got_int = FALSE;
880 }
881 else if (exmode_active)
882 {
883 MSG_PUTS(" "); /* make sure the cursor is on the right line */
884 c = CAR; /* no need for a return in ex mode */
885 got_int = FALSE;
886 }
887 else
888 {
889 /* Make sure the hit-return prompt is on screen when 'guioptions' was
890 * just changed. */
891 screenalloc(FALSE);
892
893 State = HITRETURN;
894#ifdef FEAT_MOUSE
895 setmouse();
896#endif
897#ifdef USE_ON_FLY_SCROLL
898 dont_scroll = TRUE; /* disallow scrolling here */
899#endif
900 hit_return_msg();
901
Bram Moolenaar071d4272004-06-13 20:20:40 +0000902 do
903 {
904 /* Remember "got_int", if it is set vgetc() probably returns a
905 * CTRL-C, but we need to loop then. */
906 had_got_int = got_int;
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000907
908 /* Don't do mappings here, we put the character back in the
909 * typeahead buffer. */
910 ++no_mapping;
911 ++allow_keys;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000912 c = safe_vgetc();
Bram Moolenaar4317d9b2005-03-18 20:25:31 +0000913 if (had_got_int && !global_busy)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000914 got_int = FALSE;
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000915 --no_mapping;
916 --allow_keys;
917
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918#ifdef FEAT_CLIPBOARD
919 /* Strange way to allow copying (yanking) a modeless selection at
920 * the hit-enter prompt. Use CTRL-Y, because the same is used in
921 * Cmdline-mode and it's harmless when there is no selection. */
922 if (c == Ctrl_Y && clip_star.state == SELECT_DONE)
923 {
924 clip_copy_modeless_selection(TRUE);
925 c = K_IGNORE;
926 }
927#endif
928 } while ((had_got_int && c == Ctrl_C)
929 || c == K_IGNORE
930#ifdef FEAT_GUI
931 || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR
932#endif
933#ifdef FEAT_MOUSE
934 || c == K_LEFTDRAG || c == K_LEFTRELEASE
935 || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
936 || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
937 || c == K_MOUSEDOWN || c == K_MOUSEUP
938 || (!mouse_has(MOUSE_RETURN)
939 && mouse_row < msg_row
940 && (c == K_LEFTMOUSE
941 || c == K_MIDDLEMOUSE
942 || c == K_RIGHTMOUSE
943 || c == K_X1MOUSE
944 || c == K_X2MOUSE))
945#endif
946 );
947 ui_breakcheck();
948#ifdef FEAT_MOUSE
949 /*
950 * Avoid that the mouse-up event causes visual mode to start.
951 */
952 if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
953 || c == K_X1MOUSE || c == K_X2MOUSE)
954 (void)jump_to_mouse(MOUSE_SETPOS, NULL, 0);
955 else
956#endif
957 if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C)
958 {
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000959 char_u buf[2];
960
961 /* Put the character back in the typeahead buffer. Don't use the
962 * stuff buffer, because lmaps wouldn't work. */
963 buf[0] = c;
964 buf[1] = NUL;
965 ins_typebuf(buf, REMAP_YES, 0, !KeyTyped, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 do_redraw = TRUE; /* need a redraw even though there is
Bram Moolenaarf95dc3b2005-05-22 22:02:25 +0000967 typeahead */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000968 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969 }
970 redir_off = FALSE;
971
972 /*
973 * If the user hits ':', '?' or '/' we get a command line from the next
974 * line.
975 */
976 if (c == ':' || c == '?' || c == '/')
977 {
978 if (!exmode_active)
979 cmdline_row = msg_row;
980 skip_redraw = TRUE; /* skip redraw once */
981 do_redraw = FALSE;
982 }
983
984 /*
985 * If the window size changed set_shellsize() will redraw the screen.
986 * Otherwise the screen is only redrawn if 'redraw' is set and no ':'
987 * typed.
988 */
989 tmpState = State;
990 State = oldState; /* restore State before set_shellsize */
991#ifdef FEAT_MOUSE
992 setmouse();
993#endif
994 msg_check();
995
996#if defined(UNIX) || defined(VMS)
997 /*
998 * When switching screens, we need to output an extra newline on exit.
999 */
1000 if (swapping_screen() && !termcap_active)
1001 newline_on_exit = TRUE;
1002#endif
1003
1004 need_wait_return = FALSE;
1005 did_wait_return = TRUE;
1006 emsg_on_display = FALSE; /* can delete error message now */
1007 lines_left = -1; /* reset lines_left at next msg_start() */
1008 reset_last_sourcing();
1009 if (keep_msg != NULL && vim_strsize(keep_msg) >=
1010 (Rows - cmdline_row - 1) * Columns + sc_col)
1011 {
1012 vim_free(keep_msg);
1013 keep_msg = NULL; /* don't redisplay message, it's too long */
1014 }
1015
1016 if (tmpState == SETWSIZE) /* got resize event while in vgetc() */
1017 {
1018 starttermcap(); /* start termcap before redrawing */
1019 shell_resized();
1020 }
1021 else if (!skip_redraw
1022 && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1)))
1023 {
1024 starttermcap(); /* start termcap before redrawing */
1025 redraw_later(VALID);
1026 }
1027}
1028
1029/*
1030 * Write the hit-return prompt.
1031 */
1032 static void
1033hit_return_msg()
1034{
1035 if (msg_didout) /* start on a new line */
1036 msg_putchar('\n');
1037 if (got_int)
1038 MSG_PUTS(_("Interrupt: "));
1039
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 MSG_PUTS_ATTR(_("Hit ENTER or type command to continue"), hl_attr(HLF_R));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001041 if (!msg_use_printf())
1042 msg_clr_eos();
1043}
1044
1045/*
1046 * Set "keep_msg" to "s". Free the old value and check for NULL pointer.
1047 */
1048 void
1049set_keep_msg(s)
1050 char_u *s;
1051{
1052 vim_free(keep_msg);
1053 if (s != NULL && msg_silent == 0)
1054 keep_msg = vim_strsave(s);
1055 else
1056 keep_msg = NULL;
Bram Moolenaaraab21c32005-01-25 21:46:35 +00001057 keep_msg_more = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001058}
1059
1060/*
1061 * Prepare for outputting characters in the command line.
1062 */
1063 void
1064msg_start()
1065{
1066 int did_return = FALSE;
1067
1068 vim_free(keep_msg);
1069 keep_msg = NULL; /* don't display old message now */
1070 if (!msg_scroll && full_screen) /* overwrite last message */
1071 {
1072 msg_row = cmdline_row;
1073 msg_col =
1074#ifdef FEAT_RIGHTLEFT
1075 cmdmsg_rl ? Columns - 1 :
1076#endif
1077 0;
1078 }
1079 else if (msg_didout) /* start message on next line */
1080 {
1081 msg_putchar('\n');
1082 did_return = TRUE;
1083 if (exmode_active != EXMODE_NORMAL)
1084 cmdline_row = msg_row;
1085 }
1086 if (!msg_didany || lines_left < 0)
1087 msg_starthere();
1088 if (msg_silent == 0)
1089 {
1090 msg_didout = FALSE; /* no output on current line yet */
1091 cursor_off();
1092 }
1093
1094 /* when redirecting, may need to start a new line. */
1095 if (!did_return)
1096 redir_write((char_u *)"\n", -1);
1097}
1098
1099/*
1100 * Note that the current msg position is where messages start.
1101 */
1102 void
1103msg_starthere()
1104{
1105 lines_left = cmdline_row;
1106 msg_didany = FALSE;
1107}
1108
1109 void
1110msg_putchar(c)
1111 int c;
1112{
1113 msg_putchar_attr(c, 0);
1114}
1115
1116 void
1117msg_putchar_attr(c, attr)
1118 int c;
1119 int attr;
1120{
1121#ifdef FEAT_MBYTE
1122 char_u buf[MB_MAXBYTES + 1];
1123#else
1124 char_u buf[4];
1125#endif
1126
1127 if (IS_SPECIAL(c))
1128 {
1129 buf[0] = K_SPECIAL;
1130 buf[1] = K_SECOND(c);
1131 buf[2] = K_THIRD(c);
1132 buf[3] = NUL;
1133 }
1134 else
1135 {
1136#ifdef FEAT_MBYTE
1137 buf[(*mb_char2bytes)(c, buf)] = NUL;
1138#else
1139 buf[0] = c;
1140 buf[1] = NUL;
1141#endif
1142 }
1143 msg_puts_attr(buf, attr);
1144}
1145
1146 void
1147msg_outnum(n)
1148 long n;
1149{
1150 char_u buf[20];
1151
1152 sprintf((char *)buf, "%ld", n);
1153 msg_puts(buf);
1154}
1155
1156 void
1157msg_home_replace(fname)
1158 char_u *fname;
1159{
1160 msg_home_replace_attr(fname, 0);
1161}
1162
1163#if defined(FEAT_FIND_ID) || defined(PROTO)
1164 void
1165msg_home_replace_hl(fname)
1166 char_u *fname;
1167{
1168 msg_home_replace_attr(fname, hl_attr(HLF_D));
1169}
1170#endif
1171
1172 static void
1173msg_home_replace_attr(fname, attr)
1174 char_u *fname;
1175 int attr;
1176{
1177 char_u *name;
1178
1179 name = home_replace_save(NULL, fname);
1180 if (name != NULL)
1181 msg_outtrans_attr(name, attr);
1182 vim_free(name);
1183}
1184
1185/*
1186 * Output 'len' characters in 'str' (including NULs) with translation
1187 * if 'len' is -1, output upto a NUL character.
1188 * Use attributes 'attr'.
1189 * Return the number of characters it takes on the screen.
1190 */
1191 int
1192msg_outtrans(str)
1193 char_u *str;
1194{
1195 return msg_outtrans_attr(str, 0);
1196}
1197
1198 int
1199msg_outtrans_attr(str, attr)
1200 char_u *str;
1201 int attr;
1202{
1203 return msg_outtrans_len_attr(str, (int)STRLEN(str), attr);
1204}
1205
1206 int
1207msg_outtrans_len(str, len)
1208 char_u *str;
1209 int len;
1210{
1211 return msg_outtrans_len_attr(str, len, 0);
1212}
1213
1214/*
1215 * Output one character at "p". Return pointer to the next character.
1216 * Handles multi-byte characters.
1217 */
1218 char_u *
1219msg_outtrans_one(p, attr)
1220 char_u *p;
1221 int attr;
1222{
1223#ifdef FEAT_MBYTE
1224 int l;
1225
1226 if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
1227 {
1228 msg_outtrans_len_attr(p, l, attr);
1229 return p + l;
1230 }
1231#endif
1232 msg_puts_attr(transchar_byte(*p), attr);
1233 return p + 1;
1234}
1235
1236 int
1237msg_outtrans_len_attr(msgstr, len, attr)
1238 char_u *msgstr;
1239 int len;
1240 int attr;
1241{
1242 int retval = 0;
1243 char_u *str = msgstr;
1244 char_u *plain_start = msgstr;
1245 char_u *s;
1246#ifdef FEAT_MBYTE
1247 int mb_l;
1248 int c;
1249#endif
1250
1251 /* if MSG_HIST flag set, add message to history */
1252 if (attr & MSG_HIST)
1253 {
1254 add_msg_hist(str, len, attr);
1255 attr &= ~MSG_HIST;
1256 }
1257
1258#ifdef FEAT_MBYTE
1259 /* If the string starts with a composing character first draw a space on
1260 * which the composing char can be drawn. */
1261 if (enc_utf8 && utf_iscomposing(utf_ptr2char(msgstr)))
1262 msg_puts_attr((char_u *)" ", attr);
1263#endif
1264
1265 /*
1266 * Go over the string. Special characters are translated and printed.
1267 * Normal characters are printed several at a time.
1268 */
1269 while (--len >= 0)
1270 {
1271#ifdef FEAT_MBYTE
1272 if (enc_utf8)
1273 /* Don't include composing chars after the end. */
1274 mb_l = utfc_ptr2len_check_len(str, len + 1);
1275 else if (has_mbyte)
1276 mb_l = (*mb_ptr2len_check)(str);
1277 else
1278 mb_l = 1;
1279 if (has_mbyte && mb_l > 1)
1280 {
1281 c = (*mb_ptr2char)(str);
1282 if (vim_isprintc(c))
1283 /* printable multi-byte char: count the cells. */
1284 retval += (*mb_ptr2cells)(str);
1285 else
1286 {
1287 /* unprintable multi-byte char: print the printable chars so
1288 * far and the translation of the unprintable char. */
1289 if (str > plain_start)
1290 msg_puts_attr_len(plain_start, (int)(str - plain_start),
1291 attr);
1292 plain_start = str + mb_l;
1293 msg_puts_attr(transchar(c), attr == 0 ? hl_attr(HLF_8) : attr);
1294 retval += char2cells(c);
1295 }
1296 len -= mb_l - 1;
1297 str += mb_l;
1298 }
1299 else
1300#endif
1301 {
1302 s = transchar_byte(*str);
1303 if (s[1] != NUL)
1304 {
1305 /* unprintable char: print the printable chars so far and the
1306 * translation of the unprintable char. */
1307 if (str > plain_start)
1308 msg_puts_attr_len(plain_start, (int)(str - plain_start),
1309 attr);
1310 plain_start = str + 1;
1311 msg_puts_attr(s, attr == 0 ? hl_attr(HLF_8) : attr);
1312 }
1313 retval += ptr2cells(str);
1314 ++str;
1315 }
1316 }
1317
1318 if (str > plain_start)
1319 /* print the printable chars at the end */
1320 msg_puts_attr_len(plain_start, (int)(str - plain_start), attr);
1321
1322 return retval;
1323}
1324
1325#if defined(FEAT_QUICKFIX) || defined(PROTO)
1326 void
1327msg_make(arg)
1328 char_u *arg;
1329{
1330 int i;
1331 static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB";
1332
1333 arg = skipwhite(arg);
1334 for (i = 5; *arg && i >= 0; --i)
1335 if (*arg++ != str[i])
1336 break;
1337 if (i < 0)
1338 {
1339 msg_putchar('\n');
1340 for (i = 0; rs[i]; ++i)
1341 msg_putchar(rs[i] - 3);
1342 }
1343}
1344#endif
1345
1346/*
1347 * Output the string 'str' upto a NUL character.
1348 * Return the number of characters it takes on the screen.
1349 *
1350 * If K_SPECIAL is encountered, then it is taken in conjunction with the
1351 * following character and shown as <F1>, <S-Up> etc. Any other character
1352 * which is not printable shown in <> form.
1353 * If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>.
1354 * If a character is displayed in one of these special ways, is also
1355 * highlighted (its highlight name is '8' in the p_hl variable).
1356 * Otherwise characters are not highlighted.
1357 * This function is used to show mappings, where we want to see how to type
1358 * the character/string -- webb
1359 */
1360 int
1361msg_outtrans_special(strstart, from)
1362 char_u *strstart;
1363 int from; /* TRUE for lhs of a mapping */
1364{
1365 char_u *str = strstart;
1366 int retval = 0;
1367 char_u *string;
1368 int attr;
1369 int len;
1370
1371 attr = hl_attr(HLF_8);
1372 while (*str != NUL)
1373 {
1374 /* Leading and trailing spaces need to be displayed in <> form. */
1375 if ((str == strstart || str[1] == NUL) && *str == ' ')
1376 {
1377 string = (char_u *)"<Space>";
1378 ++str;
1379 }
1380 else
1381 string = str2special(&str, from);
1382 len = vim_strsize(string);
1383 /* Highlight special keys */
1384 msg_puts_attr(string, len > 1
1385#ifdef FEAT_MBYTE
1386 && (*mb_ptr2len_check)(string) <= 1
1387#endif
1388 ? attr : 0);
1389 retval += len;
1390 }
1391 return retval;
1392}
1393
1394/*
1395 * Return the printable string for the key codes at "*sp".
1396 * Used for translating the lhs or rhs of a mapping to printable chars.
1397 * Advances "sp" to the next code.
1398 */
1399 char_u *
1400str2special(sp, from)
1401 char_u **sp;
1402 int from; /* TRUE for lhs of mapping */
1403{
1404 int c;
1405 static char_u buf[7];
1406 char_u *str = *sp;
1407 int modifiers = 0;
1408 int special = FALSE;
1409
1410#ifdef FEAT_MBYTE
1411 if (has_mbyte)
1412 {
1413 char_u *p;
1414
1415 /* Try to un-escape a multi-byte character. Return the un-escaped
1416 * string if it is a multi-byte character. */
1417 p = mb_unescape(sp);
1418 if (p != NULL)
1419 return p;
1420 }
1421#endif
1422
1423 c = *str;
1424 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1425 {
1426 if (str[1] == KS_MODIFIER)
1427 {
1428 modifiers = str[2];
1429 str += 3;
1430 c = *str;
1431 }
1432 if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL)
1433 {
1434 c = TO_SPECIAL(str[1], str[2]);
1435 str += 2;
1436 if (c == K_ZERO) /* display <Nul> as ^@ */
1437 c = NUL;
1438 }
1439 if (IS_SPECIAL(c) || modifiers) /* special key */
1440 special = TRUE;
1441 }
1442 *sp = str + 1;
1443
1444#ifdef FEAT_MBYTE
1445 /* For multi-byte characters check for an illegal byte. */
1446 if (has_mbyte && MB_BYTE2LEN(*str) > (*mb_ptr2len_check)(str))
1447 {
1448 transchar_nonprint(buf, c);
1449 return buf;
1450 }
1451#endif
1452
1453 /* Make unprintable characters in <> form, also <M-Space> and <Tab>.
1454 * Use <Space> only for lhs of a mapping. */
1455 if (special || char2cells(c) > 1 || (from && c == ' '))
1456 return get_special_key_name(c, modifiers);
1457 buf[0] = c;
1458 buf[1] = NUL;
1459 return buf;
1460}
1461
1462/*
1463 * Translate a key sequence into special key names.
1464 */
1465 void
1466str2specialbuf(sp, buf, len)
1467 char_u *sp;
1468 char_u *buf;
1469 int len;
1470{
1471 char_u *s;
1472
1473 *buf = NUL;
1474 while (*sp)
1475 {
1476 s = str2special(&sp, FALSE);
1477 if ((int)(STRLEN(s) + STRLEN(buf)) < len)
1478 STRCAT(buf, s);
1479 }
1480}
1481
1482/*
1483 * print line for :print or :list command
1484 */
1485 void
Bram Moolenaardf177f62005-02-22 08:39:57 +00001486msg_prt_line(s, list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001487 char_u *s;
Bram Moolenaardf177f62005-02-22 08:39:57 +00001488 int list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001489{
1490 int c;
1491 int col = 0;
1492 int n_extra = 0;
1493 int c_extra = 0;
1494 char_u *p_extra = NULL; /* init to make SASC shut up */
1495 int n;
1496 int attr= 0;
1497 char_u *trail = NULL;
1498#ifdef FEAT_MBYTE
1499 int l;
1500 char_u buf[MB_MAXBYTES + 1];
1501#endif
1502
Bram Moolenaardf177f62005-02-22 08:39:57 +00001503 if (curwin->w_p_list)
1504 list = TRUE;
1505
Bram Moolenaar071d4272004-06-13 20:20:40 +00001506 /* find start of trailing whitespace */
Bram Moolenaardf177f62005-02-22 08:39:57 +00001507 if (list && lcs_trail)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001508 {
1509 trail = s + STRLEN(s);
1510 while (trail > s && vim_iswhite(trail[-1]))
1511 --trail;
1512 }
1513
1514 /* output a space for an empty line, otherwise the line will be
1515 * overwritten */
Bram Moolenaardf177f62005-02-22 08:39:57 +00001516 if (*s == NUL && !(list && lcs_eol != NUL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 msg_putchar(' ');
1518
1519 for (;;)
1520 {
1521 if (n_extra)
1522 {
1523 --n_extra;
1524 if (c_extra)
1525 c = c_extra;
1526 else
1527 c = *p_extra++;
1528 }
1529#ifdef FEAT_MBYTE
1530 else if (has_mbyte && (l = (*mb_ptr2len_check)(s)) > 1)
1531 {
1532 col += (*mb_ptr2cells)(s);
1533 mch_memmove(buf, s, (size_t)l);
1534 buf[l] = NUL;
1535 msg_puts_attr(buf, attr);
1536 s += l;
1537 continue;
1538 }
1539#endif
1540 else
1541 {
1542 attr = 0;
1543 c = *s++;
Bram Moolenaardf177f62005-02-22 08:39:57 +00001544 if (c == TAB && (!list || lcs_tab1))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545 {
1546 /* tab amount depends on current column */
1547 n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
Bram Moolenaardf177f62005-02-22 08:39:57 +00001548 if (!list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001549 {
1550 c = ' ';
1551 c_extra = ' ';
1552 }
1553 else
1554 {
1555 c = lcs_tab1;
1556 c_extra = lcs_tab2;
1557 attr = hl_attr(HLF_8);
1558 }
1559 }
Bram Moolenaardf177f62005-02-22 08:39:57 +00001560 else if (c == NUL && list && lcs_eol != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001561 {
1562 p_extra = (char_u *)"";
1563 c_extra = NUL;
1564 n_extra = 1;
1565 c = lcs_eol;
1566 attr = hl_attr(HLF_AT);
1567 --s;
1568 }
1569 else if (c != NUL && (n = byte2cells(c)) > 1)
1570 {
1571 n_extra = n - 1;
1572 p_extra = transchar_byte(c);
1573 c_extra = NUL;
1574 c = *p_extra++;
1575 }
1576 else if (c == ' ' && trail != NULL && s > trail)
1577 {
1578 c = lcs_trail;
1579 attr = hl_attr(HLF_8);
1580 }
1581 }
1582
1583 if (c == NUL)
1584 break;
1585
1586 msg_putchar_attr(c, attr);
1587 col++;
1588 }
1589 msg_clr_eos();
1590}
1591
1592#ifdef FEAT_MBYTE
1593/*
1594 * Use screen_puts() to output one multi-byte character.
1595 * Return the pointer "s" advanced to the next character.
1596 */
1597 static char_u *
1598screen_puts_mbyte(s, l, attr)
1599 char_u *s;
1600 int l;
1601 int attr;
1602{
1603 int cw;
1604
1605 msg_didout = TRUE; /* remember that line is not empty */
1606 cw = (*mb_ptr2cells)(s);
1607 if (cw > 1 && (
1608#ifdef FEAT_RIGHTLEFT
1609 cmdmsg_rl ? msg_col <= 1 :
1610#endif
1611 msg_col == Columns - 1))
1612 {
1613 /* Doesn't fit, print a highlighted '>' to fill it up. */
1614 msg_screen_putchar('>', hl_attr(HLF_AT));
1615 return s;
1616 }
1617
1618 screen_puts_len(s, l, msg_row, msg_col, attr);
1619#ifdef FEAT_RIGHTLEFT
1620 if (cmdmsg_rl)
1621 {
1622 msg_col -= cw;
1623 if (msg_col == 0)
1624 {
1625 msg_col = Columns;
1626 ++msg_row;
1627 }
1628 }
1629 else
1630#endif
1631 {
1632 msg_col += cw;
1633 if (msg_col >= Columns)
1634 {
1635 msg_col = 0;
1636 ++msg_row;
1637 }
1638 }
1639 return s + l;
1640}
1641#endif
1642
1643/*
1644 * Output a string to the screen at position msg_row, msg_col.
1645 * Update msg_row and msg_col for the next message.
1646 */
1647 void
1648msg_puts(s)
1649 char_u *s;
1650{
1651 msg_puts_attr(s, 0);
1652}
1653
1654 void
1655msg_puts_title(s)
1656 char_u *s;
1657{
1658 msg_puts_attr(s, hl_attr(HLF_T));
1659}
1660
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661/*
1662 * Show a message in such a way that it always fits in the line. Cut out a
1663 * part in the middle and replace it with "..." when necessary.
1664 * Does not handle multi-byte characters!
1665 */
1666 void
1667msg_puts_long_attr(longstr, attr)
1668 char_u *longstr;
1669 int attr;
1670{
1671 msg_puts_long_len_attr(longstr, (int)strlen((char *)longstr), attr);
1672}
1673
1674 void
1675msg_puts_long_len_attr(longstr, len, attr)
1676 char_u *longstr;
1677 int len;
1678 int attr;
1679{
1680 int slen = len;
1681 int room;
1682
1683 room = Columns - msg_col;
1684 if (len > room && room >= 20)
1685 {
1686 slen = (room - 3) / 2;
1687 msg_outtrans_len_attr(longstr, slen, attr);
1688 msg_puts_attr((char_u *)"...", hl_attr(HLF_8));
1689 }
1690 msg_outtrans_len_attr(longstr + len - slen, slen, attr);
1691}
1692
1693/*
1694 * Basic function for writing a message with highlight attributes.
1695 */
1696 void
1697msg_puts_attr(s, attr)
1698 char_u *s;
1699 int attr;
1700{
1701 msg_puts_attr_len(s, -1, attr);
1702}
1703
1704/*
1705 * Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes).
1706 * When "maxlen" is -1 there is no maximum length.
1707 * When "maxlen" is >= 0 the message is not put in the history.
1708 */
1709 static void
1710msg_puts_attr_len(str, maxlen, attr)
1711 char_u *str;
1712 int maxlen;
1713 int attr;
1714{
1715 int oldState;
1716 char_u *s = str;
1717 char_u *p;
1718 char_u buf[4];
1719 char_u *t_s = str; /* string from "t_s" to "s" is still todo */
1720 int t_col = 0; /* screen cells todo, 0 when "t_s" not used */
1721#ifdef FEAT_MBYTE
1722 int l;
1723 int cw;
1724#endif
1725 int c;
1726
1727 /*
1728 * If redirection is on, also write to the redirection file.
1729 */
1730 redir_write(s, maxlen);
1731
1732 /*
1733 * Don't print anything when using ":silent cmd".
1734 */
1735 if (msg_silent != 0)
1736 return;
1737
1738 /* if MSG_HIST flag set, add message to history */
1739 if ((attr & MSG_HIST) && maxlen < 0)
1740 {
1741 add_msg_hist(s, -1, attr);
1742 attr &= ~MSG_HIST;
1743 }
1744
1745 /*
1746 * When writing something to the screen after it has scrolled, requires a
1747 * wait-return prompt later. Needed when scrolling, resetting
1748 * need_wait_return after some prompt, and then outputting something
1749 * without scrolling
1750 */
1751 if (msg_scrolled && !msg_scrolled_ign)
1752 need_wait_return = TRUE;
1753 msg_didany = TRUE; /* remember that something was outputted */
1754
1755 /*
1756 * If there is no valid screen, use fprintf so we can see error messages.
1757 * If termcap is not active, we may be writing in an alternate console
1758 * window, cursor positioning may not work correctly (window size may be
1759 * different, e.g. for Win32 console) or we just don't know where the
1760 * cursor is.
1761 */
1762 if (msg_use_printf())
1763 {
1764#ifdef WIN3264
1765 if (!(silent_mode && p_verbose == 0))
1766 mch_settmode(TMODE_COOK); /* handle '\r' and '\n' correctly */
1767#endif
1768 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
1769 {
1770 if (!(silent_mode && p_verbose == 0))
1771 {
1772 p = &buf[0];
1773 /* NL --> CR NL translation (for Unix, not for "--version") */
1774 /* NL --> CR translation (for Mac) */
1775 if (*s == '\n' && !info_message)
1776 *p++ = '\r';
1777#if defined(USE_CR) && !defined(MACOS_X_UNIX)
1778 else
1779#endif
1780 *p++ = *s;
1781 *p = '\0';
1782 if (info_message) /* informative message, not an error */
1783 mch_msg((char *)buf);
1784 else
1785 mch_errmsg((char *)buf);
1786 }
1787
1788 /* primitive way to compute the current column */
1789#ifdef FEAT_RIGHTLEFT
1790 if (cmdmsg_rl)
1791 {
1792 if (*s == '\r' || *s == '\n')
1793 msg_col = Columns - 1;
1794 else
1795 --msg_col;
1796 }
1797 else
1798#endif
1799 {
1800 if (*s == '\r' || *s == '\n')
1801 msg_col = 0;
1802 else
1803 ++msg_col;
1804 }
1805 ++s;
1806 }
1807 msg_didout = TRUE; /* assume that line is not empty */
1808
1809#ifdef WIN3264
1810 if (!(silent_mode && p_verbose == 0))
1811 mch_settmode(TMODE_RAW);
1812#endif
1813 return;
1814 }
1815
1816 did_wait_return = FALSE;
1817 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
1818 {
1819 /*
1820 * The screen is scrolled up when:
1821 * - When outputting a newline in the last row
1822 * - when outputting a character in the last column of the last row
1823 * (some terminals scroll automatically, some don't. To avoid
1824 * problems we scroll ourselves)
1825 */
1826 if (msg_row >= Rows - 1
1827 && (*s == '\n'
1828 || (
1829#ifdef FEAT_RIGHTLEFT
1830 cmdmsg_rl
1831 ? (
1832 msg_col <= 1
1833 || (*s == TAB && msg_col <= 7)
1834# ifdef FEAT_MBYTE
1835 || (has_mbyte && (*mb_ptr2cells)(s) > 1 && msg_col <= 2)
1836# endif
1837 )
1838 :
1839#endif
1840 (msg_col + t_col >= Columns - 1
1841 || (*s == TAB && msg_col + t_col >= ((Columns - 1) & ~7))
1842# ifdef FEAT_MBYTE
1843 || (has_mbyte && (*mb_ptr2cells)(s) > 1
1844 && msg_col + t_col >= Columns - 2)
1845# endif
1846 ))))
1847 {
1848 if (t_col > 0)
1849 {
1850 /* output postponed text */
1851 t_puts(t_col, t_s, s, attr);
1852 t_col = 0;
1853 }
1854
1855 /* When no more prompt an no more room, truncate here */
1856 if (msg_no_more && lines_left == 0)
1857 break;
1858#ifdef FEAT_GUI
1859 /* Remove the cursor before scrolling, ScreenLines[] is going to
1860 * become invalid. */
1861 if (gui.in_use)
1862 gui_undraw_cursor();
1863#endif
1864 /* scrolling up always works */
1865 screen_del_lines(0, 0, 1, (int)Rows, TRUE, NULL);
1866
1867 if (!can_clear((char_u *)" "))
1868 {
1869 /* Scrolling up doesn't result in the right background. Set
1870 * the background here. It's not efficient, but avoids that
1871 * we have to do it all over the code. */
1872 screen_fill((int)Rows - 1, (int)Rows, 0,
1873 (int)Columns, ' ', ' ', 0);
1874
1875 /* Also clear the last char of the last but one line if it was
1876 * not cleared before to avoid a scroll-up. */
1877 if (ScreenAttrs[LineOffset[Rows - 2] + Columns - 1]
1878 == (sattr_T)-1)
1879 screen_fill((int)Rows - 2, (int)Rows - 1,
1880 (int)Columns - 1, (int)Columns, ' ', ' ', 0);
1881 }
1882
1883 msg_row = Rows - 2;
1884 if (msg_col >= Columns) /* can happen after screen resize */
1885 msg_col = Columns - 1;
1886
1887 ++msg_scrolled;
1888 need_wait_return = TRUE; /* may need wait_return in main() */
1889 if (must_redraw < VALID)
1890 must_redraw = VALID;
1891 redraw_cmdline = TRUE;
1892 if (cmdline_row > 0 && !exmode_active)
1893 --cmdline_row;
1894
1895 /*
1896 * if screen is completely filled wait for a character
1897 */
1898 if (p_more && --lines_left == 0 && State != HITRETURN
1899 && !msg_no_more && !exmode_active)
1900 {
1901 oldState = State;
1902 State = ASKMORE;
1903#ifdef FEAT_MOUSE
1904 setmouse();
1905#endif
1906 msg_moremsg(FALSE);
1907 for (;;)
1908 {
1909 /*
1910 * Get a typed character directly from the user.
1911 */
1912 c = get_keystroke();
1913
1914#if defined(FEAT_MENU) && defined(FEAT_GUI)
1915 if (c == K_MENU)
1916 {
1917 int idx = get_menu_index(current_menu, ASKMORE);
1918
1919 /* Used a menu. If it starts with CTRL-Y, it must
1920 * be a "Copy" for the clipboard. Otherwise
1921 * assume that we end */
1922 if (idx == MENU_INDEX_INVALID)
1923 continue;
1924 c = *current_menu->strings[idx];
1925 if (c != NUL && current_menu->strings[idx][1] != NUL)
1926 ins_typebuf(current_menu->strings[idx] + 1,
1927 current_menu->noremap[idx], 0, TRUE,
1928 current_menu->silent[idx]);
1929 }
1930#endif
1931
1932 switch (c)
1933 {
1934 case BS:
1935 case 'k':
1936 case K_UP:
1937 if (!more_back_used)
1938 {
1939 msg_moremsg(TRUE);
1940 continue;
1941 }
1942 more_back = 1;
1943 lines_left = 1;
1944 break;
1945 case CAR: /* one extra line */
1946 case NL:
1947 case 'j':
1948 case K_DOWN:
1949 lines_left = 1;
1950 break;
1951 case ':': /* start new command line */
1952#ifdef FEAT_CON_DIALOG
1953 if (!confirm_msg_used)
1954#endif
1955 {
1956 /* Since got_int is set all typeahead will be
1957 * flushed, but we want to keep this ':', remember
1958 * that in a special way. */
1959 typeahead_noflush(':');
1960 cmdline_row = Rows - 1; /* put ':' on this line */
1961 skip_redraw = TRUE; /* skip redraw once */
1962 need_wait_return = FALSE; /* don't wait in main() */
1963 }
1964 /*FALLTHROUGH*/
1965 case 'q': /* quit */
1966 case Ctrl_C:
1967 case ESC:
1968#ifdef FEAT_CON_DIALOG
1969 if (confirm_msg_used)
1970 {
1971 /* Jump to the choices of the dialog. */
1972 s = confirm_msg_tail;
1973 lines_left = Rows - 1;
1974 }
1975 else
1976#endif
1977 {
1978 got_int = TRUE;
1979 quit_more = TRUE;
1980 }
1981 break;
1982 case 'u': /* Up half a page */
1983 case K_PAGEUP:
1984 if (!more_back_used)
1985 {
1986 msg_moremsg(TRUE);
1987 continue;
1988 }
1989 more_back = Rows / 2;
1990 /*FALLTHROUGH*/
1991 case 'd': /* Down half a page */
1992 lines_left = Rows / 2;
1993 break;
1994 case 'b': /* one page back */
1995 if (!more_back_used)
1996 {
1997 msg_moremsg(TRUE);
1998 continue;
1999 }
2000 more_back = Rows - 1;
2001 /*FALLTHROUGH*/
2002 case ' ': /* one extra page */
2003 case K_PAGEDOWN:
2004 case K_LEFTMOUSE:
2005 lines_left = Rows - 1;
2006 break;
2007
2008#ifdef FEAT_CLIPBOARD
2009 case Ctrl_Y:
2010 /* Strange way to allow copying (yanking) a modeless
2011 * selection at the more prompt. Use CTRL-Y,
2012 * because the same is used in Cmdline-mode and at the
2013 * hit-enter prompt. However, scrolling one line up
2014 * might be expected... */
2015 if (clip_star.state == SELECT_DONE)
2016 clip_copy_modeless_selection(TRUE);
2017 continue;
2018#endif
2019 default: /* no valid response */
2020 msg_moremsg(TRUE);
2021 continue;
2022 }
2023 break;
2024 }
2025
2026 /* clear the --more-- message */
2027 screen_fill((int)Rows - 1, (int)Rows,
2028 0, (int)Columns, ' ', ' ', 0);
2029 State = oldState;
2030#ifdef FEAT_MOUSE
2031 setmouse();
2032#endif
2033 if (quit_more)
2034 {
2035 msg_row = Rows - 1;
2036 msg_col = 0;
2037 return; /* the string is not displayed! */
2038 }
2039#ifdef FEAT_RIGHTLEFT
2040 if (cmdmsg_rl)
2041 msg_col = Columns - 1;
2042#endif
2043 }
2044 }
2045
2046 if (t_col > 0
2047 && (vim_strchr((char_u *)"\n\r\b\t", *s) != NULL
2048 || *s == BELL
2049 || msg_col + t_col >= Columns
2050#ifdef FEAT_MBYTE
2051 || (has_mbyte && (*mb_ptr2cells)(s) > 1
2052 && msg_col + t_col >= Columns - 1)
2053#endif
2054 ))
2055 {
2056 /* output any postponed text */
2057 t_puts(t_col, t_s, s, attr);
2058 t_col = 0;
2059 }
2060
2061 if (*s == '\n') /* go to next line */
2062 {
2063 msg_didout = FALSE; /* remember that line is empty */
2064 msg_col = 0;
2065 if (++msg_row >= Rows) /* safety check */
2066 msg_row = Rows - 1;
2067 }
2068 else if (*s == '\r') /* go to column 0 */
2069 {
2070 msg_col = 0;
2071 }
2072 else if (*s == '\b') /* go to previous char */
2073 {
2074 if (msg_col)
2075 --msg_col;
2076 }
2077 else if (*s == TAB) /* translate into spaces */
2078 {
2079 do
2080 msg_screen_putchar(' ', attr);
2081 while (msg_col & 7);
2082 }
2083 else if (*s == BELL) /* beep (from ":sh") */
2084 vim_beep();
2085 else
2086 {
2087#ifdef FEAT_MBYTE
2088 if (has_mbyte)
2089 {
2090 cw = (*mb_ptr2cells)(s);
2091 if (enc_utf8 && maxlen >= 0)
2092 /* avoid including composing chars after the end */
2093 l = utfc_ptr2len_check_len(s, (int)((str + maxlen) - s));
2094 else
2095 l = (*mb_ptr2len_check)(s);
2096 }
2097 else
2098 {
2099 cw = 1;
2100 l = 1;
2101 }
2102#endif
2103 /* When drawing from right to left or when a double-wide character
2104 * doesn't fit, draw a single character here. Otherwise collect
2105 * characters and draw them all at once later. */
2106#if defined(FEAT_RIGHTLEFT) || defined(FEAT_MBYTE)
2107 if (
2108# ifdef FEAT_RIGHTLEFT
2109 cmdmsg_rl
2110# ifdef FEAT_MBYTE
2111 ||
2112# endif
2113# endif
2114# ifdef FEAT_MBYTE
2115 (cw > 1 && msg_col + t_col >= Columns - 1)
2116# endif
2117 )
2118 {
2119# ifdef FEAT_MBYTE
2120 if (l > 1)
2121 s = screen_puts_mbyte(s, l, attr) - 1;
2122 else
2123# endif
2124 msg_screen_putchar(*s, attr);
2125 }
2126 else
2127#endif
2128 {
2129 /* postpone this character until later */
2130 if (t_col == 0)
2131 t_s = s;
2132#ifdef FEAT_MBYTE
2133 t_col += cw;
2134 s += l - 1;
2135#else
2136 ++t_col;
2137#endif
2138 }
2139 }
2140 ++s;
2141 }
2142
2143 /* output any postponed text */
2144 if (t_col > 0)
2145 t_puts(t_col, t_s, s, attr);
2146
2147 msg_check();
2148}
2149
2150/*
2151 * Output any postponed text for msg_puts_attr_len().
2152 */
2153 static void
2154t_puts(t_col, t_s, s, attr)
2155 int t_col;
2156 char_u *t_s;
2157 char_u *s;
2158 int attr;
2159{
2160 /* output postponed text */
2161 msg_didout = TRUE; /* remember that line is not empty */
2162 screen_puts_len(t_s, (int)(s - t_s), msg_row, msg_col, attr);
2163 msg_col += t_col;
2164#ifdef FEAT_MBYTE
2165 /* If the string starts with a composing character don't increment the
2166 * column position for it. */
2167 if (enc_utf8 && utf_iscomposing(utf_ptr2char(t_s)))
2168 --msg_col;
2169#endif
2170 if (msg_col >= Columns)
2171 {
2172 msg_col = 0;
2173 ++msg_row;
2174 }
2175}
2176
2177
2178/*
2179 * Returns TRUE when messages should be printed with mch_errmsg().
2180 * This is used when there is no valid screen, so we can see error messages.
2181 * If termcap is not active, we may be writing in an alternate console
2182 * window, cursor positioning may not work correctly (window size may be
2183 * different, e.g. for Win32 console) or we just don't know where the
2184 * cursor is.
2185 */
2186 int
2187msg_use_printf()
2188{
2189 return (!msg_check_screen()
2190#if defined(WIN3264) && !defined(FEAT_GUI_MSWIN)
2191 || !termcap_active
2192#endif
2193 || (swapping_screen() && !termcap_active)
2194 );
2195}
2196
2197#if defined(USE_MCH_ERRMSG) || defined(PROTO)
2198
2199#ifdef mch_errmsg
2200# undef mch_errmsg
2201#endif
2202#ifdef mch_msg
2203# undef mch_msg
2204#endif
2205
2206/*
2207 * Give an error message. To be used when the screen hasn't been initialized
2208 * yet. When stderr can't be used, collect error messages until the GUI has
2209 * started and they can be displayed in a message box.
2210 */
2211 void
2212mch_errmsg(str)
2213 char *str;
2214{
2215 int len;
2216
2217#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI)
2218 /* On Unix use stderr if it's a tty.
2219 * When not going to start the GUI also use stderr.
2220 * On Mac, when started from Finder, stderr is the console. */
2221 if (
2222# ifdef UNIX
2223# ifdef MACOS_X_UNIX
2224 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0)
2225# else
2226 isatty(2)
2227# endif
2228# ifdef FEAT_GUI
2229 ||
2230# endif
2231# endif
2232# ifdef FEAT_GUI
2233 !(gui.in_use || gui.starting)
2234# endif
2235 )
2236 {
2237 fprintf(stderr, "%s", str);
2238 return;
2239 }
2240#endif
2241
2242 /* avoid a delay for a message that isn't there */
2243 emsg_on_display = FALSE;
2244
2245 len = (int)STRLEN(str) + 1;
2246 if (error_ga.ga_growsize == 0)
2247 {
2248 error_ga.ga_growsize = 80;
2249 error_ga.ga_itemsize = 1;
2250 }
2251 if (ga_grow(&error_ga, len) == OK)
2252 {
2253 mch_memmove((char_u *)error_ga.ga_data + error_ga.ga_len,
2254 (char_u *)str, len);
2255#ifdef UNIX
2256 /* remove CR characters, they are displayed */
2257 {
2258 char_u *p;
2259
2260 p = (char_u *)error_ga.ga_data + error_ga.ga_len;
2261 for (;;)
2262 {
2263 p = vim_strchr(p, '\r');
2264 if (p == NULL)
2265 break;
2266 *p = ' ';
2267 }
2268 }
2269#endif
2270 --len; /* don't count the NUL at the end */
2271 error_ga.ga_len += len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002272 }
2273}
2274
2275/*
2276 * Give a message. To be used when the screen hasn't been initialized yet.
2277 * When there is no tty, collect messages until the GUI has started and they
2278 * can be displayed in a message box.
2279 */
2280 void
2281mch_msg(str)
2282 char *str;
2283{
2284#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI)
2285 /* On Unix use stdout if we have a tty. This allows "vim -h | more" and
2286 * uses mch_errmsg() when started from the desktop.
2287 * When not going to start the GUI also use stdout.
2288 * On Mac, when started from Finder, stderr is the console. */
2289 if (
2290# ifdef UNIX
2291# ifdef MACOS_X_UNIX
2292 (isatty(2) && strcmp("/dev/console", ttyname(2)) != 0)
2293# else
2294 isatty(2)
2295# endif
2296# ifdef FEAT_GUI
2297 ||
2298# endif
2299# endif
2300# ifdef FEAT_GUI
2301 !(gui.in_use || gui.starting)
2302# endif
2303 )
2304 {
2305 printf("%s", str);
2306 return;
2307 }
2308# endif
2309 mch_errmsg(str);
2310}
2311#endif /* USE_MCH_ERRMSG */
2312
2313/*
2314 * Put a character on the screen at the current message position and advance
2315 * to the next position. Only for printable ASCII!
2316 */
2317 static void
2318msg_screen_putchar(c, attr)
2319 int c;
2320 int attr;
2321{
2322 msg_didout = TRUE; /* remember that line is not empty */
2323 screen_putchar(c, msg_row, msg_col, attr);
2324#ifdef FEAT_RIGHTLEFT
2325 if (cmdmsg_rl)
2326 {
2327 if (--msg_col == 0)
2328 {
2329 msg_col = Columns;
2330 ++msg_row;
2331 }
2332 }
2333 else
2334#endif
2335 {
2336 if (++msg_col >= Columns)
2337 {
2338 msg_col = 0;
2339 ++msg_row;
2340 }
2341 }
2342}
2343
2344 void
2345msg_moremsg(full)
2346 int full;
2347{
2348 int attr;
2349
2350 attr = hl_attr(HLF_M);
2351 screen_puts((char_u *)_("-- More --"), (int)Rows - 1, 0, attr);
2352 if (full)
2353 screen_puts(more_back_used
2354 ? (char_u *)_(" (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)")
2355 : (char_u *)_(" (RET: line, SPACE: page, d: half page, q: quit)"),
2356 (int)Rows - 1, 10, attr);
2357}
2358
2359/*
2360 * Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or
2361 * exmode_active.
2362 */
2363 void
2364repeat_message()
2365{
2366 if (State == ASKMORE)
2367 {
2368 msg_moremsg(TRUE); /* display --more-- message again */
2369 msg_row = Rows - 1;
2370 }
2371#ifdef FEAT_CON_DIALOG
2372 else if (State == CONFIRM)
2373 {
2374 display_confirm_msg(); /* display ":confirm" message again */
2375 msg_row = Rows - 1;
2376 }
2377#endif
2378 else if (State == EXTERNCMD)
2379 {
2380 windgoto(msg_row, msg_col); /* put cursor back */
2381 }
2382 else if (State == HITRETURN || State == SETWSIZE)
2383 {
2384 hit_return_msg();
2385 msg_row = Rows - 1;
2386 }
2387}
2388
2389/*
2390 * msg_check_screen - check if the screen is initialized.
2391 * Also check msg_row and msg_col, if they are too big it may cause a crash.
2392 * While starting the GUI the terminal codes will be set for the GUI, but the
2393 * output goes to the terminal. Don't use the terminal codes then.
2394 */
2395 static int
2396msg_check_screen()
2397{
2398 if (!full_screen || !screen_valid(FALSE))
2399 return FALSE;
2400
2401 if (msg_row >= Rows)
2402 msg_row = Rows - 1;
2403 if (msg_col >= Columns)
2404 msg_col = Columns - 1;
2405 return TRUE;
2406}
2407
2408/*
2409 * Clear from current message position to end of screen.
2410 * Skip this when ":silent" was used, no need to clear for redirection.
2411 */
2412 void
2413msg_clr_eos()
2414{
2415 if (msg_silent == 0)
2416 msg_clr_eos_force();
2417}
2418
2419/*
2420 * Clear from current message position to end of screen.
2421 * Note: msg_col is not updated, so we remember the end of the message
2422 * for msg_check().
2423 */
2424 void
2425msg_clr_eos_force()
2426{
2427 if (msg_use_printf())
2428 {
2429 if (full_screen) /* only when termcap codes are valid */
2430 {
2431 if (*T_CD)
2432 out_str(T_CD); /* clear to end of display */
2433 else if (*T_CE)
2434 out_str(T_CE); /* clear to end of line */
2435 }
2436 }
2437 else
2438 {
2439#ifdef FEAT_RIGHTLEFT
2440 if (cmdmsg_rl)
2441 {
2442 screen_fill(msg_row, msg_row + 1, 0, msg_col + 1, ' ', ' ', 0);
2443 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
2444 }
2445 else
2446#endif
2447 {
2448 screen_fill(msg_row, msg_row + 1, msg_col, (int)Columns,
2449 ' ', ' ', 0);
2450 screen_fill(msg_row + 1, (int)Rows, 0, (int)Columns, ' ', ' ', 0);
2451 }
2452 }
2453}
2454
2455/*
2456 * Clear the command line.
2457 */
2458 void
2459msg_clr_cmdline()
2460{
2461 msg_row = cmdline_row;
2462 msg_col = 0;
2463 msg_clr_eos_force();
2464}
2465
2466/*
2467 * end putting a message on the screen
2468 * call wait_return if the message does not fit in the available space
2469 * return TRUE if wait_return not called.
2470 */
2471 int
2472msg_end()
2473{
2474 /*
2475 * if the string is larger than the window,
2476 * or the ruler option is set and we run into it,
2477 * we have to redraw the window.
2478 * Do not do this if we are abandoning the file or editing the command line.
2479 */
2480 if (!exiting && need_wait_return && !(State & CMDLINE))
2481 {
2482 wait_return(FALSE);
2483 return FALSE;
2484 }
2485 out_flush();
2486 return TRUE;
2487}
2488
2489/*
2490 * If the written message runs into the shown command or ruler, we have to
2491 * wait for hit-return and redraw the window later.
2492 */
2493 void
2494msg_check()
2495{
2496 if (msg_row == Rows - 1 && msg_col >= sc_col)
2497 {
2498 need_wait_return = TRUE;
2499 redraw_cmdline = TRUE;
2500 }
2501}
2502
2503/*
2504 * May write a string to the redirection file.
2505 * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes.
2506 */
2507 static void
2508redir_write(str, maxlen)
2509 char_u *str;
2510 int maxlen;
2511{
2512 char_u *s = str;
2513 static int cur_col = 0;
2514
Bram Moolenaar8b044b32005-05-31 22:05:58 +00002515 /* Don't do anything for displaying prompts and the like. */
2516 if (redir_off)
2517 return;
2518
2519 /*
2520 * If 'verbosefile' is set write message in that file.
2521 * Must come before the rest because of updating "msg_col".
2522 */
2523 if (*p_vfile != NUL)
2524 verbose_write(s, maxlen);
2525
2526 if (redir_fd != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527#ifdef FEAT_EVAL
Bram Moolenaardf177f62005-02-22 08:39:57 +00002528 || redir_reg || redir_vname
Bram Moolenaar071d4272004-06-13 20:20:40 +00002529#endif
Bram Moolenaar8b044b32005-05-31 22:05:58 +00002530 )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531 {
2532 /* If the string doesn't start with CR or NL, go to msg_col */
2533 if (*s != '\n' && *s != '\r')
2534 {
2535 while (cur_col < msg_col)
2536 {
2537#ifdef FEAT_EVAL
2538 if (redir_reg)
2539 write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE);
Bram Moolenaardf177f62005-02-22 08:39:57 +00002540 else if (redir_vname)
2541 var_redir_str((char_u *)" ", -1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002542 else if (redir_fd)
2543#endif
2544 fputs(" ", redir_fd);
2545 ++cur_col;
2546 }
2547 }
2548
2549#ifdef FEAT_EVAL
2550 if (redir_reg)
2551 write_reg_contents(redir_reg, s, maxlen, TRUE);
Bram Moolenaardf177f62005-02-22 08:39:57 +00002552 if (redir_vname)
2553 var_redir_str(s, maxlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002554#endif
2555
2556 /* Adjust the current column */
2557 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
2558 {
2559#ifdef FEAT_EVAL
Bram Moolenaardf177f62005-02-22 08:39:57 +00002560 if (!redir_reg && !redir_vname && redir_fd != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002561#endif
2562 putc(*s, redir_fd);
2563 if (*s == '\r' || *s == '\n')
2564 cur_col = 0;
2565 else if (*s == '\t')
2566 cur_col += (8 - cur_col % 8);
2567 else
2568 ++cur_col;
2569 ++s;
2570 }
2571
2572 if (msg_silent != 0) /* should update msg_col */
2573 msg_col = cur_col;
2574 }
2575}
2576
2577/*
Bram Moolenaar8b044b32005-05-31 22:05:58 +00002578 * Before giving verbose messsage.
2579 * Must always be called paired with verbose_leave()!
2580 */
2581 void
2582verbose_enter()
2583{
2584 if (*p_vfile != NUL)
2585 ++msg_silent;
2586}
2587
2588/*
2589 * After giving verbose message.
2590 * Must always be called paired with verbose_enter()!
2591 */
2592 void
2593verbose_leave()
2594{
2595 if (*p_vfile != NUL)
2596 if (--msg_silent < 0)
2597 msg_silent = 0;
2598}
2599
2600/*
2601 * Like verbose_enter() and set msg_scroll when displaying the message.
2602 */
2603 void
2604verbose_enter_scroll()
2605{
2606 if (*p_vfile != NUL)
2607 ++msg_silent;
2608 else
2609 /* always scroll up, don't overwrite */
2610 msg_scroll = TRUE;
2611}
2612
2613/*
2614 * Like verbose_leave() and set cmdline_row when displaying the message.
2615 */
2616 void
2617verbose_leave_scroll()
2618{
2619 if (*p_vfile != NUL)
2620 {
2621 if (--msg_silent < 0)
2622 msg_silent = 0;
2623 }
2624 else
2625 cmdline_row = msg_row;
2626}
2627
2628static FILE *verbose_fd = NULL;
2629static int verbose_did_open = FALSE;
2630
2631/*
2632 * Called when 'verbosefile' is set: stop writing to the file.
2633 */
2634 void
2635verbose_stop()
2636{
2637 if (verbose_fd != NULL)
2638 {
2639 fclose(verbose_fd);
2640 verbose_fd = NULL;
2641 }
2642 verbose_did_open = FALSE;
2643}
2644
2645/*
2646 * Open the file 'verbosefile'.
2647 * Return FAIL or OK.
2648 */
2649 int
2650verbose_open()
2651{
2652 if (verbose_fd == NULL && !verbose_did_open)
2653 {
2654 /* Only give the error message once. */
2655 verbose_did_open = TRUE;
2656
2657 verbose_fd = fopen((char *)p_vfile, "a");
2658 if (verbose_fd == NULL)
2659 {
2660 EMSG2(_(e_notopen), p_vfile);
2661 return FAIL;
2662 }
2663 }
2664 return OK;
2665}
2666
2667/*
2668 * Write a string to 'verbosefile'.
2669 * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes.
2670 */
2671 static void
2672verbose_write(str, maxlen)
2673 char_u *str;
2674 int maxlen;
2675{
2676 char_u *s = str;
2677 static int cur_col = 0;
2678
2679 /* Open the file when called the first time. */
2680 if (verbose_fd == NULL)
2681 verbose_open();
2682
2683 if (verbose_fd != NULL)
2684 {
2685 /* If the string doesn't start with CR or NL, go to msg_col */
2686 if (*s != '\n' && *s != '\r')
2687 {
2688 while (cur_col < msg_col)
2689 {
2690 fputs(" ", verbose_fd);
2691 ++cur_col;
2692 }
2693 }
2694
2695 /* Adjust the current column */
2696 while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen))
2697 {
2698 putc(*s, verbose_fd);
2699 if (*s == '\r' || *s == '\n')
2700 cur_col = 0;
2701 else if (*s == '\t')
2702 cur_col += (8 - cur_col % 8);
2703 else
2704 ++cur_col;
2705 ++s;
2706 }
2707 }
2708}
2709
2710/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 * Give a warning message (for searching).
2712 * Use 'w' highlighting and may repeat the message after redrawing
2713 */
2714 void
2715give_warning(message, hl)
2716 char_u *message;
2717 int hl;
2718{
2719 /* Don't do this for ":silent". */
2720 if (msg_silent != 0)
2721 return;
2722
2723 /* Don't want a hit-enter prompt here. */
2724 ++no_wait_return;
Bram Moolenaared203462004-06-16 11:19:22 +00002725
Bram Moolenaar071d4272004-06-13 20:20:40 +00002726#ifdef FEAT_EVAL
2727 set_vim_var_string(VV_WARNINGMSG, message, -1);
2728#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 vim_free(keep_msg);
2730 keep_msg = NULL;
2731 if (hl)
2732 keep_msg_attr = hl_attr(HLF_W);
2733 else
2734 keep_msg_attr = 0;
2735 if (msg_attr(message, keep_msg_attr) && msg_scrolled == 0)
2736 set_keep_msg(message);
2737 msg_didout = FALSE; /* overwrite this message */
2738 msg_nowait = TRUE; /* don't wait for this message */
2739 msg_col = 0;
Bram Moolenaared203462004-06-16 11:19:22 +00002740
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 --no_wait_return;
2742}
2743
2744/*
2745 * Advance msg cursor to column "col".
2746 */
2747 void
2748msg_advance(col)
2749 int col;
2750{
2751 if (msg_silent != 0) /* nothing to advance to */
2752 {
2753 msg_col = col; /* for redirection, may fill it up later */
2754 return;
2755 }
2756 if (col >= Columns) /* not enough room */
2757 col = Columns - 1;
2758 while (msg_col < col)
2759 msg_putchar(' ');
2760}
2761
2762#if defined(FEAT_CON_DIALOG) || defined(PROTO)
2763/*
2764 * Used for "confirm()" function, and the :confirm command prefix.
2765 * Versions which haven't got flexible dialogs yet, and console
2766 * versions, get this generic handler which uses the command line.
2767 *
2768 * type = one of:
2769 * VIM_QUESTION, VIM_INFO, VIM_WARNING, VIM_ERROR or VIM_GENERIC
2770 * title = title string (can be NULL for default)
2771 * (neither used in console dialogs at the moment)
2772 *
2773 * Format of the "buttons" string:
2774 * "Button1Name\nButton2Name\nButton3Name"
2775 * The first button should normally be the default/accept
2776 * The second button should be the 'Cancel' button
2777 * Other buttons- use your imagination!
2778 * A '&' in a button name becomes a shortcut, so each '&' should be before a
2779 * different letter.
2780 */
2781/* ARGSUSED */
2782 int
2783do_dialog(type, title, message, buttons, dfltbutton, textfield)
2784 int type;
2785 char_u *title;
2786 char_u *message;
2787 char_u *buttons;
2788 int dfltbutton;
2789 char_u *textfield; /* IObuff for inputdialog(), NULL otherwise */
2790{
2791 int oldState;
2792 int retval = 0;
2793 char_u *hotkeys;
2794 int c;
2795 int i;
2796
2797#ifndef NO_CONSOLE
2798 /* Don't output anything in silent mode ("ex -s") */
2799 if (silent_mode)
2800 return dfltbutton; /* return default option */
2801#endif
2802
2803#ifdef FEAT_GUI_DIALOG
2804 /* When GUI is running and 'c' not in 'guioptions', use the GUI dialog */
2805 if (gui.in_use && vim_strchr(p_go, GO_CONDIALOG) == NULL)
2806 {
2807 c = gui_mch_dialog(type, title, message, buttons, dfltbutton,
2808 textfield);
2809 msg_end_prompt();
2810
2811 /* Flush output to avoid that further messages and redrawing is done
2812 * in the wrong order. */
2813 out_flush();
2814 gui_mch_update();
2815
2816 return c;
2817 }
2818#endif
2819
2820 oldState = State;
2821 State = CONFIRM;
2822#ifdef FEAT_MOUSE
2823 setmouse();
2824#endif
2825
2826 /*
2827 * Since we wait for a keypress, don't make the
2828 * user press RETURN as well afterwards.
2829 */
2830 ++no_wait_return;
2831 hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
2832
2833 if (hotkeys != NULL)
2834 {
2835 for (;;)
2836 {
2837 /* Get a typed character directly from the user. */
2838 c = get_keystroke();
2839 switch (c)
2840 {
2841 case CAR: /* User accepts default option */
2842 case NL:
2843 retval = dfltbutton;
2844 break;
2845 case Ctrl_C: /* User aborts/cancels */
2846 case ESC:
2847 retval = 0;
2848 break;
2849 default: /* Could be a hotkey? */
2850 if (c < 0) /* special keys are ignored here */
2851 continue;
2852 /* Make the character lowercase, as chars in "hotkeys" are. */
2853 c = MB_TOLOWER(c);
2854 retval = 1;
2855 for (i = 0; hotkeys[i]; ++i)
2856 {
2857#ifdef FEAT_MBYTE
2858 if (has_mbyte)
2859 {
2860 if ((*mb_ptr2char)(hotkeys + i) == c)
2861 break;
2862 i += (*mb_ptr2len_check)(hotkeys + i) - 1;
2863 }
2864 else
2865#endif
2866 if (hotkeys[i] == c)
2867 break;
2868 ++retval;
2869 }
2870 if (hotkeys[i])
2871 break;
2872 /* No hotkey match, so keep waiting */
2873 continue;
2874 }
2875 break;
2876 }
2877
2878 vim_free(hotkeys);
2879 }
2880
2881 State = oldState;
2882#ifdef FEAT_MOUSE
2883 setmouse();
2884#endif
2885 --no_wait_return;
2886 msg_end_prompt();
2887
2888 return retval;
2889}
2890
2891static int copy_char __ARGS((char_u *from, char_u *to, int lowercase));
2892
2893/*
2894 * Copy one character from "*from" to "*to", taking care of multi-byte
2895 * characters. Return the length of the character in bytes.
2896 */
2897 static int
2898copy_char(from, to, lowercase)
2899 char_u *from;
2900 char_u *to;
2901 int lowercase; /* make character lower case */
2902{
2903#ifdef FEAT_MBYTE
2904 int len;
2905 int c;
2906
2907 if (has_mbyte)
2908 {
2909 if (lowercase)
2910 {
2911 c = MB_TOLOWER((*mb_ptr2char)(from));
2912 return (*mb_char2bytes)(c, to);
2913 }
2914 else
2915 {
2916 len = (*mb_ptr2len_check)(from);
2917 mch_memmove(to, from, (size_t)len);
2918 return len;
2919 }
2920 }
2921 else
2922#endif
2923 {
2924 if (lowercase)
2925 *to = (char_u)TOLOWER_LOC(*from);
2926 else
2927 *to = *from;
2928 return 1;
2929 }
2930}
2931
2932/*
2933 * Format the dialog string, and display it at the bottom of
2934 * the screen. Return a string of hotkey chars (if defined) for
2935 * each 'button'. If a button has no hotkey defined, the first character of
2936 * the button is used.
2937 * The hotkeys can be multi-byte characters, but without combining chars.
2938 *
2939 * Returns an allocated string with hotkeys, or NULL for error.
2940 */
2941 static char_u *
2942msg_show_console_dialog(message, buttons, dfltbutton)
2943 char_u *message;
2944 char_u *buttons;
2945 int dfltbutton;
2946{
2947 int len = 0;
2948#ifdef FEAT_MBYTE
2949# define HOTK_LEN (has_mbyte ? MB_MAXBYTES : 1)
2950#else
2951# define HOTK_LEN 1
2952#endif
2953 int lenhotkey = HOTK_LEN; /* count first button */
2954 char_u *hotk = NULL;
2955 char_u *msgp = NULL;
2956 char_u *hotkp = NULL;
2957 char_u *r;
2958 int copy;
2959#define HAS_HOTKEY_LEN 30
2960 char_u has_hotkey[HAS_HOTKEY_LEN];
2961 int first_hotkey = FALSE; /* first char of button is hotkey */
2962 int idx;
2963
2964 has_hotkey[0] = FALSE;
2965
2966 /*
2967 * First loop: compute the size of memory to allocate.
2968 * Second loop: copy to the allocated memory.
2969 */
2970 for (copy = 0; copy <= 1; ++copy)
2971 {
2972 r = buttons;
2973 idx = 0;
2974 while (*r)
2975 {
2976 if (*r == DLG_BUTTON_SEP)
2977 {
2978 if (copy)
2979 {
2980 *msgp++ = ',';
2981 *msgp++ = ' '; /* '\n' -> ', ' */
2982
2983 /* advance to next hotkey and set default hotkey */
2984#ifdef FEAT_MBYTE
2985 if (has_mbyte)
2986 hotkp += (*mb_ptr2len_check)(hotkp);
2987 else
2988#endif
2989 ++hotkp;
2990 (void)copy_char(r + 1, hotkp, TRUE);
2991 if (dfltbutton)
2992 --dfltbutton;
2993
2994 /* If no hotkey is specified first char is used. */
2995 if (idx < HAS_HOTKEY_LEN - 1 && !has_hotkey[++idx])
2996 first_hotkey = TRUE;
2997 }
2998 else
2999 {
3000 len += 3; /* '\n' -> ', '; 'x' -> '(x)' */
3001 lenhotkey += HOTK_LEN; /* each button needs a hotkey */
3002 if (idx < HAS_HOTKEY_LEN - 1)
3003 has_hotkey[++idx] = FALSE;
3004 }
3005 }
3006 else if (*r == DLG_HOTKEY_CHAR || first_hotkey)
3007 {
3008 if (*r == DLG_HOTKEY_CHAR)
3009 ++r;
3010 first_hotkey = FALSE;
3011 if (copy)
3012 {
3013 if (*r == DLG_HOTKEY_CHAR) /* '&&a' -> '&a' */
3014 *msgp++ = *r;
3015 else
3016 {
3017 /* '&a' -> '[a]' */
3018 *msgp++ = (dfltbutton == 1) ? '[' : '(';
3019 msgp += copy_char(r, msgp, FALSE);
3020 *msgp++ = (dfltbutton == 1) ? ']' : ')';
3021
3022 /* redefine hotkey */
3023 (void)copy_char(r, hotkp, TRUE);
3024 }
3025 }
3026 else
3027 {
3028 ++len; /* '&a' -> '[a]' */
3029 if (idx < HAS_HOTKEY_LEN - 1)
3030 has_hotkey[idx] = TRUE;
3031 }
3032 }
3033 else
3034 {
3035 /* everything else copy literally */
3036 if (copy)
3037 msgp += copy_char(r, msgp, FALSE);
3038 }
3039
3040 /* advance to the next character */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003041 mb_ptr_adv(r);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003042 }
3043
3044 if (copy)
3045 {
3046 *msgp++ = ':';
3047 *msgp++ = ' ';
3048 *msgp = NUL;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003049 mb_ptr_adv(hotkp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003050 *hotkp = NUL;
3051 }
3052 else
3053 {
3054 len += STRLEN(message)
3055 + 2 /* for the NL's */
3056 + STRLEN(buttons)
3057 + 3; /* for the ": " and NUL */
3058 lenhotkey++; /* for the NUL */
3059
3060 /* If no hotkey is specified first char is used. */
3061 if (!has_hotkey[0])
3062 {
3063 first_hotkey = TRUE;
3064 len += 2; /* "x" -> "[x]" */
3065 }
3066
3067 /*
3068 * Now allocate and load the strings
3069 */
3070 vim_free(confirm_msg);
3071 confirm_msg = alloc(len);
3072 if (confirm_msg == NULL)
3073 return NULL;
3074 *confirm_msg = NUL;
3075 hotk = alloc(lenhotkey);
3076 if (hotk == NULL)
3077 return NULL;
3078
3079 *confirm_msg = '\n';
3080 STRCPY(confirm_msg + 1, message);
3081
3082 msgp = confirm_msg + 1 + STRLEN(message);
3083 hotkp = hotk;
3084
3085 /* define first default hotkey */
3086 (void)copy_char(buttons, hotkp, TRUE);
3087
3088 /* Remember where the choices start, displaying starts here when
3089 * "hotkp" typed at the more prompt. */
3090 confirm_msg_tail = msgp;
3091 *msgp++ = '\n';
3092 }
3093 }
3094
3095 display_confirm_msg();
3096 return hotk;
3097}
3098
3099/*
3100 * Display the ":confirm" message. Also called when screen resized.
3101 */
3102 void
3103display_confirm_msg()
3104{
3105 /* avoid that 'q' at the more prompt truncates the message here */
3106 ++confirm_msg_used;
3107 if (confirm_msg != NULL)
3108 msg_puts_attr(confirm_msg, hl_attr(HLF_M));
3109 --confirm_msg_used;
3110}
3111
3112#endif /* FEAT_CON_DIALOG */
3113
3114#if defined(FEAT_CON_DIALOG) || defined(FEAT_GUI_DIALOG)
3115
3116 int
3117vim_dialog_yesno(type, title, message, dflt)
3118 int type;
3119 char_u *title;
3120 char_u *message;
3121 int dflt;
3122{
3123 if (do_dialog(type,
3124 title == NULL ? (char_u *)_("Question") : title,
3125 message,
3126 (char_u *)_("&Yes\n&No"), dflt, NULL) == 1)
3127 return VIM_YES;
3128 return VIM_NO;
3129}
3130
3131 int
3132vim_dialog_yesnocancel(type, title, message, dflt)
3133 int type;
3134 char_u *title;
3135 char_u *message;
3136 int dflt;
3137{
3138 switch (do_dialog(type,
3139 title == NULL ? (char_u *)_("Question") : title,
3140 message,
3141 (char_u *)_("&Yes\n&No\n&Cancel"), dflt, NULL))
3142 {
3143 case 1: return VIM_YES;
3144 case 2: return VIM_NO;
3145 }
3146 return VIM_CANCEL;
3147}
3148
3149 int
3150vim_dialog_yesnoallcancel(type, title, message, dflt)
3151 int type;
3152 char_u *title;
3153 char_u *message;
3154 int dflt;
3155{
3156 switch (do_dialog(type,
3157 title == NULL ? (char_u *)"Question" : title,
3158 message,
3159 (char_u *)_("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"),
3160 dflt, NULL))
3161 {
3162 case 1: return VIM_YES;
3163 case 2: return VIM_NO;
3164 case 3: return VIM_ALL;
3165 case 4: return VIM_DISCARDALL;
3166 }
3167 return VIM_CANCEL;
3168}
3169
3170#endif /* FEAT_GUI_DIALOG || FEAT_CON_DIALOG */
3171
3172#if defined(FEAT_BROWSE) || defined(PROTO)
3173/*
3174 * Generic browse function. Calls gui_mch_browse() when possible.
3175 * Later this may pop-up a non-GUI file selector (external command?).
3176 */
3177 char_u *
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003178do_browse(flags, title, dflt, ext, initdir, filter, buf)
3179 int flags; /* BROWSE_SAVE and BROWSE_DIR */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003180 char_u *title; /* title for the window */
3181 char_u *dflt; /* default file name (may include directory) */
3182 char_u *ext; /* extension added */
3183 char_u *initdir; /* initial directory, NULL for current dir or
3184 when using path from "dflt" */
3185 char_u *filter; /* file name filter */
3186 buf_T *buf; /* buffer to read/write for */
3187{
3188 char_u *fname;
3189 static char_u *last_dir = NULL; /* last used directory */
3190 char_u *tofree = NULL;
3191 int save_browse = cmdmod.browse;
3192
3193 /* Must turn off browse to avoid that autocommands will get the
3194 * flag too! */
3195 cmdmod.browse = FALSE;
3196
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003197 if (title == NULL || *title == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198 {
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003199 if (flags & BROWSE_DIR)
3200 title = (char_u *)_("Select Directory dialog");
3201 else if (flags & BROWSE_SAVE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202 title = (char_u *)_("Save File dialog");
3203 else
3204 title = (char_u *)_("Open File dialog");
3205 }
3206
3207 /* When no directory specified, use default file name, default dir, buffer
3208 * dir, last dir or current dir */
3209 if ((initdir == NULL || *initdir == NUL) && dflt != NULL && *dflt != NUL)
3210 {
3211 if (mch_isdir(dflt)) /* default file name is a directory */
3212 {
3213 initdir = dflt;
3214 dflt = NULL;
3215 }
3216 else if (gettail(dflt) != dflt) /* default file name includes a path */
3217 {
3218 tofree = vim_strsave(dflt);
3219 if (tofree != NULL)
3220 {
3221 initdir = tofree;
3222 *gettail(initdir) = NUL;
3223 dflt = gettail(dflt);
3224 }
3225 }
3226 }
3227
3228 if (initdir == NULL || *initdir == NUL)
3229 {
3230 /* When 'browsedir' is a directory, use it */
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003231 if (STRCMP(p_bsdir, "last") != 0
3232 && STRCMP(p_bsdir, "buffer") != 0
3233 && STRCMP(p_bsdir, "current") != 0
3234 && mch_isdir(p_bsdir))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235 initdir = p_bsdir;
3236 /* When saving or 'browsedir' is "buffer", use buffer fname */
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003237 else if (((flags & BROWSE_SAVE) || *p_bsdir == 'b')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238 && buf != NULL && buf->b_ffname != NULL)
3239 {
3240 if (dflt == NULL || *dflt == NUL)
3241 dflt = gettail(curbuf->b_ffname);
3242 tofree = vim_strsave(curbuf->b_ffname);
3243 if (tofree != NULL)
3244 {
3245 initdir = tofree;
3246 *gettail(initdir) = NUL;
3247 }
3248 }
3249 /* When 'browsedir' is "last", use dir from last browse */
3250 else if (*p_bsdir == 'l')
3251 initdir = last_dir;
3252 /* When 'browsedir is "current", use current directory. This is the
3253 * default already, leave initdir empty. */
3254 }
3255
3256# ifdef FEAT_GUI
3257 if (gui.in_use) /* when this changes, also adjust f_has()! */
3258 {
3259 if (filter == NULL
3260# ifdef FEAT_EVAL
3261 && (filter = get_var_value((char_u *)"b:browsefilter")) == NULL
3262 && (filter = get_var_value((char_u *)"g:browsefilter")) == NULL
3263# endif
3264 )
3265 filter = BROWSE_FILTER_DEFAULT;
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003266 if (flags & BROWSE_DIR)
3267 {
3268# if defined(HAVE_GTK2) || defined(WIN3264)
3269 /* For systems that have a directory dialog. */
3270 fname = gui_mch_browsedir(title, initdir);
3271# else
3272 /* Generic solution for selecting a directory: select a file and
3273 * remove the file name. */
3274 fname = gui_mch_browse(0, title, dflt, ext, initdir, (char_u *)"");
3275# endif
3276# if !defined(HAVE_GTK2)
3277 /* Win32 adds a dummy file name, others return an arbitrary file
3278 * name. GTK+ 2 returns only the directory, */
3279 if (fname != NULL && *fname != NUL && !mch_isdir(fname))
3280 {
3281 /* Remove the file name. */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003282 char_u *tail = gettail_sep(fname);
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003283
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003284 if (tail == fname)
3285 *tail++ = '.'; /* use current dir */
3286 *tail = NUL;
3287 }
3288# endif
3289 }
3290 else
3291 fname = gui_mch_browse(flags & BROWSE_SAVE,
3292 title, dflt, ext, initdir, filter);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293
3294 /* We hang around in the dialog for a while, the user might do some
3295 * things to our files. The Win32 dialog allows deleting or renaming
3296 * a file, check timestamps. */
3297 need_check_timestamps = TRUE;
3298 did_check_timestamps = FALSE;
3299 }
3300 else
3301# endif
3302 {
3303 /* TODO: non-GUI file selector here */
3304 EMSG(_("E338: Sorry, no file browser in console mode"));
3305 fname = NULL;
3306 }
3307
3308 /* keep the directory for next time */
3309 if (fname != NULL)
3310 {
3311 vim_free(last_dir);
3312 last_dir = vim_strsave(fname);
Bram Moolenaar7171abe2004-10-11 10:06:20 +00003313 if (last_dir != NULL && !(flags & BROWSE_DIR))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003314 {
3315 *gettail(last_dir) = NUL;
3316 if (*last_dir == NUL)
3317 {
3318 /* filename only returned, must be in current dir */
3319 vim_free(last_dir);
3320 last_dir = alloc(MAXPATHL);
3321 if (last_dir != NULL)
3322 mch_dirname(last_dir, MAXPATHL);
3323 }
3324 }
3325 }
3326
3327 vim_free(tofree);
3328 cmdmod.browse = save_browse;
3329
3330 return fname;
3331}
3332#endif
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003333
3334/*
3335 * This code was included to provide a portable vsnprintf() and snprintf().
3336 * Some systems may provide their own, but we always use these for
3337 * consistency.
3338 *
3339 * This code is based on snprintf.c - a portable implementation of snprintf
3340 * by Mark Martinec <mark.martinec@ijs.si>, Version 2.2, 2000-10-06.
Bram Moolenaar8b044b32005-05-31 22:05:58 +00003341 * Included with permission. It was heavely modified to fit in Vim.
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003342 * The original code, including useful comments, can be found here:
3343 * http://www.ijs.si/software/snprintf/
3344 *
3345 * This snprintf() only supports the following conversion specifiers:
3346 * s, c, d, u, o, x, X, p (and synonyms: i, D, U, O - see below)
3347 * with flags: '-', '+', ' ', '0' and '#'.
3348 * An asterisk is supported for field width as well as precision.
3349 *
3350 * Length modifiers 'h' (short int) and 'l' (long int) are supported.
3351 * 'll' (long long int) is not supported.
3352 *
3353 * It is permitted for str_m to be zero, and it is permitted to specify NULL
3354 * pointer for resulting string argument if str_m is zero (as per ISO C99).
3355 *
3356 * The return value is the number of characters which would be generated
3357 * for the given input, excluding the trailing null. If this value
3358 * is greater or equal to str_m, not all characters from the result
3359 * have been stored in str, output bytes beyond the (str_m-1) -th character
3360 * are discarded. If str_m is greater than zero it is guaranteed
3361 * the resulting string will be null-terminated.
3362 */
3363
3364/*
3365 * When va_list is not supported we only define vim_snprintf().
3366 */
3367
3368/* When generating prototypes all of this is skipped, cproto doesn't
3369 * understand this. */
3370#ifndef PROTO
3371# ifdef HAVE_STDARG_H
3372 int
3373vim_snprintf(char *str, size_t str_m, char *fmt, ...)
3374{
3375 va_list ap;
3376 int str_l;
3377
3378 va_start(ap, fmt);
3379 str_l = vim_vsnprintf(str, str_m, fmt, ap);
3380 va_end(ap);
3381 return str_l;
3382}
3383
3384 static int
3385vim_vsnprintf(str, str_m, fmt, ap)
3386# else
3387 /* clumsy way to work around missing va_list */
3388# 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)
3389
3390/* VARARGS */
3391 int
3392#ifdef __BORLANDC__
3393_RTLENTRYF
3394#endif
3395vim_snprintf(str, str_m, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
3396# endif
3397 char *str;
3398 size_t str_m;
3399 char *fmt;
3400# ifdef HAVE_STDARG_H
3401 va_list ap;
3402# else
3403 long a1, a2, a3, a4, a5, a6, a7, a8, a9, a10;
3404# endif
3405{
3406 size_t str_l = 0;
3407 char *p = fmt;
3408# ifndef HAVE_STDARG_H
3409 int arg_idx = 1;
3410# endif
3411
3412 if (p == NULL)
3413 p = "";
3414 while (*p != NUL)
3415 {
3416 if (*p != '%')
3417 {
3418 char *q = strchr(p + 1, '%');
3419 size_t n = (q == NULL) ? STRLEN(p) : (q - p);
3420
3421 if (str_l < str_m)
3422 {
3423 size_t avail = str_m - str_l;
3424
3425 mch_memmove(str + str_l, p, n > avail ? avail : n);
3426 }
3427 p += n;
3428 str_l += n;
3429 }
3430 else
3431 {
3432 size_t min_field_width = 0, precision = 0;
3433 int zero_padding = 0, precision_specified = 0, justify_left = 0;
3434 int alternate_form = 0, force_sign = 0;
3435
3436 /* If both the ' ' and '+' flags appear, the ' ' flag should be
3437 * ignored. */
3438 int space_for_positive = 1;
3439
3440 /* allowed values: \0, h, l, L */
3441 char length_modifier = '\0';
3442
3443 /* temporary buffer for simple numeric->string conversion */
3444 char tmp[32];
3445
3446 /* string address in case of string argument */
3447 char *str_arg;
3448
3449 /* natural field width of arg without padding and sign */
3450 size_t str_arg_l;
3451
3452 /* unsigned char argument value - only defined for c conversion.
3453 * N.B. standard explicitly states the char argument for the c
3454 * conversion is unsigned */
3455 unsigned char uchar_arg;
3456
3457 /* number of zeros to be inserted for numeric conversions as
3458 * required by the precision or minimal field width */
3459 size_t number_of_zeros_to_pad = 0;
3460
3461 /* index into tmp where zero padding is to be inserted */
3462 size_t zero_padding_insertion_ind = 0;
3463
3464 /* current conversion specifier character */
3465 char fmt_spec = '\0';
3466
3467 str_arg = NULL;
3468 p++; /* skip '%' */
3469
3470 /* parse flags */
3471 while (*p == '0' || *p == '-' || *p == '+' || *p == ' '
3472 || *p == '#' || *p == '\'')
3473 {
3474 switch (*p)
3475 {
3476 case '0': zero_padding = 1; break;
3477 case '-': justify_left = 1; break;
3478 case '+': force_sign = 1; space_for_positive = 0; break;
3479 case ' ': force_sign = 1;
3480 /* If both the ' ' and '+' flags appear, the ' '
3481 * flag should be ignored */
3482 break;
3483 case '#': alternate_form = 1; break;
3484 case '\'': break;
3485 }
3486 p++;
3487 }
3488 /* If the '0' and '-' flags both appear, the '0' flag should be
3489 * ignored. */
3490
3491 /* parse field width */
3492 if (*p == '*')
3493 {
3494 int j;
3495
3496 p++;
3497#ifndef HAVE_STDARG_H
3498 j = get_a_arg(arg_idx);
3499 ++arg_idx;
3500#else
3501 j = va_arg(ap, int);
3502#endif
3503 if (j >= 0)
3504 min_field_width = j;
3505 else
3506 {
3507 min_field_width = -j;
3508 justify_left = 1;
3509 }
3510 }
3511 else if (VIM_ISDIGIT((int)(*p)))
3512 {
3513 /* size_t could be wider than unsigned int; make sure we treat
3514 * argument like common implementations do */
3515 unsigned int uj = *p++ - '0';
3516
3517 while (VIM_ISDIGIT((int)(*p)))
3518 uj = 10 * uj + (unsigned int)(*p++ - '0');
3519 min_field_width = uj;
3520 }
3521
3522 /* parse precision */
3523 if (*p == '.')
3524 {
3525 p++;
3526 precision_specified = 1;
3527 if (*p == '*')
3528 {
3529 int j;
3530
3531#ifndef HAVE_STDARG_H
3532 j = get_a_arg(arg_idx);
3533 ++arg_idx;
3534#else
3535 j = va_arg(ap, int);
3536#endif
3537 p++;
3538 if (j >= 0)
3539 precision = j;
3540 else
3541 {
3542 precision_specified = 0;
3543 precision = 0;
3544 }
3545 }
3546 else if (VIM_ISDIGIT((int)(*p)))
3547 {
3548 /* size_t could be wider than unsigned int; make sure we
3549 * treat argument like common implementations do */
3550 unsigned int uj = *p++ - '0';
3551
3552 while (VIM_ISDIGIT((int)(*p)))
3553 uj = 10 * uj + (unsigned int)(*p++ - '0');
3554 precision = uj;
3555 }
3556 }
3557
3558 /* parse 'h', 'l' and 'll' length modifiers */
3559 if (*p == 'h' || *p == 'l')
3560 {
3561 length_modifier = *p;
3562 p++;
3563 if (length_modifier == 'l' && *p == 'l')
3564 {
3565 /* double l = long long */
3566 length_modifier = 'l'; /* treat it as a single 'l' */
3567 p++;
3568 }
3569 }
3570 fmt_spec = *p;
3571
3572 /* common synonyms: */
3573 switch (fmt_spec)
3574 {
3575 case 'i': fmt_spec = 'd'; break;
3576 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
3577 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
3578 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
3579 default: break;
3580 }
3581
3582 /* get parameter value, do initial processing */
3583 switch (fmt_spec)
3584 {
3585 /* '%' and 'c' behave similar to 's' regarding flags and field
3586 * widths */
3587 case '%':
3588 case 'c':
3589 case 's':
3590 length_modifier = '\0';
3591 zero_padding = 0; /* turn zero padding off for string
3592 conversions */
3593 str_arg_l = 1;
3594 switch (fmt_spec)
3595 {
3596 case '%':
3597 str_arg = p;
3598 break;
3599
3600 case 'c':
3601 {
3602 int j;
3603#ifndef HAVE_STDARG_H
3604 j = get_a_arg(arg_idx);
3605 ++arg_idx;
3606#else
3607 j = va_arg(ap, int);
3608#endif
3609 /* standard demands unsigned char */
3610 uchar_arg = (unsigned char)j;
3611 str_arg = (char *)&uchar_arg;
3612 break;
3613 }
3614
3615 case 's':
3616#ifndef HAVE_STDARG_H
3617 str_arg = (char *)get_a_arg(arg_idx);
3618 ++arg_idx;
3619#else
3620 str_arg = va_arg(ap, char *);
3621#endif
3622 if (str_arg == NULL)
3623 {
3624 str_arg = "[NULL]";
3625 str_arg_l = 6;
3626 }
3627 /* make sure not to address string beyond the specified
3628 * precision !!! */
3629 else if (!precision_specified)
3630 str_arg_l = strlen(str_arg);
3631 /* truncate string if necessary as requested by precision */
3632 else if (precision == 0)
3633 str_arg_l = 0;
3634 else
3635 {
3636 /* memchr on HP does not like n > 2^31 !!! */
3637 char *q = memchr(str_arg, '\0',
3638 precision <= 0x7fffffff ? precision
3639 : 0x7fffffff);
3640 str_arg_l = (q == NULL) ? precision : q - str_arg;
3641 }
3642 break;
3643
3644 default:
3645 break;
3646 }
3647 break;
3648
3649 case 'd': case 'u': case 'o': case 'x': case 'X': case 'p':
3650 {
3651 /* NOTE: the u, o, x, X and p conversion specifiers
3652 * imply the value is unsigned; d implies a signed
3653 * value */
3654
3655 /* 0 if numeric argument is zero (or if pointer is
3656 * NULL for 'p'), +1 if greater than zero (or nonzero
3657 * for unsigned arguments), -1 if negative (unsigned
3658 * argument is never negative) */
3659 int arg_sign = 0;
3660
3661 /* only defined for length modifier h, or for no
3662 * length modifiers */
3663 int int_arg = 0;
3664 unsigned int uint_arg = 0;
3665
3666 /* only defined for length modifier l */
3667 long int long_arg = 0;
3668 unsigned long int ulong_arg = 0;
3669
3670 /* pointer argument value -only defined for p
3671 * conversion */
3672 void *ptr_arg = NULL;
3673
3674 if (fmt_spec == 'p')
3675 {
3676 length_modifier = '\0';
3677#ifndef HAVE_STDARG_H
3678 ptr_arg = (void *)get_a_arg(arg_idx);
3679 ++arg_idx;
3680#else
3681 ptr_arg = va_arg(ap, void *);
3682#endif
3683 if (ptr_arg != NULL)
3684 arg_sign = 1;
3685 }
3686 else if (fmt_spec == 'd')
3687 {
3688 /* signed */
3689 switch (length_modifier)
3690 {
3691 case '\0':
3692 case 'h':
3693 /* It is non-portable to specify a second argument
3694 * of char or short to va_arg, because arguments
3695 * seen by the called function are not char or
3696 * short. C converts char and short arguments to
3697 * int before passing them to a function. */
3698#ifndef HAVE_STDARG_H
3699 int_arg = get_a_arg(arg_idx);
3700 ++arg_idx;
3701#else
3702 int_arg = va_arg(ap, int);
3703#endif
3704 if (int_arg > 0)
3705 arg_sign = 1;
3706 else if (int_arg < 0)
3707 arg_sign = -1;
3708 break;
3709 case 'l':
3710#ifndef HAVE_STDARG_H
3711 long_arg = get_a_arg(arg_idx);
3712 ++arg_idx;
3713#else
3714 long_arg = va_arg(ap, long int);
3715#endif
3716 if (long_arg > 0)
3717 arg_sign = 1;
3718 else if (long_arg < 0)
3719 arg_sign = -1;
3720 break;
3721 }
3722 }
3723 else
3724 {
3725 /* unsigned */
3726 switch (length_modifier)
3727 {
3728 case '\0':
3729 case 'h':
3730#ifndef HAVE_STDARG_H
3731 uint_arg = get_a_arg(arg_idx);
3732 ++arg_idx;
3733#else
3734 uint_arg = va_arg(ap, unsigned int);
3735#endif
3736 if (uint_arg != 0)
3737 arg_sign = 1;
3738 break;
3739 case 'l':
3740#ifndef HAVE_STDARG_H
3741 ulong_arg = get_a_arg(arg_idx);
3742 ++arg_idx;
3743#else
3744 ulong_arg = va_arg(ap, unsigned long int);
3745#endif
3746 if (ulong_arg != 0)
3747 arg_sign = 1;
3748 break;
3749 }
3750 }
3751
3752 str_arg = tmp;
3753 str_arg_l = 0;
3754
3755 /* NOTE:
3756 * For d, i, u, o, x, and X conversions, if precision is
3757 * specified, the '0' flag should be ignored. This is so
3758 * with Solaris 2.6, Digital UNIX 4.0, HPUX 10, Linux,
3759 * FreeBSD, NetBSD; but not with Perl.
3760 */
3761 if (precision_specified)
3762 zero_padding = 0;
3763 if (fmt_spec == 'd')
3764 {
3765 if (force_sign && arg_sign >= 0)
3766 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
3767 /* leave negative numbers for sprintf to handle, to
3768 * avoid handling tricky cases like (short int)-32768 */
3769 }
3770 else if (alternate_form)
3771 {
3772 if (arg_sign != 0
3773 && (fmt_spec == 'x' || fmt_spec == 'X') )
3774 {
3775 tmp[str_arg_l++] = '0';
3776 tmp[str_arg_l++] = fmt_spec;
3777 }
3778 /* alternate form should have no effect for p
3779 * conversion, but ... */
3780 }
3781
3782 zero_padding_insertion_ind = str_arg_l;
3783 if (!precision_specified)
3784 precision = 1; /* default precision is 1 */
3785 if (precision == 0 && arg_sign == 0)
3786 {
3787 /* When zero value is formatted with an explicit
3788 * precision 0, the resulting formatted string is
3789 * empty (d, i, u, o, x, X, p). */
3790 }
3791 else
3792 {
3793 char f[5];
3794 int f_l = 0;
3795
3796 /* construct a simple format string for sprintf */
3797 f[f_l++] = '%';
3798 if (!length_modifier)
3799 ;
3800 else if (length_modifier == '2')
3801 {
3802 f[f_l++] = 'l';
3803 f[f_l++] = 'l';
3804 }
3805 else
3806 f[f_l++] = length_modifier;
3807 f[f_l++] = fmt_spec;
3808 f[f_l++] = '\0';
3809
3810 if (fmt_spec == 'p')
3811 str_arg_l += sprintf(tmp + str_arg_l, f, ptr_arg);
3812 else if (fmt_spec == 'd')
3813 {
3814 /* signed */
3815 switch (length_modifier)
3816 {
3817 case '\0':
3818 case 'h': str_arg_l += sprintf(
3819 tmp + str_arg_l, f, int_arg);
3820 break;
3821 case 'l': str_arg_l += sprintf(
3822 tmp + str_arg_l, f, long_arg);
3823 break;
3824 }
3825 }
3826 else
3827 {
3828 /* unsigned */
3829 switch (length_modifier)
3830 {
3831 case '\0':
3832 case 'h': str_arg_l += sprintf(
3833 tmp + str_arg_l, f, uint_arg);
3834 break;
3835 case 'l': str_arg_l += sprintf(
3836 tmp + str_arg_l, f, ulong_arg);
3837 break;
3838 }
3839 }
3840
3841 /* include the optional minus sign and possible
3842 * "0x" in the region before the zero padding
3843 * insertion point */
3844 if (zero_padding_insertion_ind < str_arg_l
3845 && tmp[zero_padding_insertion_ind] == '-')
3846 zero_padding_insertion_ind++;
3847 if (zero_padding_insertion_ind + 1 < str_arg_l
3848 && tmp[zero_padding_insertion_ind] == '0'
3849 && (tmp[zero_padding_insertion_ind + 1] == 'x'
3850 || tmp[zero_padding_insertion_ind + 1] == 'X'))
3851 zero_padding_insertion_ind += 2;
3852 }
3853
3854 {
3855 size_t num_of_digits = str_arg_l
3856 - zero_padding_insertion_ind;
3857
3858 if (alternate_form && fmt_spec == 'o'
3859 /* unless zero is already the first
3860 * character */
3861 && !(zero_padding_insertion_ind < str_arg_l
3862 && tmp[zero_padding_insertion_ind] == '0'))
3863 {
3864 /* assure leading zero for alternate-form
3865 * octal numbers */
3866 if (!precision_specified
3867 || precision < num_of_digits + 1)
3868 {
3869 /* precision is increased to force the
3870 * first character to be zero, except if a
3871 * zero value is formatted with an
3872 * explicit precision of zero */
3873 precision = num_of_digits + 1;
3874 precision_specified = 1;
3875 }
3876 }
3877 /* zero padding to specified precision? */
3878 if (num_of_digits < precision)
3879 number_of_zeros_to_pad = precision - num_of_digits;
3880 }
3881 /* zero padding to specified minimal field width? */
3882 if (!justify_left && zero_padding)
3883 {
3884 int n = min_field_width - (str_arg_l
3885 + number_of_zeros_to_pad);
3886 if (n > 0)
3887 number_of_zeros_to_pad += n;
3888 }
3889 break;
3890 }
3891
3892 default:
3893 /* unrecognized conversion specifier, keep format string
3894 * as-is */
3895 zero_padding = 0; /* turn zero padding off for non-numeric
3896 convers. */
3897 justify_left = 1;
3898 min_field_width = 0; /* reset flags */
3899
3900 /* discard the unrecognized conversion, just keep *
3901 * the unrecognized conversion character */
3902 str_arg = p;
3903 str_arg_l = 0;
3904 if (*p != NUL)
3905 str_arg_l++; /* include invalid conversion specifier
3906 unchanged if not at end-of-string */
3907 break;
3908 }
3909
3910 if (*p != NUL)
3911 p++; /* step over the just processed conversion specifier */
3912
3913 /* insert padding to the left as requested by min_field_width;
3914 * this does not include the zero padding in case of numerical
3915 * conversions*/
3916 if (!justify_left)
3917 {
3918 /* left padding with blank or zero */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003919 int pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003920
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003921 if (pn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003922 {
3923 if (str_l < str_m)
3924 {
3925 size_t avail = str_m - str_l;
3926
3927 vim_memset(str + str_l, zero_padding ? '0' : ' ',
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003928 (size_t)pn > avail ? avail : pn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003929 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003930 str_l += pn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003931 }
3932 }
3933
3934 /* zero padding as requested by the precision or by the minimal
3935 * field width for numeric conversions required? */
Bram Moolenaarea424162005-06-16 21:51:00 +00003936 if (number_of_zeros_to_pad == 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003937 {
3938 /* will not copy first part of numeric right now, *
3939 * force it to be copied later in its entirety */
3940 zero_padding_insertion_ind = 0;
3941 }
3942 else
3943 {
3944 /* insert first part of numerics (sign or '0x') before zero
3945 * padding */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003946 int zn = zero_padding_insertion_ind;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003947
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003948 if (zn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003949 {
3950 if (str_l < str_m)
3951 {
3952 size_t avail = str_m - str_l;
3953
3954 mch_memmove(str + str_l, str_arg,
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003955 (size_t)zn > avail ? avail : zn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003956 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003957 str_l += zn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003958 }
3959
3960 /* insert zero padding as requested by the precision or min
3961 * field width */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003962 zn = number_of_zeros_to_pad;
3963 if (zn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003964 {
3965 if (str_l < str_m)
3966 {
3967 size_t avail = str_m-str_l;
3968
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003969 vim_memset(str + str_l, '0',
3970 (size_t)zn > avail ? avail : zn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003971 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003972 str_l += zn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003973 }
3974 }
3975
3976 /* insert formatted string
3977 * (or as-is conversion specifier for unknown conversions) */
3978 {
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003979 int sn = str_arg_l - zero_padding_insertion_ind;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003980
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003981 if (sn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003982 {
3983 if (str_l < str_m)
3984 {
3985 size_t avail = str_m - str_l;
3986
3987 mch_memmove(str + str_l,
3988 str_arg + zero_padding_insertion_ind,
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003989 (size_t)sn > avail ? avail : sn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003990 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003991 str_l += sn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00003992 }
3993 }
3994
3995 /* insert right padding */
3996 if (justify_left)
3997 {
3998 /* right blank padding to the field width */
Bram Moolenaar686f51e2005-05-20 21:19:57 +00003999 int pn = min_field_width - (str_arg_l + number_of_zeros_to_pad);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004000
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004001 if (pn > 0)
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004002 {
4003 if (str_l < str_m)
4004 {
4005 size_t avail = str_m - str_l;
4006
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004007 vim_memset(str + str_l, ' ',
4008 (size_t)pn > avail ? avail : pn);
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004009 }
Bram Moolenaar686f51e2005-05-20 21:19:57 +00004010 str_l += pn;
Bram Moolenaar9c13b352005-05-19 20:53:52 +00004011 }
4012 }
4013 }
4014 }
4015
4016 if (str_m > 0)
4017 {
4018 /* make sure the string is null-terminated even at the expense of
4019 * overwriting the last character (shouldn't happen, but just in case)
4020 * */
4021 str[str_l <= str_m - 1 ? str_l : str_m - 1] = '\0';
4022 }
4023
4024 /* Return the number of characters formatted (excluding trailing null
4025 * character), that is, the number of characters that would have been
4026 * written to the buffer if it were large enough. */
4027 return (int)str_l;
4028}
4029
4030#endif /* PROTO */