blob: fd55605a155008a1a8bea9a173ce0b3d1a1fbcdb [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 * ex_cmds.c: some functions for command line commands
12 */
13
14#include "vim.h"
15#include "version.h"
16
17#ifdef FEAT_EX_EXTRA
18static int linelen __ARGS((int *has_tab));
19#endif
20static void do_filter __ARGS((linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, int do_in, int do_out));
21#ifdef FEAT_VIMINFO
22static char_u *viminfo_filename __ARGS((char_u *));
23static void do_viminfo __ARGS((FILE *fp_in, FILE *fp_out, int want_info, int want_marks, int force_read));
24static int viminfo_encoding __ARGS((vir_T *virp));
25static int read_viminfo_up_to_marks __ARGS((vir_T *virp, int forceit, int writing));
26#endif
27
28static int check_overwrite __ARGS((exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int other));
29static int check_readonly __ARGS((int *forceit, buf_T *buf));
30#ifdef FEAT_AUTOCMD
31static void delbuf_msg __ARGS((char_u *name));
32#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +000033static int do_sub_msg __ARGS((int count_only));
Bram Moolenaar071d4272004-06-13 20:20:40 +000034static int
35#ifdef __BORLANDC__
36 _RTLENTRYF
37#endif
38 help_compare __ARGS((const void *s1, const void *s2));
39
40/*
41 * ":ascii" and "ga".
42 */
43/*ARGSUSED*/
44 void
45do_ascii(eap)
46 exarg_T *eap;
47{
48 int c;
49 char buf1[20];
50 char buf2[20];
51 char_u buf3[7];
52#ifdef FEAT_MBYTE
53 int c1 = 0;
54 int c2 = 0;
55 int len;
56
57 if (enc_utf8)
58 c = utfc_ptr2char(ml_get_cursor(), &c1, &c2);
59 else
60#endif
61 c = gchar_cursor();
62 if (c == NUL)
63 {
64 MSG("NUL");
65 return;
66 }
67
68#ifdef FEAT_MBYTE
69 IObuff[0] = NUL;
70 if (!has_mbyte || (enc_dbcs != 0 && c < 0x100) || c < 0x80)
71#endif
72 {
73 if (c == NL) /* NUL is stored as NL */
74 c = NUL;
75 if (vim_isprintc_strict(c) && (c < ' '
76#ifndef EBCDIC
77 || c > '~'
78#endif
79 ))
80 {
81 transchar_nonprint(buf3, c);
82 sprintf(buf1, " <%s>", (char *)buf3);
83 }
84 else
85 buf1[0] = NUL;
86#ifndef EBCDIC
87 if (c >= 0x80)
88 sprintf(buf2, " <M-%s>", transchar(c & 0x7f));
89 else
90#endif
91 buf2[0] = NUL;
Bram Moolenaar555b2802005-05-19 21:08:39 +000092 vim_snprintf((char *)IObuff, IOSIZE,
93 _("<%s>%s%s %d, Hex %02x, Octal %03o"),
94 transchar(c), buf1, buf2, c, c, c);
Bram Moolenaar071d4272004-06-13 20:20:40 +000095#ifdef FEAT_MBYTE
96 c = c1;
97 c1 = c2;
98 c2 = 0;
99#endif
100 }
101
102#ifdef FEAT_MBYTE
103 /* Repeat for combining characters. */
104 while (has_mbyte && (c >= 0x100 || (enc_utf8 && c >= 0x80)))
105 {
106 len = (int)STRLEN(IObuff);
107 /* This assumes every multi-byte char is printable... */
108 if (len > 0)
109 IObuff[len++] = ' ';
110 IObuff[len++] = '<';
111 if (utf_iscomposing(c)
112#ifdef USE_GUI
113 && !gui.in_use
114#endif
115 )
116 IObuff[len++] = ' '; /* draw composing char on top of a space */
Bram Moolenaar555b2802005-05-19 21:08:39 +0000117 len += (*mb_char2bytes)(c, IObuff + len);
118 vim_snprintf((char *)IObuff + len, IOSIZE - len,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000119 c < 0x10000 ? _("> %d, Hex %04x, Octal %o")
120 : _("> %d, Hex %08x, Octal %o"), c, c, c);
121 c = c1;
122 c1 = c2;
123 c2 = 0;
124 }
125#endif
126
127 msg(IObuff);
128}
129
130#if defined(FEAT_EX_EXTRA) || defined(PROTO)
131/*
132 * ":left", ":center" and ":right": align text.
133 */
134 void
135ex_align(eap)
136 exarg_T *eap;
137{
138 pos_T save_curpos;
139 int len;
140 int indent = 0;
141 int new_indent;
142 int has_tab;
143 int width;
144
145#ifdef FEAT_RIGHTLEFT
146 if (curwin->w_p_rl)
147 {
148 /* switch left and right aligning */
149 if (eap->cmdidx == CMD_right)
150 eap->cmdidx = CMD_left;
151 else if (eap->cmdidx == CMD_left)
152 eap->cmdidx = CMD_right;
153 }
154#endif
155
156 width = atoi((char *)eap->arg);
157 save_curpos = curwin->w_cursor;
158 if (eap->cmdidx == CMD_left) /* width is used for new indent */
159 {
160 if (width >= 0)
161 indent = width;
162 }
163 else
164 {
165 /*
166 * if 'textwidth' set, use it
167 * else if 'wrapmargin' set, use it
168 * if invalid value, use 80
169 */
170 if (width <= 0)
171 width = curbuf->b_p_tw;
172 if (width == 0 && curbuf->b_p_wm > 0)
173 width = W_WIDTH(curwin) - curbuf->b_p_wm;
174 if (width <= 0)
175 width = 80;
176 }
177
178 if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL)
179 return;
180
181 for (curwin->w_cursor.lnum = eap->line1;
182 curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum)
183 {
184 if (eap->cmdidx == CMD_left) /* left align */
185 new_indent = indent;
186 else
187 {
188 len = linelen(eap->cmdidx == CMD_right ? &has_tab
189 : NULL) - get_indent();
190
191 if (len <= 0) /* skip blank lines */
192 continue;
193
194 if (eap->cmdidx == CMD_center)
195 new_indent = (width - len) / 2;
196 else
197 {
198 new_indent = width - len; /* right align */
199
200 /*
201 * Make sure that embedded TABs don't make the text go too far
202 * to the right.
203 */
204 if (has_tab)
205 while (new_indent > 0)
206 {
207 (void)set_indent(new_indent, 0);
208 if (linelen(NULL) <= width)
209 {
210 /*
211 * Now try to move the line as much as possible to
212 * the right. Stop when it moves too far.
213 */
214 do
215 (void)set_indent(++new_indent, 0);
216 while (linelen(NULL) <= width);
217 --new_indent;
218 break;
219 }
220 --new_indent;
221 }
222 }
223 }
224 if (new_indent < 0)
225 new_indent = 0;
226 (void)set_indent(new_indent, 0); /* set indent */
227 }
228 changed_lines(eap->line1, 0, eap->line2 + 1, 0L);
229 curwin->w_cursor = save_curpos;
230 beginline(BL_WHITE | BL_FIX);
231}
232
233/*
234 * Get the length of the current line, excluding trailing white space.
235 */
236 static int
237linelen(has_tab)
238 int *has_tab;
239{
240 char_u *line;
241 char_u *first;
242 char_u *last;
243 int save;
244 int len;
245
246 /* find the first non-blank character */
247 line = ml_get_curline();
248 first = skipwhite(line);
249
250 /* find the character after the last non-blank character */
251 for (last = first + STRLEN(first);
252 last > first && vim_iswhite(last[-1]); --last)
253 ;
254 save = *last;
255 *last = NUL;
256 len = linetabsize(line); /* get line length */
257 if (has_tab != NULL) /* check for embedded TAB */
258 *has_tab = (vim_strrchr(first, TAB) != NULL);
259 *last = save;
260
261 return len;
262}
263
264/*
265 * ":retab".
266 */
267 void
268ex_retab(eap)
269 exarg_T *eap;
270{
271 linenr_T lnum;
272 int got_tab = FALSE;
273 long num_spaces = 0;
274 long num_tabs;
275 long len;
276 long col;
277 long vcol;
278 long start_col = 0; /* For start of white-space string */
279 long start_vcol = 0; /* For start of white-space string */
280 int temp;
281 long old_len;
282 char_u *ptr;
283 char_u *new_line = (char_u *)1; /* init to non-NULL */
284 int did_undo; /* called u_save for current line */
285 int new_ts;
286 int save_list;
287 linenr_T first_line = 0; /* first changed line */
288 linenr_T last_line = 0; /* last changed line */
289
290 save_list = curwin->w_p_list;
291 curwin->w_p_list = 0; /* don't want list mode here */
292
293 new_ts = getdigits(&(eap->arg));
294 if (new_ts < 0)
295 {
296 EMSG(_(e_positive));
297 return;
298 }
299 if (new_ts == 0)
300 new_ts = curbuf->b_p_ts;
301 for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum)
302 {
303 ptr = ml_get(lnum);
304 col = 0;
305 vcol = 0;
306 did_undo = FALSE;
307 for (;;)
308 {
309 if (vim_iswhite(ptr[col]))
310 {
311 if (!got_tab && num_spaces == 0)
312 {
313 /* First consecutive white-space */
314 start_vcol = vcol;
315 start_col = col;
316 }
317 if (ptr[col] == ' ')
318 num_spaces++;
319 else
320 got_tab = TRUE;
321 }
322 else
323 {
324 if (got_tab || (eap->forceit && num_spaces > 1))
325 {
326 /* Retabulate this string of white-space */
327
328 /* len is virtual length of white string */
329 len = num_spaces = vcol - start_vcol;
330 num_tabs = 0;
331 if (!curbuf->b_p_et)
332 {
333 temp = new_ts - (start_vcol % new_ts);
334 if (num_spaces >= temp)
335 {
336 num_spaces -= temp;
337 num_tabs++;
338 }
339 num_tabs += num_spaces / new_ts;
340 num_spaces -= (num_spaces / new_ts) * new_ts;
341 }
342 if (curbuf->b_p_et || got_tab ||
343 (num_spaces + num_tabs < len))
344 {
345 if (did_undo == FALSE)
346 {
347 did_undo = TRUE;
348 if (u_save((linenr_T)(lnum - 1),
349 (linenr_T)(lnum + 1)) == FAIL)
350 {
351 new_line = NULL; /* flag out-of-memory */
352 break;
353 }
354 }
355
356 /* len is actual number of white characters used */
357 len = num_spaces + num_tabs;
358 old_len = (long)STRLEN(ptr);
359 new_line = lalloc(old_len - col + start_col + len + 1,
360 TRUE);
361 if (new_line == NULL)
362 break;
363 if (start_col > 0)
364 mch_memmove(new_line, ptr, (size_t)start_col);
365 mch_memmove(new_line + start_col + len,
366 ptr + col, (size_t)(old_len - col + 1));
367 ptr = new_line + start_col;
368 for (col = 0; col < len; col++)
369 ptr[col] = (col < num_tabs) ? '\t' : ' ';
370 ml_replace(lnum, new_line, FALSE);
371 if (first_line == 0)
372 first_line = lnum;
373 last_line = lnum;
374 ptr = new_line;
375 col = start_col + len;
376 }
377 }
378 got_tab = FALSE;
379 num_spaces = 0;
380 }
381 if (ptr[col] == NUL)
382 break;
383 vcol += chartabsize(ptr + col, (colnr_T)vcol);
384#ifdef FEAT_MBYTE
385 if (has_mbyte)
386 col += (*mb_ptr2len_check)(ptr + col);
387 else
388#endif
389 ++col;
390 }
391 if (new_line == NULL) /* out of memory */
392 break;
393 line_breakcheck();
394 }
395 if (got_int)
396 EMSG(_(e_interr));
397
398 if (curbuf->b_p_ts != new_ts)
399 redraw_curbuf_later(NOT_VALID);
400 if (first_line != 0)
401 changed_lines(first_line, 0, last_line + 1, 0L);
402
403 curwin->w_p_list = save_list; /* restore 'list' */
404
405 curbuf->b_p_ts = new_ts;
406 coladvance(curwin->w_curswant);
407
408 u_clearline();
409}
410#endif
411
412/*
413 * :move command - move lines line1-line2 to line dest
414 *
415 * return FAIL for failure, OK otherwise
416 */
417 int
418do_move(line1, line2, dest)
419 linenr_T line1;
420 linenr_T line2;
421 linenr_T dest;
422{
423 char_u *str;
424 linenr_T l;
425 linenr_T extra; /* Num lines added before line1 */
426 linenr_T num_lines; /* Num lines moved */
427 linenr_T last_line; /* Last line in file after adding new text */
428
429 if (dest >= line1 && dest < line2)
430 {
431 EMSG(_("E134: Move lines into themselves"));
432 return FAIL;
433 }
434
435 num_lines = line2 - line1 + 1;
436
437 /*
438 * First we copy the old text to its new location -- webb
439 * Also copy the flag that ":global" command uses.
440 */
441 if (u_save(dest, dest + 1) == FAIL)
442 return FAIL;
443 for (extra = 0, l = line1; l <= line2; l++)
444 {
445 str = vim_strsave(ml_get(l + extra));
446 if (str != NULL)
447 {
448 ml_append(dest + l - line1, str, (colnr_T)0, FALSE);
449 vim_free(str);
450 if (dest < line1)
451 extra++;
452 }
453 }
454
455 /*
456 * Now we must be careful adjusting our marks so that we don't overlap our
457 * mark_adjust() calls.
458 *
459 * We adjust the marks within the old text so that they refer to the
460 * last lines of the file (temporarily), because we know no other marks
461 * will be set there since these line numbers did not exist until we added
462 * our new lines.
463 *
464 * Then we adjust the marks on lines between the old and new text positions
465 * (either forwards or backwards).
466 *
467 * And Finally we adjust the marks we put at the end of the file back to
468 * their final destination at the new text position -- webb
469 */
470 last_line = curbuf->b_ml.ml_line_count;
471 mark_adjust(line1, line2, last_line - line2, 0L);
472 if (dest >= line2)
473 {
474 mark_adjust(line2 + 1, dest, -num_lines, 0L);
475 curbuf->b_op_start.lnum = dest - num_lines + 1;
476 curbuf->b_op_end.lnum = dest;
477 }
478 else
479 {
480 mark_adjust(dest + 1, line1 - 1, num_lines, 0L);
481 curbuf->b_op_start.lnum = dest + 1;
482 curbuf->b_op_end.lnum = dest + num_lines;
483 }
484 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
485 mark_adjust(last_line - num_lines + 1, last_line,
486 -(last_line - dest - extra), 0L);
487
488 /*
489 * Now we delete the original text -- webb
490 */
491 if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
492 return FAIL;
493
494 for (l = line1; l <= line2; l++)
495 ml_delete(line1 + extra, TRUE);
496
497 if (!global_busy && num_lines > p_report)
498 {
499 if (num_lines == 1)
500 MSG(_("1 line moved"));
501 else
502 smsg((char_u *)_("%ld lines moved"), num_lines);
503 }
504
505 /*
506 * Leave the cursor on the last of the moved lines.
507 */
508 if (dest >= line1)
509 curwin->w_cursor.lnum = dest;
510 else
511 curwin->w_cursor.lnum = dest + (line2 - line1) + 1;
512
513 if (line1 < dest)
514 changed_lines(line1, 0, dest + num_lines + 1, 0L);
515 else
516 changed_lines(dest + 1, 0, line1 + num_lines, 0L);
517
518 return OK;
519}
520
521/*
522 * ":copy"
523 */
524 void
525ex_copy(line1, line2, n)
526 linenr_T line1;
527 linenr_T line2;
528 linenr_T n;
529{
530 linenr_T count;
531 char_u *p;
532
533 count = line2 - line1 + 1;
534 curbuf->b_op_start.lnum = n + 1;
535 curbuf->b_op_end.lnum = n + count;
536 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
537
538 /*
539 * there are three situations:
540 * 1. destination is above line1
541 * 2. destination is between line1 and line2
542 * 3. destination is below line2
543 *
544 * n = destination (when starting)
545 * curwin->w_cursor.lnum = destination (while copying)
546 * line1 = start of source (while copying)
547 * line2 = end of source (while copying)
548 */
549 if (u_save(n, n + 1) == FAIL)
550 return;
551
552 curwin->w_cursor.lnum = n;
553 while (line1 <= line2)
554 {
555 /* need to use vim_strsave() because the line will be unlocked within
556 * ml_append() */
557 p = vim_strsave(ml_get(line1));
558 if (p != NULL)
559 {
560 ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE);
561 vim_free(p);
562 }
563 /* situation 2: skip already copied lines */
564 if (line1 == n)
565 line1 = curwin->w_cursor.lnum;
566 ++line1;
567 if (curwin->w_cursor.lnum < line1)
568 ++line1;
569 if (curwin->w_cursor.lnum < line2)
570 ++line2;
571 ++curwin->w_cursor.lnum;
572 }
573
574 appended_lines_mark(n, count);
575
576 msgmore((long)count);
577}
578
579/*
580 * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd"
581 * Bangs in the argument are replaced with the previously entered command.
582 * Remember the argument.
583 *
584 * RISCOS: Bangs only replaced when followed by a space, since many
585 * pathnames contain one.
586 */
587 void
588do_bang(addr_count, eap, forceit, do_in, do_out)
589 int addr_count;
590 exarg_T *eap;
591 int forceit;
592 int do_in, do_out;
593{
594 char_u *arg = eap->arg; /* command */
595 linenr_T line1 = eap->line1; /* start of range */
596 linenr_T line2 = eap->line2; /* end of range */
597 static char_u *prevcmd = NULL; /* the previous command */
598 char_u *newcmd = NULL; /* the new command */
599 int free_newcmd = FALSE; /* need to free() newcmd */
600 int ins_prevcmd;
601 char_u *t;
602 char_u *p;
603 char_u *trailarg;
604 int len;
605 int scroll_save = msg_scroll;
606
607 /*
608 * Disallow shell commands for "rvim".
609 * Disallow shell commands from .exrc and .vimrc in current directory for
610 * security reasons.
611 */
612 if (check_restricted() || check_secure())
613 return;
614
615 if (addr_count == 0) /* :! */
616 {
617 msg_scroll = FALSE; /* don't scroll here */
618 autowrite_all();
619 msg_scroll = scroll_save;
620 }
621
622 /*
623 * Try to find an embedded bang, like in :!<cmd> ! [args]
624 * (:!! is indicated by the 'forceit' variable)
625 */
626 ins_prevcmd = forceit;
627 trailarg = arg;
628 do
629 {
630 len = (int)STRLEN(trailarg) + 1;
631 if (newcmd != NULL)
632 len += (int)STRLEN(newcmd);
633 if (ins_prevcmd)
634 {
635 if (prevcmd == NULL)
636 {
637 EMSG(_(e_noprev));
638 vim_free(newcmd);
639 return;
640 }
641 len += (int)STRLEN(prevcmd);
642 }
643 if ((t = alloc(len)) == NULL)
644 {
645 vim_free(newcmd);
646 return;
647 }
648 *t = NUL;
649 if (newcmd != NULL)
650 STRCAT(t, newcmd);
651 if (ins_prevcmd)
652 STRCAT(t, prevcmd);
653 p = t + STRLEN(t);
654 STRCAT(t, trailarg);
655 vim_free(newcmd);
656 newcmd = t;
657
658 /*
659 * Scan the rest of the argument for '!', which is replaced by the
660 * previous command. "\!" is replaced by "!" (this is vi compatible).
661 */
662 trailarg = NULL;
663 while (*p)
664 {
665 if (*p == '!'
666#ifdef RISCOS
667 && (p[1] == ' ' || p[1] == NUL)
668#endif
669 )
670 {
671 if (p > newcmd && p[-1] == '\\')
672 mch_memmove(p - 1, p, (size_t)(STRLEN(p) + 1));
673 else
674 {
675 trailarg = p;
676 *trailarg++ = NUL;
677 ins_prevcmd = TRUE;
678 break;
679 }
680 }
681 ++p;
682 }
683 } while (trailarg != NULL);
684
685 vim_free(prevcmd);
686 prevcmd = newcmd;
687
688 if (bangredo) /* put cmd in redo buffer for ! command */
689 {
690 AppendToRedobuffLit(prevcmd);
691 AppendToRedobuff((char_u *)"\n");
692 bangredo = FALSE;
693 }
694 /*
695 * Add quotes around the command, for shells that need them.
696 */
697 if (*p_shq != NUL)
698 {
699 newcmd = alloc((unsigned)(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1));
700 if (newcmd == NULL)
701 return;
702 STRCPY(newcmd, p_shq);
703 STRCAT(newcmd, prevcmd);
704 STRCAT(newcmd, p_shq);
705 free_newcmd = TRUE;
706 }
707 if (addr_count == 0) /* :! */
708 {
709 /* echo the command */
710 msg_start();
711 msg_putchar(':');
712 msg_putchar('!');
713 msg_outtrans(newcmd);
714 msg_clr_eos();
715 windgoto(msg_row, msg_col);
716
717 do_shell(newcmd, 0);
718 }
719 else /* :range! */
720 /* Careful: This may recursively call do_bang() again! (because of
721 * autocommands) */
722 do_filter(line1, line2, eap, newcmd, do_in, do_out);
723 if (free_newcmd)
724 vim_free(newcmd);
725}
726
727/*
728 * do_filter: filter lines through a command given by the user
729 *
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000730 * We mostly use temp files and the call_shell() routine here. This would
731 * normally be done using pipes on a UNIX machine, but this is more portable
732 * to non-unix machines. The call_shell() routine needs to be able
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 * to deal with redirection somehow, and should handle things like looking
734 * at the PATH env. variable, and adding reasonable extensions to the
735 * command name given by the user. All reasonable versions of call_shell()
736 * do this.
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000737 * Alternatively, if on Unix and redirecting input or output, but not both,
738 * and the 'shelltemp' option isn't set, use pipes.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000739 * We use input redirection if do_in is TRUE.
740 * We use output redirection if do_out is TRUE.
741 */
742 static void
743do_filter(line1, line2, eap, cmd, do_in, do_out)
744 linenr_T line1, line2;
745 exarg_T *eap; /* for forced 'ff' and 'fenc' */
746 char_u *cmd;
747 int do_in, do_out;
748{
749 char_u *itmp = NULL;
750 char_u *otmp = NULL;
751 linenr_T linecount;
752 linenr_T read_linecount;
753 pos_T cursor_save;
754 char_u *cmd_buf;
755#ifdef FEAT_AUTOCMD
756 buf_T *old_curbuf = curbuf;
757#endif
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000758 int shell_flags = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759
760 if (*cmd == NUL) /* no filter command */
761 return;
762
763#ifdef WIN3264
764 /*
765 * Check if external commands are allowed now.
766 */
767 if (can_end_termcap_mode(TRUE) == FALSE)
768 return;
769#endif
770
771 cursor_save = curwin->w_cursor;
772 linecount = line2 - line1 + 1;
773 curwin->w_cursor.lnum = line1;
774 curwin->w_cursor.col = 0;
775 changed_line_abv_curs();
776 invalidate_botline();
777
778 /*
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000779 * When using temp files:
780 * 1. * Form temp file names
781 * 2. * Write the lines to a temp file
782 * 3. Run the filter command on the temp file
783 * 4. * Read the output of the command into the buffer
784 * 5. * Delete the original lines to be filtered
785 * 6. * Remove the temp files
786 *
787 * When writing the input with a pipe or when catching the output with a
788 * pipe only need to do 3.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000789 */
790
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000791 if (do_out)
792 shell_flags |= SHELL_DOOUT;
793
794#if !defined(USE_SYSTEM) && defined(UNIX)
795 if (!do_in && do_out && !p_stmp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000796 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000797 /* Use a pipe to fetch stdout of the command, do not use a temp file. */
798 shell_flags |= SHELL_READ;
799 curwin->w_cursor.lnum = line2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800 }
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000801 else if (do_in && !do_out && !p_stmp)
802 {
803 /* Use a pipe to write stdin of the command, do not use a temp file. */
804 shell_flags |= SHELL_WRITE;
805 curbuf->b_op_start.lnum = line1;
806 curbuf->b_op_end.lnum = line2;
807 }
808 else if (do_in && do_out && !p_stmp)
809 {
810 /* Use a pipe to write stdin and fetch stdout of the command, do not
811 * use a temp file. */
812 shell_flags |= SHELL_READ|SHELL_WRITE;
813 curbuf->b_op_start.lnum = line1;
814 curbuf->b_op_end.lnum = line2;
815 curwin->w_cursor.lnum = line2;
816 }
817 else
818#endif
819 if ((do_in && (itmp = vim_tempname('i')) == NULL)
820 || (do_out && (otmp = vim_tempname('o')) == NULL))
821 {
822 EMSG(_(e_notmp));
823 goto filterend;
824 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000825
826/*
827 * The writing and reading of temp files will not be shown.
828 * Vi also doesn't do this and the messages are not very informative.
829 */
830 ++no_wait_return; /* don't call wait_return() while busy */
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000831 if (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000832 FALSE, FALSE, FALSE, TRUE) == FAIL)
833 {
834 msg_putchar('\n'); /* keep message from buf_write() */
835 --no_wait_return;
836#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
837 if (!aborting())
838#endif
839 (void)EMSG2(_(e_notcreate), itmp); /* will call wait_return */
840 goto filterend;
841 }
842#ifdef FEAT_AUTOCMD
843 if (curbuf != old_curbuf)
844 goto filterend;
845#endif
846
847 if (!do_out)
848 msg_putchar('\n');
849
850 cmd_buf = make_filter_cmd(cmd, itmp, otmp);
851 if (cmd_buf == NULL)
852 goto filterend;
853
854 windgoto((int)Rows - 1, 0);
855 cursor_on();
856
857 /*
858 * When not redirecting the output the command can write anything to the
859 * screen. If 'shellredir' is equal to ">", screen may be messed up by
860 * stderr output of external command. Clear the screen later.
861 * If do_in is FALSE, this could be something like ":r !cat", which may
862 * also mess up the screen, clear it later.
863 */
864 if (!do_out || STRCMP(p_srr, ">") == 0 || !do_in)
865 redraw_later_clear();
866
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000867 if (do_out)
868 {
869 if (u_save((linenr_T)(line2), (linenr_T)(line2 + 1)) == FAIL)
870 goto error;
871 redraw_curbuf_later(VALID);
872 }
873 read_linecount = curbuf->b_ml.ml_line_count;
874
Bram Moolenaar071d4272004-06-13 20:20:40 +0000875 /*
876 * When call_shell() fails wait_return() is called to give the user a
877 * chance to read the error messages. Otherwise errors are ignored, so you
878 * can see the error messages from the command that appear on stdout; use
879 * 'u' to fix the text
880 * Switch to cooked mode when not redirecting stdin, avoids that something
881 * like ":r !cat" hangs.
882 * Pass on the SHELL_DOOUT flag when the output is being redirected.
883 */
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000884 if (call_shell(cmd_buf, SHELL_FILTER | SHELL_COOKED | shell_flags))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000885 {
886 redraw_later_clear();
887 wait_return(FALSE);
888 }
889 vim_free(cmd_buf);
890
891 did_check_timestamps = FALSE;
892 need_check_timestamps = TRUE;
893
894 /* When interrupting the shell command, it may still have produced some
895 * useful output. Reset got_int here, so that readfile() won't cancel
896 * reading. */
897 ui_breakcheck();
898 got_int = FALSE;
899
900 if (do_out)
901 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000902 if (otmp != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000904 if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM,
905 eap, READ_FILTER) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000906 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000907#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
908 if (!aborting())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000909#endif
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000910 {
911 msg_putchar('\n');
912 EMSG2(_(e_notread), otmp);
913 }
914 goto error;
915 }
916#ifdef FEAT_AUTOCMD
917 if (curbuf != old_curbuf)
918 goto filterend;
919#endif
920 }
921
922 read_linecount = curbuf->b_ml.ml_line_count - read_linecount;
923
924 if (shell_flags & SHELL_READ)
925 {
926 curbuf->b_op_start.lnum = line2 + 1;
927 curbuf->b_op_end.lnum = curwin->w_cursor.lnum;
928 appended_lines_mark(line2, read_linecount);
929 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930
931 if (do_in)
932 {
933 if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL)
934 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935 if (read_linecount >= linecount)
936 /* move all marks from old lines to new lines */
937 mark_adjust(line1, line2, linecount, 0L);
938 else
939 {
940 /* move marks from old lines to new lines, delete marks
941 * that are in deleted lines */
942 mark_adjust(line1, line1 + read_linecount - 1,
943 linecount, 0L);
944 mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L);
945 }
946 }
947
948 /*
949 * Put cursor on first filtered line for ":range!cmd".
950 * Adjust '[ and '] (set by buf_write()).
951 */
952 curwin->w_cursor.lnum = line1;
953 del_lines(linecount, TRUE);
954 curbuf->b_op_start.lnum -= linecount; /* adjust '[ */
955 curbuf->b_op_end.lnum -= linecount; /* adjust '] */
956 write_lnum_adjust(-linecount); /* adjust last line
957 for next write */
Bram Moolenaar8c711452005-01-14 21:53:12 +0000958#ifdef FEAT_FOLDING
959 foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum);
960#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000961 }
962 else
963 {
964 /*
965 * Put cursor on last new line for ":r !cmd".
966 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967 linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +0000968 curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969 }
Bram Moolenaar8c711452005-01-14 21:53:12 +0000970
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971 beginline(BL_WHITE | BL_FIX); /* cursor on first non-blank */
972 --no_wait_return;
973
974 if (linecount > p_report)
975 {
976 if (do_in)
977 {
Bram Moolenaar555b2802005-05-19 21:08:39 +0000978 vim_snprintf((char *)msg_buf, sizeof(msg_buf),
979 _("%ld lines filtered"), (long)linecount);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000980 if (msg(msg_buf) && !msg_scroll)
981 {
982 /* save message to display it after redraw */
983 set_keep_msg(msg_buf);
984 keep_msg_attr = 0;
985 }
986 }
987 else
988 msgmore((long)linecount);
989 }
990 }
991 else
992 {
993error:
994 /* put cursor back in same position for ":w !cmd" */
995 curwin->w_cursor = cursor_save;
996 --no_wait_return;
997 wait_return(FALSE);
998 }
999
1000filterend:
1001
1002#ifdef FEAT_AUTOCMD
1003 if (curbuf != old_curbuf)
1004 {
1005 --no_wait_return;
1006 EMSG(_("E135: *Filter* Autocommands must not change current buffer"));
1007 }
1008#endif
1009 if (itmp != NULL)
1010 mch_remove(itmp);
1011 if (otmp != NULL)
1012 mch_remove(otmp);
1013 vim_free(itmp);
1014 vim_free(otmp);
1015}
1016
1017/*
1018 * Call a shell to execute a command.
1019 * When "cmd" is NULL start an interactive shell.
1020 */
1021 void
1022do_shell(cmd, flags)
1023 char_u *cmd;
1024 int flags; /* may be SHELL_DOOUT when output is redirected */
1025{
1026 buf_T *buf;
1027#ifndef FEAT_GUI_MSWIN
1028 int save_nwr;
1029#endif
1030#ifdef MSWIN
1031 int winstart = FALSE;
1032#endif
1033
1034 /*
1035 * Disallow shell commands for "rvim".
1036 * Disallow shell commands from .exrc and .vimrc in current directory for
1037 * security reasons.
1038 */
1039 if (check_restricted() || check_secure())
1040 {
1041 msg_end();
1042 return;
1043 }
1044
1045#ifdef MSWIN
1046 /*
1047 * Check if external commands are allowed now.
1048 */
1049 if (can_end_termcap_mode(TRUE) == FALSE)
1050 return;
1051
1052 /*
1053 * Check if ":!start" is used.
1054 */
1055 if (cmd != NULL)
1056 winstart = (STRNICMP(cmd, "start ", 6) == 0);
1057#endif
1058
1059 /*
1060 * For autocommands we want to get the output on the current screen, to
1061 * avoid having to type return below.
1062 */
1063 msg_putchar('\r'); /* put cursor at start of line */
1064#ifdef FEAT_AUTOCMD
1065 if (!autocmd_busy)
1066#endif
1067 {
1068#ifdef MSWIN
1069 if (!winstart)
1070#endif
1071 stoptermcap();
1072 }
1073#ifdef MSWIN
1074 if (!winstart)
1075#endif
1076 msg_putchar('\n'); /* may shift screen one line up */
1077
1078 /* warning message before calling the shell */
1079 if (p_warn
1080#ifdef FEAT_AUTOCMD
1081 && !autocmd_busy
1082#endif
1083 && msg_silent == 0)
1084 for (buf = firstbuf; buf; buf = buf->b_next)
1085 if (bufIsChanged(buf))
1086 {
1087#ifdef FEAT_GUI_MSWIN
1088 if (!winstart)
1089 starttermcap(); /* don't want a message box here */
1090#endif
1091 MSG_PUTS(_("[No write since last change]\n"));
1092#ifdef FEAT_GUI_MSWIN
1093 if (!winstart)
1094 stoptermcap();
1095#endif
1096 break;
1097 }
1098
1099 /* This windgoto is required for when the '\n' resulted in a "delete line
1100 * 1" command to the terminal. */
1101 if (!swapping_screen())
1102 windgoto(msg_row, msg_col);
1103 cursor_on();
1104 (void)call_shell(cmd, SHELL_COOKED | flags);
1105 did_check_timestamps = FALSE;
1106 need_check_timestamps = TRUE;
1107
1108 /*
1109 * put the message cursor at the end of the screen, avoids wait_return()
1110 * to overwrite the text that the external command showed
1111 */
1112 if (!swapping_screen())
1113 {
1114 msg_row = Rows - 1;
1115 msg_col = 0;
1116 }
1117
1118#ifdef FEAT_AUTOCMD
1119 if (autocmd_busy)
1120 {
1121 if (msg_silent == 0)
1122 redraw_later_clear();
1123 }
1124 else
1125#endif
1126 {
1127 /*
1128 * For ":sh" there is no need to call wait_return(), just redraw.
1129 * Also for the Win32 GUI (the output is in a console window).
1130 * Otherwise there is probably text on the screen that the user wants
1131 * to read before redrawing, so call wait_return().
1132 */
1133#ifndef FEAT_GUI_MSWIN
1134 if (cmd == NULL
1135# ifdef WIN3264
1136 || (winstart && !need_wait_return)
1137# endif
1138 )
1139 {
1140 if (msg_silent == 0)
1141 redraw_later_clear();
1142 need_wait_return = FALSE;
1143 }
1144 else
1145 {
1146 /*
1147 * If we switch screens when starttermcap() is called, we really
1148 * want to wait for "hit return to continue".
1149 */
1150 save_nwr = no_wait_return;
1151 if (swapping_screen())
1152 no_wait_return = FALSE;
1153# ifdef AMIGA
1154 wait_return(term_console ? -1 : msg_silent == 0); /* see below */
1155# else
1156 wait_return(msg_silent == 0);
1157# endif
1158 no_wait_return = save_nwr;
1159 }
1160#endif /* FEAT_GUI_W32 */
1161
1162#ifdef MSWIN
1163 if (!winstart) /* if winstart==TRUE, never stopped termcap! */
1164#endif
1165 starttermcap(); /* start termcap if not done by wait_return() */
1166
1167 /*
1168 * In an Amiga window redrawing is caused by asking the window size.
1169 * If we got an interrupt this will not work. The chance that the
1170 * window size is wrong is very small, but we need to redraw the
1171 * screen. Don't do this if ':' hit in wait_return(). THIS IS UGLY
1172 * but it saves an extra redraw.
1173 */
1174#ifdef AMIGA
1175 if (skip_redraw) /* ':' hit in wait_return() */
1176 {
1177 if (msg_silent == 0)
1178 redraw_later_clear();
1179 }
1180 else if (term_console)
1181 {
1182 OUT_STR(IF_EB("\033[0 q", ESC_STR "[0 q")); /* get window size */
1183 if (got_int && msg_silent == 0)
1184 redraw_later_clear(); /* if got_int is TRUE, redraw needed */
1185 else
1186 must_redraw = 0; /* no extra redraw needed */
1187 }
1188#endif
1189 }
1190
1191 /* display any error messages now */
1192 display_errors();
1193}
1194
1195/*
1196 * Create a shell command from a command string, input redirection file and
1197 * output redirection file.
1198 * Returns an allocated string with the shell command, or NULL for failure.
1199 */
1200 char_u *
1201make_filter_cmd(cmd, itmp, otmp)
1202 char_u *cmd; /* command */
1203 char_u *itmp; /* NULL or name of input file */
1204 char_u *otmp; /* NULL or name of output file */
1205{
1206 char_u *buf;
1207 long_u len;
1208
1209 len = (long_u)STRLEN(cmd) + 3; /* "()" + NUL */
1210 if (itmp != NULL)
1211 len += (long_u)STRLEN(itmp) + 9; /* " { < " + " } " */
1212 if (otmp != NULL)
1213 len += (long_u)STRLEN(otmp) + (long_u)STRLEN(p_srr) + 2; /* " " */
1214 buf = lalloc(len, TRUE);
1215 if (buf == NULL)
1216 return NULL;
1217
1218#if (defined(UNIX) && !defined(ARCHIE)) || defined(OS2)
1219 /*
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00001220 * Put braces around the command (for concatenated commands) when
1221 * redirecting input and/or output.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222 */
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00001223 if (itmp != NULL || otmp != NULL)
1224 sprintf((char *)buf, "(%s)", (char *)cmd);
1225 else
1226 STRCPY(buf, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227 if (itmp != NULL)
1228 {
1229 STRCAT(buf, " < ");
1230 STRCAT(buf, itmp);
1231 }
1232#else
1233 /*
1234 * for shells that don't understand braces around commands, at least allow
1235 * the use of commands in a pipe.
1236 */
1237 STRCPY(buf, cmd);
1238 if (itmp != NULL)
1239 {
1240 char_u *p;
1241
1242 /*
1243 * If there is a pipe, we have to put the '<' in front of it.
1244 * Don't do this when 'shellquote' is not empty, otherwise the
1245 * redirection would be inside the quotes.
1246 */
1247 if (*p_shq == NUL)
1248 {
1249 p = vim_strchr(buf, '|');
1250 if (p != NULL)
1251 *p = NUL;
1252 }
1253# ifdef RISCOS
1254 STRCAT(buf, " { < "); /* Use RISC OS notation for input. */
1255 STRCAT(buf, itmp);
1256 STRCAT(buf, " } ");
1257# else
1258 STRCAT(buf, " <"); /* " < " causes problems on Amiga */
1259 STRCAT(buf, itmp);
1260# endif
1261 if (*p_shq == NUL)
1262 {
1263 p = vim_strchr(cmd, '|');
1264 if (p != NULL)
1265 {
1266 STRCAT(buf, " "); /* insert a space before the '|' for DOS */
1267 STRCAT(buf, p);
1268 }
1269 }
1270 }
1271#endif
1272 if (otmp != NULL)
1273 append_redir(buf, p_srr, otmp);
1274
1275 return buf;
1276}
1277
1278/*
1279 * Append output redirection for file "fname" to the end of string buffer "buf"
1280 * Works with the 'shellredir' and 'shellpipe' options.
1281 * The caller should make sure that there is enough room:
1282 * STRLEN(opt) + STRLEN(fname) + 3
1283 */
1284 void
1285append_redir(buf, opt, fname)
1286 char_u *buf;
1287 char_u *opt;
1288 char_u *fname;
1289{
1290 char_u *p;
1291
1292 buf += STRLEN(buf);
1293 /* find "%s", skipping "%%" */
1294 for (p = opt; (p = vim_strchr(p, '%')) != NULL; ++p)
1295 if (p[1] == 's')
1296 break;
1297 if (p != NULL)
1298 {
1299 *buf = ' '; /* not really needed? Not with sh, ksh or bash */
1300 sprintf((char *)buf + 1, (char *)opt, (char *)fname);
1301 }
1302 else
1303 sprintf((char *)buf,
1304#ifdef FEAT_QUICKFIX
1305# ifndef RISCOS
1306 opt != p_sp ? " %s%s" :
1307# endif
1308 " %s %s",
1309#else
1310# ifndef RISCOS
1311 " %s%s", /* " > %s" causes problems on Amiga */
1312# else
1313 " %s %s", /* But is needed for 'shellpipe' and RISC OS */
1314# endif
1315#endif
1316 (char *)opt, (char *)fname);
1317}
1318
1319#ifdef FEAT_VIMINFO
1320
1321static int no_viminfo __ARGS((void));
1322static int viminfo_errcnt;
1323
1324 static int
1325no_viminfo()
1326{
1327 /* "vim -i NONE" does not read or write a viminfo file */
1328 return (use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0);
1329}
1330
1331/*
1332 * Report an error for reading a viminfo file.
1333 * Count the number of errors. When there are more than 10, return TRUE.
1334 */
1335 int
1336viminfo_error(errnum, message, line)
1337 char *errnum;
1338 char *message;
1339 char_u *line;
1340{
Bram Moolenaar555b2802005-05-19 21:08:39 +00001341 vim_snprintf((char *)IObuff, IOSIZE, _("%sviminfo: %s in line: "),
1342 errnum, message);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343 STRNCAT(IObuff, line, IOSIZE - STRLEN(IObuff));
Bram Moolenaar758711c2005-02-02 23:11:38 +00001344 if (IObuff[STRLEN(IObuff) - 1] == '\n')
1345 IObuff[STRLEN(IObuff) - 1] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346 emsg(IObuff);
1347 if (++viminfo_errcnt >= 10)
1348 {
1349 EMSG(_("E136: viminfo: Too many errors, skipping rest of file"));
1350 return TRUE;
1351 }
1352 return FALSE;
1353}
1354
1355/*
1356 * read_viminfo() -- Read the viminfo file. Registers etc. which are already
1357 * set are not over-written unless force is TRUE. -- webb
1358 */
1359 int
1360read_viminfo(file, want_info, want_marks, forceit)
1361 char_u *file;
1362 int want_info;
1363 int want_marks;
1364 int forceit;
1365{
1366 FILE *fp;
1367 char_u *fname;
1368
1369 if (no_viminfo())
1370 return FAIL;
1371
1372 fname = viminfo_filename(file); /* may set to default if NULL */
1373 if (fname == NULL)
1374 return FAIL;
1375 fp = mch_fopen((char *)fname, READBIN);
1376
1377 if (p_verbose > 0)
Bram Moolenaar555b2802005-05-19 21:08:39 +00001378 smsg((char_u *)_("Reading viminfo file \"%s\"%s%s%s"),
1379 fname,
1380 want_info ? _(" info") : "",
1381 want_marks ? _(" marks") : "",
1382 fp == NULL ? _(" FAILED") : "");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383
1384 vim_free(fname);
1385 if (fp == NULL)
1386 return FAIL;
1387
1388 viminfo_errcnt = 0;
1389 do_viminfo(fp, NULL, want_info, want_marks, forceit);
1390
1391 fclose(fp);
1392
1393 return OK;
1394}
1395
1396/*
1397 * write_viminfo() -- Write the viminfo file. The old one is read in first so
1398 * that effectively a merge of current info and old info is done. This allows
1399 * multiple vims to run simultaneously, without losing any marks etc. If
1400 * forceit is TRUE, then the old file is not read in, and only internal info is
1401 * written to the file. -- webb
1402 */
1403 void
1404write_viminfo(file, forceit)
1405 char_u *file;
1406 int forceit;
1407{
1408 char_u *fname;
1409 FILE *fp_in = NULL; /* input viminfo file, if any */
1410 FILE *fp_out = NULL; /* output viminfo file */
1411 char_u *tempname = NULL; /* name of temp viminfo file */
1412 struct stat st_new; /* mch_stat() of potential new file */
1413 char_u *wp;
1414#if defined(UNIX) || defined(VMS)
1415 mode_t umask_save;
1416#endif
1417#ifdef UNIX
1418 int shortname = FALSE; /* use 8.3 file name */
1419 struct stat st_old; /* mch_stat() of existing viminfo file */
1420#endif
1421
1422 if (no_viminfo())
1423 return;
1424
1425 fname = viminfo_filename(file); /* may set to default if NULL */
1426 if (fname == NULL)
1427 return;
1428
1429 fp_in = mch_fopen((char *)fname, READBIN);
1430 if (fp_in == NULL)
1431 {
1432 /* if it does exist, but we can't read it, don't try writing */
1433 if (mch_stat((char *)fname, &st_new) == 0)
1434 goto end;
1435#if defined(UNIX) || defined(VMS)
1436 /*
1437 * For Unix we create the .viminfo non-accessible for others,
1438 * because it may contain text from non-accessible documents.
1439 */
1440 umask_save = umask(077);
1441#endif
1442 fp_out = mch_fopen((char *)fname, WRITEBIN);
1443#if defined(UNIX) || defined(VMS)
1444 (void)umask(umask_save);
1445#endif
1446 }
1447 else
1448 {
1449 /*
1450 * There is an existing viminfo file. Create a temporary file to
1451 * write the new viminfo into, in the same directory as the
1452 * existing viminfo file, which will be renamed later.
1453 */
1454#ifdef UNIX
1455 /*
1456 * For Unix we check the owner of the file. It's not very nice to
1457 * overwrite a user's viminfo file after a "su root", with a
1458 * viminfo file that the user can't read.
1459 */
1460 st_old.st_dev = st_old.st_ino = 0;
1461 st_old.st_mode = 0600;
1462 if (mch_stat((char *)fname, &st_old) == 0 && getuid() &&
1463 !(st_old.st_uid == getuid()
1464 ? (st_old.st_mode & 0200)
1465 : (st_old.st_gid == getgid()
1466 ? (st_old.st_mode & 0020)
1467 : (st_old.st_mode & 0002))))
1468 {
1469 int tt;
1470
1471 /* avoid a wait_return for this message, it's annoying */
1472 tt = msg_didany;
1473 EMSG2(_("E137: Viminfo file is not writable: %s"), fname);
1474 msg_didany = tt;
1475 goto end;
1476 }
1477#endif
1478
1479 /*
1480 * Make tempname.
1481 * May try twice: Once normal and once with shortname set, just in
1482 * case somebody puts his viminfo file in an 8.3 filesystem.
1483 */
1484 for (;;)
1485 {
1486 tempname = buf_modname(
1487#ifdef UNIX
1488 shortname,
1489#else
1490# ifdef SHORT_FNAME
1491 TRUE,
1492# else
1493# ifdef FEAT_GUI_W32
1494 gui_is_win32s(),
1495# else
1496 FALSE,
1497# endif
1498# endif
1499#endif
1500 fname,
1501#ifdef VMS
1502 (char_u *)"-tmp",
1503#else
1504# ifdef RISCOS
1505 (char_u *)"/tmp",
1506# else
1507 (char_u *)".tmp",
1508# endif
1509#endif
1510 FALSE);
1511 if (tempname == NULL) /* out of memory */
1512 break;
1513
1514 /*
1515 * Check if tempfile already exists. Never overwrite an
1516 * existing file!
1517 */
1518 if (mch_stat((char *)tempname, &st_new) == 0)
1519 {
1520#ifdef UNIX
1521 /*
1522 * Check if tempfile is same as original file. May happen
1523 * when modname() gave the same file back. E.g. silly
1524 * link, or file name-length reached. Try again with
1525 * shortname set.
1526 */
1527 if (!shortname && st_new.st_dev == st_old.st_dev &&
1528 st_new.st_ino == st_old.st_ino)
1529 {
1530 vim_free(tempname);
1531 tempname = NULL;
1532 shortname = TRUE;
1533 continue;
1534 }
1535#endif
1536 /*
1537 * Try another name. Change one character, just before
1538 * the extension. This should also work for an 8.3
1539 * file name, when after adding the extension it still is
1540 * the same file as the original.
1541 */
1542 wp = tempname + STRLEN(tempname) - 5;
1543 if (wp < gettail(tempname)) /* empty file name? */
1544 wp = gettail(tempname);
1545 for (*wp = 'z'; mch_stat((char *)tempname, &st_new) == 0;
1546 --*wp)
1547 {
1548 /*
1549 * They all exist? Must be something wrong! Don't
1550 * write the viminfo file then.
1551 */
1552 if (*wp == 'a')
1553 {
1554 vim_free(tempname);
1555 tempname = NULL;
1556 break;
1557 }
1558 }
1559 }
1560 break;
1561 }
1562
1563 if (tempname != NULL)
1564 {
1565 fp_out = mch_fopen((char *)tempname, WRITEBIN);
1566
1567 /*
1568 * If we can't create in the same directory, try creating a
1569 * "normal" temp file.
1570 */
1571 if (fp_out == NULL)
1572 {
1573 vim_free(tempname);
1574 if ((tempname = vim_tempname('o')) != NULL)
1575 fp_out = mch_fopen((char *)tempname, WRITEBIN);
1576 }
1577#ifdef UNIX
1578 /*
1579 * Set file protection same as original file, but strip s-bit
1580 * and make sure the owner can read/write it.
1581 */
1582 if (fp_out != NULL)
1583 {
1584 (void)mch_setperm(tempname,
1585 (long)((st_old.st_mode & 0777) | 0600));
1586 /* this only works for root: */
1587 (void)chown((char *)tempname, st_old.st_uid, st_old.st_gid);
1588 }
1589#endif
1590 }
1591 }
1592
1593 /*
1594 * Check if the new viminfo file can be written to.
1595 */
1596 if (fp_out == NULL)
1597 {
1598 EMSG2(_("E138: Can't write viminfo file %s!"),
1599 (fp_in == NULL || tempname == NUL) ? fname : tempname);
1600 if (fp_in != NULL)
1601 fclose(fp_in);
1602 goto end;
1603 }
1604
1605 if (p_verbose > 0)
Bram Moolenaar555b2802005-05-19 21:08:39 +00001606 smsg((char_u *)_("Writing viminfo file \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001607
1608 viminfo_errcnt = 0;
1609 do_viminfo(fp_in, fp_out, !forceit, !forceit, FALSE);
1610
1611 fclose(fp_out); /* errors are ignored !? */
1612 if (fp_in != NULL)
1613 {
1614 fclose(fp_in);
1615 /*
1616 * In case of an error, don't overwrite the original viminfo file.
1617 */
1618 if (viminfo_errcnt || vim_rename(tempname, fname) == -1)
1619 mch_remove(tempname);
1620 }
1621end:
1622 vim_free(fname);
1623 vim_free(tempname);
1624}
1625
1626/*
1627 * Get the viminfo file name to use.
1628 * If "file" is given and not empty, use it (has already been expanded by
1629 * cmdline functions).
1630 * Otherwise use "-i file_name", value from 'viminfo' or the default, and
1631 * expand environment variables.
1632 * Returns an allocated string. NULL when out of memory.
1633 */
1634 static char_u *
1635viminfo_filename(file)
1636 char_u *file;
1637{
1638 if (file == NULL || *file == NUL)
1639 {
1640 if (use_viminfo != NULL)
1641 file = use_viminfo;
1642 else if ((file = find_viminfo_parameter('n')) == NULL || *file == NUL)
1643 {
1644#ifdef VIMINFO_FILE2
1645 /* don't use $HOME when not defined (turned into "c:/"!). */
1646# ifdef VMS
1647 if (mch_getenv((char_u *)"SYS$LOGIN") == NULL)
1648# else
1649 if (mch_getenv((char_u *)"HOME") == NULL)
1650# endif
1651 {
1652 /* don't use $VIM when not available. */
1653 expand_env((char_u *)"$VIM", NameBuff, MAXPATHL);
1654 if (STRCMP("$VIM", NameBuff) != 0) /* $VIM was expanded */
1655 file = (char_u *)VIMINFO_FILE2;
1656 else
1657 file = (char_u *)VIMINFO_FILE;
1658 }
1659 else
1660#endif
1661 file = (char_u *)VIMINFO_FILE;
1662 }
1663 expand_env(file, NameBuff, MAXPATHL);
1664 file = NameBuff;
1665 }
1666 return vim_strsave(file);
1667}
1668
1669/*
1670 * do_viminfo() -- Should only be called from read_viminfo() & write_viminfo().
1671 */
1672 static void
1673do_viminfo(fp_in, fp_out, want_info, want_marks, force_read)
1674 FILE *fp_in;
1675 FILE *fp_out;
1676 int want_info;
1677 int want_marks;
1678 int force_read;
1679{
1680 int count = 0;
1681 int eof = FALSE;
1682 vir_T vir;
1683
1684 if ((vir.vir_line = alloc(LSIZE)) == NULL)
1685 return;
1686 vir.vir_fd = fp_in;
1687#ifdef FEAT_MBYTE
1688 vir.vir_conv.vc_type = CONV_NONE;
1689#endif
1690
1691 if (fp_in != NULL)
1692 {
1693 if (want_info)
1694 eof = read_viminfo_up_to_marks(&vir, force_read, fp_out != NULL);
1695 else
1696 /* Skip info, find start of marks */
1697 while (!(eof = viminfo_readline(&vir))
1698 && vir.vir_line[0] != '>')
1699 ;
1700 }
1701 if (fp_out != NULL)
1702 {
1703 /* Write the info: */
1704 fprintf(fp_out, _("# This viminfo file was generated by Vim %s.\n"),
1705 VIM_VERSION_MEDIUM);
1706 fprintf(fp_out, _("# You may edit it if you're careful!\n\n"));
1707#ifdef FEAT_MBYTE
1708 fprintf(fp_out, _("# Value of 'encoding' when this file was written\n"));
1709 fprintf(fp_out, "*encoding=%s\n\n", p_enc);
1710#endif
1711 write_viminfo_search_pattern(fp_out);
1712 write_viminfo_sub_string(fp_out);
1713#ifdef FEAT_CMDHIST
1714 write_viminfo_history(fp_out);
1715#endif
1716 write_viminfo_registers(fp_out);
1717#ifdef FEAT_EVAL
1718 write_viminfo_varlist(fp_out);
1719#endif
1720 write_viminfo_filemarks(fp_out);
1721 write_viminfo_bufferlist(fp_out);
1722 count = write_viminfo_marks(fp_out);
1723 }
1724 if (fp_in != NULL && want_marks)
1725 copy_viminfo_marks(&vir, fp_out, count, eof);
1726
1727 vim_free(vir.vir_line);
1728#ifdef FEAT_MBYTE
1729 if (vir.vir_conv.vc_type != CONV_NONE)
1730 convert_setup(&vir.vir_conv, NULL, NULL);
1731#endif
1732}
1733
1734/*
1735 * read_viminfo_up_to_marks() -- Only called from do_viminfo(). Reads in the
1736 * first part of the viminfo file which contains everything but the marks that
1737 * are local to a file. Returns TRUE when end-of-file is reached. -- webb
1738 */
1739 static int
1740read_viminfo_up_to_marks(virp, forceit, writing)
1741 vir_T *virp;
1742 int forceit;
1743 int writing;
1744{
1745 int eof;
1746 buf_T *buf;
1747
1748#ifdef FEAT_CMDHIST
1749 prepare_viminfo_history(forceit ? 9999 : 0);
1750#endif
1751 eof = viminfo_readline(virp);
1752 while (!eof && virp->vir_line[0] != '>')
1753 {
1754 switch (virp->vir_line[0])
1755 {
1756 /* Characters reserved for future expansion, ignored now */
1757 case '+': /* "+40 /path/dir file", for running vim without args */
1758 case '|': /* to be defined */
1759 case '^': /* to be defined */
1760 case '<': /* long line - ignored */
1761 /* A comment or empty line. */
1762 case NUL:
1763 case '\r':
1764 case '\n':
1765 case '#':
1766 eof = viminfo_readline(virp);
1767 break;
1768 case '*': /* "*encoding=value" */
1769 eof = viminfo_encoding(virp);
1770 break;
1771 case '!': /* global variable */
1772#ifdef FEAT_EVAL
1773 eof = read_viminfo_varlist(virp, writing);
1774#else
1775 eof = viminfo_readline(virp);
1776#endif
1777 break;
1778 case '%': /* entry for buffer list */
1779 eof = read_viminfo_bufferlist(virp, writing);
1780 break;
1781 case '"':
1782 eof = read_viminfo_register(virp, forceit);
1783 break;
1784 case '/': /* Search string */
1785 case '&': /* Substitute search string */
1786 case '~': /* Last search string, followed by '/' or '&' */
1787 eof = read_viminfo_search_pattern(virp, forceit);
1788 break;
1789 case '$':
1790 eof = read_viminfo_sub_string(virp, forceit);
1791 break;
1792 case ':':
1793 case '?':
1794 case '=':
1795 case '@':
1796#ifdef FEAT_CMDHIST
1797 eof = read_viminfo_history(virp);
1798#else
1799 eof = viminfo_readline(virp);
1800#endif
1801 break;
1802 case '-':
1803 case '\'':
1804 eof = read_viminfo_filemark(virp, forceit);
1805 break;
1806 default:
1807 if (viminfo_error("E575: ", _("Illegal starting char"),
1808 virp->vir_line))
1809 eof = TRUE;
1810 else
1811 eof = viminfo_readline(virp);
1812 break;
1813 }
1814 }
1815
1816#ifdef FEAT_CMDHIST
1817 /* Finish reading history items. */
1818 finish_viminfo_history();
1819#endif
1820
1821 /* Change file names to buffer numbers for fmarks. */
1822 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1823 fmarks_check_names(buf);
1824
1825 return eof;
1826}
1827
1828/*
1829 * Compare the 'encoding' value in the viminfo file with the current value of
1830 * 'encoding'. If different and the 'c' flag is in 'viminfo', setup for
1831 * conversion of text with iconv() in viminfo_readstring().
1832 */
1833 static int
1834viminfo_encoding(virp)
1835 vir_T *virp;
1836{
1837#ifdef FEAT_MBYTE
1838 char_u *p;
1839 int i;
1840
1841 if (get_viminfo_parameter('c') != 0)
1842 {
1843 p = vim_strchr(virp->vir_line, '=');
1844 if (p != NULL)
1845 {
1846 /* remove trailing newline */
1847 ++p;
1848 for (i = 0; vim_isprintc(p[i]); ++i)
1849 ;
1850 p[i] = NUL;
1851
1852 convert_setup(&virp->vir_conv, p, p_enc);
1853 }
1854 }
1855#endif
1856 return viminfo_readline(virp);
1857}
1858
1859/*
1860 * Read a line from the viminfo file.
1861 * Returns TRUE for end-of-file;
1862 */
1863 int
1864viminfo_readline(virp)
1865 vir_T *virp;
1866{
1867 return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
1868}
1869
1870/*
1871 * check string read from viminfo file
1872 * remove '\n' at the end of the line
1873 * - replace CTRL-V CTRL-V with CTRL-V
1874 * - replace CTRL-V 'n' with '\n'
1875 *
1876 * Check for a long line as written by viminfo_writestring().
1877 *
1878 * Return the string in allocated memory (NULL when out of memory).
1879 */
1880/*ARGSUSED*/
1881 char_u *
1882viminfo_readstring(virp, off, convert)
1883 vir_T *virp;
1884 int off; /* offset for virp->vir_line */
1885 int convert; /* convert the string */
1886{
1887 char_u *retval;
1888 char_u *s, *d;
1889 long len;
1890
1891 if (virp->vir_line[off] == Ctrl_V && vim_isdigit(virp->vir_line[off + 1]))
1892 {
1893 len = atol((char *)virp->vir_line + off + 1);
1894 retval = lalloc(len, TRUE);
1895 if (retval == NULL)
1896 {
1897 /* Line too long? File messed up? Skip next line. */
1898 (void)vim_fgets(virp->vir_line, 10, virp->vir_fd);
1899 return NULL;
1900 }
1901 (void)vim_fgets(retval, (int)len, virp->vir_fd);
1902 s = retval + 1; /* Skip the leading '<' */
1903 }
1904 else
1905 {
1906 retval = vim_strsave(virp->vir_line + off);
1907 if (retval == NULL)
1908 return NULL;
1909 s = retval;
1910 }
1911
1912 /* Change CTRL-V CTRL-V to CTRL-V and CTRL-V n to \n in-place. */
1913 d = retval;
1914 while (*s != NUL && *s != '\n')
1915 {
1916 if (s[0] == Ctrl_V && s[1] != NUL)
1917 {
1918 if (s[1] == 'n')
1919 *d++ = '\n';
1920 else
1921 *d++ = Ctrl_V;
1922 s += 2;
1923 }
1924 else
1925 *d++ = *s++;
1926 }
1927 *d = NUL;
1928
1929#ifdef FEAT_MBYTE
1930 if (convert && virp->vir_conv.vc_type != CONV_NONE && *retval != NUL)
1931 {
1932 d = string_convert(&virp->vir_conv, retval, NULL);
1933 if (d != NULL)
1934 {
1935 vim_free(retval);
1936 retval = d;
1937 }
1938 }
1939#endif
1940
1941 return retval;
1942}
1943
1944/*
1945 * write string to viminfo file
1946 * - replace CTRL-V with CTRL-V CTRL-V
1947 * - replace '\n' with CTRL-V 'n'
1948 * - add a '\n' at the end
1949 *
1950 * For a long line:
1951 * - write " CTRL-V <length> \n " in first line
1952 * - write " < <string> \n " in second line
1953 */
1954 void
1955viminfo_writestring(fd, p)
1956 FILE *fd;
1957 char_u *p;
1958{
1959 int c;
1960 char_u *s;
1961 int len = 0;
1962
1963 for (s = p; *s != NUL; ++s)
1964 {
1965 if (*s == Ctrl_V || *s == '\n')
1966 ++len;
1967 ++len;
1968 }
1969
1970 /* If the string will be too long, write its length and put it in the next
1971 * line. Take into account that some room is needed for what comes before
1972 * the string (e.g., variable name). Add something to the length for the
1973 * '<', NL and trailing NUL. */
1974 if (len > LSIZE / 2)
1975 fprintf(fd, IF_EB("\026%d\n<", CTRL_V_STR "%d\n<"), len + 3);
1976
1977 while ((c = *p++) != NUL)
1978 {
1979 if (c == Ctrl_V || c == '\n')
1980 {
1981 putc(Ctrl_V, fd);
1982 if (c == '\n')
1983 c = 'n';
1984 }
1985 putc(c, fd);
1986 }
1987 putc('\n', fd);
1988}
1989#endif /* FEAT_VIMINFO */
1990
1991/*
1992 * Implementation of ":fixdel", also used by get_stty().
1993 * <BS> resulting <Del>
1994 * ^? ^H
1995 * not ^? ^?
1996 */
1997/*ARGSUSED*/
1998 void
1999do_fixdel(eap)
2000 exarg_T *eap;
2001{
2002 char_u *p;
2003
2004 p = find_termcode((char_u *)"kb");
2005 add_termcode((char_u *)"kD", p != NULL
2006 && *p == DEL ? (char_u *)CTRL_H_STR : DEL_STR, FALSE);
2007}
2008
2009 void
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002010print_line_no_prefix(lnum, use_number, list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002011 linenr_T lnum;
2012 int use_number;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002013 int list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014{
Bram Moolenaar592e0a22004-07-03 16:05:59 +00002015 char_u numbuf[30];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016
2017 if (curwin->w_p_nu || use_number)
2018 {
Bram Moolenaar592e0a22004-07-03 16:05:59 +00002019 sprintf((char *)numbuf, "%*ld ", number_width(curwin), (long)lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002020 msg_puts_attr(numbuf, hl_attr(HLF_N)); /* Highlight line nrs */
2021 }
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002022 msg_prt_line(ml_get(lnum), list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023}
2024
2025/*
2026 * Print a text line. Also in silent mode ("ex -s").
2027 */
2028 void
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002029print_line(lnum, use_number, list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002030 linenr_T lnum;
2031 int use_number;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002032 int list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002033{
2034 int save_silent = silent_mode;
2035
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 msg_start();
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002037 silent_mode = FALSE;
2038 info_message = TRUE; /* use mch_msg(), not mch_errmsg() */
2039 print_line_no_prefix(lnum, use_number, list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002040 if (save_silent)
2041 {
2042 msg_putchar('\n');
2043 cursor_on(); /* msg_start() switches it off */
2044 out_flush();
2045 silent_mode = save_silent;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00002046 info_message = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047 }
2048}
2049
2050/*
2051 * ":file[!] [fname]".
2052 */
2053 void
2054ex_file(eap)
2055 exarg_T *eap;
2056{
2057 char_u *fname, *sfname, *xfname;
2058 buf_T *buf;
2059
Bram Moolenaar325b7a22004-07-05 15:58:32 +00002060 /* ":0file" removes the file name. Check for illegal uses ":3file",
2061 * "0file name", etc. */
2062 if (eap->addr_count > 0
2063 && (*eap->arg != NUL
2064 || eap->line2 > 0
2065 || eap->addr_count > 1))
2066 {
2067 EMSG(_(e_invarg));
2068 return;
2069 }
2070
2071 if (*eap->arg != NUL || eap->addr_count == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002072 {
2073#ifdef FEAT_AUTOCMD
2074 buf = curbuf;
2075 apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
2076 /* buffer changed, don't change name now */
2077 if (buf != curbuf)
2078 return;
2079# ifdef FEAT_EVAL
2080 if (aborting()) /* autocmds may abort script processing */
2081 return;
2082# endif
2083#endif
2084 /*
2085 * The name of the current buffer will be changed.
2086 * A new (unlisted) buffer entry needs to be made to hold the old file
2087 * name, which will become the alternate file name.
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002088 * But don't set the alternate file name if the buffer didn't have a
2089 * name.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002090 */
2091 fname = curbuf->b_ffname;
2092 sfname = curbuf->b_sfname;
2093 xfname = curbuf->b_fname;
2094 curbuf->b_ffname = NULL;
2095 curbuf->b_sfname = NULL;
2096 if (setfname(curbuf, eap->arg, NULL, TRUE) == FAIL)
2097 {
2098 curbuf->b_ffname = fname;
2099 curbuf->b_sfname = sfname;
2100 return;
2101 }
2102 curbuf->b_flags |= BF_NOTEDITED;
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002103 if (xfname != NULL && *xfname != NUL)
2104 {
2105 buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0);
2106 if (buf != NULL && !cmdmod.keepalt)
2107 curwin->w_alt_fnum = buf->b_fnum;
2108 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002109 vim_free(fname);
2110 vim_free(sfname);
2111#ifdef FEAT_AUTOCMD
2112 apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
2113#endif
2114 }
2115 /* print full file name if :cd used */
2116 fileinfo(FALSE, FALSE, eap->forceit);
2117}
2118
2119/*
2120 * ":update".
2121 */
2122 void
2123ex_update(eap)
2124 exarg_T *eap;
2125{
2126 if (curbufIsChanged())
2127 (void)do_write(eap);
2128}
2129
2130/*
2131 * ":write" and ":saveas".
2132 */
2133 void
2134ex_write(eap)
2135 exarg_T *eap;
2136{
2137 if (eap->usefilter) /* input lines to shell command */
2138 do_bang(1, eap, FALSE, TRUE, FALSE);
2139 else
2140 (void)do_write(eap);
2141}
2142
2143/*
2144 * write current buffer to file 'eap->arg'
2145 * if 'eap->append' is TRUE, append to the file
2146 *
2147 * if *eap->arg == NUL write to current file
2148 *
2149 * return FAIL for failure, OK otherwise
2150 */
2151 int
2152do_write(eap)
2153 exarg_T *eap;
2154{
2155 int other;
2156 char_u *fname = NULL; /* init to shut up gcc */
2157 char_u *ffname;
2158 int retval = FAIL;
2159 char_u *free_fname = NULL;
2160#ifdef FEAT_BROWSE
2161 char_u *browse_file = NULL;
2162#endif
2163 buf_T *alt_buf = NULL;
2164
2165 if (not_writing()) /* check 'write' option */
2166 return FAIL;
2167
2168 ffname = eap->arg;
2169#ifdef FEAT_BROWSE
2170 if (cmdmod.browse)
2171 {
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002172 browse_file = do_browse(BROWSE_SAVE, (char_u *)_("Save As"), ffname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002173 NULL, NULL, NULL, curbuf);
2174 if (browse_file == NULL)
2175 goto theend;
2176 ffname = browse_file;
2177 }
2178#endif
2179 if (*ffname == NUL)
2180 {
2181 if (eap->cmdidx == CMD_saveas)
2182 {
2183 EMSG(_(e_argreq));
2184 goto theend;
2185 }
2186 other = FALSE;
2187 }
2188 else
2189 {
2190 fname = ffname;
2191 free_fname = fix_fname(ffname);
2192 /*
2193 * When out-of-memory, keep unexpanded file name, because we MUST be
2194 * able to write the file in this situation.
2195 */
2196 if (free_fname != NULL)
2197 ffname = free_fname;
2198 other = otherfile(ffname);
2199 }
2200
2201 /*
2202 * If we have a new file, put its name in the list of alternate file names.
2203 */
2204 if (other)
2205 {
2206 if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL
2207 || eap->cmdidx == CMD_saveas)
2208 alt_buf = setaltfname(ffname, fname, (linenr_T)1);
2209 else
2210 alt_buf = buflist_findname(ffname);
2211 if (alt_buf != NULL && alt_buf->b_ml.ml_mfp != NULL)
2212 {
2213 /* Overwriting a file that is loaded in another buffer is not a
2214 * good idea. */
2215 EMSG(_("E139: File is loaded in another buffer"));
2216 goto theend;
2217 }
2218 }
2219
2220 /*
2221 * Writing to the current file is not allowed in readonly mode
2222 * and a file name is required.
2223 * "nofile" and "nowrite" buffers cannot be written implicitly either.
2224 */
2225 if (!other && (
2226#ifdef FEAT_QUICKFIX
2227 bt_dontwrite_msg(curbuf) ||
2228#endif
2229 check_fname() == FAIL || check_readonly(&eap->forceit, curbuf)))
2230 goto theend;
2231
2232 if (!other)
2233 {
2234 ffname = curbuf->b_ffname;
2235 fname = curbuf->b_fname;
2236 /*
2237 * Not writing the whole file is only allowed with '!'.
2238 */
2239 if ( (eap->line1 != 1
2240 || eap->line2 != curbuf->b_ml.ml_line_count)
2241 && !eap->forceit
2242 && !eap->append
2243 && !p_wa)
2244 {
2245#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2246 if (p_confirm || cmdmod.confirm)
2247 {
2248 if (vim_dialog_yesno(VIM_QUESTION, NULL,
2249 (char_u *)_("Write partial file?"), 2) != VIM_YES)
2250 goto theend;
2251 eap->forceit = TRUE;
2252 }
2253 else
2254#endif
2255 {
2256 EMSG(_("E140: Use ! to write partial buffer"));
2257 goto theend;
2258 }
2259 }
2260 }
2261
2262 if (check_overwrite(eap, curbuf, fname, ffname, other) == OK)
2263 {
2264 if (eap->cmdidx == CMD_saveas && alt_buf != NULL)
2265 {
2266#ifdef FEAT_AUTOCMD
2267 buf_T *was_curbuf = curbuf;
2268
2269 apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
Bram Moolenaar269ec652004-07-29 08:43:53 +00002270 apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, alt_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002271# ifdef FEAT_EVAL
2272 if (curbuf != was_curbuf || aborting())
2273# else
2274 if (curbuf != was_curbuf)
2275# endif
2276 {
2277 /* buffer changed, don't change name now */
2278 retval = FAIL;
2279 goto theend;
2280 }
2281#endif
2282 /* Exchange the file names for the current and the alternate
2283 * buffer. This makes it look like we are now editing the buffer
2284 * under the new name. Must be done before buf_write(), because
2285 * if there is no file name and 'cpo' contains 'F', it will set
2286 * the file name. */
2287 fname = alt_buf->b_fname;
2288 alt_buf->b_fname = curbuf->b_fname;
2289 curbuf->b_fname = fname;
2290 fname = alt_buf->b_ffname;
2291 alt_buf->b_ffname = curbuf->b_ffname;
2292 curbuf->b_ffname = fname;
2293 fname = alt_buf->b_sfname;
2294 alt_buf->b_sfname = curbuf->b_sfname;
2295 curbuf->b_sfname = fname;
2296 buf_name_changed(curbuf);
2297#ifdef FEAT_AUTOCMD
2298 apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
Bram Moolenaar269ec652004-07-29 08:43:53 +00002299 apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, alt_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300 if (!alt_buf->b_p_bl)
2301 {
2302 alt_buf->b_p_bl = TRUE;
2303 apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, alt_buf);
2304 }
2305# ifdef FEAT_EVAL
2306 if (curbuf != was_curbuf || aborting())
2307# else
2308 if (curbuf != was_curbuf)
2309# endif
2310 {
2311 /* buffer changed, don't write the file */
2312 retval = FAIL;
2313 goto theend;
2314 }
2315#endif
2316 }
2317
2318 retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
2319 eap, eap->append, eap->forceit, TRUE, FALSE);
2320 }
2321
2322theend:
2323#ifdef FEAT_BROWSE
2324 vim_free(browse_file);
2325#endif
2326 vim_free(free_fname);
2327 return retval;
2328}
2329
2330/*
2331 * Check if it is allowed to overwrite a file. If b_flags has BF_NOTEDITED,
2332 * BF_NEW or BF_READERR, check for overwriting current file.
2333 * May set eap->forceit if a dialog says it's OK to overwrite.
2334 * Return OK if it's OK, FAIL if it is not.
2335 */
2336/*ARGSUSED*/
2337 static int
2338check_overwrite(eap, buf, fname, ffname, other)
2339 exarg_T *eap;
2340 buf_T *buf;
2341 char_u *fname; /* file name to be used (can differ from
2342 buf->ffname) */
2343 char_u *ffname; /* full path version of fname */
2344 int other; /* writing under other name */
2345{
2346 /*
2347 * write to other file or b_flags set or not writing the whole file:
2348 * overwriting only allowed with '!'
2349 */
2350 if ( (other
2351 || (buf->b_flags & BF_NOTEDITED)
2352 || ((buf->b_flags & BF_NEW)
2353 && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
2354 || (buf->b_flags & BF_READERR))
2355 && !eap->forceit
2356 && !eap->append
2357 && !p_wa
2358 && vim_fexists(ffname))
2359 {
2360#ifdef UNIX
2361 /* with UNIX it is possible to open a directory */
2362 if (mch_isdir(ffname))
2363 {
2364 EMSG2(_(e_isadir2), ffname);
2365 return FAIL;
2366 }
2367#endif
2368#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2369 if (p_confirm || cmdmod.confirm)
2370 {
2371 char_u buff[IOSIZE];
2372
2373 dialog_msg(buff, _("Overwrite existing file \"%.*s\"?"), fname);
2374 if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES)
2375 return FAIL;
2376 eap->forceit = TRUE;
2377 }
2378 else
2379#endif
2380 {
2381 EMSG(_(e_exists));
2382 return FAIL;
2383 }
2384 }
2385 return OK;
2386}
2387
2388/*
2389 * Handle ":wnext", ":wNext" and ":wprevious" commands.
2390 */
2391 void
2392ex_wnext(eap)
2393 exarg_T *eap;
2394{
2395 int i;
2396
2397 if (eap->cmd[1] == 'n')
2398 i = curwin->w_arg_idx + (int)eap->line2;
2399 else
2400 i = curwin->w_arg_idx - (int)eap->line2;
2401 eap->line1 = 1;
2402 eap->line2 = curbuf->b_ml.ml_line_count;
2403 if (do_write(eap) != FAIL)
2404 do_argfile(eap, i);
2405}
2406
2407/*
2408 * ":wall", ":wqall" and ":xall": Write all changed files (and exit).
2409 */
2410 void
2411do_wqall(eap)
2412 exarg_T *eap;
2413{
2414 buf_T *buf;
2415 int error = 0;
2416 int save_forceit = eap->forceit;
2417
2418 if (eap->cmdidx == CMD_xall || eap->cmdidx == CMD_wqall)
2419 exiting = TRUE;
2420
2421 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
2422 {
2423 if (bufIsChanged(buf))
2424 {
2425 /*
2426 * Check if there is a reason the buffer cannot be written:
2427 * 1. if the 'write' option is set
2428 * 2. if there is no file name (even after browsing)
2429 * 3. if the 'readonly' is set (even after a dialog)
2430 * 4. if overwriting is allowed (even after a dialog)
2431 */
2432 if (not_writing())
2433 {
2434 ++error;
2435 break;
2436 }
2437#ifdef FEAT_BROWSE
2438 /* ":browse wall": ask for file name if there isn't one */
2439 if (buf->b_ffname == NULL && cmdmod.browse)
2440 browse_save_fname(buf);
2441#endif
2442 if (buf->b_ffname == NULL)
2443 {
2444 EMSGN(_("E141: No file name for buffer %ld"), (long)buf->b_fnum);
2445 ++error;
2446 }
2447 else if (check_readonly(&eap->forceit, buf)
2448 || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname,
2449 FALSE) == FAIL)
2450 {
2451 ++error;
2452 }
2453 else
2454 {
2455 if (buf_write_all(buf, eap->forceit) == FAIL)
2456 ++error;
2457#ifdef FEAT_AUTOCMD
2458 /* an autocommand may have deleted the buffer */
2459 if (!buf_valid(buf))
2460 buf = firstbuf;
2461#endif
2462 }
2463 eap->forceit = save_forceit; /* check_overwrite() may set it */
2464 }
2465 }
2466 if (exiting)
2467 {
2468 if (!error)
2469 getout(0); /* exit Vim */
2470 not_exiting();
2471 }
2472}
2473
2474/*
2475 * Check the 'write' option.
2476 * Return TRUE and give a message when it's not st.
2477 */
2478 int
2479not_writing()
2480{
2481 if (p_write)
2482 return FALSE;
2483 EMSG(_("E142: File not written: Writing is disabled by 'write' option"));
2484 return TRUE;
2485}
2486
2487/*
2488 * Check if a buffer is read-only. Ask for overruling in a dialog.
2489 * Return TRUE and give an error message when the buffer is readonly.
2490 */
2491 static int
2492check_readonly(forceit, buf)
2493 int *forceit;
2494 buf_T *buf;
2495{
2496 if (!*forceit && buf->b_p_ro)
2497 {
2498#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2499 if ((p_confirm || cmdmod.confirm) && buf->b_fname != NULL)
2500 {
2501 char_u buff[IOSIZE];
2502
2503 dialog_msg(buff, _("'readonly' option is set for \"%.*s\".\nDo you wish to write anyway?"),
2504 buf->b_fname);
2505
2506 if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) == VIM_YES)
2507 {
2508 /* Set forceit, to force the writing of a readonly file */
2509 *forceit = TRUE;
2510 return FALSE;
2511 }
2512 else
2513 return TRUE;
2514 }
2515 else
2516#endif
2517 EMSG(_(e_readonly));
2518 return TRUE;
2519 }
2520 return FALSE;
2521}
2522
2523/*
2524 * try to abandon current file and edit a new or existing file
2525 * 'fnum' is the number of the file, if zero use ffname/sfname
2526 *
2527 * return 1 for "normal" error, 2 for "not written" error, 0 for success
2528 * -1 for succesfully opening another file
2529 * 'lnum' is the line number for the cursor in the new file (if non-zero).
2530 */
2531 int
2532getfile(fnum, ffname, sfname, setpm, lnum, forceit)
2533 int fnum;
2534 char_u *ffname;
2535 char_u *sfname;
2536 int setpm;
2537 linenr_T lnum;
2538 int forceit;
2539{
2540 int other;
2541 int retval;
2542 char_u *free_me = NULL;
2543
2544#ifdef FEAT_CMDWIN
2545 if (cmdwin_type != 0)
2546 return 1;
2547#endif
2548
2549 if (fnum == 0)
2550 {
2551 /* make ffname full path, set sfname */
2552 fname_expand(curbuf, &ffname, &sfname);
2553 other = otherfile(ffname);
2554 free_me = ffname; /* has been allocated, free() later */
2555 }
2556 else
2557 other = (fnum != curbuf->b_fnum);
2558
2559 if (other)
2560 ++no_wait_return; /* don't wait for autowrite message */
2561 if (other && !forceit && curbuf->b_nwindows == 1 && !P_HID(curbuf)
2562 && curbufIsChanged() && autowrite(curbuf, forceit) == FAIL)
2563 {
2564#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2565 if (p_confirm && p_write)
2566 dialog_changed(curbuf, FALSE);
2567 if (curbufIsChanged())
2568#endif
2569 {
2570 if (other)
2571 --no_wait_return;
2572 EMSG(_(e_nowrtmsg));
2573 retval = 2; /* file has been changed */
2574 goto theend;
2575 }
2576 }
2577 if (other)
2578 --no_wait_return;
2579 if (setpm)
2580 setpcmark();
2581 if (!other)
2582 {
2583 if (lnum != 0)
2584 curwin->w_cursor.lnum = lnum;
2585 check_cursor_lnum();
2586 beginline(BL_SOL | BL_FIX);
2587 retval = 0; /* it's in the same file */
2588 }
2589 else if (do_ecmd(fnum, ffname, sfname, NULL, lnum,
2590 (P_HID(curbuf) ? ECMD_HIDE : 0) + (forceit ? ECMD_FORCEIT : 0)) == OK)
2591 retval = -1; /* opened another file */
2592 else
2593 retval = 1; /* error encountered */
2594
2595theend:
2596 vim_free(free_me);
2597 return retval;
2598}
2599
2600/*
2601 * start editing a new file
2602 *
2603 * fnum: file number; if zero use ffname/sfname
2604 * ffname: the file name
2605 * - full path if sfname used,
2606 * - any file name if sfname is NULL
2607 * - empty string to re-edit with the same file name (but may be
2608 * in a different directory)
2609 * - NULL to start an empty buffer
2610 * sfname: the short file name (or NULL)
2611 * eap: contains the command to be executed after loading the file and
2612 * forced 'ff' and 'fenc'
2613 * newlnum: if > 0: put cursor on this line number (if possible)
2614 * if ECMD_LASTL: use last position in loaded file
2615 * if ECMD_LAST: use last position in all files
2616 * if ECMD_ONE: use first line
2617 * flags:
2618 * ECMD_HIDE: if TRUE don't free the current buffer
2619 * ECMD_SET_HELP: set b_help flag of (new) buffer before opening file
2620 * ECMD_OLDBUF: use existing buffer if it exists
2621 * ECMD_FORCEIT: ! used for Ex command
2622 * ECMD_ADDBUF: don't edit, just add to buffer list
2623 *
2624 * return FAIL for failure, OK otherwise
2625 */
2626 int
2627do_ecmd(fnum, ffname, sfname, eap, newlnum, flags)
2628 int fnum;
2629 char_u *ffname;
2630 char_u *sfname;
2631 exarg_T *eap; /* can be NULL! */
2632 linenr_T newlnum;
2633 int flags;
2634{
2635 int other_file; /* TRUE if editing another file */
2636 int oldbuf; /* TRUE if using existing buffer */
2637#ifdef FEAT_AUTOCMD
2638 int auto_buf = FALSE; /* TRUE if autocommands brought us
2639 into the buffer unexpectedly */
2640 char_u *new_name = NULL;
2641#endif
2642 buf_T *buf;
2643#if defined(FEAT_AUTOCMD) || defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2644 buf_T *old_curbuf = curbuf;
2645#endif
2646 char_u *free_fname = NULL;
2647#ifdef FEAT_BROWSE
2648 char_u *browse_file = NULL;
2649#endif
2650 int retval = FAIL;
2651 long n;
2652 linenr_T lnum;
2653 linenr_T topline = 0;
2654 int newcol = -1;
2655 int solcol = -1;
2656 pos_T *pos;
2657#ifdef FEAT_SUN_WORKSHOP
2658 char_u *cp;
2659#endif
2660 char_u *command = NULL;
2661
2662 if (eap != NULL)
2663 command = eap->do_ecmd_cmd;
2664
2665 if (fnum != 0)
2666 {
2667 if (fnum == curbuf->b_fnum) /* file is already being edited */
2668 return OK; /* nothing to do */
2669 other_file = TRUE;
2670 }
2671 else
2672 {
2673#ifdef FEAT_BROWSE
2674 if (cmdmod.browse)
2675 {
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002676 browse_file = do_browse(0, (char_u *)_("Edit File"), ffname,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002677 NULL, NULL, NULL, curbuf);
2678 if (browse_file == NULL)
2679 goto theend;
2680 ffname = browse_file;
2681 }
2682#endif
2683 /* if no short name given, use ffname for short name */
2684 if (sfname == NULL)
2685 sfname = ffname;
2686#ifdef USE_FNAME_CASE
2687# ifdef USE_LONG_FNAME
2688 if (USE_LONG_FNAME)
2689# endif
2690 fname_case(sfname, 0); /* set correct case for short file name */
2691#endif
2692
2693#ifdef FEAT_LISTCMDS
2694 if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL))
2695 goto theend;
2696#endif
2697
2698 if (ffname == NULL)
2699 other_file = TRUE;
2700 /* there is no file name */
2701 else if (*ffname == NUL && curbuf->b_ffname == NULL)
2702 other_file = FALSE;
2703 else
2704 {
2705 if (*ffname == NUL) /* re-edit with same file name */
2706 {
2707 ffname = curbuf->b_ffname;
2708 sfname = curbuf->b_fname;
2709 }
2710 free_fname = fix_fname(ffname); /* may expand to full path name */
2711 if (free_fname != NULL)
2712 ffname = free_fname;
2713 other_file = otherfile(ffname);
2714#ifdef FEAT_SUN_WORKSHOP
2715 if (usingSunWorkShop && p_acd
2716 && (cp = vim_strrchr(sfname, '/')) != NULL)
2717 sfname = ++cp;
2718#endif
2719 }
2720 }
2721
2722 /*
2723 * if the file was changed we may not be allowed to abandon it
2724 * - if we are going to re-edit the same file
2725 * - or if we are the only window on this file and if ECMD_HIDE is FALSE
2726 */
2727 if ( ((!other_file && !(flags & ECMD_OLDBUF))
2728 || (curbuf->b_nwindows == 1
2729 && !(flags & (ECMD_HIDE | ECMD_ADDBUF))))
2730 && check_changed(curbuf, p_awa, !other_file,
2731 (flags & ECMD_FORCEIT), FALSE))
2732 {
2733 if (fnum == 0 && other_file && ffname != NULL)
2734 (void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum);
2735 goto theend;
2736 }
2737
2738#ifdef FEAT_VISUAL
2739 /*
2740 * End Visual mode before switching to another buffer, so the text can be
2741 * copied into the GUI selection buffer.
2742 */
2743 reset_VIsual();
2744#endif
2745
2746 /*
2747 * If we are starting to edit another file, open a (new) buffer.
2748 * Otherwise we re-use the current buffer.
2749 */
2750 if (other_file)
2751 {
2752#ifdef FEAT_LISTCMDS
2753 if (!(flags & ECMD_ADDBUF))
2754#endif
2755 {
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002756 if (!cmdmod.keepalt)
2757 curwin->w_alt_fnum = curbuf->b_fnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002758 buflist_altfpos();
2759 }
2760
2761 if (fnum)
2762 buf = buflist_findnr(fnum);
2763 else
2764 {
2765#ifdef FEAT_LISTCMDS
2766 if (flags & ECMD_ADDBUF)
2767 {
2768 linenr_T tlnum = 1L;
2769
2770 if (command != NULL)
2771 {
2772 tlnum = atol((char *)command);
2773 if (tlnum <= 0)
2774 tlnum = 1L;
2775 }
2776 (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED);
2777 goto theend;
2778 }
2779#endif
2780 buf = buflist_new(ffname, sfname, 0L,
2781 BLN_CURBUF | ((flags & ECMD_SET_HELP) ? 0 : BLN_LISTED));
2782 }
2783 if (buf == NULL)
2784 goto theend;
2785 if (buf->b_ml.ml_mfp == NULL) /* no memfile yet */
2786 {
2787 oldbuf = FALSE;
2788 buf->b_nwindows = 0;
2789 }
2790 else /* existing memfile */
2791 {
2792 oldbuf = TRUE;
2793 (void)buf_check_timestamp(buf, FALSE);
2794 /* Check if autocommands made buffer invalid or changed the current
2795 * buffer. */
2796 if (!buf_valid(buf)
2797#ifdef FEAT_AUTOCMD
2798 || curbuf != old_curbuf
2799#endif
2800 )
2801 goto theend;
2802#ifdef FEAT_EVAL
2803 if (aborting()) /* autocmds may abort script processing */
2804 goto theend;
2805#endif
2806 }
2807
2808 /* May jump to last used line number for a loaded buffer or when asked
2809 * for explicitly */
2810 if ((oldbuf && newlnum == ECMD_LASTL) || newlnum == ECMD_LAST)
2811 {
2812 pos = buflist_findfpos(buf);
2813 newlnum = pos->lnum;
2814 solcol = pos->col;
2815 }
2816
2817 /*
2818 * Make the (new) buffer the one used by the current window.
2819 * If the old buffer becomes unused, free it if ECMD_HIDE is FALSE.
2820 * If the current buffer was empty and has no file name, curbuf
2821 * is returned by buflist_new().
2822 */
2823 if (buf != curbuf)
2824 {
2825#ifdef FEAT_AUTOCMD
2826 /*
2827 * Be careful: The autocommands may delete any buffer and change
2828 * the current buffer.
2829 * - If the buffer we are going to edit is deleted, give up.
2830 * - If the current buffer is deleted, prefer to load the new
2831 * buffer when loading a buffer is required. This avoids
2832 * loading another buffer which then must be closed again.
2833 * - If we ended up in the new buffer already, need to skip a few
2834 * things, set auto_buf.
2835 */
2836 if (buf->b_fname != NULL)
2837 new_name = vim_strsave(buf->b_fname);
2838 au_new_curbuf = buf;
2839 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
2840 if (!buf_valid(buf)) /* new buffer has been deleted */
2841 {
2842 delbuf_msg(new_name); /* frees new_name */
2843 goto theend;
2844 }
2845# ifdef FEAT_EVAL
2846 if (aborting()) /* autocmds may abort script processing */
2847 {
2848 vim_free(new_name);
2849 goto theend;
2850 }
2851# endif
2852 if (buf == curbuf) /* already in new buffer */
2853 auto_buf = TRUE;
2854 else
2855 {
2856 if (curbuf == old_curbuf)
2857#endif
2858 buf_copy_options(buf, BCO_ENTER);
2859
2860 /* close the link to the current buffer */
2861 close_buffer(curwin, curbuf,
2862 (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD);
2863
2864#ifdef FEAT_AUTOCMD
2865# ifdef FEAT_EVAL
2866 if (aborting()) /* autocmds may abort script processing */
2867 {
2868 vim_free(new_name);
2869 goto theend;
2870 }
2871# endif
2872 /* Be careful again, like above. */
2873 if (!buf_valid(buf)) /* new buffer has been deleted */
2874 {
2875 delbuf_msg(new_name); /* frees new_name */
2876 goto theend;
2877 }
2878 if (buf == curbuf) /* already in new buffer */
2879 auto_buf = TRUE;
2880 else
2881#endif
2882 {
2883 curwin->w_buffer = buf;
2884 curbuf = buf;
2885 ++curbuf->b_nwindows;
2886 /* set 'fileformat' */
2887 if (*p_ffs && !oldbuf)
2888 set_fileformat(default_fileformat(), OPT_LOCAL);
2889 }
2890
2891 /* May get the window options from the last time this buffer
2892 * was in this window (or another window). If not used
2893 * before, reset the local window options to the global
2894 * values. Also restores old folding stuff. */
2895 get_winopts(buf);
2896
2897#ifdef FEAT_AUTOCMD
2898 }
2899 vim_free(new_name);
2900 au_new_curbuf = NULL;
2901#endif
2902 }
2903 else
2904 ++curbuf->b_nwindows;
2905
2906 curwin->w_pcmark.lnum = 1;
2907 curwin->w_pcmark.col = 0;
2908 }
2909 else /* !other_file */
2910 {
2911 if (
2912#ifdef FEAT_LISTCMDS
2913 (flags & ECMD_ADDBUF) ||
2914#endif
2915 check_fname() == FAIL)
2916 goto theend;
2917 oldbuf = (flags & ECMD_OLDBUF);
2918 }
2919
2920 if ((flags & ECMD_SET_HELP) || keep_help_flag)
2921 {
2922 char_u *p;
2923
2924 curbuf->b_help = TRUE;
2925#ifdef FEAT_QUICKFIX
2926 set_string_option_direct((char_u *)"buftype", -1,
2927 (char_u *)"help", OPT_FREE|OPT_LOCAL);
2928#endif
2929
2930 /*
2931 * Always set these options after jumping to a help tag, because the
2932 * user may have an autocommand that gets in the way.
2933 * Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
2934 * latin1 word characters (for translated help files).
2935 * Only set it when needed, buf_init_chartab() is some work.
2936 */
2937 p =
2938#ifdef EBCDIC
2939 (char_u *)"65-255,^*,^|,^\"";
2940#else
2941 (char_u *)"!-~,^*,^|,^\",192-255";
2942#endif
2943 if (STRCMP(curbuf->b_p_isk, p) != 0)
2944 {
2945 set_string_option_direct((char_u *)"isk", -1, p,
2946 OPT_FREE|OPT_LOCAL);
2947 check_buf_options(curbuf);
2948 (void)buf_init_chartab(curbuf, FALSE);
2949 }
2950
2951 curbuf->b_p_ts = 8; /* 'tabstop' is 8 */
2952 curwin->w_p_list = FALSE; /* no list mode */
2953
2954 curbuf->b_p_ma = FALSE; /* not modifiable */
2955 curbuf->b_p_bin = FALSE; /* reset 'bin' before reading file */
2956 curwin->w_p_nu = 0; /* no line numbers */
2957#ifdef FEAT_SCROLLBIND
2958 curwin->w_p_scb = FALSE; /* no scroll binding */
2959#endif
2960#ifdef FEAT_ARABIC
2961 curwin->w_p_arab = FALSE; /* no arabic mode */
2962#endif
2963#ifdef FEAT_RIGHTLEFT
2964 curwin->w_p_rl = FALSE; /* help window is left-to-right */
2965#endif
2966#ifdef FEAT_FOLDING
2967 curwin->w_p_fen = FALSE; /* No folding in the help window */
2968#endif
2969#ifdef FEAT_DIFF
2970 curwin->w_p_diff = FALSE; /* No 'diff' */
2971#endif
2972
2973#ifdef FEAT_AUTOCMD
2974 buf = curbuf;
2975#endif
2976 set_buflisted(FALSE);
2977 }
2978 else
2979 {
2980#ifdef FEAT_AUTOCMD
2981 buf = curbuf;
2982#endif
2983 /* Don't make a buffer listed if it's a help buffer. Useful when
2984 * using CTRL-O to go back to a help file. */
2985 if (!curbuf->b_help)
2986 set_buflisted(TRUE);
2987 }
2988
2989#ifdef FEAT_AUTOCMD
2990 /* If autocommands change buffers under our fingers, forget about
2991 * editing the file. */
2992 if (buf != curbuf)
2993 goto theend;
2994# ifdef FEAT_EVAL
2995 if (aborting()) /* autocmds may abort script processing */
2996 goto theend;
2997# endif
2998
2999 /* Since we are starting to edit a file, consider the filetype to be
3000 * unset. Helps for when an autocommand changes files and expects syntax
3001 * highlighting to work in the other file. */
3002 did_filetype = FALSE;
3003#endif
3004
3005/*
3006 * other_file oldbuf
3007 * FALSE FALSE re-edit same file, buffer is re-used
3008 * FALSE TRUE re-edit same file, nothing changes
3009 * TRUE FALSE start editing new file, new buffer
3010 * TRUE TRUE start editing in existing buffer (nothing to do)
3011 */
3012 if (!other_file && !oldbuf) /* re-use the buffer */
3013 {
3014 set_last_cursor(curwin); /* may set b_last_cursor */
3015 if (newlnum == ECMD_LAST || newlnum == ECMD_LASTL)
3016 {
3017 newlnum = curwin->w_cursor.lnum;
3018 solcol = curwin->w_cursor.col;
3019 }
3020#ifdef FEAT_AUTOCMD
3021 buf = curbuf;
3022 if (buf->b_fname != NULL)
3023 new_name = vim_strsave(buf->b_fname);
3024 else
3025 new_name = NULL;
3026#endif
3027 buf_freeall(curbuf, FALSE, FALSE); /* free all things for buffer */
3028#ifdef FEAT_AUTOCMD
3029 /* If autocommands deleted the buffer we were going to re-edit, give
3030 * up and jump to the end. */
3031 if (!buf_valid(buf))
3032 {
3033 delbuf_msg(new_name); /* frees new_name */
3034 goto theend;
3035 }
3036 vim_free(new_name);
3037
3038 /* If autocommands change buffers under our fingers, forget about
3039 * re-editing the file. Should do the buf_clear_file(), but perhaps
3040 * the autocommands changed the buffer... */
3041 if (buf != curbuf)
3042 goto theend;
3043# ifdef FEAT_EVAL
3044 if (aborting()) /* autocmds may abort script processing */
3045 goto theend;
3046# endif
3047#endif
3048 buf_clear_file(curbuf);
3049 curbuf->b_op_start.lnum = 0; /* clear '[ and '] marks */
3050 curbuf->b_op_end.lnum = 0;
3051 }
3052
3053/*
3054 * If we get here we are sure to start editing
3055 */
3056 /* don't redraw until the cursor is in the right line */
3057 ++RedrawingDisabled;
3058
3059 /* Assume success now */
3060 retval = OK;
3061
3062 /*
3063 * Reset cursor position, could be used by autocommands.
3064 */
3065 check_cursor();
3066
3067 /*
3068 * Check if we are editing the w_arg_idx file in the argument list.
3069 */
3070 check_arg_idx(curwin);
3071
3072#ifdef FEAT_AUTOCMD
3073 if (!auto_buf)
3074#endif
3075 {
3076 /*
3077 * Set cursor and init window before reading the file and executing
3078 * autocommands. This allows for the autocommands to position the
3079 * cursor.
3080 */
3081 win_init(curwin);
3082
3083#ifdef FEAT_FOLDING
3084 /* It's like all lines in the buffer changed. Need to update
3085 * automatic folding. */
3086 foldUpdateAll(curwin);
3087#endif
3088
3089#if defined(FEAT_SUN_WORKSHOP) || defined(FEAT_NETBEANS_INTG)
3090 if (p_acd && curbuf->b_ffname != NULL
3091 && vim_chdirfile(curbuf->b_ffname) == OK)
3092 shorten_fnames(TRUE);
3093#endif
3094 /*
3095 * Careful: open_buffer() and apply_autocmds() may change the current
3096 * buffer and window.
3097 */
3098 lnum = curwin->w_cursor.lnum;
3099 topline = curwin->w_topline;
3100 if (!oldbuf) /* need to read the file */
3101 {
3102#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
3103 swap_exists_action = SEA_DIALOG;
3104#endif
3105 curbuf->b_flags |= BF_CHECK_RO; /* set/reset 'ro' flag */
3106
3107 /*
3108 * Open the buffer and read the file.
3109 */
3110#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
3111 if (should_abort(open_buffer(FALSE, eap)))
3112 retval = FAIL;
3113#else
3114 (void)open_buffer(FALSE, eap);
3115#endif
3116
3117#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
3118 if (swap_exists_action == SEA_QUIT)
3119 retval = FAIL;
3120 handle_swap_exists(old_curbuf);
3121#endif
3122 }
3123#ifdef FEAT_AUTOCMD
3124 else
3125 {
Bram Moolenaar15d0a8c2004-09-06 17:44:46 +00003126 /* Read the modelines, but only to set window-local options. Any
3127 * buffer-local options have already been set and may have been
3128 * changed by the user. */
3129 do_modelines(TRUE);
3130
Bram Moolenaar071d4272004-06-13 20:20:40 +00003131 apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf,
3132 &retval);
3133 apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf,
3134 &retval);
3135 }
3136 check_arg_idx(curwin);
3137#endif
3138
3139 /*
3140 * If autocommands change the cursor position or topline, we should
3141 * keep it.
3142 */
3143 if (curwin->w_cursor.lnum != lnum)
3144 {
3145 newlnum = curwin->w_cursor.lnum;
3146 newcol = curwin->w_cursor.col;
3147 }
3148 if (curwin->w_topline == topline)
3149 topline = 0;
3150
3151 /* Even when cursor didn't move we need to recompute topline. */
3152 changed_line_abv_curs();
3153
3154#ifdef FEAT_TITLE
3155 maketitle();
3156#endif
3157 }
3158
3159#ifdef FEAT_DIFF
3160 /* Tell the diff stuff that this buffer is new and/or needs updating.
3161 * Also needed when re-editing the same buffer, because unloading will
3162 * have removed it as a diff buffer. */
3163 diff_new_buffer();
3164 diff_invalidate();
3165#endif
3166
3167 if (command == NULL)
3168 {
3169 if (newcol >= 0) /* position set by autocommands */
3170 {
3171 curwin->w_cursor.lnum = newlnum;
3172 curwin->w_cursor.col = newcol;
3173 check_cursor();
3174 }
3175 else if (newlnum > 0) /* line number from caller or old position */
3176 {
3177 curwin->w_cursor.lnum = newlnum;
3178 check_cursor_lnum();
3179 if (solcol >= 0 && !p_sol)
3180 {
3181 /* 'sol' is off: Use last known column. */
3182 curwin->w_cursor.col = solcol;
3183 check_cursor_col();
3184#ifdef FEAT_VIRTUALEDIT
3185 curwin->w_cursor.coladd = 0;
3186#endif
3187 curwin->w_set_curswant = TRUE;
3188 }
3189 else
3190 beginline(BL_SOL | BL_FIX);
3191 }
3192 else /* no line number, go to last line in Ex mode */
3193 {
3194 if (exmode_active)
3195 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
3196 beginline(BL_WHITE | BL_FIX);
3197 }
3198 }
3199
3200#ifdef FEAT_WINDOWS
3201 /* Check if cursors in other windows on the same buffer are still valid */
3202 check_lnums(FALSE);
3203#endif
3204
3205 /*
3206 * Did not read the file, need to show some info about the file.
3207 * Do this after setting the cursor.
3208 */
3209 if (oldbuf
3210#ifdef FEAT_AUTOCMD
3211 && !auto_buf
3212#endif
3213 )
3214 {
3215 int msg_scroll_save = msg_scroll;
3216
3217 /* Obey the 'O' flag in 'cpoptions': overwrite any previous file
3218 * message. */
3219 if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0)
3220 msg_scroll = FALSE;
3221 if (!msg_scroll) /* wait a bit when overwriting an error msg */
3222 check_for_delay(FALSE);
3223 msg_start();
3224 msg_scroll = msg_scroll_save;
3225 msg_scrolled_ign = TRUE;
3226
3227 fileinfo(FALSE, TRUE, FALSE);
3228
3229 msg_scrolled_ign = FALSE;
3230 }
3231
3232 if (command != NULL)
3233 do_cmdline(command, NULL, NULL, DOCMD_VERBOSE);
3234
3235#ifdef FEAT_KEYMAP
3236 if (curbuf->b_kmap_state & KEYMAP_INIT)
3237 keymap_init();
3238#endif
3239
3240 --RedrawingDisabled;
3241 if (!skip_redraw)
3242 {
3243 n = p_so;
3244 if (topline == 0 && command == NULL)
3245 p_so = 999; /* force cursor halfway the window */
3246 update_topline();
3247#ifdef FEAT_SCROLLBIND
3248 curwin->w_scbind_pos = curwin->w_topline;
3249#endif
3250 p_so = n;
3251 redraw_curbuf_later(NOT_VALID); /* redraw this buffer later */
3252 }
3253
3254 if (p_im)
3255 need_start_insertmode = TRUE;
3256
3257#if defined(FEAT_SUN_WORKSHOP) || defined(FEAT_NETBEANS_INTG)
3258 /* Change directories when the acd option is set on. */
3259 if (p_acd && curbuf->b_ffname != NULL
3260 && vim_chdirfile(curbuf->b_ffname) == OK)
3261 shorten_fnames(TRUE);
3262
3263 if (gui.in_use && curbuf->b_ffname != NULL)
3264 {
3265# ifdef FEAT_SUN_WORKSHOP
3266 if (usingSunWorkShop)
3267 workshop_file_opened((char *)curbuf->b_ffname, curbuf->b_p_ro);
3268# endif
3269# ifdef FEAT_NETBEANS_INTG
Bram Moolenaar35a9aaa2004-10-24 19:23:07 +00003270 if (usingNetbeans & ((flags & ECMD_SET_HELP) != ECMD_SET_HELP))
3271 netbeans_file_opened(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272# endif
3273 }
3274#endif
3275
3276theend:
3277#ifdef FEAT_BROWSE
3278 vim_free(browse_file);
3279#endif
3280 vim_free(free_fname);
3281 return retval;
3282}
3283
3284#ifdef FEAT_AUTOCMD
3285 static void
3286delbuf_msg(name)
3287 char_u *name;
3288{
3289 EMSG2(_("E143: Autocommands unexpectedly deleted new buffer %s"),
3290 name == NULL ? (char_u *)"" : name);
3291 vim_free(name);
3292 au_new_curbuf = NULL;
3293}
3294#endif
3295
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003296static int append_indent = 0; /* autoindent for first line */
3297
Bram Moolenaar071d4272004-06-13 20:20:40 +00003298/*
3299 * ":insert" and ":append", also used by ":change"
3300 */
3301 void
3302ex_append(eap)
3303 exarg_T *eap;
3304{
3305 char_u *theline;
3306 int did_undo = FALSE;
3307 linenr_T lnum = eap->line2;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003308 int indent = 0;
3309 char_u *p;
3310 int vcol;
3311 int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003312
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003313 /* the ! flag toggles autoindent */
3314 if (eap->forceit)
3315 curbuf->b_p_ai = !curbuf->b_p_ai;
3316
3317 /* First autoindent comes from the line we start on */
3318 if (eap->cmdidx != CMD_change && curbuf->b_p_ai && lnum > 0)
3319 append_indent = get_indent_lnum(lnum);
3320
Bram Moolenaar071d4272004-06-13 20:20:40 +00003321 if (eap->cmdidx != CMD_append)
3322 --lnum;
3323
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003324 /* when the buffer is empty append to line 0 and delete the dummy line */
3325 if (empty && lnum == 1)
3326 lnum = 0;
3327
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328 State = INSERT; /* behave like in Insert mode */
3329 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
3330 State |= LANGMAP;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003331
Bram Moolenaar071d4272004-06-13 20:20:40 +00003332 while (1)
3333 {
3334 msg_scroll = TRUE;
3335 need_wait_return = FALSE;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003336 if (curbuf->b_p_ai)
3337 {
3338 if (append_indent >= 0)
3339 {
3340 indent = append_indent;
3341 append_indent = -1;
3342 }
3343 else if (lnum > 0)
3344 indent = get_indent_lnum(lnum);
3345 }
3346 ex_keep_indent = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347 if (eap->getline == NULL)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003348 {
3349 /* No getline() function, use the lines that follow. This ends
3350 * when there is no more. */
3351 if (eap->nextcmd == NULL || *eap->nextcmd == NUL)
3352 break;
3353 p = vim_strchr(eap->nextcmd, NL);
3354 if (p == NULL)
3355 p = eap->nextcmd + STRLEN(eap->nextcmd);
3356 theline = vim_strnsave(eap->nextcmd, (int)(p - eap->nextcmd));
3357 if (*p != NUL)
3358 ++p;
3359 eap->nextcmd = p;
3360 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003361 else
3362 theline = eap->getline(
3363#ifdef FEAT_EVAL
Bram Moolenaar3d60ec22005-01-05 22:19:46 +00003364 eap->cstack->cs_looplevel > 0 ? -1 :
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365#endif
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003366 NUL, eap->cookie, indent);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 lines_left = Rows - 1;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003368 if (theline == NULL)
3369 break;
3370
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003371 /* Using ^ CTRL-D in getexmodeline() makes us repeat the indent. */
3372 if (ex_keep_indent)
3373 append_indent = indent;
3374
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003375 /* Look for the "." after automatic indent. */
3376 vcol = 0;
3377 for (p = theline; indent > vcol; ++p)
3378 {
3379 if (*p == ' ')
3380 ++vcol;
3381 else if (*p == TAB)
3382 vcol += 8 - vcol % 8;
3383 else
3384 break;
3385 }
3386 if ((p[0] == '.' && p[1] == NUL)
3387 || (!did_undo && u_save(lnum, lnum + 1) == FAIL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388 {
3389 vim_free(theline);
3390 break;
3391 }
3392
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003393 /* don't use autoindent if nothing was typed. */
3394 if (p[0] == NUL)
3395 theline[0] = NUL;
3396
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397 did_undo = TRUE;
3398 ml_append(lnum, theline, (colnr_T)0, FALSE);
3399 appended_lines_mark(lnum, 1L);
3400
3401 vim_free(theline);
3402 ++lnum;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003403
3404 if (empty)
3405 {
3406 ml_delete(2L, FALSE);
3407 empty = FALSE;
3408 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003409 }
3410 State = NORMAL;
3411
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003412 if (eap->forceit)
3413 curbuf->b_p_ai = !curbuf->b_p_ai;
3414
Bram Moolenaar071d4272004-06-13 20:20:40 +00003415 /* "start" is set to eap->line2+1 unless that position is invalid (when
3416 * eap->line2 pointed to the end of the buffer and nothig was appended)
3417 * "end" is set to lnum when something has been appended, otherwise
3418 * it is the same than "start" -- Acevedo */
3419 curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ?
3420 eap->line2 + 1 : curbuf->b_ml.ml_line_count;
3421 if (eap->cmdidx != CMD_append)
3422 --curbuf->b_op_start.lnum;
3423 curbuf->b_op_end.lnum = (eap->line2 < lnum)
3424 ? lnum : curbuf->b_op_start.lnum;
3425 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
3426 curwin->w_cursor.lnum = lnum;
3427 check_cursor_lnum();
3428 beginline(BL_SOL | BL_FIX);
3429
3430 need_wait_return = FALSE; /* don't use wait_return() now */
3431 ex_no_reprint = TRUE;
3432}
3433
3434/*
3435 * ":change"
3436 */
3437 void
3438ex_change(eap)
3439 exarg_T *eap;
3440{
3441 linenr_T lnum;
3442
3443 if (eap->line2 >= eap->line1
3444 && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL)
3445 return;
3446
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003447 /* the ! flag toggles autoindent */
3448 if (eap->forceit ? !curbuf->b_p_ai : curbuf->b_p_ai)
3449 append_indent = get_indent_lnum(eap->line1);
3450
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451 for (lnum = eap->line2; lnum >= eap->line1; --lnum)
3452 {
3453 if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
3454 break;
3455 ml_delete(eap->line1, FALSE);
3456 }
3457 deleted_lines_mark(eap->line1, (long)(eap->line2 - lnum));
3458
3459 /* ":append" on the line above the deleted lines. */
3460 eap->line2 = eap->line1;
3461 ex_append(eap);
3462}
3463
3464 void
3465ex_z(eap)
3466 exarg_T *eap;
3467{
3468 char_u *x;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003469 int bigness;
3470 char_u *kind;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003471 int minus = 0;
3472 linenr_T start, end, curs, i;
3473 int j;
3474 linenr_T lnum = eap->line2;
3475
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003476 /* Vi compatible: ":z!" uses display height, without a count uses
3477 * 'scroll' */
3478 if (eap->forceit)
3479 bigness = curwin->w_height;
3480 else if (firstwin == lastwin)
3481 bigness = curwin->w_p_scr * 2;
3482 else
3483 bigness = curwin->w_height - 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003484 if (bigness < 1)
3485 bigness = 1;
3486
3487 x = eap->arg;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003488 kind = x;
3489 if (*kind == '-' || *kind == '+' || *kind == '='
3490 || *kind == '^' || *kind == '.')
3491 ++x;
3492 while (*x == '-' || *x == '+')
Bram Moolenaar071d4272004-06-13 20:20:40 +00003493 ++x;
3494
3495 if (*x != 0)
3496 {
3497 if (!VIM_ISDIGIT(*x))
3498 {
3499 EMSG(_("E144: non-numeric argument to :z"));
3500 return;
3501 }
3502 else
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003503 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504 bigness = atoi((char *)x);
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003505 p_window = bigness;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003506 if (*kind == '=')
3507 bigness += 2;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003508 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003509 }
3510
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003511 /* the number of '-' and '+' multiplies the distance */
3512 if (*kind == '-' || *kind == '+')
3513 for (x = kind + 1; *x == *kind; ++x)
3514 ;
3515
3516 switch (*kind)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003517 {
3518 case '-':
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003519 start = lnum - bigness * (x - kind);
3520 end = start + bigness;
3521 curs = end;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522 break;
3523
3524 case '=':
Bram Moolenaar2a8d1f82005-02-05 21:43:56 +00003525 start = lnum - (bigness + 1) / 2 + 1;
3526 end = lnum + (bigness + 1) / 2 - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527 curs = lnum;
3528 minus = 1;
3529 break;
3530
3531 case '^':
3532 start = lnum - bigness * 2;
3533 end = lnum - bigness;
3534 curs = lnum - bigness;
3535 break;
3536
3537 case '.':
Bram Moolenaar2a8d1f82005-02-05 21:43:56 +00003538 start = lnum - (bigness + 1) / 2 + 1;
3539 end = lnum + (bigness + 1) / 2 - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540 curs = end;
3541 break;
3542
3543 default: /* '+' */
3544 start = lnum;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003545 if (*kind == '+')
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003546 start += bigness * (x - kind - 1) + 1;
3547 else if (eap->addr_count == 0)
3548 ++start;
3549 end = start + bigness - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003550 curs = end;
3551 break;
3552 }
3553
3554 if (start < 1)
3555 start = 1;
3556
3557 if (end > curbuf->b_ml.ml_line_count)
3558 end = curbuf->b_ml.ml_line_count;
3559
3560 if (curs > curbuf->b_ml.ml_line_count)
3561 curs = curbuf->b_ml.ml_line_count;
3562
3563 for (i = start; i <= end; i++)
3564 {
3565 if (minus && i == lnum)
3566 {
3567 msg_putchar('\n');
3568
3569 for (j = 1; j < Columns; j++)
3570 msg_putchar('-');
3571 }
3572
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003573 print_line(i, eap->flags & EXFLAG_NR, eap->flags & EXFLAG_LIST);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574
3575 if (minus && i == lnum)
3576 {
3577 msg_putchar('\n');
3578
3579 for (j = 1; j < Columns; j++)
3580 msg_putchar('-');
3581 }
3582 }
3583
3584 curwin->w_cursor.lnum = curs;
3585 ex_no_reprint = TRUE;
3586}
3587
3588/*
3589 * Check if the restricted flag is set.
3590 * If so, give an error message and return TRUE.
3591 * Otherwise, return FALSE.
3592 */
3593 int
3594check_restricted()
3595{
3596 if (restricted)
3597 {
3598 EMSG(_("E145: Shell commands not allowed in rvim"));
3599 return TRUE;
3600 }
3601 return FALSE;
3602}
3603
3604/*
3605 * Check if the secure flag is set (.exrc or .vimrc in current directory).
3606 * If so, give an error message and return TRUE.
3607 * Otherwise, return FALSE.
3608 */
3609 int
3610check_secure()
3611{
3612 if (secure)
3613 {
3614 secure = 2;
3615 EMSG(_(e_curdir));
3616 return TRUE;
3617 }
3618#ifdef HAVE_SANDBOX
3619 /*
3620 * In the sandbox more things are not allowed, including the things
3621 * disallowed in secure mode.
3622 */
3623 if (sandbox != 0)
3624 {
3625 EMSG(_(e_sandbox));
3626 return TRUE;
3627 }
3628#endif
3629 return FALSE;
3630}
3631
3632static char_u *old_sub = NULL; /* previous substitute pattern */
3633static int global_need_beginline; /* call beginline() after ":g" */
3634
3635/*
3636 * When ":global" is used to number of substitutions and changed lines is
3637 * accumulated until it's finished.
3638 */
3639static long sub_nsubs; /* total number of substitutions */
3640static linenr_T sub_nlines; /* total number of lines changed */
3641
3642/* do_sub()
3643 *
3644 * Perform a substitution from line eap->line1 to line eap->line2 using the
3645 * command pointed to by eap->arg which should be of the form:
3646 *
3647 * /pattern/substitution/{flags}
3648 *
3649 * The usual escapes are supported as described in the regexp docs.
3650 */
3651 void
3652do_sub(eap)
3653 exarg_T *eap;
3654{
3655 linenr_T lnum;
3656 long i = 0;
3657 regmmatch_T regmatch;
3658 static int do_all = FALSE; /* do multiple substitutions per line */
3659 static int do_ask = FALSE; /* ask for confirmation */
Bram Moolenaar05159a02005-02-26 23:04:13 +00003660 static int do_count = FALSE; /* count only */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003661 static int do_error = TRUE; /* if false, ignore errors */
3662 static int do_print = FALSE; /* print last line with subs. */
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003663 static int do_list = FALSE; /* list last line with subs. */
3664 static int do_number = FALSE; /* list last line with line nr*/
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 static int do_ic = 0; /* ignore case flag */
3666 char_u *pat = NULL, *sub = NULL; /* init for GCC */
3667 int delimiter;
3668 int sublen;
3669 int got_quit = FALSE;
3670 int got_match = FALSE;
3671 int temp;
3672 int which_pat;
3673 char_u *cmd;
3674 int save_State;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003675 linenr_T first_line = 0; /* first changed line */
3676 linenr_T last_line= 0; /* below last changed line AFTER the
Bram Moolenaar071d4272004-06-13 20:20:40 +00003677 * change */
3678 linenr_T old_line_count = curbuf->b_ml.ml_line_count;
3679 linenr_T line2;
Bram Moolenaar81bf7082005-02-12 14:31:42 +00003680 long nmatch; /* number of lines in match */
3681 linenr_T sub_firstlnum; /* nr of first sub line */
3682 char_u *sub_firstline; /* allocated copy of first sub line */
3683 int endcolumn = FALSE; /* cursor in last column when done */
Bram Moolenaar05159a02005-02-26 23:04:13 +00003684 pos_T old_cursor = curwin->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685
3686 cmd = eap->arg;
3687 if (!global_busy)
3688 {
3689 sub_nsubs = 0;
3690 sub_nlines = 0;
3691 }
3692
3693#ifdef FEAT_FKMAP /* reverse the flow of the Farsi characters */
3694 if (p_altkeymap && curwin->w_p_rl)
3695 lrF_sub(cmd);
3696#endif
3697
3698 if (eap->cmdidx == CMD_tilde)
3699 which_pat = RE_LAST; /* use last used regexp */
3700 else
3701 which_pat = RE_SUBST; /* use last substitute regexp */
3702
3703 /* new pattern and substitution */
3704 if (eap->cmd[0] == 's' && *cmd != NUL && !vim_iswhite(*cmd)
3705 && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL)
3706 {
3707 /* don't accept alphanumeric for separator */
3708 if (isalpha(*cmd))
3709 {
3710 EMSG(_("E146: Regular expressions can't be delimited by letters"));
3711 return;
3712 }
3713 /*
3714 * undocumented vi feature:
3715 * "\/sub/" and "\?sub?" use last used search pattern (almost like
3716 * //sub/r). "\&sub&" use last substitute pattern (like //sub/).
3717 */
3718 if (*cmd == '\\')
3719 {
3720 ++cmd;
3721 if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
3722 {
3723 EMSG(_(e_backslash));
3724 return;
3725 }
3726 if (*cmd != '&')
3727 which_pat = RE_SEARCH; /* use last '/' pattern */
3728 pat = (char_u *)""; /* empty search pattern */
3729 delimiter = *cmd++; /* remember delimiter character */
3730 }
3731 else /* find the end of the regexp */
3732 {
3733 which_pat = RE_LAST; /* use last used regexp */
3734 delimiter = *cmd++; /* remember delimiter character */
3735 pat = cmd; /* remember start of search pat */
3736 cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg);
3737 if (cmd[0] == delimiter) /* end delimiter found */
3738 *cmd++ = NUL; /* replace it with a NUL */
3739 }
3740
3741 /*
3742 * Small incompatibility: vi sees '\n' as end of the command, but in
3743 * Vim we want to use '\n' to find/substitute a NUL.
3744 */
3745 sub = cmd; /* remember the start of the substitution */
3746
3747 while (cmd[0])
3748 {
3749 if (cmd[0] == delimiter) /* end delimiter found */
3750 {
3751 *cmd++ = NUL; /* replace it with a NUL */
3752 break;
3753 }
3754 if (cmd[0] == '\\' && cmd[1] != 0) /* skip escaped characters */
3755 ++cmd;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003756 mb_ptr_adv(cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003757 }
3758
3759 if (!eap->skip)
3760 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003761 /* In POSIX vi ":s/pat/%/" uses the previous subst. string. */
3762 if (STRCMP(sub, "%") == 0
3763 && vim_strchr(p_cpo, CPO_SUBPERCENT) != NULL)
3764 {
3765 if (old_sub == NULL) /* there is no previous command */
3766 {
3767 EMSG(_(e_nopresub));
3768 return;
3769 }
3770 sub = old_sub;
3771 }
3772 else
3773 {
3774 vim_free(old_sub);
3775 old_sub = vim_strsave(sub);
3776 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003777 }
3778 }
3779 else if (!eap->skip) /* use previous pattern and substitution */
3780 {
3781 if (old_sub == NULL) /* there is no previous command */
3782 {
3783 EMSG(_(e_nopresub));
3784 return;
3785 }
3786 pat = NULL; /* search_regcomp() will use previous pattern */
3787 sub = old_sub;
Bram Moolenaarb11bd7e2005-02-07 22:05:52 +00003788
3789 /* Vi compatibility quirk: repeating with ":s" keeps the cursor in the
3790 * last column after using "$". */
3791 endcolumn = (curwin->w_curswant == MAXCOL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003792 }
3793
3794 /*
3795 * Find trailing options. When '&' is used, keep old options.
3796 */
3797 if (*cmd == '&')
3798 ++cmd;
3799 else
3800 {
3801 if (!p_ed)
3802 {
3803 if (p_gd) /* default is global on */
3804 do_all = TRUE;
3805 else
3806 do_all = FALSE;
3807 do_ask = FALSE;
3808 }
3809 do_error = TRUE;
3810 do_print = FALSE;
3811 do_ic = 0;
3812 }
3813 while (*cmd)
3814 {
3815 /*
3816 * Note that 'g' and 'c' are always inverted, also when p_ed is off.
3817 * 'r' is never inverted.
3818 */
3819 if (*cmd == 'g')
3820 do_all = !do_all;
3821 else if (*cmd == 'c')
3822 do_ask = !do_ask;
Bram Moolenaar05159a02005-02-26 23:04:13 +00003823 else if (*cmd == 'n')
3824 do_count = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 else if (*cmd == 'e')
3826 do_error = !do_error;
3827 else if (*cmd == 'r') /* use last used regexp */
3828 which_pat = RE_LAST;
3829 else if (*cmd == 'p')
3830 do_print = TRUE;
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00003831 else if (*cmd == '#')
3832 {
3833 do_print = TRUE;
3834 do_number = TRUE;
3835 }
3836 else if (*cmd == 'l')
3837 {
3838 do_print = TRUE;
3839 do_list = TRUE;
3840 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841 else if (*cmd == 'i') /* ignore case */
3842 do_ic = 'i';
3843 else if (*cmd == 'I') /* don't ignore case */
3844 do_ic = 'I';
3845 else
3846 break;
3847 ++cmd;
3848 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00003849 if (do_count)
3850 do_ask = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851
3852 /*
3853 * check for a trailing count
3854 */
3855 cmd = skipwhite(cmd);
3856 if (VIM_ISDIGIT(*cmd))
3857 {
3858 i = getdigits(&cmd);
3859 if (i <= 0 && !eap->skip && do_error)
3860 {
3861 EMSG(_(e_zerocount));
3862 return;
3863 }
3864 eap->line1 = eap->line2;
3865 eap->line2 += i - 1;
3866 if (eap->line2 > curbuf->b_ml.ml_line_count)
3867 eap->line2 = curbuf->b_ml.ml_line_count;
3868 }
3869
3870 /*
3871 * check for trailing command or garbage
3872 */
3873 cmd = skipwhite(cmd);
3874 if (*cmd && *cmd != '"') /* if not end-of-line or comment */
3875 {
3876 eap->nextcmd = check_nextcmd(cmd);
3877 if (eap->nextcmd == NULL)
3878 {
3879 EMSG(_(e_trailing));
3880 return;
3881 }
3882 }
3883
3884 if (eap->skip) /* not executing commands, only parsing */
3885 return;
3886
3887 if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, &regmatch) == FAIL)
3888 {
3889 if (do_error)
3890 EMSG(_(e_invcmd));
3891 return;
3892 }
3893
3894 /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
3895 if (do_ic == 'i')
3896 regmatch.rmm_ic = TRUE;
3897 else if (do_ic == 'I')
3898 regmatch.rmm_ic = FALSE;
3899
3900 sub_firstline = NULL;
3901
3902 /*
3903 * ~ in the substitute pattern is replaced with the old pattern.
3904 * We do it here once to avoid it to be replaced over and over again.
3905 * But don't do it when it starts with "\=", then it's an expression.
3906 */
3907 if (!(sub[0] == '\\' && sub[1] == '='))
3908 sub = regtilde(sub, p_magic);
3909
3910 /*
3911 * Check for a match on each line.
3912 */
3913 line2 = eap->line2;
3914 for (lnum = eap->line1; lnum <= line2 && !(got_quit
3915#if defined(FEAT_EVAL) && defined(FEAT_AUTOCMD)
3916 || aborting()
3917#endif
3918 ); ++lnum)
3919 {
3920 sub_firstlnum = lnum;
3921 nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum, (colnr_T)0);
3922 if (nmatch)
3923 {
3924 colnr_T copycol;
3925 colnr_T matchcol;
3926 colnr_T prev_matchcol = MAXCOL;
3927 char_u *new_end, *new_start = NULL;
3928 unsigned new_start_len = 0;
3929 char_u *p1;
3930 int did_sub = FALSE;
3931 int lastone;
3932 unsigned len, needed_len;
3933 long nmatch_tl = 0; /* nr of lines matched below lnum */
3934 int do_again; /* do it again after joining lines */
Bram Moolenaar8299df92004-07-10 09:47:34 +00003935 int skip_match = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003936
3937 /*
3938 * The new text is build up step by step, to avoid too much
3939 * copying. There are these pieces:
3940 * sub_firstline The old text, unmodifed.
3941 * copycol Column in the old text where we started
3942 * looking for a match; from here old text still
3943 * needs to be copied to the new text.
3944 * matchcol Column number of the old text where to look
3945 * for the next match. It's just after the
3946 * previous match or one further.
3947 * prev_matchcol Column just after the previous match (if any).
3948 * Mostly equal to matchcol, except for the first
3949 * match and after skipping an empty match.
3950 * regmatch.*pos Where the pattern matched in the old text.
3951 * new_start The new text, all that has been produced so
3952 * far.
3953 * new_end The new text, where to append new text.
3954 *
3955 * lnum The line number where we were looking for the
3956 * first match in the old line.
3957 * sub_firstlnum The line number in the buffer where to look
3958 * for a match. Can be different from "lnum"
3959 * when the pattern or substitute string contains
3960 * line breaks.
3961 *
3962 * Special situations:
3963 * - When the substitute string contains a line break, the part up
3964 * to the line break is inserted in the text, but the copy of
3965 * the original line is kept. "sub_firstlnum" is adjusted for
3966 * the inserted lines.
3967 * - When the matched pattern contains a line break, the old line
3968 * is taken from the line at the end of the pattern. The lines
3969 * in the match are deleted later, "sub_firstlnum" is adjusted
3970 * accordingly.
3971 *
3972 * The new text is built up in new_start[]. It has some extra
3973 * room to avoid using alloc()/free() too often. new_start_len is
3974 * the lenght of the allocated memory at new_start.
3975 *
3976 * Make a copy of the old line, so it won't be taken away when
3977 * updating the screen or handling a multi-line match. The "old_"
3978 * pointers point into this copy.
3979 */
3980 sub_firstline = vim_strsave(ml_get(sub_firstlnum));
3981 if (sub_firstline == NULL)
3982 {
3983 vim_free(new_start);
3984 goto outofmem;
3985 }
3986 copycol = 0;
3987 matchcol = 0;
3988
3989 /* At first match, remember current cursor position. */
3990 if (!got_match)
3991 {
3992 setpcmark();
3993 got_match = TRUE;
3994 }
3995
3996 /*
3997 * Loop until nothing more to replace in this line.
3998 * 1. Handle match with empty string.
3999 * 2. If do_ask is set, ask for confirmation.
4000 * 3. substitute the string.
4001 * 4. if do_all is set, find next match
4002 * 5. break if there isn't another match in this line
4003 */
4004 for (;;)
4005 {
4006 /* Save the line number of the last change for the final
4007 * cursor position (just like Vi). */
4008 curwin->w_cursor.lnum = lnum;
4009 do_again = FALSE;
4010
4011 /*
4012 * 1. Match empty string does not count, except for first
4013 * match. This reproduces the strange vi behaviour.
4014 * This also catches endless loops.
4015 */
4016 if (matchcol == prev_matchcol
4017 && regmatch.endpos[0].lnum == 0
4018 && matchcol == regmatch.endpos[0].col)
4019 {
Bram Moolenaar8299df92004-07-10 09:47:34 +00004020 if (sub_firstline[matchcol] == NUL)
4021 /* We already were at the end of the line. Don't look
4022 * for a match in this line again. */
4023 skip_match = TRUE;
4024 else
4025 ++matchcol; /* search for a match at next column */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026 goto skip;
4027 }
4028
4029 /* Normally we continue searching for a match just after the
4030 * previous match. */
4031 matchcol = regmatch.endpos[0].col;
4032 prev_matchcol = matchcol;
4033
4034 /*
Bram Moolenaar05159a02005-02-26 23:04:13 +00004035 * 2. If do_count is set only increase the counter.
4036 * If do_ask is set, ask for confirmation.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037 */
Bram Moolenaar05159a02005-02-26 23:04:13 +00004038 if (do_count)
4039 {
4040 /* For a multi-line match, put matchcol at the NUL at
4041 * the end of the line and set nmatch to one, so that
4042 * we continue looking for a match on the next line.
4043 * Avoids that ":s/\nB\@=//gc" get stuck. */
4044 if (nmatch > 1)
4045 {
4046 matchcol = STRLEN(sub_firstline);
4047 nmatch = 1;
4048 }
4049 sub_nsubs++;
4050 did_sub = TRUE;
4051 goto skip;
4052 }
4053
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054 if (do_ask)
4055 {
4056 /* change State to CONFIRM, so that the mouse works
4057 * properly */
4058 save_State = State;
4059 State = CONFIRM;
4060#ifdef FEAT_MOUSE
4061 setmouse(); /* disable mouse in xterm */
4062#endif
4063 curwin->w_cursor.col = regmatch.startpos[0].col;
4064
4065 /* When 'cpoptions' contains "u" don't sync undo when
4066 * asking for confirmation. */
4067 if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
4068 ++no_u_sync;
4069
4070 /*
4071 * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
4072 */
4073 while (do_ask)
4074 {
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004075 if (exmode_active)
4076 {
4077 char_u *resp;
4078 colnr_T sc, ec;
4079
4080 print_line_no_prefix(lnum, FALSE, FALSE);
4081
4082 getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
4083 curwin->w_cursor.col = regmatch.endpos[0].col - 1;
4084 getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
4085 msg_start();
Bram Moolenaar05159a02005-02-26 23:04:13 +00004086 for (i = 0; i < (long)sc; ++i)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004087 msg_putchar(' ');
Bram Moolenaar05159a02005-02-26 23:04:13 +00004088 for ( ; i <= (long)ec; ++i)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004089 msg_putchar('^');
4090
4091 resp = getexmodeline('?', NULL, 0);
4092 if (resp != NULL)
4093 {
4094 i = *resp;
4095 vim_free(resp);
4096 }
4097 }
4098 else
4099 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004100#ifdef FEAT_FOLDING
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004101 int save_p_fen = curwin->w_p_fen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004102
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004103 curwin->w_p_fen = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004104#endif
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004105 /* Invert the matched string.
4106 * Remove the inversion afterwards. */
4107 temp = RedrawingDisabled;
4108 RedrawingDisabled = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004109
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004110 search_match_lines = regmatch.endpos[0].lnum;
4111 search_match_endcol = regmatch.endpos[0].col;
4112 highlight_match = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004114 update_topline();
4115 validate_cursor();
4116 update_screen(NOT_VALID);
4117 highlight_match = FALSE;
4118 redraw_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004119
4120#ifdef FEAT_FOLDING
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004121 curwin->w_p_fen = save_p_fen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004122#endif
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004123 if (msg_row == Rows - 1)
4124 msg_didout = FALSE; /* avoid a scroll-up */
4125 msg_starthere();
4126 i = msg_scroll;
4127 msg_scroll = 0; /* truncate msg when
4128 needed */
4129 msg_no_more = TRUE;
4130 /* write message same highlighting as for
4131 * wait_return */
4132 smsg_attr(hl_attr(HLF_R),
4133 (char_u *)_("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
4134 msg_no_more = FALSE;
4135 msg_scroll = i;
4136 showruler(TRUE);
4137 windgoto(msg_row, msg_col);
4138 RedrawingDisabled = temp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139
4140#ifdef USE_ON_FLY_SCROLL
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004141 dont_scroll = FALSE; /* allow scrolling here */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142#endif
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004143 ++no_mapping; /* don't map this key */
4144 ++allow_keys; /* allow special keys */
4145 i = safe_vgetc();
4146 --allow_keys;
4147 --no_mapping;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004149 /* clear the question */
4150 msg_didout = FALSE; /* don't scroll up */
4151 msg_col = 0;
4152 gotocmdline(TRUE);
4153 }
4154
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 need_wait_return = FALSE; /* no hit-return prompt */
4156 if (i == 'q' || i == ESC || i == Ctrl_C
4157#ifdef UNIX
4158 || i == intr_char
4159#endif
4160 )
4161 {
4162 got_quit = TRUE;
4163 break;
4164 }
4165 if (i == 'n')
4166 break;
4167 if (i == 'y')
4168 break;
4169 if (i == 'l')
4170 {
4171 /* last: replace and then stop */
4172 do_all = FALSE;
4173 line2 = lnum;
4174 break;
4175 }
4176 if (i == 'a')
4177 {
4178 do_ask = FALSE;
4179 break;
4180 }
4181#ifdef FEAT_INS_EXPAND
4182 if (i == Ctrl_E)
4183 scrollup_clamp();
4184 else if (i == Ctrl_Y)
4185 scrolldown_clamp();
4186#endif
4187 }
4188 State = save_State;
4189#ifdef FEAT_MOUSE
4190 setmouse();
4191#endif
4192 if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
4193 --no_u_sync;
4194
4195 if (i == 'n')
4196 {
4197 /* For a multi-line match, put matchcol at the NUL at
4198 * the end of the line and set nmatch to one, so that
4199 * we continue looking for a match on the next line.
4200 * Avoids that ":s/\nB\@=//gc" get stuck. */
4201 if (nmatch > 1)
4202 {
4203 matchcol = STRLEN(sub_firstline);
4204 nmatch = 1;
4205 }
4206 goto skip;
4207 }
4208 if (got_quit)
4209 break;
4210 }
4211
4212 /* Move the cursor to the start of the match, so that we can
4213 * use "\=col("."). */
4214 curwin->w_cursor.col = regmatch.startpos[0].col;
4215
4216 /*
4217 * 3. substitute the string.
4218 */
4219 /* get length of substitution part */
4220 sublen = vim_regsub_multi(&regmatch, sub_firstlnum,
4221 sub, sub_firstline, FALSE, p_magic, TRUE);
4222
4223 /* Need room for:
4224 * - result so far in new_start (not for first sub in line)
4225 * - original text up to match
4226 * - length of substituted part
4227 * - original text after match
4228 */
4229 if (nmatch == 1)
4230 p1 = sub_firstline;
4231 else
4232 {
4233 p1 = ml_get(sub_firstlnum + nmatch - 1);
4234 nmatch_tl += nmatch - 1;
4235 }
4236 i = regmatch.startpos[0].col - copycol;
4237 needed_len = i + ((unsigned)STRLEN(p1) - regmatch.endpos[0].col)
4238 + sublen + 1;
4239 if (new_start == NULL)
4240 {
4241 /*
4242 * Get some space for a temporary buffer to do the
4243 * substitution into (and some extra space to avoid
4244 * too many calls to alloc()/free()).
4245 */
4246 new_start_len = needed_len + 50;
4247 if ((new_start = alloc_check(new_start_len)) == NULL)
4248 goto outofmem;
4249 *new_start = NUL;
4250 new_end = new_start;
4251 }
4252 else
4253 {
4254 /*
4255 * Check if the temporary buffer is long enough to do the
4256 * substitution into. If not, make it larger (with a bit
4257 * extra to avoid too many calls to alloc()/free()).
4258 */
4259 len = (unsigned)STRLEN(new_start);
4260 needed_len += len;
4261 if (needed_len > new_start_len)
4262 {
4263 new_start_len = needed_len + 50;
4264 if ((p1 = alloc_check(new_start_len)) == NULL)
4265 {
4266 vim_free(new_start);
4267 goto outofmem;
4268 }
4269 mch_memmove(p1, new_start, (size_t)(len + 1));
4270 vim_free(new_start);
4271 new_start = p1;
4272 }
4273 new_end = new_start + len;
4274 }
4275
4276 /*
4277 * copy the text up to the part that matched
4278 */
4279 mch_memmove(new_end, sub_firstline + copycol, (size_t)i);
4280 new_end += i;
4281
4282 (void)vim_regsub_multi(&regmatch, sub_firstlnum,
4283 sub, new_end, TRUE, p_magic, TRUE);
4284 sub_nsubs++;
4285 did_sub = TRUE;
4286
4287 /* Move the cursor to the start of the line, to avoid that it
4288 * is beyond the end of the line after the substitution. */
4289 curwin->w_cursor.col = 0;
4290
4291 /* For a multi-line match, make a copy of the last matched
4292 * line and continue in that one. */
4293 if (nmatch > 1)
4294 {
4295 sub_firstlnum += nmatch - 1;
4296 vim_free(sub_firstline);
4297 sub_firstline = vim_strsave(ml_get(sub_firstlnum));
4298 /* When going beyond the last line, stop substituting. */
4299 if (sub_firstlnum <= line2)
4300 do_again = TRUE;
4301 else
4302 do_all = FALSE;
4303 }
4304
4305 /* Remember next character to be copied. */
4306 copycol = regmatch.endpos[0].col;
4307
4308 /*
4309 * Now the trick is to replace CTRL-M chars with a real line
4310 * break. This would make it impossible to insert a CTRL-M in
4311 * the text. The line break can be avoided by preceding the
4312 * CTRL-M with a backslash. To be able to insert a backslash,
4313 * they must be doubled in the string and are halved here.
4314 * That is Vi compatible.
4315 */
4316 for (p1 = new_end; *p1; ++p1)
4317 {
4318 if (p1[0] == '\\' && p1[1] != NUL) /* remove backslash */
4319 mch_memmove(p1, p1 + 1, STRLEN(p1));
4320 else if (*p1 == CAR)
4321 {
4322 if (u_inssub(lnum) == OK) /* prepare for undo */
4323 {
4324 *p1 = NUL; /* truncate up to the CR */
4325 ml_append(lnum - 1, new_start,
4326 (colnr_T)(p1 - new_start + 1), FALSE);
4327 mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
4328 if (do_ask)
4329 appended_lines(lnum - 1, 1L);
4330 else
4331 {
4332 if (first_line == 0)
4333 first_line = lnum;
4334 last_line = lnum + 1;
4335 }
4336 /* All line numbers increase. */
4337 ++sub_firstlnum;
4338 ++lnum;
4339 ++line2;
4340 /* move the cursor to the new line, like Vi */
4341 ++curwin->w_cursor.lnum;
4342 STRCPY(new_start, p1 + 1); /* copy the rest */
4343 p1 = new_start - 1;
4344 }
4345 }
4346#ifdef FEAT_MBYTE
4347 else if (has_mbyte)
4348 p1 += (*mb_ptr2len_check)(p1) - 1;
4349#endif
4350 }
4351
4352 /*
4353 * 4. If do_all is set, find next match.
4354 * Prevent endless loop with patterns that match empty
4355 * strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
4356 * But ":s/\n/#/" is OK.
4357 */
4358skip:
4359 /* We already know that we did the last subst when we are at
4360 * the end of the line, except that a pattern like
4361 * "bar\|\nfoo" may match at the NUL. */
Bram Moolenaar8299df92004-07-10 09:47:34 +00004362 lastone = (skip_match
4363 || got_int
4364 || got_quit
4365 || !(do_all || do_again)
4366 || (sub_firstline[matchcol] == NUL && nmatch <= 1
4367 && !re_multiline(regmatch.regprog)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 nmatch = -1;
4369
4370 /*
4371 * Replace the line in the buffer when needed. This is
4372 * skipped when there are more matches.
4373 * The check for nmatch_tl is needed for when multi-line
4374 * matching must replace the lines before trying to do another
4375 * match, otherwise "\@<=" won't work.
4376 * When asking the user we like to show the already replaced
4377 * text, but don't do it when "\<@=" or "\<@!" is used, it
4378 * changes what matches.
4379 */
4380 if (lastone
4381 || (do_ask && !re_lookbehind(regmatch.regprog))
4382 || nmatch_tl > 0
4383 || (nmatch = vim_regexec_multi(&regmatch, curwin,
4384 curbuf, sub_firstlnum, matchcol)) == 0)
4385 {
4386 if (new_start != NULL)
4387 {
4388 /*
4389 * Copy the rest of the line, that didn't match.
4390 * "matchcol" has to be adjusted, we use the end of
4391 * the line as reference, because the substitute may
4392 * have changed the number of characters. Same for
4393 * "prev_matchcol".
4394 */
4395 STRCAT(new_start, sub_firstline + copycol);
4396 matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
4397 prev_matchcol = (colnr_T)STRLEN(sub_firstline)
4398 - prev_matchcol;
4399
4400 if (u_savesub(lnum) != OK)
4401 break;
4402 ml_replace(lnum, new_start, TRUE);
4403
4404 if (nmatch_tl > 0)
4405 {
4406 /*
4407 * Matched lines have now been substituted and are
4408 * useless, delete them. The part after the match
4409 * has been appended to new_start, we don't need
4410 * it in the buffer.
4411 */
4412 ++lnum;
4413 if (u_savedel(lnum, nmatch_tl) != OK)
4414 break;
4415 for (i = 0; i < nmatch_tl; ++i)
4416 ml_delete(lnum, (int)FALSE);
4417 mark_adjust(lnum, lnum + nmatch_tl - 1,
4418 (long)MAXLNUM, -nmatch_tl);
4419 if (do_ask)
4420 deleted_lines(lnum, nmatch_tl);
4421 --lnum;
4422 line2 -= nmatch_tl; /* nr of lines decreases */
4423 nmatch_tl = 0;
4424 }
4425
4426 /* When asking, undo is saved each time, must also set
4427 * changed flag each time. */
4428 if (do_ask)
4429 changed_bytes(lnum, 0);
4430 else
4431 {
4432 if (first_line == 0)
4433 first_line = lnum;
4434 last_line = lnum + 1;
4435 }
4436
4437 sub_firstlnum = lnum;
4438 vim_free(sub_firstline); /* free the temp buffer */
4439 sub_firstline = new_start;
4440 new_start = NULL;
4441 matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
4442 prev_matchcol = (colnr_T)STRLEN(sub_firstline)
4443 - prev_matchcol;
4444 copycol = 0;
4445 }
4446 if (nmatch == -1 && !lastone)
4447 nmatch = vim_regexec_multi(&regmatch, curwin, curbuf,
4448 sub_firstlnum, matchcol);
4449
4450 /*
4451 * 5. break if there isn't another match in this line
4452 */
4453 if (nmatch <= 0)
4454 break;
4455 }
4456
4457 line_breakcheck();
4458 }
4459
4460 if (did_sub)
4461 ++sub_nlines;
4462 vim_free(sub_firstline); /* free the copy of the original line */
4463 sub_firstline = NULL;
4464 }
4465
4466 line_breakcheck();
4467 }
4468
4469 if (first_line != 0)
4470 {
4471 /* Need to subtract the number of added lines from "last_line" to get
4472 * the line number before the change (same as adding the number of
4473 * deleted lines). */
4474 i = curbuf->b_ml.ml_line_count - old_line_count;
4475 changed_lines(first_line, 0, last_line - i, i);
4476 }
4477
4478outofmem:
4479 vim_free(sub_firstline); /* may have to free allocated copy of the line */
Bram Moolenaar05159a02005-02-26 23:04:13 +00004480
4481 /* ":s/pat//n" doesn't move the cursor */
4482 if (do_count)
4483 curwin->w_cursor = old_cursor;
4484
Bram Moolenaar071d4272004-06-13 20:20:40 +00004485 if (sub_nsubs)
4486 {
4487 /* Set the '[ and '] marks. */
4488 curbuf->b_op_start.lnum = eap->line1;
4489 curbuf->b_op_end.lnum = line2;
4490 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
4491
4492 if (!global_busy)
4493 {
Bram Moolenaarb11bd7e2005-02-07 22:05:52 +00004494 if (endcolumn)
4495 coladvance((colnr_T)MAXCOL);
4496 else
4497 beginline(BL_WHITE | BL_FIX);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004498 if (!do_sub_msg(do_count) && do_ask)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004499 MSG("");
4500 }
4501 else
4502 global_need_beginline = TRUE;
4503 if (do_print)
Bram Moolenaar5313dcb2005-02-22 08:56:13 +00004504 print_line(curwin->w_cursor.lnum, do_number, do_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505 }
4506 else if (!global_busy)
4507 {
4508 if (got_int) /* interrupted */
4509 EMSG(_(e_interr));
4510 else if (got_match) /* did find something but nothing substituted */
4511 MSG("");
4512 else if (do_error) /* nothing found */
4513 EMSG2(_(e_patnotf2), get_search_pat());
4514 }
4515
4516 vim_free(regmatch.regprog);
4517}
4518
4519/*
4520 * Give message for number of substitutions.
4521 * Can also be used after a ":global" command.
4522 * Return TRUE if a message was given.
4523 */
4524 static int
Bram Moolenaar05159a02005-02-26 23:04:13 +00004525do_sub_msg(count_only)
4526 int count_only; /* used 'n' flag for ":s" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527{
Bram Moolenaar555b2802005-05-19 21:08:39 +00004528 int len = 0;
4529
Bram Moolenaar071d4272004-06-13 20:20:40 +00004530 /*
4531 * Only report substitutions when:
4532 * - more than 'report' substitutions
4533 * - command was typed by user, or number of changed lines > 'report'
4534 * - giving messages is not disabled by 'lazyredraw'
4535 */
Bram Moolenaar05159a02005-02-26 23:04:13 +00004536 if (((sub_nsubs > p_report && (KeyTyped || sub_nlines > 1 || p_report < 1))
4537 || count_only)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 && messaging())
4539 {
4540 if (got_int)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004541 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542 STRCPY(msg_buf, _("(Interrupted) "));
Bram Moolenaar555b2802005-05-19 21:08:39 +00004543 len = STRLEN(msg_buf);
4544 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545 if (sub_nsubs == 1)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004546 vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len,
4547 "%s", count_only ? _("1 match") : _("1 substitution"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548 else
Bram Moolenaar555b2802005-05-19 21:08:39 +00004549 vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len,
Bram Moolenaar05159a02005-02-26 23:04:13 +00004550 count_only ? _("%ld matches") : _("%ld substitutions"),
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 sub_nsubs);
Bram Moolenaar555b2802005-05-19 21:08:39 +00004552 len = STRLEN(msg_buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 if (sub_nlines == 1)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004554 vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len,
4555 "%s", _(" on 1 line"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556 else
Bram Moolenaar555b2802005-05-19 21:08:39 +00004557 vim_snprintf((char *)msg_buf + len, sizeof(msg_buf) - len,
4558 _(" on %ld lines"), (long)sub_nlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 if (msg(msg_buf))
4560 {
4561 /* save message to display it after redraw */
4562 set_keep_msg(msg_buf);
4563 keep_msg_attr = 0;
4564 }
4565 return TRUE;
4566 }
4567 if (got_int)
4568 {
4569 EMSG(_(e_interr));
4570 return TRUE;
4571 }
4572 return FALSE;
4573}
4574
4575/*
4576 * Execute a global command of the form:
4577 *
4578 * g/pattern/X : execute X on all lines where pattern matches
4579 * v/pattern/X : execute X on all lines where pattern does not match
4580 *
4581 * where 'X' is an EX command
4582 *
4583 * The command character (as well as the trailing slash) is optional, and
4584 * is assumed to be 'p' if missing.
4585 *
4586 * This is implemented in two passes: first we scan the file for the pattern and
4587 * set a mark for each line that (not) matches. secondly we execute the command
4588 * for each line that has a mark. This is required because after deleting
4589 * lines we do not know where to search for the next match.
4590 */
4591 void
4592ex_global(eap)
4593 exarg_T *eap;
4594{
4595 linenr_T lnum; /* line number according to old situation */
Bram Moolenaar05159a02005-02-26 23:04:13 +00004596 int ndone = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004597 int type; /* first char of cmd: 'v' or 'g' */
4598 char_u *cmd; /* command argument */
4599
4600 char_u delim; /* delimiter, normally '/' */
4601 char_u *pat;
4602 regmmatch_T regmatch;
4603 int match;
4604 int which_pat;
4605
4606 if (global_busy)
4607 {
4608 EMSG(_("E147: Cannot do :global recursive")); /* will increment global_busy */
4609 return;
4610 }
4611
4612 if (eap->forceit) /* ":global!" is like ":vglobal" */
4613 type = 'v';
4614 else
4615 type = *eap->cmd;
4616 cmd = eap->arg;
4617 which_pat = RE_LAST; /* default: use last used regexp */
4618 sub_nsubs = 0;
4619 sub_nlines = 0;
4620
4621 /*
4622 * undocumented vi feature:
4623 * "\/" and "\?": use previous search pattern.
4624 * "\&": use previous substitute pattern.
4625 */
4626 if (*cmd == '\\')
4627 {
4628 ++cmd;
4629 if (vim_strchr((char_u *)"/?&", *cmd) == NULL)
4630 {
4631 EMSG(_(e_backslash));
4632 return;
4633 }
4634 if (*cmd == '&')
4635 which_pat = RE_SUBST; /* use previous substitute pattern */
4636 else
4637 which_pat = RE_SEARCH; /* use previous search pattern */
4638 ++cmd;
4639 pat = (char_u *)"";
4640 }
4641 else if (*cmd == NUL)
4642 {
4643 EMSG(_("E148: Regular expression missing from global"));
4644 return;
4645 }
4646 else
4647 {
4648 delim = *cmd; /* get the delimiter */
4649 if (delim)
4650 ++cmd; /* skip delimiter if there is one */
4651 pat = cmd; /* remember start of pattern */
4652 cmd = skip_regexp(cmd, delim, p_magic, &eap->arg);
4653 if (cmd[0] == delim) /* end delimiter found */
4654 *cmd++ = NUL; /* replace it with a NUL */
4655 }
4656
4657#ifdef FEAT_FKMAP /* when in Farsi mode, reverse the character flow */
4658 if (p_altkeymap && curwin->w_p_rl)
4659 lrFswap(pat,0);
4660#endif
4661
4662 if (search_regcomp(pat, RE_BOTH, which_pat, SEARCH_HIS, &regmatch) == FAIL)
4663 {
4664 EMSG(_(e_invcmd));
4665 return;
4666 }
4667
4668 /*
4669 * pass 1: set marks for each (not) matching line
4670 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004671 for (lnum = eap->line1; lnum <= eap->line2 && !got_int; ++lnum)
4672 {
4673 /* a match on this line? */
4674 match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum, (colnr_T)0);
4675 if ((type == 'g' && match) || (type == 'v' && !match))
4676 {
4677 ml_setmarked(lnum);
4678 ndone++;
4679 }
4680 line_breakcheck();
4681 }
4682
4683 /*
4684 * pass 2: execute the command for each line that has been marked
4685 */
4686 if (got_int)
4687 MSG(_(e_interr));
4688 else if (ndone == 0)
4689 {
4690 if (type == 'v')
Bram Moolenaar555b2802005-05-19 21:08:39 +00004691 smsg((char_u *)_("Pattern found in every line: %s"), pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 else
Bram Moolenaar555b2802005-05-19 21:08:39 +00004693 smsg((char_u *)_(e_patnotf2), pat);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694 }
4695 else
4696 global_exe(cmd);
4697
4698 ml_clearmarked(); /* clear rest of the marks */
4699 vim_free(regmatch.regprog);
4700}
4701
4702/*
4703 * Execute "cmd" on lines marked with ml_setmarked().
4704 */
4705 void
4706global_exe(cmd)
4707 char_u *cmd;
4708{
4709 linenr_T old_lcount; /* b_ml.ml_line_count before the command */
4710 linenr_T lnum; /* line number according to old situation */
4711
4712 /*
4713 * Set current position only once for a global command.
4714 * If global_busy is set, setpcmark() will not do anything.
4715 * If there is an error, global_busy will be incremented.
4716 */
4717 setpcmark();
4718
4719 /* When the command writes a message, don't overwrite the command. */
4720 msg_didout = TRUE;
4721
4722 global_need_beginline = FALSE;
4723 global_busy = 1;
4724 old_lcount = curbuf->b_ml.ml_line_count;
4725 while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1)
4726 {
4727 curwin->w_cursor.lnum = lnum;
4728 curwin->w_cursor.col = 0;
4729 if (*cmd == NUL || *cmd == '\n')
4730 do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT);
4731 else
4732 do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT);
4733 ui_breakcheck();
4734 }
4735
4736 global_busy = 0;
4737 if (global_need_beginline)
4738 beginline(BL_WHITE | BL_FIX);
4739 else
4740 check_cursor(); /* cursor may be beyond the end of the line */
4741
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00004742 /* the cursor may not have moved in the text but a change in a previous
4743 * line may move it on the screen */
4744 changed_line_abv_curs();
4745
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746 /* If it looks like no message was written, allow overwriting the
4747 * command with the report for number of changes. */
4748 if (msg_col == 0 && msg_scrolled == 0)
4749 msg_didout = FALSE;
4750
4751 /* If subsitutes done, report number of substitues, otherwise report
4752 * number of extra or deleted lines. */
Bram Moolenaar05159a02005-02-26 23:04:13 +00004753 if (!do_sub_msg(FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754 msgmore(curbuf->b_ml.ml_line_count - old_lcount);
4755}
4756
4757#ifdef FEAT_VIMINFO
4758 int
4759read_viminfo_sub_string(virp, force)
4760 vir_T *virp;
4761 int force;
4762{
4763 if (old_sub != NULL && force)
4764 vim_free(old_sub);
4765 if (force || old_sub == NULL)
4766 old_sub = viminfo_readstring(virp, 1, TRUE);
4767 return viminfo_readline(virp);
4768}
4769
4770 void
4771write_viminfo_sub_string(fp)
4772 FILE *fp;
4773{
4774 if (get_viminfo_parameter('/') != 0 && old_sub != NULL)
4775 {
4776 fprintf(fp, _("\n# Last Substitute String:\n$"));
4777 viminfo_writestring(fp, old_sub);
4778 }
4779}
4780#endif /* FEAT_VIMINFO */
4781
4782#if (defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)) || defined(PROTO)
4783/*
4784 * Set up for a tagpreview.
4785 */
4786 void
4787prepare_tagpreview()
4788{
4789 win_T *wp;
4790
4791# ifdef FEAT_GUI
4792 need_mouse_correct = TRUE;
4793# endif
4794
4795 /*
4796 * If there is already a preview window open, use that one.
4797 */
4798 if (!curwin->w_p_pvw)
4799 {
4800 for (wp = firstwin; wp != NULL; wp = wp->w_next)
4801 if (wp->w_p_pvw)
4802 break;
4803 if (wp != NULL)
4804 win_enter(wp, TRUE);
4805 else
4806 {
4807 /*
4808 * There is no preview window open yet. Create one.
4809 */
4810 if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0)
4811 == FAIL)
4812 return;
4813 curwin->w_p_pvw = TRUE;
4814 curwin->w_p_wfh = TRUE;
4815# ifdef FEAT_SCROLLBIND
4816 curwin->w_p_scb = FALSE; /* don't take over 'scrollbind' */
4817# endif
4818# ifdef FEAT_DIFF
4819 curwin->w_p_diff = FALSE; /* no 'diff' */
4820# endif
4821# ifdef FEAT_FOLDING
4822 curwin->w_p_fdc = 0; /* no 'foldcolumn' */
4823# endif
4824 }
4825 }
4826}
4827
4828#endif
4829
4830
4831/*
4832 * ":help": open a read-only window on a help file
4833 */
4834 void
4835ex_help(eap)
4836 exarg_T *eap;
4837{
4838 char_u *arg;
4839 char_u *tag;
4840 FILE *helpfd; /* file descriptor of help file */
4841 int n;
4842 int i;
4843#ifdef FEAT_WINDOWS
4844 win_T *wp;
4845#endif
4846 int num_matches;
4847 char_u **matches;
4848 char_u *p;
4849 int empty_fnum = 0;
4850 int alt_fnum = 0;
4851 buf_T *buf;
4852#ifdef FEAT_MULTI_LANG
4853 int len;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004854 char_u *lang;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004855#endif
4856
4857 if (eap != NULL)
4858 {
4859 /*
4860 * A ":help" command ends at the first LF, or at a '|' that is
4861 * followed by some text. Set nextcmd to the following command.
4862 */
4863 for (arg = eap->arg; *arg; ++arg)
4864 {
4865 if (*arg == '\n' || *arg == '\r'
4866 || (*arg == '|' && arg[1] != NUL && arg[1] != '|'))
4867 {
4868 *arg++ = NUL;
4869 eap->nextcmd = arg;
4870 break;
4871 }
4872 }
4873 arg = eap->arg;
4874
4875 if (eap->forceit && *arg == NUL)
4876 {
4877 EMSG(_("E478: Don't panic!"));
4878 return;
4879 }
4880
4881 if (eap->skip) /* not executing commands */
4882 return;
4883 }
4884 else
4885 arg = (char_u *)"";
4886
4887 /* remove trailing blanks */
4888 p = arg + STRLEN(arg) - 1;
4889 while (p > arg && vim_iswhite(*p) && p[-1] != '\\')
4890 *p-- = NUL;
4891
4892#ifdef FEAT_MULTI_LANG
4893 /* Check for a specified language */
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00004894 lang = check_help_lang(arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895#endif
4896
4897 /* When no argument given go to the index. */
4898 if (*arg == NUL)
4899 arg = (char_u *)"help.txt";
4900
4901 /*
4902 * Check if there is a match for the argument.
4903 */
4904 n = find_help_tags(arg, &num_matches, &matches,
4905 eap != NULL && eap->forceit);
4906
4907 i = 0;
4908#ifdef FEAT_MULTI_LANG
4909 if (n != FAIL && lang != NULL)
4910 /* Find first item with the requested language. */
4911 for (i = 0; i < num_matches; ++i)
4912 {
4913 len = STRLEN(matches[i]);
4914 if (len > 3 && matches[i][len - 3] == '@'
4915 && STRICMP(matches[i] + len - 2, lang) == 0)
4916 break;
4917 }
4918#endif
4919 if (i >= num_matches || n == FAIL)
4920 {
4921#ifdef FEAT_MULTI_LANG
4922 if (lang != NULL)
4923 EMSG3(_("E661: Sorry, no '%s' help for %s"), lang, arg);
4924 else
4925#endif
4926 EMSG2(_("E149: Sorry, no help for %s"), arg);
4927 if (n != FAIL)
4928 FreeWild(num_matches, matches);
4929 return;
4930 }
4931
4932 /* The first match (in the requested language) is the best match. */
4933 tag = vim_strsave(matches[i]);
4934 FreeWild(num_matches, matches);
4935
4936#ifdef FEAT_GUI
4937 need_mouse_correct = TRUE;
4938#endif
4939
4940 /*
4941 * Re-use an existing help window or open a new one.
4942 */
4943 if (!curwin->w_buffer->b_help)
4944 {
4945#ifdef FEAT_WINDOWS
4946 for (wp = firstwin; wp != NULL; wp = wp->w_next)
4947 if (wp->w_buffer != NULL && wp->w_buffer->b_help)
4948 break;
4949 if (wp != NULL && wp->w_buffer->b_nwindows > 0)
4950 win_enter(wp, TRUE);
4951 else
4952#endif
4953 {
4954 /*
4955 * There is no help window yet.
4956 * Try to open the file specified by the "helpfile" option.
4957 */
4958 if ((helpfd = mch_fopen((char *)p_hf, READBIN)) == NULL)
4959 {
Bram Moolenaar555b2802005-05-19 21:08:39 +00004960 smsg((char_u *)_("Sorry, help file \"%s\" not found"), p_hf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961 goto erret;
4962 }
4963 fclose(helpfd);
4964
4965#ifdef FEAT_WINDOWS
4966 /* Split off help window; put it at far top if no position
4967 * specified, the current window is vertically split and narrow. */
4968 n = WSP_HELP;
4969# ifdef FEAT_VERTSPLIT
4970 if (cmdmod.split == 0 && curwin->w_width != Columns
4971 && curwin->w_width < 80)
4972 n |= WSP_TOP;
4973# endif
4974 if (win_split(0, n) == FAIL)
4975#else
4976 /* use current window */
4977 if (!can_abandon(curbuf, FALSE))
4978#endif
4979 goto erret;
4980
4981#ifdef FEAT_WINDOWS
4982 if (curwin->w_height < p_hh)
4983 win_setheight((int)p_hh);
4984#endif
4985
4986 /*
4987 * Open help file (do_ecmd() will set b_help flag, readfile() will
4988 * set b_p_ro flag).
4989 * Set the alternate file to the previously edited file.
4990 */
4991 alt_fnum = curbuf->b_fnum;
4992 (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
4993 ECMD_HIDE + ECMD_SET_HELP);
Bram Moolenaard4755bb2004-09-02 19:12:26 +00004994 if (!cmdmod.keepalt)
4995 curwin->w_alt_fnum = alt_fnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004996 empty_fnum = curbuf->b_fnum;
4997 }
4998 }
4999
5000 if (!p_im)
5001 restart_edit = 0; /* don't want insert mode in help file */
5002
5003 if (tag != NULL)
5004 do_tag(tag, DT_HELP, 1, FALSE, TRUE);
5005
5006 /* Delete the empty buffer if we're not using it. */
5007 if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum)
5008 {
5009 buf = buflist_findnr(empty_fnum);
5010 if (buf != NULL)
5011 wipe_buffer(buf, TRUE);
5012 }
5013
5014 /* keep the previous alternate file */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00005015 if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum && !cmdmod.keepalt)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005016 curwin->w_alt_fnum = alt_fnum;
5017
5018erret:
5019 vim_free(tag);
5020}
5021
5022
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005023#if defined(FEAT_MULTI_LANG) || defined(PROTO)
5024/*
5025 * In an argument search for a language specifiers in the form "@xx".
5026 * Changes the "@" to NUL if found, and returns a pointer to "xx".
5027 * Returns NULL if not found.
5028 */
5029 char_u *
5030check_help_lang(arg)
5031 char_u *arg;
5032{
5033 int len = STRLEN(arg);
5034
5035 if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
5036 && ASCII_ISALPHA(arg[len - 1]))
5037 {
5038 arg[len - 3] = NUL; /* remove the '@' */
5039 return arg + len - 2;
5040 }
5041 return NULL;
5042}
5043#endif
5044
Bram Moolenaar071d4272004-06-13 20:20:40 +00005045/*
5046 * Return a heuristic indicating how well the given string matches. The
5047 * smaller the number, the better the match. This is the order of priorities,
5048 * from best match to worst match:
5049 * - Match with least alpha-numeric characters is better.
5050 * - Match with least total characters is better.
5051 * - Match towards the start is better.
5052 * - Match starting with "+" is worse (feature instead of command)
5053 * Assumption is made that the matched_string passed has already been found to
5054 * match some string for which help is requested. webb.
5055 */
5056 int
5057help_heuristic(matched_string, offset, wrong_case)
5058 char_u *matched_string;
5059 int offset; /* offset for match */
5060 int wrong_case; /* no matching case */
5061{
5062 int num_letters;
5063 char_u *p;
5064
5065 num_letters = 0;
5066 for (p = matched_string; *p; p++)
5067 if (ASCII_ISALNUM(*p))
5068 num_letters++;
5069
5070 /*
5071 * Multiply the number of letters by 100 to give it a much bigger
5072 * weighting than the number of characters.
5073 * If there only is a match while ignoring case, add 5000.
5074 * If the match starts in the middle of a word, add 10000 to put it
5075 * somewhere in the last half.
5076 * If the match is more than 2 chars from the start, multiply by 200 to
5077 * put it after matches at the start.
5078 */
5079 if (ASCII_ISALNUM(matched_string[offset]) && offset > 0
5080 && ASCII_ISALNUM(matched_string[offset - 1]))
5081 offset += 10000;
5082 else if (offset > 2)
5083 offset *= 200;
5084 if (wrong_case)
5085 offset += 5000;
5086 /* Features are less interesting than the subjects themselves, but "+"
5087 * alone is not a feature. */
5088 if (matched_string[0] == '+' && matched_string[1] != NUL)
5089 offset += 100;
5090 return (int)(100 * num_letters + STRLEN(matched_string) + offset);
5091}
5092
5093/*
5094 * Compare functions for qsort() below, that checks the help heuristics number
5095 * that has been put after the tagname by find_tags().
5096 */
5097 static int
5098#ifdef __BORLANDC__
5099_RTLENTRYF
5100#endif
5101help_compare(s1, s2)
5102 const void *s1;
5103 const void *s2;
5104{
5105 char *p1;
5106 char *p2;
5107
5108 p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
5109 p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
5110 return strcmp(p1, p2);
5111}
5112
5113/*
5114 * Find all help tags matching "arg", sort them and return in matches[], with
5115 * the number of matches in num_matches.
5116 * The matches will be sorted with a "best" match algorithm.
5117 * When "keep_lang" is TRUE try keeping the language of the current buffer.
5118 */
5119 int
5120find_help_tags(arg, num_matches, matches, keep_lang)
5121 char_u *arg;
5122 int *num_matches;
5123 char_u ***matches;
5124 int keep_lang;
5125{
5126 char_u *s, *d;
5127 int i;
5128 static char *(mtable[]) = {"*", "g*", "[*", "]*", ":*",
5129 "/*", "/\\*", "\"*", "/\\(\\)",
5130 "?", ":?", "?<CR>", "g?", "g?g?", "g??",
5131 "/\\?", "/\\z(\\)",
5132 "[count]", "[quotex]", "[range]",
5133 "[pattern]", "\\|", "\\%$"};
5134 static char *(rtable[]) = {"star", "gstar", "[star", "]star", ":star",
5135 "/star", "/\\\\star", "quotestar", "/\\\\(\\\\)",
5136 "?", ":?", "?<CR>", "g?", "g?g?", "g??",
5137 "/\\\\?", "/\\\\z(\\\\)",
5138 "\\[count]", "\\[quotex]", "\\[range]",
5139 "\\[pattern]", "\\\\bar", "/\\\\%\\$"};
5140 int flags;
5141
5142 d = IObuff; /* assume IObuff is long enough! */
5143
5144 /*
5145 * Recognize a few exceptions to the rule. Some strings that contain '*'
5146 * with "star". Otherwise '*' is recognized as a wildcard.
5147 */
5148 for (i = sizeof(mtable) / sizeof(char *); --i >= 0; )
5149 if (STRCMP(arg, mtable[i]) == 0)
5150 {
5151 STRCPY(d, rtable[i]);
5152 break;
5153 }
5154
5155 if (i < 0) /* no match in table */
5156 {
5157 /* Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
5158 * Also replace "\%^" and "\%(", they match every tag too.
5159 * Also "\zs", "\z1", etc.
5160 * Also "\@<", "\@=", "\@<=", etc.
5161 * And also "\_$" and "\_^". */
5162 if (arg[0] == '\\'
5163 && ((arg[1] != NUL && arg[2] == NUL)
5164 || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL
5165 && arg[2] != NUL)))
5166 {
5167 STRCPY(d, "/\\\\");
5168 STRCPY(d + 3, arg + 1);
5169 /* Check for "/\\_$", should be "/\\_\$" */
5170 if (d[3] == '_' && d[4] == '$')
5171 STRCPY(d + 4, "\\$");
5172 }
5173 else
5174 {
5175 /* replace "[:...:]" with "\[:...:]"; "[+...]" with "\[++...]" */
5176 if (arg[0] == '[' && (arg[1] == ':'
5177 || (arg[1] == '+' && arg[2] == '+')))
5178 *d++ = '\\';
5179
5180 for (s = arg; *s; ++s)
5181 {
5182 /*
5183 * Replace "|" with "bar" and '"' with "quote" to match the name of
5184 * the tags for these commands.
5185 * Replace "*" with ".*" and "?" with "." to match command line
5186 * completion.
5187 * Insert a backslash before '~', '$' and '.' to avoid their
5188 * special meaning.
5189 */
5190 if (d - IObuff > IOSIZE - 10) /* getting too long!? */
5191 break;
5192 switch (*s)
5193 {
5194 case '|': STRCPY(d, "bar");
5195 d += 3;
5196 continue;
5197 case '"': STRCPY(d, "quote");
5198 d += 5;
5199 continue;
5200 case '*': *d++ = '.';
5201 break;
5202 case '?': *d++ = '.';
5203 continue;
5204 case '$':
5205 case '.':
5206 case '~': *d++ = '\\';
5207 break;
5208 }
5209
5210 /*
5211 * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
5212 * ":help i_^_CTRL-D" work.
5213 * Insert '-' before and after "CTRL-X" when applicable.
5214 */
5215 if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1])
5216 || vim_strchr((char_u *)"?@[\\]^", s[1]) != NULL)))
5217 {
5218 if (d > IObuff && d[-1] != '_')
5219 *d++ = '_'; /* prepend a '_' */
5220 STRCPY(d, "CTRL-");
5221 d += 5;
5222 if (*s < ' ')
5223 {
5224#ifdef EBCDIC
5225 *d++ = CtrlChar(*s);
5226#else
5227 *d++ = *s + '@';
5228#endif
5229 if (d[-1] == '\\')
5230 *d++ = '\\'; /* double a backslash */
5231 }
5232 else
5233 *d++ = *++s;
5234 if (s[1] != NUL && s[1] != '_')
5235 *d++ = '_'; /* append a '_' */
5236 continue;
5237 }
5238 else if (*s == '^') /* "^" or "CTRL-^" or "^_" */
5239 *d++ = '\\';
5240
5241 /*
5242 * Insert a backslash before a backslash after a slash, for search
5243 * pattern tags: "/\|" --> "/\\|".
5244 */
5245 else if (s[0] == '\\' && s[1] != '\\'
5246 && *arg == '/' && s == arg + 1)
5247 *d++ = '\\';
5248
5249 /* "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in
5250 * "CTRL-\_CTRL-N" */
5251 if (STRNICMP(s, "CTRL-\\_", 7) == 0)
5252 {
5253 STRCPY(d, "CTRL-\\\\");
5254 d += 7;
5255 s += 6;
5256 }
5257
5258 *d++ = *s;
5259
5260 /*
5261 * If tag starts with ', toss everything after a second '. Fixes
5262 * CTRL-] on 'option'. (would include the trailing '.').
5263 */
5264 if (*s == '\'' && s > arg && *arg == '\'')
5265 break;
5266 }
5267 *d = NUL;
5268 }
5269 }
5270
5271 *matches = (char_u **)"";
5272 *num_matches = 0;
5273 flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE;
5274 if (keep_lang)
5275 flags |= TAG_KEEP_LANG;
5276 if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK
5277 && *num_matches > 0)
5278 /* Sort the matches found on the heuristic number that is after the
5279 * tag name. */
5280 qsort((void *)*matches, (size_t)*num_matches,
5281 sizeof(char_u *), help_compare);
5282 return OK;
5283}
5284
5285/*
5286 * After reading a help file: May cleanup a help buffer when syntax
5287 * highlighting is not used.
5288 */
5289 void
5290fix_help_buffer()
5291{
5292 linenr_T lnum;
5293 char_u *line;
5294 int in_example = FALSE;
5295 int len;
5296 char_u *p;
5297 char_u *rt;
5298 int mustfree;
5299
5300 /* set filetype to "help". */
5301 set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL);
5302
5303#ifdef FEAT_SYN_HL
5304 if (!syntax_present(curbuf))
5305#endif
5306 {
5307 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
5308 {
5309 line = ml_get_buf(curbuf, lnum, FALSE);
5310 len = (int)STRLEN(line);
5311 if (in_example && len > 0 && !vim_iswhite(line[0]))
5312 {
5313 /* End of example: non-white or '<' in first column. */
5314 if (line[0] == '<')
5315 {
5316 /* blank-out a '<' in the first column */
5317 line = ml_get_buf(curbuf, lnum, TRUE);
5318 line[0] = ' ';
5319 }
5320 in_example = FALSE;
5321 }
5322 if (!in_example && len > 0)
5323 {
5324 if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' '))
5325 {
5326 /* blank-out a '>' in the last column (start of example) */
5327 line = ml_get_buf(curbuf, lnum, TRUE);
5328 line[len - 1] = ' ';
5329 in_example = TRUE;
5330 }
5331 else if (line[len - 1] == '~')
5332 {
5333 /* blank-out a '~' at the end of line (header marker) */
5334 line = ml_get_buf(curbuf, lnum, TRUE);
5335 line[len - 1] = ' ';
5336 }
5337 }
5338 }
5339 }
5340
5341 /*
5342 * In the "help.txt" file, add the locally added help files.
5343 * This uses the very first line in the help file.
5344 */
5345 if (fnamecmp(gettail(curbuf->b_fname), "help.txt") == 0)
5346 {
5347 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum)
5348 {
5349 line = ml_get_buf(curbuf, lnum, FALSE);
5350 if (strstr((char *)line, "*local-additions*") != NULL)
5351 {
5352 /* Go through all directories in 'runtimepath', skipping
5353 * $VIMRUNTIME. */
5354 p = p_rtp;
5355 while (*p != NUL)
5356 {
5357 copy_option_part(&p, NameBuff, MAXPATHL, ",");
5358 mustfree = FALSE;
5359 rt = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
5360 if (fullpathcmp(rt, NameBuff, FALSE) != FPC_SAME)
5361 {
5362 int fcount;
5363 char_u **fnames;
5364 FILE *fd;
5365 char_u *s;
5366 int fi;
5367#ifdef FEAT_MBYTE
5368 vimconv_T vc;
5369 char_u *cp;
5370#endif
5371
5372 /* Find all "doc/ *.txt" files in this directory. */
5373 add_pathsep(NameBuff);
5374 STRCAT(NameBuff, "doc/*.txt");
5375 if (gen_expand_wildcards(1, &NameBuff, &fcount,
5376 &fnames, EW_FILE|EW_SILENT) == OK
5377 && fcount > 0)
5378 {
5379 for (fi = 0; fi < fcount; ++fi)
5380 {
5381 fd = fopen((char *)fnames[fi], "r");
5382 if (fd != NULL)
5383 {
5384 vim_fgets(IObuff, IOSIZE, fd);
5385 if (IObuff[0] == '*'
5386 && (s = vim_strchr(IObuff + 1, '*'))
5387 != NULL)
5388 {
5389#ifdef FEAT_MBYTE
5390 int this_utf = MAYBE;
5391#endif
5392 /* Change tag definition to a
5393 * reference and remove <CR>/<NL>. */
5394 IObuff[0] = '|';
5395 *s = '|';
5396 while (*s != NUL)
5397 {
5398 if (*s == '\r' || *s == '\n')
5399 *s = NUL;
5400#ifdef FEAT_MBYTE
5401 /* The text is utf-8 when a byte
5402 * above 127 is found and no
5403 * illegal byte sequence is found.
5404 */
5405 if (*s >= 0x80 && this_utf != FALSE)
5406 {
5407 int l;
5408
5409 this_utf = TRUE;
5410 l = utf_ptr2len_check(s);
5411 if (l == 1)
5412 this_utf = FALSE;
5413 s += l - 1;
5414 }
5415#endif
5416 ++s;
5417 }
5418#ifdef FEAT_MBYTE
5419 /* The help file is latin1 or utf-8;
5420 * conversion to the current
5421 * 'encoding' may be required. */
5422 vc.vc_type = CONV_NONE;
5423 convert_setup(&vc, (char_u *)(
5424 this_utf == TRUE ? "utf-8"
5425 : "latin1"), p_enc);
5426 if (vc.vc_type == CONV_NONE)
5427 /* No conversion needed. */
5428 cp = IObuff;
5429 else
5430 {
5431 /* Do the conversion. If it fails
5432 * use the unconverted text. */
5433 cp = string_convert(&vc, IObuff,
5434 NULL);
5435 if (cp == NULL)
5436 cp = IObuff;
5437 }
5438 convert_setup(&vc, NULL, NULL);
5439
5440 ml_append(lnum, cp, (colnr_T)0, FALSE);
5441 if (cp != IObuff)
5442 vim_free(cp);
5443#else
5444 ml_append(lnum, IObuff, (colnr_T)0,
5445 FALSE);
5446#endif
5447 ++lnum;
5448 }
5449 fclose(fd);
5450 }
5451 }
5452 FreeWild(fcount, fnames);
5453 }
5454 }
5455 if (mustfree)
5456 vim_free(rt);
5457 }
5458 break;
5459 }
5460 }
5461 }
5462}
5463
Bram Moolenaarb5bf5b82004-12-24 14:35:23 +00005464/*
5465 * ":exusage"
5466 */
5467/*ARGSUSED*/
5468 void
5469ex_exusage(eap)
5470 exarg_T *eap;
5471{
5472 do_cmdline_cmd((char_u *)"help ex-cmd-index");
5473}
5474
5475/*
5476 * ":viusage"
5477 */
5478/*ARGSUSED*/
5479 void
5480ex_viusage(eap)
5481 exarg_T *eap;
5482{
5483 do_cmdline_cmd((char_u *)"help normal-index");
5484}
5485
Bram Moolenaar071d4272004-06-13 20:20:40 +00005486#if defined(FEAT_EX_EXTRA) || defined(PROTO)
5487static void helptags_one __ARGS((char_u *dir, char_u *ext, char_u *lang));
5488
5489/*
5490 * ":helptags"
5491 */
5492 void
5493ex_helptags(eap)
5494 exarg_T *eap;
5495{
5496 garray_T ga;
5497 int i, j;
5498 int len;
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005499#ifdef FEAT_MULTI_LANG
Bram Moolenaar071d4272004-06-13 20:20:40 +00005500 char_u lang[2];
Bram Moolenaar69a7cb42004-06-20 12:51:53 +00005501#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502 char_u ext[5];
5503 char_u fname[8];
5504 int filecount;
5505 char_u **files;
5506
5507 if (!mch_isdir(eap->arg))
5508 {
5509 EMSG2(_("E150: Not a directory: %s"), eap->arg);
5510 return;
5511 }
5512
5513#ifdef FEAT_MULTI_LANG
5514 /* Get a list of all files in the directory. */
5515 STRCPY(NameBuff, eap->arg);
5516 add_pathsep(NameBuff);
5517 STRCAT(NameBuff, "*");
5518 if (gen_expand_wildcards(1, &NameBuff, &filecount, &files,
5519 EW_FILE|EW_SILENT) == FAIL
5520 || filecount == 0)
5521 {
5522 EMSG2("E151: No match: %s", NameBuff);
5523 return;
5524 }
5525
5526 /* Go over all files in the directory to find out what languages are
5527 * present. */
5528 ga_init2(&ga, 1, 10);
5529 for (i = 0; i < filecount; ++i)
5530 {
5531 len = STRLEN(files[i]);
5532 if (len > 4)
5533 {
5534 if (STRICMP(files[i] + len - 4, ".txt") == 0)
5535 {
5536 /* ".txt" -> language "en" */
5537 lang[0] = 'e';
5538 lang[1] = 'n';
5539 }
5540 else if (files[i][len - 4] == '.'
5541 && ASCII_ISALPHA(files[i][len - 3])
5542 && ASCII_ISALPHA(files[i][len - 2])
5543 && TOLOWER_ASC(files[i][len - 1]) == 'x')
5544 {
5545 /* ".abx" -> language "ab" */
5546 lang[0] = TOLOWER_ASC(files[i][len - 3]);
5547 lang[1] = TOLOWER_ASC(files[i][len - 2]);
5548 }
5549 else
5550 continue;
5551
5552 /* Did we find this language already? */
5553 for (j = 0; j < ga.ga_len; j += 2)
5554 if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0)
5555 break;
5556 if (j == ga.ga_len)
5557 {
5558 /* New language, add it. */
5559 if (ga_grow(&ga, 2) == FAIL)
5560 break;
5561 ((char_u *)ga.ga_data)[ga.ga_len++] = lang[0];
5562 ((char_u *)ga.ga_data)[ga.ga_len++] = lang[1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563 }
5564 }
5565 }
5566
5567 /*
5568 * Loop over the found languages to generate a tags file for each one.
5569 */
5570 for (j = 0; j < ga.ga_len; j += 2)
5571 {
5572 STRCPY(fname, "tags-xx");
5573 fname[5] = ((char_u *)ga.ga_data)[j];
5574 fname[6] = ((char_u *)ga.ga_data)[j + 1];
5575 if (fname[5] == 'e' && fname[6] == 'n')
5576 {
5577 /* English is an exception: use ".txt" and "tags". */
5578 fname[4] = NUL;
5579 STRCPY(ext, ".txt");
5580 }
5581 else
5582 {
5583 /* Language "ab" uses ".abx" and "tags-ab". */
5584 STRCPY(ext, ".xxx");
5585 ext[1] = fname[5];
5586 ext[2] = fname[6];
5587 }
5588 helptags_one(eap->arg, ext, fname);
5589 }
5590
5591 ga_clear(&ga);
5592 FreeWild(filecount, files);
5593
5594#else
5595 /* No language support, just use "*.txt" and "tags". */
5596 helptags_one(eap->arg, (char_u *)".txt", (char_u *)"tags");
5597#endif
5598}
5599
5600 static void
5601helptags_one(dir, ext, tagfname)
5602 char_u *dir; /* doc directory */
5603 char_u *ext; /* suffix, ".txt", ".itx", ".frx", etc. */
5604 char_u *tagfname; /* "tags" for English, "tags-it" for Italian. */
5605{
5606 FILE *fd_tags;
5607 FILE *fd;
5608 garray_T ga;
5609 int filecount;
5610 char_u **files;
5611 char_u *p1, *p2;
5612 int fi;
5613 char_u *s;
5614 int i;
5615 char_u *fname;
5616# ifdef FEAT_MBYTE
5617 int utf8 = MAYBE;
5618 int this_utf8;
5619 int firstline;
5620 int mix = FALSE; /* detected mixed encodings */
5621# endif
5622
5623 /*
5624 * Find all *.txt files.
5625 */
5626 STRCPY(NameBuff, dir);
5627 add_pathsep(NameBuff);
5628 STRCAT(NameBuff, "*");
5629 STRCAT(NameBuff, ext);
5630 if (gen_expand_wildcards(1, &NameBuff, &filecount, &files,
5631 EW_FILE|EW_SILENT) == FAIL
5632 || filecount == 0)
5633 {
5634 if (!got_int)
5635 EMSG2("E151: No match: %s", NameBuff);
5636 return;
5637 }
5638
5639 /*
5640 * Open the tags file for writing.
5641 * Do this before scanning through all the files.
5642 */
5643 STRCPY(NameBuff, dir);
5644 add_pathsep(NameBuff);
5645 STRCAT(NameBuff, tagfname);
5646 fd_tags = fopen((char *)NameBuff, "w");
5647 if (fd_tags == NULL)
5648 {
5649 EMSG2(_("E152: Cannot open %s for writing"), NameBuff);
5650 FreeWild(filecount, files);
5651 return;
5652 }
5653
5654 /*
5655 * If generating tags for "$VIMRUNTIME/doc" add the "help-tags" tag.
5656 */
5657 ga_init2(&ga, (int)sizeof(char_u *), 100);
5658 if (fullpathcmp((char_u *)"$VIMRUNTIME/doc", dir, FALSE) == FPC_SAME)
5659 {
5660 if (ga_grow(&ga, 1) == FAIL)
5661 got_int = TRUE;
5662 else
5663 {
Bram Moolenaar555b2802005-05-19 21:08:39 +00005664 s = alloc(18 + STRLEN(tagfname));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665 if (s == NULL)
5666 got_int = TRUE;
5667 else
5668 {
5669 sprintf((char *)s, "help-tags\t%s\t1\n", tagfname);
5670 ((char_u **)ga.ga_data)[ga.ga_len] = s;
5671 ++ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005672 }
5673 }
5674 }
5675
5676 /*
5677 * Go over all the files and extract the tags.
5678 */
5679 for (fi = 0; fi < filecount && !got_int; ++fi)
5680 {
5681 fd = fopen((char *)files[fi], "r");
5682 if (fd == NULL)
5683 {
5684 EMSG2(_("E153: Unable to open %s for reading"), files[fi]);
5685 continue;
5686 }
5687 fname = gettail(files[fi]);
5688
5689# ifdef FEAT_MBYTE
5690 firstline = TRUE;
5691# endif
5692 while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int)
5693 {
5694# ifdef FEAT_MBYTE
5695 if (firstline)
5696 {
5697 /* Detect utf-8 file by a non-ASCII char in the first line. */
5698 this_utf8 = MAYBE;
5699 for (s = IObuff; *s != NUL; ++s)
5700 if (*s >= 0x80)
5701 {
5702 int l;
5703
5704 this_utf8 = TRUE;
5705 l = utf_ptr2len_check(s);
5706 if (l == 1)
5707 {
5708 /* Illegal UTF-8 byte sequence. */
5709 this_utf8 = FALSE;
5710 break;
5711 }
5712 s += l - 1;
5713 }
5714 if (this_utf8 == MAYBE) /* only ASCII characters found */
5715 this_utf8 = FALSE;
5716 if (utf8 == MAYBE) /* first file */
5717 utf8 = this_utf8;
5718 else if (utf8 != this_utf8)
5719 {
5720 EMSG2(_("E670: Mix of help file encodings within a language: %s"), files[fi]);
5721 mix = !got_int;
5722 got_int = TRUE;
5723 }
5724 firstline = FALSE;
5725 }
5726# endif
5727 p1 = vim_strchr(IObuff, '*'); /* find first '*' */
5728 while (p1 != NULL)
5729 {
5730 p2 = vim_strchr(p1 + 1, '*'); /* find second '*' */
5731 if (p2 != NULL && p2 > p1 + 1) /* skip "*" and "**" */
5732 {
5733 for (s = p1 + 1; s < p2; ++s)
5734 if (*s == ' ' || *s == '\t' || *s == '|')
5735 break;
5736
5737 /*
5738 * Only accept a *tag* when it consists of valid
5739 * characters, there is white space before it and is
5740 * followed by a white character or end-of-line.
5741 */
5742 if (s == p2
5743 && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
5744 && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL
5745 || s[1] == '\0'))
5746 {
5747 *p2 = '\0';
5748 ++p1;
5749 if (ga_grow(&ga, 1) == FAIL)
5750 {
5751 got_int = TRUE;
5752 break;
5753 }
5754 s = alloc((unsigned)(p2 - p1 + STRLEN(fname) + 2));
5755 if (s == NULL)
5756 {
5757 got_int = TRUE;
5758 break;
5759 }
5760 ((char_u **)ga.ga_data)[ga.ga_len] = s;
5761 ++ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762 sprintf((char *)s, "%s\t%s", p1, fname);
5763
5764 /* find next '*' */
5765 p2 = vim_strchr(p2 + 1, '*');
5766 }
5767 }
5768 p1 = p2;
5769 }
5770 line_breakcheck();
5771 }
5772
5773 fclose(fd);
5774 }
5775
5776 FreeWild(filecount, files);
5777
5778 if (!got_int)
5779 {
5780 /*
5781 * Sort the tags.
5782 */
5783 sort_strings((char_u **)ga.ga_data, ga.ga_len);
5784
5785 /*
5786 * Check for duplicates.
5787 */
5788 for (i = 1; i < ga.ga_len; ++i)
5789 {
5790 p1 = ((char_u **)ga.ga_data)[i - 1];
5791 p2 = ((char_u **)ga.ga_data)[i];
5792 while (*p1 == *p2)
5793 {
5794 if (*p2 == '\t')
5795 {
5796 *p2 = NUL;
Bram Moolenaar555b2802005-05-19 21:08:39 +00005797 vim_snprintf((char *)NameBuff, MAXPATHL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798 _("E154: Duplicate tag \"%s\" in file %s/%s"),
5799 ((char_u **)ga.ga_data)[i], dir, p2 + 1);
5800 EMSG(NameBuff);
5801 *p2 = '\t';
5802 break;
5803 }
5804 ++p1;
5805 ++p2;
5806 }
5807 }
5808
5809# ifdef FEAT_MBYTE
5810 if (utf8 == TRUE)
5811 fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n");
5812# endif
5813
5814 /*
5815 * Write the tags into the file.
5816 */
5817 for (i = 0; i < ga.ga_len; ++i)
5818 {
5819 s = ((char_u **)ga.ga_data)[i];
5820 if (STRNCMP(s, "help-tags", 9) == 0)
5821 /* help-tags entry was added in formatted form */
5822 fprintf(fd_tags, (char *)s);
5823 else
5824 {
5825 fprintf(fd_tags, "%s\t/*", s);
5826 for (p1 = s; *p1 != '\t'; ++p1)
5827 {
5828 /* insert backslash before '\\' and '/' */
5829 if (*p1 == '\\' || *p1 == '/')
5830 putc('\\', fd_tags);
5831 putc(*p1, fd_tags);
5832 }
5833 fprintf(fd_tags, "*\n");
5834 }
5835 }
5836 }
5837#ifdef FEAT_MBYTE
5838 if (mix)
5839 got_int = FALSE; /* continue with other languages */
5840#endif
5841
5842 for (i = 0; i < ga.ga_len; ++i)
5843 vim_free(((char_u **)ga.ga_data)[i]);
5844 ga_clear(&ga);
5845 fclose(fd_tags); /* there is no check for an error... */
5846}
5847#endif
5848
5849#if defined(FEAT_SIGNS) || defined(PROTO)
5850
5851/*
5852 * Struct to hold the sign properties.
5853 */
5854typedef struct sign sign_T;
5855
5856struct sign
5857{
5858 sign_T *sn_next; /* next sign in list */
5859 int sn_typenr; /* type number of sign (negative if not equal
5860 to name) */
5861 char_u *sn_name; /* name of sign */
5862 char_u *sn_icon; /* name of pixmap */
5863#ifdef FEAT_SIGN_ICONS
5864 void *sn_image; /* icon image */
5865#endif
5866 char_u *sn_text; /* text used instead of pixmap */
5867 int sn_line_hl; /* highlight ID for line */
5868 int sn_text_hl; /* highlight ID for text */
5869};
5870
Bram Moolenaar071d4272004-06-13 20:20:40 +00005871static sign_T *first_sign = NULL;
5872static int last_sign_typenr = MAX_TYPENR; /* is decremented */
5873
5874static void sign_list_defined __ARGS((sign_T *sp));
5875
5876/*
5877 * ":sign" command
5878 */
5879 void
5880ex_sign(eap)
5881 exarg_T *eap;
5882{
5883 char_u *arg = eap->arg;
5884 char_u *p;
5885 int idx;
5886 sign_T *sp;
5887 sign_T *sp_prev;
5888 buf_T *buf;
5889 static char *cmds[] = {
5890 "define",
5891#define SIGNCMD_DEFINE 0
5892 "undefine",
5893#define SIGNCMD_UNDEFINE 1
5894 "list",
5895#define SIGNCMD_LIST 2
5896 "place",
5897#define SIGNCMD_PLACE 3
5898 "unplace",
5899#define SIGNCMD_UNPLACE 4
5900 "jump",
5901#define SIGNCMD_JUMP 5
5902#define SIGNCMD_LAST 6
5903 };
5904
5905 /* Parse the subcommand. */
5906 p = skiptowhite(arg);
5907 if (*p != NUL)
5908 *p++ = NUL;
5909 for (idx = 0; ; ++idx)
5910 {
5911 if (idx == SIGNCMD_LAST)
5912 {
5913 EMSG2(_("E160: Unknown sign command: %s"), arg);
5914 return;
5915 }
5916 if (STRCMP(arg, cmds[idx]) == 0)
5917 break;
5918 }
5919 arg = skipwhite(p);
5920
5921 if (idx <= SIGNCMD_LIST)
5922 {
5923 /*
5924 * Define, undefine or list signs.
5925 */
5926 if (idx == SIGNCMD_LIST && *arg == NUL)
5927 {
5928 /* ":sign list": list all defined signs */
5929 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
5930 sign_list_defined(sp);
5931 }
5932 else if (*arg == NUL)
5933 EMSG(_("E156: Missing sign name"));
5934 else
5935 {
5936 p = skiptowhite(arg);
5937 if (*p != NUL)
5938 *p++ = NUL;
5939 sp_prev = NULL;
5940 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
5941 {
5942 if (STRCMP(sp->sn_name, arg) == 0)
5943 break;
5944 sp_prev = sp;
5945 }
5946 if (idx == SIGNCMD_DEFINE)
5947 {
5948 /* ":sign define {name} ...": define a sign */
5949 if (sp == NULL)
5950 {
5951 /* Allocate a new sign. */
5952 sp = (sign_T *)alloc_clear((unsigned)sizeof(sign_T));
5953 if (sp == NULL)
5954 return;
5955 if (sp_prev == NULL)
5956 first_sign = sp;
5957 else
5958 sp_prev->sn_next = sp;
5959 sp->sn_name = vim_strnsave(arg, (int)(p - arg));
5960
5961 /* If the name is a number use that for the typenr,
5962 * otherwise use a negative number. */
5963 if (VIM_ISDIGIT(*arg))
5964 sp->sn_typenr = atoi((char *)arg);
5965 else
5966 {
5967 sign_T *lp;
5968 int start = last_sign_typenr;
5969
5970 for (lp = first_sign; lp != NULL; lp = lp->sn_next)
5971 {
5972 if (lp->sn_typenr == last_sign_typenr)
5973 {
5974 --last_sign_typenr;
5975 if (last_sign_typenr == 0)
5976 last_sign_typenr = MAX_TYPENR;
5977 if (last_sign_typenr == start)
5978 {
5979 EMSG(_("E612: Too many signs defined"));
5980 return;
5981 }
5982 lp = first_sign;
5983 continue;
5984 }
5985 }
5986
5987 sp->sn_typenr = last_sign_typenr--;
5988 if (last_sign_typenr == 0)
5989 last_sign_typenr = MAX_TYPENR; /* wrap around */
5990 }
5991 }
5992
5993 /* set values for a defined sign. */
5994 for (;;)
5995 {
5996 arg = skipwhite(p);
5997 if (*arg == NUL)
5998 break;
5999 p = skiptowhite_esc(arg);
6000 if (STRNCMP(arg, "icon=", 5) == 0)
6001 {
6002 arg += 5;
6003 vim_free(sp->sn_icon);
6004 sp->sn_icon = vim_strnsave(arg, (int)(p - arg));
6005 backslash_halve(sp->sn_icon);
6006#ifdef FEAT_SIGN_ICONS
6007 if (gui.in_use)
6008 {
6009 out_flush();
6010 if (sp->sn_image != NULL)
6011 gui_mch_destroy_sign(sp->sn_image);
6012 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
6013 }
6014#endif
6015 }
6016 else if (STRNCMP(arg, "text=", 5) == 0)
6017 {
6018 char_u *s;
6019 int cells;
6020 int len;
6021
6022 arg += 5;
6023#ifdef FEAT_MBYTE
6024 /* Count cells and check for non-printable chars */
6025 if (has_mbyte)
6026 {
6027 cells = 0;
6028 for (s = arg; s < p; s += (*mb_ptr2len_check)(s))
6029 {
6030 if (!vim_isprintc((*mb_ptr2char)(s)))
6031 break;
6032 cells += (*mb_ptr2cells)(s);
6033 }
6034 }
6035 else
6036#endif
6037 {
6038 for (s = arg; s < p; ++s)
6039 if (!vim_isprintc(*s))
6040 break;
6041 cells = s - arg;
6042 }
6043 /* Currently must be one or two display cells */
6044 if (s != p || cells < 1 || cells > 2)
6045 {
6046 *p = NUL;
6047 EMSG2(_("E239: Invalid sign text: %s"), arg);
6048 return;
6049 }
6050
6051 vim_free(sp->sn_text);
6052 /* Allocate one byte more if we need to pad up
6053 * with a space. */
6054 len = p - arg + ((cells == 1) ? 1 : 0);
6055 sp->sn_text = vim_strnsave(arg, len);
6056
6057 if (sp->sn_text != NULL && cells == 1)
6058 STRCPY(sp->sn_text + len - 1, " ");
6059 }
6060 else if (STRNCMP(arg, "linehl=", 7) == 0)
6061 {
6062 arg += 7;
6063 sp->sn_line_hl = syn_check_group(arg, (int)(p - arg));
6064 }
6065 else if (STRNCMP(arg, "texthl=", 7) == 0)
6066 {
6067 arg += 7;
6068 sp->sn_text_hl = syn_check_group(arg, (int)(p - arg));
6069 }
6070 else
6071 {
6072 EMSG2(_(e_invarg2), arg);
6073 return;
6074 }
6075 }
6076 }
6077 else if (sp == NULL)
6078 EMSG2(_("E155: Unknown sign: %s"), arg);
6079 else if (idx == SIGNCMD_LIST)
6080 /* ":sign list {name}" */
6081 sign_list_defined(sp);
6082 else
6083 {
6084 /* ":sign undefine {name}" */
6085 vim_free(sp->sn_name);
6086 vim_free(sp->sn_icon);
6087#ifdef FEAT_SIGN_ICONS
6088 if (sp->sn_image != NULL)
6089 {
6090 out_flush();
6091 gui_mch_destroy_sign(sp->sn_image);
6092 }
6093#endif
6094 vim_free(sp->sn_text);
6095 if (sp_prev == NULL)
6096 first_sign = sp->sn_next;
6097 else
6098 sp_prev->sn_next = sp->sn_next;
6099 vim_free(sp);
6100 }
6101 }
6102 }
6103 else
6104 {
6105 int id = -1;
6106 linenr_T lnum = -1;
6107 char_u *sign_name = NULL;
6108 char_u *arg1;
6109
6110 if (*arg == NUL)
6111 {
6112 if (idx == SIGNCMD_PLACE)
6113 {
6114 /* ":sign place": list placed signs in all buffers */
6115 sign_list_placed(NULL);
6116 }
6117 else if (idx == SIGNCMD_UNPLACE)
6118 {
6119 /* ":sign unplace": remove placed sign at cursor */
6120 id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum);
6121 if (id > 0)
6122 {
6123 buf_delsign(curwin->w_buffer, id);
6124 update_debug_sign(curwin->w_buffer, curwin->w_cursor.lnum);
6125 }
6126 else
6127 EMSG(_("E159: Missing sign number"));
6128 }
6129 else
6130 EMSG(_(e_argreq));
6131 return;
6132 }
6133
6134 if (idx == SIGNCMD_UNPLACE && arg[0] == '*' && arg[1] == NUL)
6135 {
6136 /* ":sign unplace *": remove all placed signs */
6137 buf_delete_all_signs();
6138 return;
6139 }
6140
6141 /* first arg could be placed sign id */
6142 arg1 = arg;
6143 if (VIM_ISDIGIT(*arg))
6144 {
6145 id = getdigits(&arg);
6146 if (!vim_iswhite(*arg) && *arg != NUL)
6147 {
6148 id = -1;
6149 arg = arg1;
6150 }
6151 else
6152 {
6153 arg = skipwhite(arg);
6154 if (idx == SIGNCMD_UNPLACE && *arg == NUL)
6155 {
6156 /* ":sign unplace {id}": remove placed sign by number */
6157 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
6158 if ((lnum = buf_delsign(buf, id)) != 0)
6159 update_debug_sign(buf, lnum);
6160 return;
6161 }
6162 }
6163 }
6164
6165 /*
6166 * Check for line={lnum} name={name} and file={fname} or buffer={nr}.
6167 * Leave "arg" pointing to {fname}.
6168 */
6169 for (;;)
6170 {
6171 if (STRNCMP(arg, "line=", 5) == 0)
6172 {
6173 arg += 5;
6174 lnum = atoi((char *)arg);
6175 arg = skiptowhite(arg);
6176 }
6177 else if (STRNCMP(arg, "name=", 5) == 0)
6178 {
6179 arg += 5;
6180 sign_name = arg;
6181 arg = skiptowhite(arg);
6182 if (*arg != NUL)
6183 *arg++ = NUL;
6184 }
6185 else if (STRNCMP(arg, "file=", 5) == 0)
6186 {
6187 arg += 5;
6188 buf = buflist_findname(arg);
6189 break;
6190 }
6191 else if (STRNCMP(arg, "buffer=", 7) == 0)
6192 {
6193 arg += 7;
6194 buf = buflist_findnr((int)getdigits(&arg));
6195 if (*skipwhite(arg) != NUL)
6196 EMSG(_(e_trailing));
6197 break;
6198 }
6199 else
6200 {
6201 EMSG(_(e_invarg));
6202 return;
6203 }
6204 arg = skipwhite(arg);
6205 }
6206
6207 if (buf == NULL)
6208 {
6209 EMSG2(_("E158: Invalid buffer name: %s"), arg);
6210 }
6211 else if (id <= 0)
6212 {
6213 if (lnum >= 0 || sign_name != NULL)
6214 EMSG(_(e_invarg));
6215 else
6216 /* ":sign place file={fname}": list placed signs in one file */
6217 sign_list_placed(buf);
6218 }
6219 else if (idx == SIGNCMD_JUMP)
6220 {
6221 /* ":sign jump {id} file={fname}" */
6222 if (lnum >= 0 || sign_name != NULL)
6223 EMSG(_(e_invarg));
6224 else if ((lnum = buf_findsign(buf, id)) > 0)
6225 { /* goto a sign ... */
6226 if (buf_jump_open_win(buf) != NULL)
6227 { /* ... in a current window */
6228 curwin->w_cursor.lnum = lnum;
6229 check_cursor_lnum();
6230 beginline(BL_WHITE);
6231 }
6232 else
6233 { /* ... not currently in a window */
6234 char_u *cmd;
6235
6236 cmd = alloc((unsigned)STRLEN(buf->b_fname) + 25);
6237 if (cmd == NULL)
6238 return;
6239 sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname);
6240 do_cmdline_cmd(cmd);
6241 vim_free(cmd);
6242 }
6243#ifdef FEAT_FOLDING
6244 foldOpenCursor();
6245#endif
6246 }
6247 else
6248 EMSGN(_("E157: Invalid sign ID: %ld"), id);
6249 }
6250 else if (idx == SIGNCMD_UNPLACE)
6251 {
6252 /* ":sign unplace {id} file={fname}" */
6253 if (lnum >= 0 || sign_name != NULL)
6254 EMSG(_(e_invarg));
6255 else
6256 {
6257 lnum = buf_delsign(buf, id);
6258 update_debug_sign(buf, lnum);
6259 }
6260 }
6261 /* idx == SIGNCMD_PLACE */
6262 else if (sign_name != NULL)
6263 {
6264 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
6265 if (STRCMP(sp->sn_name, sign_name) == 0)
6266 break;
6267 if (sp == NULL)
6268 {
6269 EMSG2(_("E155: Unknown sign: %s"), sign_name);
6270 return;
6271 }
6272 if (lnum > 0)
6273 /* ":sign place {id} line={lnum} name={name} file={fname}":
6274 * place a sign */
6275 buf_addsign(buf, id, lnum, sp->sn_typenr);
6276 else
6277 /* ":sign place {id} file={fname}": change sign type */
6278 lnum = buf_change_sign_type(buf, id, sp->sn_typenr);
6279 update_debug_sign(buf, lnum);
6280 }
6281 else
6282 EMSG(_(e_invarg));
6283 }
6284}
6285
6286#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
6287/*
6288 * Allocate the icons. Called when the GUI has started. Allows defining
6289 * signs before it starts.
6290 */
6291 void
6292sign_gui_started()
6293{
6294 sign_T *sp;
6295
6296 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
6297 if (sp->sn_icon != NULL)
6298 sp->sn_image = gui_mch_register_sign(sp->sn_icon);
6299}
6300#endif
6301
6302/*
6303 * List one sign.
6304 */
6305 static void
6306sign_list_defined(sp)
6307 sign_T *sp;
6308{
6309 char_u *p;
6310
Bram Moolenaar555b2802005-05-19 21:08:39 +00006311 smsg((char_u *)"sign %s", sp->sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006312 if (sp->sn_icon != NULL)
6313 {
6314 MSG_PUTS(" icon=");
6315 msg_outtrans(sp->sn_icon);
6316#ifdef FEAT_SIGN_ICONS
6317 if (sp->sn_image == NULL)
6318 MSG_PUTS(_(" (NOT FOUND)"));
6319#else
6320 MSG_PUTS(_(" (not supported)"));
6321#endif
6322 }
6323 if (sp->sn_text != NULL)
6324 {
6325 MSG_PUTS(" text=");
6326 msg_outtrans(sp->sn_text);
6327 }
6328 if (sp->sn_line_hl > 0)
6329 {
6330 MSG_PUTS(" linehl=");
6331 p = get_highlight_name(NULL, sp->sn_line_hl - 1);
6332 if (p == NULL)
6333 MSG_PUTS("NONE");
6334 else
6335 msg_puts(p);
6336 }
6337 if (sp->sn_text_hl > 0)
6338 {
6339 MSG_PUTS(" texthl=");
6340 p = get_highlight_name(NULL, sp->sn_text_hl - 1);
6341 if (p == NULL)
6342 MSG_PUTS("NONE");
6343 else
6344 msg_puts(p);
6345 }
6346}
6347
6348/*
6349 * Get highlighting attribute for sign "typenr".
6350 * If "line" is TRUE: line highl, if FALSE: text highl.
6351 */
6352 int
6353sign_get_attr(typenr, line)
6354 int typenr;
6355 int line;
6356{
6357 sign_T *sp;
6358
6359 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
6360 if (sp->sn_typenr == typenr)
6361 {
6362 if (line)
6363 {
6364 if (sp->sn_line_hl > 0)
6365 return syn_id2attr(sp->sn_line_hl);
6366 }
6367 else
6368 {
6369 if (sp->sn_text_hl > 0)
6370 return syn_id2attr(sp->sn_text_hl);
6371 }
6372 break;
6373 }
6374 return 0;
6375}
6376
6377/*
6378 * Get text mark for sign "typenr".
6379 * Returns NULL if there isn't one.
6380 */
6381 char_u *
6382sign_get_text(typenr)
6383 int typenr;
6384{
6385 sign_T *sp;
6386
6387 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
6388 if (sp->sn_typenr == typenr)
6389 return sp->sn_text;
6390 return NULL;
6391}
6392
6393#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
6394 void *
6395sign_get_image(typenr)
6396 int typenr; /* the attribute which may have a sign */
6397{
6398 sign_T *sp;
6399
6400 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
6401 if (sp->sn_typenr == typenr)
6402 return sp->sn_image;
6403 return NULL;
6404}
6405#endif
6406
6407/*
6408 * Get the name of a sign by its typenr.
6409 */
6410 char_u *
6411sign_typenr2name(typenr)
6412 int typenr;
6413{
6414 sign_T *sp;
6415
6416 for (sp = first_sign; sp != NULL; sp = sp->sn_next)
6417 if (sp->sn_typenr == typenr)
6418 return sp->sn_name;
6419 return (char_u *)_("[Deleted]");
6420}
6421
6422#endif
6423
6424#if defined(FEAT_GUI) || defined(FEAT_CLIENTSERVER) || defined(PROTO)
6425/*
6426 * ":drop"
6427 * Opens the first argument in a window. When there are two or more arguments
6428 * the argument list is redefined.
6429 */
6430 void
6431ex_drop(eap)
6432 exarg_T *eap;
6433{
6434 int split = FALSE;
6435 int incurwin = FALSE;
6436 char_u *arg;
6437 char_u *first = NULL;
6438 win_T *wp;
6439 buf_T *buf;
6440
6441 /*
6442 * Check if the first argument is already being edited in a window. If
6443 * so, jump to that window.
6444 * We would actually need to check all arguments, but that's complicated
6445 * and mostly only one file is dropped.
6446 * This also ignores wildcards, since it is very unlikely the user is
6447 * editing a file name with a wildcard character.
6448 */
6449 arg = vim_strsave(eap->arg);
6450 if (arg != NULL)
6451 {
6452 /* Get the first argument, remove quotes, make it a full path. */
6453 first = fix_fname(arg);
6454 if (first != NULL)
6455 {
6456 buf = buflist_findname(first);
6457 FOR_ALL_WINDOWS(wp)
6458 {
6459 if (wp->w_buffer == buf)
6460 {
6461 incurwin = TRUE;
6462# ifdef FEAT_WINDOWS
6463 win_enter(wp, TRUE);
6464 break;
6465# endif
6466 }
6467 }
6468 vim_free(first);
6469
6470 if (incurwin)
6471 {
6472 /* Already editing the file. Redefine the argument list. */
6473 set_arglist(eap->arg);
6474 curwin->w_arg_idx = 0;
6475 vim_free(arg);
6476 return;
6477 }
6478 }
6479 vim_free(arg);
6480 }
6481
6482 /*
6483 * Check whether the current buffer is changed. If so, we will need
6484 * to split the current window or data could be lost.
6485 * Skip the check if the 'hidden' option is set, as in this case the
6486 * buffer won't be lost.
6487 */
6488 if (!P_HID(curbuf))
6489 {
6490# ifdef FEAT_WINDOWS
6491 ++emsg_off;
6492# endif
6493 split = check_changed(curbuf, TRUE, FALSE, FALSE, FALSE);
6494# ifdef FEAT_WINDOWS
6495 --emsg_off;
6496# else
6497 if (split)
6498 return;
6499# endif
6500 }
6501
6502 /* Fake a ":snext" or ":next" command, redefine the arglist. */
6503 if (split)
6504 {
6505 eap->cmdidx = CMD_snext;
6506 eap->cmd[0] = 's';
6507 }
6508 else
6509 eap->cmdidx = CMD_next;
6510 ex_next(eap);
6511}
6512#endif