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