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