1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4/*
5 * ex_cmds.c: some functions for command line commands
6 */
7
8#include <assert.h>
9#include <float.h>
10#include <stdbool.h>
11#include <string.h>
12#include <stdlib.h>
13#include <inttypes.h>
14#include <math.h>
15
16#include "nvim/api/private/defs.h"
17#include "nvim/api/buffer.h"
18#include "nvim/log.h"
19#include "nvim/vim.h"
20#include "nvim/ascii.h"
21#include "nvim/ex_cmds.h"
22#include "nvim/buffer.h"
23#include "nvim/change.h"
24#include "nvim/charset.h"
25#include "nvim/cursor.h"
26#include "nvim/diff.h"
27#include "nvim/digraph.h"
28#include "nvim/edit.h"
29#include "nvim/eval.h"
30#include "nvim/ex_cmds2.h"
31#include "nvim/ex_docmd.h"
32#include "nvim/ex_eval.h"
33#include "nvim/ex_getln.h"
34#include "nvim/fileio.h"
35#include "nvim/fold.h"
36#include "nvim/getchar.h"
37#include "nvim/highlight.h"
38#include "nvim/indent.h"
39#include "nvim/buffer_updates.h"
40#include "nvim/main.h"
41#include "nvim/mark.h"
42#include "nvim/mbyte.h"
43#include "nvim/memline.h"
44#include "nvim/message.h"
45#include "nvim/misc1.h"
46#include "nvim/garray.h"
47#include "nvim/memory.h"
48#include "nvim/move.h"
49#include "nvim/mouse.h"
50#include "nvim/normal.h"
51#include "nvim/ops.h"
52#include "nvim/option.h"
53#include "nvim/os_unix.h"
54#include "nvim/path.h"
55#include "nvim/quickfix.h"
56#include "nvim/regexp.h"
57#include "nvim/screen.h"
58#include "nvim/search.h"
59#include "nvim/spell.h"
60#include "nvim/strings.h"
61#include "nvim/syntax.h"
62#include "nvim/tag.h"
63#include "nvim/ui.h"
64#include "nvim/undo.h"
65#include "nvim/window.h"
66#include "nvim/os/os.h"
67#include "nvim/os/shell.h"
68#include "nvim/os/input.h"
69#include "nvim/os/time.h"
70
71
72/// Case matching style to use for :substitute
73typedef enum {
74 kSubHonorOptions = 0, ///< Honor the user's 'ignorecase'/'smartcase' options
75 kSubIgnoreCase, ///< Ignore case of the search
76 kSubMatchCase, ///< Match case of the search
77} SubIgnoreType;
78
79/// Flags kept between calls to :substitute.
80typedef struct {
81 bool do_all; ///< do multiple substitutions per line
82 bool do_ask; ///< ask for confirmation
83 bool do_count; ///< count only
84 bool do_error; ///< if false, ignore errors
85 bool do_print; ///< print last line with subs
86 bool do_list; ///< list last line with subs
87 bool do_number; ///< list last line with line nr
88 SubIgnoreType do_ic; ///< ignore case flag
89} subflags_T;
90
91/// Partial result of a substitution during :substitute.
92/// Numbers refer to the buffer _after_ substitution
93typedef struct {
94 lpos_T start; // start of the match
95 lpos_T end; // end of the match
96 linenr_T pre_match; // where to begin showing lines before the match
97} SubResult;
98
99// Collected results of a substitution for showing them in
100// the preview window
101typedef struct {
102 kvec_t(SubResult) subresults;
103 linenr_T lines_needed; // lines neede in the preview window
104} PreviewLines;
105
106#ifdef INCLUDE_GENERATED_DECLARATIONS
107# include "ex_cmds.c.generated.h"
108#endif
109
110/// ":ascii" and "ga" implementation
111void do_ascii(const exarg_T *const eap)
112{
113 char_u *dig;
114 int cc[MAX_MCO];
115 int c = utfc_ptr2char(get_cursor_pos_ptr(), cc);
116 if (c == NUL) {
117 MSG("NUL");
118 return;
119 }
120
121 size_t iobuff_len = 0;
122
123 int ci = 0;
124 if (c < 0x80) {
125 if (c == NL) { // NUL is stored as NL.
126 c = NUL;
127 }
128 const int cval = (c == CAR && get_fileformat(curbuf) == EOL_MAC
129 ? NL // NL is stored as CR.
130 : c);
131 char buf1[20];
132 if (vim_isprintc_strict(c) && (c < ' ' || c > '~')) {
133 char_u buf3[7];
134 transchar_nonprint(buf3, c);
135 vim_snprintf(buf1, sizeof(buf1), " <%s>", (char *)buf3);
136 } else {
137 buf1[0] = NUL;
138 }
139 char buf2[20];
140 buf2[0] = NUL;
141
142 dig = get_digraph_for_char(cval);
143 if (dig != NULL) {
144 iobuff_len += (
145 vim_snprintf((char *)IObuff + iobuff_len,
146 sizeof(IObuff) - iobuff_len,
147 _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"),
148 transchar(c), buf1, buf2, cval, cval, cval, dig));
149 } else {
150 iobuff_len += (
151 vim_snprintf((char *)IObuff + iobuff_len,
152 sizeof(IObuff) - iobuff_len,
153 _("<%s>%s%s %d, Hex %02x, Octal %03o"),
154 transchar(c), buf1, buf2, cval, cval, cval));
155 }
156
157 c = cc[ci++];
158 }
159
160#define SPACE_FOR_DESC (1 + 1 + 1 + MB_MAXBYTES + 16 + 4 + 3 + 3 + 1)
161 // Space for description:
162 // - 1 byte for separator (starting from second entry)
163 // - 1 byte for "<"
164 // - 1 byte for space to draw composing character on (optional, but really
165 // mostly required)
166 // - up to MB_MAXBYTES bytes for character itself
167 // - 16 bytes for raw text ("> , Hex , Octal ").
168 // - at least 4 bytes for hexadecimal representation
169 // - at least 3 bytes for decimal representation
170 // - at least 3 bytes for octal representation
171 // - 1 byte for NUL
172 //
173 // Taking into account MAX_MCO and characters which need 8 bytes for
174 // hexadecimal representation, but not taking translation into account:
175 // resulting string will occupy less then 400 bytes (conservative estimate).
176 //
177 // Less then 1000 bytes if translation multiplies number of bytes needed for
178 // raw text by 6, so it should always fit into 1025 bytes reserved for IObuff.
179
180 // Repeat for combining characters, also handle multiby here.
181 while (c >= 0x80 && iobuff_len < sizeof(IObuff) - SPACE_FOR_DESC) {
182 // This assumes every multi-byte char is printable...
183 if (iobuff_len > 0) {
184 IObuff[iobuff_len++] = ' ';
185 }
186 IObuff[iobuff_len++] = '<';
187 if (utf_iscomposing(c)) {
188 IObuff[iobuff_len++] = ' '; // Draw composing char on top of a space.
189 }
190 iobuff_len += utf_char2bytes(c, IObuff + iobuff_len);
191
192 dig = get_digraph_for_char(c);
193 if (dig != NULL) {
194 iobuff_len += (
195 vim_snprintf((char *)IObuff + iobuff_len,
196 sizeof(IObuff) - iobuff_len,
197 (c < 0x10000
198 ? _("> %d, Hex %04x, Oct %o, Digr %s")
199 : _("> %d, Hex %08x, Oct %o, Digr %s")),
200 c, c, c, dig));
201 } else {
202 iobuff_len += (
203 vim_snprintf((char *)IObuff + iobuff_len,
204 sizeof(IObuff) - iobuff_len,
205 (c < 0x10000
206 ? _("> %d, Hex %04x, Octal %o")
207 : _("> %d, Hex %08x, Octal %o")),
208 c, c, c));
209 }
210 if (ci == MAX_MCO) {
211 break;
212 }
213 c = cc[ci++];
214 }
215 if (ci != MAX_MCO && c != 0) {
216 xstrlcpy((char *)IObuff + iobuff_len, " ...", sizeof(IObuff) - iobuff_len);
217 }
218
219 msg(IObuff);
220}
221
222/*
223 * ":left", ":center" and ":right": align text.
224 */
225void ex_align(exarg_T *eap)
226{
227 pos_T save_curpos;
228 int len;
229 int indent = 0;
230 int new_indent;
231 int has_tab;
232 int width;
233
234 if (curwin->w_p_rl) {
235 /* switch left and right aligning */
236 if (eap->cmdidx == CMD_right)
237 eap->cmdidx = CMD_left;
238 else if (eap->cmdidx == CMD_left)
239 eap->cmdidx = CMD_right;
240 }
241
242 width = atoi((char *)eap->arg);
243 save_curpos = curwin->w_cursor;
244 if (eap->cmdidx == CMD_left) { /* width is used for new indent */
245 if (width >= 0)
246 indent = width;
247 } else {
248 /*
249 * if 'textwidth' set, use it
250 * else if 'wrapmargin' set, use it
251 * if invalid value, use 80
252 */
253 if (width <= 0)
254 width = curbuf->b_p_tw;
255 if (width == 0 && curbuf->b_p_wm > 0) {
256 width = curwin->w_width_inner - curbuf->b_p_wm;
257 }
258 if (width <= 0) {
259 width = 80;
260 }
261 }
262
263 if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL)
264 return;
265
266 for (curwin->w_cursor.lnum = eap->line1;
267 curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum) {
268 if (eap->cmdidx == CMD_left) /* left align */
269 new_indent = indent;
270 else {
271 has_tab = FALSE; /* avoid uninit warnings */
272 len = linelen(eap->cmdidx == CMD_right ? &has_tab
273 : NULL) - get_indent();
274
275 if (len <= 0) /* skip blank lines */
276 continue;
277
278 if (eap->cmdidx == CMD_center)
279 new_indent = (width - len) / 2;
280 else {
281 new_indent = width - len; /* right align */
282
283 /*
284 * Make sure that embedded TABs don't make the text go too far
285 * to the right.
286 */
287 if (has_tab)
288 while (new_indent > 0) {
289 (void)set_indent(new_indent, 0);
290 if (linelen(NULL) <= width) {
291 /*
292 * Now try to move the line as much as possible to
293 * the right. Stop when it moves too far.
294 */
295 do
296 (void)set_indent(++new_indent, 0);
297 while (linelen(NULL) <= width);
298 --new_indent;
299 break;
300 }
301 --new_indent;
302 }
303 }
304 }
305 if (new_indent < 0)
306 new_indent = 0;
307 (void)set_indent(new_indent, 0); /* set indent */
308 }
309 changed_lines(eap->line1, 0, eap->line2 + 1, 0L, true);
310 curwin->w_cursor = save_curpos;
311 beginline(BL_WHITE | BL_FIX);
312}
313
314/*
315 * Get the length of the current line, excluding trailing white space.
316 */
317static int linelen(int *has_tab)
318{
319 char_u *line;
320 char_u *first;
321 char_u *last;
322 int save;
323 int len;
324
325 /* find the first non-blank character */
326 line = get_cursor_line_ptr();
327 first = skipwhite(line);
328
329 /* find the character after the last non-blank character */
330 for (last = first + STRLEN(first);
331 last > first && ascii_iswhite(last[-1]); --last)
332 ;
333 save = *last;
334 *last = NUL;
335 // Get line length.
336 len = linetabsize(line);
337 // Check for embedded TAB.
338 if (has_tab != NULL) {
339 *has_tab = vim_strchr(first, TAB) != NULL;
340 }
341 *last = save;
342
343 return len;
344}
345
346/* Buffer for two lines used during sorting. They are allocated to
347 * contain the longest line being sorted. */
348static char_u *sortbuf1;
349static char_u *sortbuf2;
350
351static int sort_ic; ///< ignore case
352static int sort_nr; ///< sort on number
353static int sort_rx; ///< sort on regex instead of skipping it
354static int sort_flt; ///< sort on floating number
355
356static int sort_abort; ///< flag to indicate if sorting has been interrupted
357
358/// Struct to store info to be sorted.
359typedef struct {
360 linenr_T lnum; ///< line number
361 union {
362 struct {
363 varnumber_T start_col_nr; ///< starting column number
364 varnumber_T end_col_nr; ///< ending column number
365 } line;
366 struct {
367 varnumber_T value; ///< value if sorting by integer
368 bool is_number; ///< true when line contains a number
369 } num;
370 float_T value_flt; ///< value if sorting by float
371 } st_u;
372} sorti_T;
373
374
375static int sort_compare(const void *s1, const void *s2)
376{
377 sorti_T l1 = *(sorti_T *)s1;
378 sorti_T l2 = *(sorti_T *)s2;
379 int result = 0;
380
381 /* If the user interrupts, there's no way to stop qsort() immediately, but
382 * if we return 0 every time, qsort will assume it's done sorting and
383 * exit. */
384 if (sort_abort)
385 return 0;
386 fast_breakcheck();
387 if (got_int)
388 sort_abort = TRUE;
389
390 // When sorting numbers "start_col_nr" is the number, not the column
391 // number.
392 if (sort_nr) {
393 if (l1.st_u.num.is_number != l2.st_u.num.is_number) {
394 result = l1.st_u.num.is_number - l2.st_u.num.is_number;
395 } else {
396 result = l1.st_u.num.value == l2.st_u.num.value
397 ? 0
398 : l1.st_u.num.value > l2.st_u.num.value
399 ? 1
400 : -1;
401 }
402 } else if (sort_flt) {
403 result = l1.st_u.value_flt == l2.st_u.value_flt
404 ? 0 : l1.st_u.value_flt > l2.st_u.value_flt
405 ? 1 : -1;
406 } else {
407 // We need to copy one line into "sortbuf1", because there is no
408 // guarantee that the first pointer becomes invalid when obtaining the
409 // second one.
410 memcpy(sortbuf1, ml_get(l1.lnum) + l1.st_u.line.start_col_nr,
411 l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr + 1);
412 sortbuf1[l1.st_u.line.end_col_nr - l1.st_u.line.start_col_nr] = NUL;
413 memcpy(sortbuf2, ml_get(l2.lnum) + l2.st_u.line.start_col_nr,
414 l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr + 1);
415 sortbuf2[l2.st_u.line.end_col_nr - l2.st_u.line.start_col_nr] = NUL;
416
417 result = sort_ic ? STRICMP(sortbuf1, sortbuf2)
418 : STRCMP(sortbuf1, sortbuf2);
419 }
420
421 /* If two lines have the same value, preserve the original line order. */
422 if (result == 0)
423 return (int)(l1.lnum - l2.lnum);
424 return result;
425}
426
427// ":sort".
428void ex_sort(exarg_T *eap)
429{
430 regmatch_T regmatch;
431 int len;
432 linenr_T lnum;
433 long maxlen = 0;
434 size_t count = (size_t)(eap->line2 - eap->line1 + 1);
435 size_t i;
436 char_u *p;
437 char_u *s;
438 char_u *s2;
439 char_u c; // temporary character storage
440 bool unique = false;
441 long deleted;
442 colnr_T start_col;
443 colnr_T end_col;
444 int sort_what = 0;
445
446 // Sorting one line is really quick!
447 if (count <= 1) {
448 return;
449 }
450
451 if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) {
452 return;
453 }
454 sortbuf1 = NULL;
455 sortbuf2 = NULL;
456 regmatch.regprog = NULL;
457 sorti_T *nrs = xmalloc(count * sizeof(sorti_T));
458
459 sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0;
460 size_t format_found = 0;
461 bool change_occurred = false; // Buffer contents changed.
462
463 for (p = eap->arg; *p != NUL; ++p) {
464 if (ascii_iswhite(*p)) {
465 } else if (*p == 'i') {
466 sort_ic = true;
467 } else if (*p == 'r') {
468 sort_rx = true;
469 } else if (*p == 'n') {
470 sort_nr = 1;
471 format_found++;
472 } else if (*p == 'f') {
473 sort_flt = 1;
474 format_found++;
475 } else if (*p == 'b') {
476 sort_what = STR2NR_BIN + STR2NR_FORCE;
477 format_found++;
478 } else if (*p == 'o') {
479 sort_what = STR2NR_OCT + STR2NR_FORCE;
480 format_found++;
481 } else if (*p == 'x') {
482 sort_what = STR2NR_HEX + STR2NR_FORCE;
483 format_found++;
484 } else if (*p == 'u') {
485 unique = true;
486 } else if (*p == '"') {
487 // comment start
488 break;
489 } else if (check_nextcmd(p) != NULL) {
490 eap->nextcmd = check_nextcmd(p);
491 break;
492 } else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
493 s = skip_regexp(p + 1, *p, true, NULL);
494 if (*s != *p) {
495 EMSG(_(e_invalpat));
496 goto sortend;
497 }
498 *s = NUL;
499 // Use last search pattern if sort pattern is empty.
500 if (s == p + 1) {
501 if (last_search_pat() == NULL) {
502 EMSG(_(e_noprevre));
503 goto sortend;
504 }
505 regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
506 } else {
507 regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC);
508 }
509 if (regmatch.regprog == NULL) {
510 goto sortend;
511 }
512 p = s; // continue after the regexp
513 regmatch.rm_ic = p_ic;
514 } else {
515 EMSG2(_(e_invarg2), p);
516 goto sortend;
517 }
518 }
519
520 // Can only have one of 'n', 'b', 'o' and 'x'.
521 if (format_found > 1) {
522 EMSG(_(e_invarg));
523 goto sortend;
524 }
525
526 // From here on "sort_nr" is used as a flag for any integer number
527 // sorting.
528 sort_nr += sort_what;
529
530 // Make an array with all line numbers. This avoids having to copy all
531 // the lines into allocated memory.
532 // When sorting on strings "start_col_nr" is the offset in the line, for
533 // numbers sorting it's the number to sort on. This means the pattern
534 // matching and number conversion only has to be done once per line.
535 // Also get the longest line length for allocating "sortbuf".
536 for (lnum = eap->line1; lnum <= eap->line2; ++lnum) {
537 s = ml_get(lnum);
538 len = (int)STRLEN(s);
539 if (maxlen < len) {
540 maxlen = len;
541 }
542
543 start_col = 0;
544 end_col = len;
545 if (regmatch.regprog != NULL && vim_regexec(&regmatch, s, 0)) {
546 if (sort_rx) {
547 start_col = (colnr_T)(regmatch.startp[0] - s);
548 end_col = (colnr_T)(regmatch.endp[0] - s);
549 } else {
550 start_col = (colnr_T)(regmatch.endp[0] - s);
551 }
552 } else if (regmatch.regprog != NULL) {
553 end_col = 0;
554 }
555
556 if (sort_nr || sort_flt) {
557 // Make sure vim_str2nr doesn't read any digits past the end
558 // of the match, by temporarily terminating the string there
559 s2 = s + end_col;
560 c = *s2;
561 *s2 = NUL;
562 // Sorting on number: Store the number itself.
563 p = s + start_col;
564 if (sort_nr) {
565 if (sort_what & STR2NR_HEX) {
566 s = skiptohex(p);
567 } else if (sort_what & STR2NR_BIN) {
568 s = (char_u *)skiptobin((char *)p);
569 } else {
570 s = skiptodigit(p);
571 }
572 if (s > p && s[-1] == '-') {
573 s--; // include preceding negative sign
574 }
575 if (*s == NUL) {
576 // line without number should sort before any number
577 nrs[lnum - eap->line1].st_u.num.is_number = false;
578 nrs[lnum - eap->line1].st_u.num.value = 0;
579 } else {
580 nrs[lnum - eap->line1].st_u.num.is_number = true;
581 vim_str2nr(s, NULL, NULL, sort_what,
582 &nrs[lnum - eap->line1].st_u.num.value, NULL, 0);
583 }
584 } else {
585 s = skipwhite(p);
586 if (*s == '+') {
587 s = skipwhite(s + 1);
588 }
589
590 if (*s == NUL) {
591 // empty line should sort before any number
592 nrs[lnum - eap->line1].st_u.value_flt = -DBL_MAX;
593 } else {
594 nrs[lnum - eap->line1].st_u.value_flt = strtod((char *)s, NULL);
595 }
596 }
597 *s2 = c;
598 } else {
599 // Store the column to sort at.
600 nrs[lnum - eap->line1].st_u.line.start_col_nr = start_col;
601 nrs[lnum - eap->line1].st_u.line.end_col_nr = end_col;
602 }
603
604 nrs[lnum - eap->line1].lnum = lnum;
605
606 if (regmatch.regprog != NULL)
607 fast_breakcheck();
608 if (got_int)
609 goto sortend;
610 }
611
612 // Allocate a buffer that can hold the longest line.
613 sortbuf1 = xmalloc(maxlen + 1);
614 sortbuf2 = xmalloc(maxlen + 1);
615
616 // Sort the array of line numbers. Note: can't be interrupted!
617 qsort((void *)nrs, count, sizeof(sorti_T), sort_compare);
618
619 if (sort_abort)
620 goto sortend;
621
622 // Insert the lines in the sorted order below the last one.
623 lnum = eap->line2;
624 for (i = 0; i < count; i++) {
625 const linenr_T get_lnum = nrs[eap->forceit ? count - i - 1 : i].lnum;
626
627 // If the original line number of the line being placed is not the same
628 // as "lnum" (accounting for offset), we know that the buffer changed.
629 if (get_lnum + ((linenr_T)count - 1) != lnum) {
630 change_occurred = true;
631 }
632
633 s = ml_get(get_lnum);
634 if (!unique || i == 0
635 || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) {
636 // Copy the line into a buffer, it may become invalid in
637 // ml_append(). And it's needed for "unique".
638 STRCPY(sortbuf1, s);
639 if (ml_append(lnum++, sortbuf1, (colnr_T)0, false) == FAIL) {
640 break;
641 }
642 }
643 fast_breakcheck();
644 if (got_int)
645 goto sortend;
646 }
647
648 // delete the original lines if appending worked
649 if (i == count) {
650 for (i = 0; i < count; ++i) {
651 ml_delete(eap->line1, false);
652 }
653 } else {
654 count = 0;
655 }
656
657 // Adjust marks for deleted (or added) lines and prepare for displaying.
658 deleted = (long)(count - (lnum - eap->line2));
659 if (deleted > 0) {
660 mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted,
661 false);
662 msgmore(-deleted);
663 } else if (deleted < 0) {
664 mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false);
665 }
666 if (change_occurred || deleted != 0) {
667 changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true);
668 }
669
670 curwin->w_cursor.lnum = eap->line1;
671 beginline(BL_WHITE | BL_FIX);
672
673sortend:
674 xfree(nrs);
675 xfree(sortbuf1);
676 xfree(sortbuf2);
677 vim_regfree(regmatch.regprog);
678 if (got_int) {
679 EMSG(_(e_interr));
680 }
681}
682
683/*
684 * ":retab".
685 */
686void ex_retab(exarg_T *eap)
687{
688 linenr_T lnum;
689 int got_tab = FALSE;
690 long num_spaces = 0;
691 long num_tabs;
692 long len;
693 long col;
694 long vcol;
695 long start_col = 0; /* For start of white-space string */
696 long start_vcol = 0; /* For start of white-space string */
697 int temp;
698 long old_len;
699 char_u *ptr;
700 char_u *new_line = (char_u *)1; /* init to non-NULL */
701 int did_undo; /* called u_save for current line */
702 int new_ts;
703 int save_list;
704 linenr_T first_line = 0; /* first changed line */
705 linenr_T last_line = 0; /* last changed line */
706
707 save_list = curwin->w_p_list;
708 curwin->w_p_list = 0; /* don't want list mode here */
709
710 new_ts = getdigits_int(&(eap->arg), false, -1);
711 if (new_ts < 0) {
712 EMSG(_(e_positive));
713 return;
714 }
715 if (new_ts == 0)
716 new_ts = curbuf->b_p_ts;
717 for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) {
718 ptr = ml_get(lnum);
719 col = 0;
720 vcol = 0;
721 did_undo = FALSE;
722 for (;; ) {
723 if (ascii_iswhite(ptr[col])) {
724 if (!got_tab && num_spaces == 0) {
725 /* First consecutive white-space */
726 start_vcol = vcol;
727 start_col = col;
728 }
729 if (ptr[col] == ' ')
730 num_spaces++;
731 else
732 got_tab = TRUE;
733 } else {
734 if (got_tab || (eap->forceit && num_spaces > 1)) {
735 /* Retabulate this string of white-space */
736
737 /* len is virtual length of white string */
738 len = num_spaces = vcol - start_vcol;
739 num_tabs = 0;
740 if (!curbuf->b_p_et) {
741 temp = new_ts - (start_vcol % new_ts);
742 if (num_spaces >= temp) {
743 num_spaces -= temp;
744 num_tabs++;
745 }
746 num_tabs += num_spaces / new_ts;
747 num_spaces -= (num_spaces / new_ts) * new_ts;
748 }
749 if (curbuf->b_p_et || got_tab
750 || (num_spaces + num_tabs < len)) {
751 if (did_undo == false) {
752 did_undo = true;
753 if (u_save((linenr_T)(lnum - 1),
754 (linenr_T)(lnum + 1)) == FAIL) {
755 new_line = NULL; // flag out-of-memory
756 break;
757 }
758 }
759
760 /* len is actual number of white characters used */
761 len = num_spaces + num_tabs;
762 old_len = (long)STRLEN(ptr);
763 new_line = xmalloc(old_len - col + start_col + len + 1);
764
765 if (start_col > 0)
766 memmove(new_line, ptr, (size_t)start_col);
767 memmove(new_line + start_col + len,
768 ptr + col, (size_t)(old_len - col + 1));
769 ptr = new_line + start_col;
770 for (col = 0; col < len; col++) {
771 ptr[col] = (col < num_tabs) ? '\t' : ' ';
772 }
773 ml_replace(lnum, new_line, false);
774 if (first_line == 0) {
775 first_line = lnum;
776 }
777 last_line = lnum;
778 ptr = new_line;
779 col = start_col + len;
780 }
781 }
782 got_tab = FALSE;
783 num_spaces = 0;
784 }
785 if (ptr[col] == NUL)
786 break;
787 vcol += chartabsize(ptr + col, (colnr_T)vcol);
788 if (has_mbyte)
789 col += (*mb_ptr2len)(ptr + col);
790 else
791 ++col;
792 }
793 if (new_line == NULL) /* out of memory */
794 break;
795 line_breakcheck();
796 }
797 if (got_int)
798 EMSG(_(e_interr));
799
800 if (curbuf->b_p_ts != new_ts)
801 redraw_curbuf_later(NOT_VALID);
802 if (first_line != 0) {
803 changed_lines(first_line, 0, last_line + 1, 0L, true);
804 }
805
806 curwin->w_p_list = save_list; /* restore 'list' */
807
808 curbuf->b_p_ts = new_ts;
809 coladvance(curwin->w_curswant);
810
811 u_clearline();
812}
813
814/*
815 * :move command - move lines line1-line2 to line dest
816 *
817 * return FAIL for failure, OK otherwise
818 */
819int do_move(linenr_T line1, linenr_T line2, linenr_T dest)
820{
821 char_u *str;
822 linenr_T l;
823 linenr_T extra; // Num lines added before line1
824 linenr_T num_lines; // Num lines moved
825 linenr_T last_line; // Last line in file after adding new text
826
827 if (dest >= line1 && dest < line2) {
828 EMSG(_("E134: Cannot move a range of lines into itself"));
829 return FAIL;
830 }
831
832 // Do nothing if we are not actually moving any lines. This will prevent
833 // the 'modified' flag from being set without cause.
834 if (dest == line1 - 1 || dest == line2) {
835 // Move the cursor as if lines were moved (see below) to be backwards
836 // compatible.
837 if (dest >= line1) {
838 curwin->w_cursor.lnum = dest;
839 } else {
840 curwin->w_cursor.lnum = dest + (line2 - line1) + 1;
841 }
842 return OK;
843 }
844
845 num_lines = line2 - line1 + 1;
846
847 /*
848 * First we copy the old text to its new location -- webb
849 * Also copy the flag that ":global" command uses.
850 */
851 if (u_save(dest, dest + 1) == FAIL)
852 return FAIL;
853 for (extra = 0, l = line1; l <= line2; l++) {
854 str = vim_strsave(ml_get(l + extra));
855 ml_append(dest + l - line1, str, (colnr_T)0, false);
856 xfree(str);
857 if (dest < line1)
858 extra++;
859 }
860
861 /*
862 * Now we must be careful adjusting our marks so that we don't overlap our
863 * mark_adjust() calls.
864 *
865 * We adjust the marks within the old text so that they refer to the
866 * last lines of the file (temporarily), because we know no other marks
867 * will be set there since these line numbers did not exist until we added
868 * our new lines.
869 *
870 * Then we adjust the marks on lines between the old and new text positions
871 * (either forwards or backwards).
872 *
873 * And Finally we adjust the marks we put at the end of the file back to
874 * their final destination at the new text position -- webb
875 */
876 last_line = curbuf->b_ml.ml_line_count;
877 mark_adjust_nofold(line1, line2, last_line - line2, 0L, true);
878 changed_lines(last_line - num_lines + 1, 0, last_line + 1, num_lines, false);
879 if (dest >= line2) {
880 mark_adjust_nofold(line2 + 1, dest, -num_lines, 0L, false);
881 FOR_ALL_TAB_WINDOWS(tab, win) {
882 if (win->w_buffer == curbuf) {
883 foldMoveRange(&win->w_folds, line1, line2, dest);
884 }
885 }
886 curbuf->b_op_start.lnum = dest - num_lines + 1;
887 curbuf->b_op_end.lnum = dest;
888 } else {
889 mark_adjust_nofold(dest + 1, line1 - 1, num_lines, 0L, false);
890 FOR_ALL_TAB_WINDOWS(tab, win) {
891 if (win->w_buffer == curbuf) {
892 foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2);
893 }
894 }
895 curbuf->b_op_start.lnum = dest + 1;
896 curbuf->b_op_end.lnum = dest + num_lines;
897 }
898 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
899 mark_adjust_nofold(last_line - num_lines + 1, last_line,
900 -(last_line - dest - extra), 0L, true);
901 changed_lines(last_line - num_lines + 1, 0, last_line + 1, -extra, false);
902
903 // send update regarding the new lines that were added
904 buf_updates_send_changes(curbuf, dest + 1, num_lines, 0, true);
905
906 /*
907 * Now we delete the original text -- webb
908 */
909 if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
910 return FAIL;
911
912 for (l = line1; l <= line2; l++) {
913 ml_delete(line1 + extra, true);
914 }
915 if (!global_busy && num_lines > p_report) {
916 if (num_lines == 1)
917 MSG(_("1 line moved"));
918 else
919 smsg(_("%" PRId64 " lines moved"), (int64_t)num_lines);
920 }
921
922 /*
923 * Leave the cursor on the last of the moved lines.
924 */
925 if (dest >= line1)
926 curwin->w_cursor.lnum = dest;
927 else
928 curwin->w_cursor.lnum = dest + (line2 - line1) + 1;
929
930 if (line1 < dest) {
931 dest += num_lines + 1;
932 last_line = curbuf->b_ml.ml_line_count;
933 if (dest > last_line + 1)
934 dest = last_line + 1;
935 changed_lines(line1, 0, dest, 0L, false);
936 } else {
937 changed_lines(dest + 1, 0, line1 + num_lines, 0L, false);
938 }
939
940 // send nvim_buf_lines_event regarding lines that were deleted
941 buf_updates_send_changes(curbuf, line1 + extra, 0, num_lines, true);
942
943 return OK;
944}
945
946/*
947 * ":copy"
948 */
949void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
950{
951 linenr_T count;
952 char_u *p;
953
954 count = line2 - line1 + 1;
955 curbuf->b_op_start.lnum = n + 1;
956 curbuf->b_op_end.lnum = n + count;
957 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
958
959 /*
960 * there are three situations:
961 * 1. destination is above line1
962 * 2. destination is between line1 and line2
963 * 3. destination is below line2
964 *
965 * n = destination (when starting)
966 * curwin->w_cursor.lnum = destination (while copying)
967 * line1 = start of source (while copying)
968 * line2 = end of source (while copying)
969 */
970 if (u_save(n, n + 1) == FAIL)
971 return;
972
973 curwin->w_cursor.lnum = n;
974 while (line1 <= line2) {
975 /* need to use vim_strsave() because the line will be unlocked within
976 * ml_append() */
977 p = vim_strsave(ml_get(line1));
978 ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, false);
979 xfree(p);
980
981 /* situation 2: skip already copied lines */
982 if (line1 == n)
983 line1 = curwin->w_cursor.lnum;
984 ++line1;
985 if (curwin->w_cursor.lnum < line1)
986 ++line1;
987 if (curwin->w_cursor.lnum < line2)
988 ++line2;
989 ++curwin->w_cursor.lnum;
990 }
991
992 appended_lines_mark(n, count);
993
994 msgmore((long)count);
995}
996
997static char_u *prevcmd = NULL; /* the previous command */
998
999#if defined(EXITFREE)
1000void free_prev_shellcmd(void)
1001{
1002 xfree(prevcmd);
1003}
1004
1005#endif
1006
1007/*
1008 * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd"
1009 * Bangs in the argument are replaced with the previously entered command.
1010 * Remember the argument.
1011 */
1012void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out)
1013{
1014 char_u *arg = eap->arg; /* command */
1015 linenr_T line1 = eap->line1; /* start of range */
1016 linenr_T line2 = eap->line2; /* end of range */
1017 char_u *newcmd = NULL; /* the new command */
1018 int free_newcmd = FALSE; /* need to free() newcmd */
1019 int ins_prevcmd;
1020 char_u *t;
1021 char_u *p;
1022 char_u *trailarg;
1023 int len;
1024 int scroll_save = msg_scroll;
1025
1026 /*
1027 * Disallow shell commands in restricted mode (-Z)
1028 * Disallow shell commands from .exrc and .vimrc in current directory for
1029 * security reasons.
1030 */
1031 if (check_restricted() || check_secure())
1032 return;
1033
1034 if (addr_count == 0) { /* :! */
1035 msg_scroll = FALSE; /* don't scroll here */
1036 autowrite_all();
1037 msg_scroll = scroll_save;
1038 }
1039
1040 /*
1041 * Try to find an embedded bang, like in :!<cmd> ! [args]
1042 * (:!! is indicated by the 'forceit' variable)
1043 */
1044 ins_prevcmd = forceit;
1045 trailarg = arg;
1046 do {
1047 len = (int)STRLEN(trailarg) + 1;
1048 if (newcmd != NULL)
1049 len += (int)STRLEN(newcmd);
1050 if (ins_prevcmd) {
1051 if (prevcmd == NULL) {
1052 EMSG(_(e_noprev));
1053 xfree(newcmd);
1054 return;
1055 }
1056 len += (int)STRLEN(prevcmd);
1057 }
1058 t = xmalloc(len);
1059 *t = NUL;
1060 if (newcmd != NULL)
1061 STRCAT(t, newcmd);
1062 if (ins_prevcmd)
1063 STRCAT(t, prevcmd);
1064 p = t + STRLEN(t);
1065 STRCAT(t, trailarg);
1066 xfree(newcmd);
1067 newcmd = t;
1068
1069 /*
1070 * Scan the rest of the argument for '!', which is replaced by the
1071 * previous command. "\!" is replaced by "!" (this is vi compatible).
1072 */
1073 trailarg = NULL;
1074 while (*p) {
1075 if (*p == '!') {
1076 if (p > newcmd && p[-1] == '\\')
1077 STRMOVE(p - 1, p);
1078 else {
1079 trailarg = p;
1080 *trailarg++ = NUL;
1081 ins_prevcmd = TRUE;
1082 break;
1083 }
1084 }
1085 ++p;
1086 }
1087 } while (trailarg != NULL);
1088
1089 xfree(prevcmd);
1090 prevcmd = newcmd;
1091
1092 if (bangredo) { /* put cmd in redo buffer for ! command */
1093 /* If % or # appears in the command, it must have been escaped.
1094 * Reescape them, so that redoing them does not substitute them by the
1095 * buffername. */
1096 char_u *cmd = vim_strsave_escaped(prevcmd, (char_u *)"%#");
1097
1098 AppendToRedobuffLit(cmd, -1);
1099 xfree(cmd);
1100 AppendToRedobuff("\n");
1101 bangredo = false;
1102 }
1103 /*
1104 * Add quotes around the command, for shells that need them.
1105 */
1106 if (*p_shq != NUL) {
1107 newcmd = xmalloc(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1);
1108 STRCPY(newcmd, p_shq);
1109 STRCAT(newcmd, prevcmd);
1110 STRCAT(newcmd, p_shq);
1111 free_newcmd = TRUE;
1112 }
1113 if (addr_count == 0) { /* :! */
1114 /* echo the command */
1115 msg_start();
1116 msg_putchar(':');
1117 msg_putchar('!');
1118 msg_outtrans(newcmd);
1119 msg_clr_eos();
1120 ui_cursor_goto(msg_row, msg_col);
1121
1122 do_shell(newcmd, 0);
1123 } else { /* :range! */
1124 /* Careful: This may recursively call do_bang() again! (because of
1125 * autocommands) */
1126 do_filter(line1, line2, eap, newcmd, do_in, do_out);
1127 apply_autocmds(EVENT_SHELLFILTERPOST, NULL, NULL, FALSE, curbuf);
1128 }
1129 if (free_newcmd)
1130 xfree(newcmd);
1131}
1132
1133// do_filter: filter lines through a command given by the user
1134//
1135// We mostly use temp files and the call_shell() routine here. This would
1136// normally be done using pipes on a Unix system, but this is more portable
1137// to non-Unix systems. The call_shell() routine needs to be able
1138// to deal with redirection somehow, and should handle things like looking
1139// at the PATH env. variable, and adding reasonable extensions to the
1140// command name given by the user. All reasonable versions of call_shell()
1141// do this.
1142// Alternatively, if on Unix and redirecting input or output, but not both,
1143// and the 'shelltemp' option isn't set, use pipes.
1144// We use input redirection if do_in is TRUE.
1145// We use output redirection if do_out is TRUE.
1146static void do_filter(
1147 linenr_T line1,
1148 linenr_T line2,
1149 exarg_T *eap, /* for forced 'ff' and 'fenc' */
1150 char_u *cmd,
1151 int do_in,
1152 int do_out)
1153{
1154 char_u *itmp = NULL;
1155 char_u *otmp = NULL;
1156 linenr_T linecount;
1157 linenr_T read_linecount;
1158 pos_T cursor_save;
1159 char_u *cmd_buf;
1160 buf_T *old_curbuf = curbuf;
1161 int shell_flags = 0;
1162
1163 if (*cmd == NUL) /* no filter command */
1164 return;
1165
1166
1167 cursor_save = curwin->w_cursor;
1168 linecount = line2 - line1 + 1;
1169 curwin->w_cursor.lnum = line1;
1170 curwin->w_cursor.col = 0;
1171 changed_line_abv_curs();
1172 invalidate_botline();
1173
1174 /*
1175 * When using temp files:
1176 * 1. * Form temp file names
1177 * 2. * Write the lines to a temp file
1178 * 3. Run the filter command on the temp file
1179 * 4. * Read the output of the command into the buffer
1180 * 5. * Delete the original lines to be filtered
1181 * 6. * Remove the temp files
1182 *
1183 * When writing the input with a pipe or when catching the output with a
1184 * pipe only need to do 3.
1185 */
1186
1187 if (do_out)
1188 shell_flags |= kShellOptDoOut;
1189
1190 if (!do_in && do_out && !p_stmp) {
1191 // Use a pipe to fetch stdout of the command, do not use a temp file.
1192 shell_flags |= kShellOptRead;
1193 curwin->w_cursor.lnum = line2;
1194 } else if (do_in && !do_out && !p_stmp) {
1195 // Use a pipe to write stdin of the command, do not use a temp file.
1196 shell_flags |= kShellOptWrite;
1197 curbuf->b_op_start.lnum = line1;
1198 curbuf->b_op_end.lnum = line2;
1199 } else if (do_in && do_out && !p_stmp) {
1200 // Use a pipe to write stdin and fetch stdout of the command, do not
1201 // use a temp file.
1202 shell_flags |= kShellOptRead | kShellOptWrite;
1203 curbuf->b_op_start.lnum = line1;
1204 curbuf->b_op_end.lnum = line2;
1205 curwin->w_cursor.lnum = line2;
1206 } else if ((do_in && (itmp = vim_tempname()) == NULL)
1207 || (do_out && (otmp = vim_tempname()) == NULL)) {
1208 EMSG(_(e_notmp));
1209 goto filterend;
1210 }
1211
1212 /*
1213 * The writing and reading of temp files will not be shown.
1214 * Vi also doesn't do this and the messages are not very informative.
1215 */
1216 ++no_wait_return; /* don't call wait_return() while busy */
1217 if (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap,
1218 false, false, false, true) == FAIL) {
1219 msg_putchar('\n'); // Keep message from buf_write().
1220 no_wait_return--;
1221 if (!aborting()) {
1222 EMSG2(_("E482: Can't create file %s"), itmp); // Will call wait_return.
1223 }
1224 goto filterend;
1225 }
1226 if (curbuf != old_curbuf)
1227 goto filterend;
1228
1229 if (!do_out)
1230 msg_putchar('\n');
1231
1232 /* Create the shell command in allocated memory. */
1233 cmd_buf = make_filter_cmd(cmd, itmp, otmp);
1234 ui_cursor_goto(Rows - 1, 0);
1235
1236 if (do_out) {
1237 if (u_save((linenr_T)(line2), (linenr_T)(line2 + 1)) == FAIL) {
1238 xfree(cmd_buf);
1239 goto error;
1240 }
1241 redraw_curbuf_later(VALID);
1242 }
1243 read_linecount = curbuf->b_ml.ml_line_count;
1244
1245 // Pass on the kShellOptDoOut flag when the output is being redirected.
1246 call_shell(cmd_buf, kShellOptFilter | shell_flags, NULL);
1247 xfree(cmd_buf);
1248
1249 did_check_timestamps = FALSE;
1250 need_check_timestamps = TRUE;
1251
1252 /* When interrupting the shell command, it may still have produced some
1253 * useful output. Reset got_int here, so that readfile() won't cancel
1254 * reading. */
1255 os_breakcheck();
1256 got_int = FALSE;
1257
1258 if (do_out) {
1259 if (otmp != NULL) {
1260 if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, eap,
1261 READ_FILTER) != OK) {
1262 if (!aborting()) {
1263 msg_putchar('\n');
1264 EMSG2(_(e_notread), otmp);
1265 }
1266 goto error;
1267 }
1268 if (curbuf != old_curbuf)
1269 goto filterend;
1270 }
1271
1272 read_linecount = curbuf->b_ml.ml_line_count - read_linecount;
1273
1274 if (shell_flags & kShellOptRead) {
1275 curbuf->b_op_start.lnum = line2 + 1;
1276 curbuf->b_op_end.lnum = curwin->w_cursor.lnum;
1277 appended_lines_mark(line2, read_linecount);
1278 }
1279
1280 if (do_in) {
1281 if (cmdmod.keepmarks || vim_strchr(p_cpo, CPO_REMMARK) == NULL) {
1282 if (read_linecount >= linecount) {
1283 // move all marks from old lines to new lines
1284 mark_adjust(line1, line2, linecount, 0L, false);
1285 } else {
1286 // move marks from old lines to new lines, delete marks
1287 // that are in deleted lines
1288 mark_adjust(line1, line1 + read_linecount - 1, linecount, 0L, false);
1289 mark_adjust(line1 + read_linecount, line2, MAXLNUM, 0L, false);
1290 }
1291 }
1292
1293 /*
1294 * Put cursor on first filtered line for ":range!cmd".
1295 * Adjust '[ and '] (set by buf_write()).
1296 */
1297 curwin->w_cursor.lnum = line1;
1298 del_lines(linecount, TRUE);
1299 curbuf->b_op_start.lnum -= linecount; /* adjust '[ */
1300 curbuf->b_op_end.lnum -= linecount; /* adjust '] */
1301 write_lnum_adjust(-linecount); /* adjust last line
1302 for next write */
1303 foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum);
1304 } else {
1305 /*
1306 * Put cursor on last new line for ":r !cmd".
1307 */
1308 linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
1309 curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
1310 }
1311
1312 beginline(BL_WHITE | BL_FIX); /* cursor on first non-blank */
1313 --no_wait_return;
1314
1315 if (linecount > p_report) {
1316 if (do_in) {
1317 vim_snprintf((char *)msg_buf, sizeof(msg_buf),
1318 _("%" PRId64 " lines filtered"), (int64_t)linecount);
1319 if (msg(msg_buf) && !msg_scroll)
1320 /* save message to display it after redraw */
1321 set_keep_msg(msg_buf, 0);
1322 } else
1323 msgmore((long)linecount);
1324 }
1325 } else {
1326error:
1327 /* put cursor back in same position for ":w !cmd" */
1328 curwin->w_cursor = cursor_save;
1329 --no_wait_return;
1330 wait_return(FALSE);
1331 }
1332
1333filterend:
1334
1335 if (curbuf != old_curbuf) {
1336 --no_wait_return;
1337 EMSG(_("E135: *Filter* Autocommands must not change current buffer"));
1338 }
1339 if (itmp != NULL)
1340 os_remove((char *)itmp);
1341 if (otmp != NULL)
1342 os_remove((char *)otmp);
1343 xfree(itmp);
1344 xfree(otmp);
1345}
1346
1347// Call a shell to execute a command.
1348// When "cmd" is NULL start an interactive shell.
1349void
1350do_shell(
1351 char_u *cmd,
1352 int flags // may be SHELL_DOOUT when output is redirected
1353)
1354{
1355 // Disallow shell commands in restricted mode (-Z)
1356 // Disallow shell commands from .exrc and .vimrc in current directory for
1357 // security reasons.
1358 if (check_restricted() || check_secure()) {
1359 msg_end();
1360 return;
1361 }
1362
1363
1364 /*
1365 * For autocommands we want to get the output on the current screen, to
1366 * avoid having to type return below.
1367 */
1368 msg_putchar('\r'); /* put cursor at start of line */
1369 msg_putchar('\n'); /* may shift screen one line up */
1370
1371 /* warning message before calling the shell */
1372 if (p_warn
1373 && !autocmd_busy
1374 && msg_silent == 0)
1375 FOR_ALL_BUFFERS(buf) {
1376 if (bufIsChanged(buf)) {
1377 MSG_PUTS(_("[No write since last change]\n"));
1378 break;
1379 }
1380 }
1381
1382 // This ui_cursor_goto is required for when the '\n' resulted in a "delete line
1383 // 1" command to the terminal.
1384 ui_cursor_goto(msg_row, msg_col);
1385 (void)call_shell(cmd, flags, NULL);
1386 msg_didout = true;
1387 did_check_timestamps = false;
1388 need_check_timestamps = true;
1389
1390 // put the message cursor at the end of the screen, avoids wait_return()
1391 // to overwrite the text that the external command showed
1392 msg_row = Rows - 1;
1393 msg_col = 0;
1394
1395 apply_autocmds(EVENT_SHELLCMDPOST, NULL, NULL, FALSE, curbuf);
1396}
1397
1398#if !defined(UNIX)
1399static char *find_pipe(const char *cmd)
1400{
1401 bool inquote = false;
1402
1403 for (const char *p = cmd; *p != NUL; p++) {
1404 if (!inquote && *p == '|') {
1405 return p;
1406 }
1407 if (*p == '"') {
1408 inquote = !inquote;
1409 } else if (rem_backslash((const char_u *)p)) {
1410 p++;
1411 }
1412 }
1413 return NULL;
1414}
1415#endif
1416
1417/// Create a shell command from a command string, input redirection file and
1418/// output redirection file.
1419///
1420/// @param cmd Command to execute.
1421/// @param itmp NULL or the input file.
1422/// @param otmp NULL or the output file.
1423/// @returns an allocated string with the shell command.
1424char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp)
1425{
1426 bool is_fish_shell =
1427#if defined(UNIX)
1428 STRNCMP(invocation_path_tail(p_sh, NULL), "fish", 4) == 0;
1429#else
1430 false;
1431#endif
1432
1433 size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL.
1434
1435 len += is_fish_shell ? sizeof("begin; ""; end") - 1
1436 : sizeof("("")") - 1;
1437
1438 if (itmp != NULL) {
1439 len += STRLEN(itmp) + sizeof(" { "" < "" } ") - 1;
1440 }
1441 if (otmp != NULL) {
1442 len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "),
1443 }
1444 char *const buf = xmalloc(len);
1445
1446#if defined(UNIX)
1447 // Put delimiters around the command (for concatenated commands) when
1448 // redirecting input and/or output.
1449 if (itmp != NULL || otmp != NULL) {
1450 char *fmt = is_fish_shell ? "begin; %s; end"
1451 : "(%s)";
1452 vim_snprintf(buf, len, fmt, (char *)cmd);
1453 } else {
1454 xstrlcpy(buf, (char *)cmd, len);
1455 }
1456
1457 if (itmp != NULL) {
1458 xstrlcat(buf, " < ", len - 1);
1459 xstrlcat(buf, (const char *)itmp, len - 1);
1460 }
1461#else
1462 // For shells that don't understand braces around commands, at least allow
1463 // the use of commands in a pipe.
1464 xstrlcpy(buf, (char *)cmd, len);
1465 if (itmp != NULL) {
1466 // If there is a pipe, we have to put the '<' in front of it.
1467 // Don't do this when 'shellquote' is not empty, otherwise the
1468 // redirection would be inside the quotes.
1469 if (*p_shq == NUL) {
1470 char *const p = find_pipe(buf);
1471 if (p != NULL) {
1472 *p = NUL;
1473 }
1474 }
1475 xstrlcat(buf, " < ", len);
1476 xstrlcat(buf, (const char *)itmp, len);
1477 if (*p_shq == NUL) {
1478 const char *const p = find_pipe((const char *)cmd);
1479 if (p != NULL) {
1480 xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS
1481 xstrlcat(buf, p, len - 1);
1482 }
1483 }
1484 }
1485#endif
1486 if (otmp != NULL) {
1487 append_redir(buf, len, (char *) p_srr, (char *) otmp);
1488 }
1489 return (char_u *) buf;
1490}
1491
1492/// Append output redirection for the given file to the end of the buffer
1493///
1494/// @param[out] buf Buffer to append to.
1495/// @param[in] buflen Buffer length.
1496/// @param[in] opt Separator or format string to append: will append
1497/// `printf(' ' . opt, fname)` if `%s` is found in `opt` or
1498/// a space, opt, a space and then fname if `%s` is not found
1499/// there.
1500/// @param[in] fname File name to append.
1501void append_redir(char *const buf, const size_t buflen,
1502 const char *const opt, const char *const fname)
1503{
1504 char *const end = buf + strlen(buf);
1505 // find "%s"
1506 const char *p = opt;
1507 for (; (p = strchr(p, '%')) != NULL; p++) {
1508 if (p[1] == 's') { // found %s
1509 break;
1510 } else if (p[1] == '%') { // skip %%
1511 p++;
1512 }
1513 }
1514 if (p != NULL) {
1515 *end = ' '; // not really needed? Not with sh, ksh or bash
1516 vim_snprintf(end + 1, (size_t) (buflen - (end + 1 - buf)), opt, fname);
1517 } else {
1518 vim_snprintf(end, (size_t) (buflen - (end - buf)), " %s %s", opt, fname);
1519 }
1520}
1521
1522void print_line_no_prefix(linenr_T lnum, int use_number, int list)
1523{
1524 char numbuf[30];
1525
1526 if (curwin->w_p_nu || use_number) {
1527 vim_snprintf(numbuf, sizeof(numbuf), "%*" PRIdLINENR " ",
1528 number_width(curwin), lnum);
1529 msg_puts_attr(numbuf, HL_ATTR(HLF_N)); // Highlight line nrs.
1530 }
1531 msg_prt_line(ml_get(lnum), list);
1532}
1533
1534/*
1535 * Print a text line. Also in silent mode ("ex -s").
1536 */
1537void print_line(linenr_T lnum, int use_number, int list)
1538{
1539 int save_silent = silent_mode;
1540
1541 // apply :filter /pat/
1542 if (message_filtered(ml_get(lnum))) {
1543 return;
1544 }
1545
1546 msg_start();
1547 silent_mode = FALSE;
1548 info_message = TRUE; /* use mch_msg(), not mch_errmsg() */
1549 print_line_no_prefix(lnum, use_number, list);
1550 if (save_silent) {
1551 msg_putchar('\n');
1552 ui_flush();
1553 silent_mode = save_silent;
1554 }
1555 info_message = FALSE;
1556}
1557
1558int rename_buffer(char_u *new_fname)
1559{
1560 char_u *fname, *sfname, *xfname;
1561 buf_T *buf;
1562
1563 buf = curbuf;
1564 apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
1565 /* buffer changed, don't change name now */
1566 if (buf != curbuf)
1567 return FAIL;
1568 if (aborting()) /* autocmds may abort script processing */
1569 return FAIL;
1570 /*
1571 * The name of the current buffer will be changed.
1572 * A new (unlisted) buffer entry needs to be made to hold the old file
1573 * name, which will become the alternate file name.
1574 * But don't set the alternate file name if the buffer didn't have a
1575 * name.
1576 */
1577 fname = curbuf->b_ffname;
1578 sfname = curbuf->b_sfname;
1579 xfname = curbuf->b_fname;
1580 curbuf->b_ffname = NULL;
1581 curbuf->b_sfname = NULL;
1582 if (setfname(curbuf, new_fname, NULL, TRUE) == FAIL) {
1583 curbuf->b_ffname = fname;
1584 curbuf->b_sfname = sfname;
1585 return FAIL;
1586 }
1587 curbuf->b_flags |= BF_NOTEDITED;
1588 if (xfname != NULL && *xfname != NUL) {
1589 buf = buflist_new(fname, xfname, curwin->w_cursor.lnum, 0);
1590 if (buf != NULL && !cmdmod.keepalt) {
1591 curwin->w_alt_fnum = buf->b_fnum;
1592 }
1593 }
1594 xfree(fname);
1595 xfree(sfname);
1596 apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
1597 /* Change directories when the 'acd' option is set. */
1598 do_autochdir();
1599 return OK;
1600}
1601
1602/*
1603 * ":file[!] [fname]".
1604 */
1605void ex_file(exarg_T *eap)
1606{
1607 /* ":0file" removes the file name. Check for illegal uses ":3file",
1608 * "0file name", etc. */
1609 if (eap->addr_count > 0
1610 && (*eap->arg != NUL
1611 || eap->line2 > 0
1612 || eap->addr_count > 1)) {
1613 EMSG(_(e_invarg));
1614 return;
1615 }
1616
1617 if (*eap->arg != NUL || eap->addr_count == 1) {
1618 if (rename_buffer(eap->arg) == FAIL) {
1619 return;
1620 }
1621 redraw_tabline = true;
1622 }
1623
1624 // print file name if no argument or 'F' is not in 'shortmess'
1625 if (*eap->arg == NUL || !shortmess(SHM_FILEINFO)) {
1626 fileinfo(false, false, eap->forceit);
1627 }
1628}
1629
1630/*
1631 * ":update".
1632 */
1633void ex_update(exarg_T *eap)
1634{
1635 if (curbufIsChanged())
1636 (void)do_write(eap);
1637}
1638
1639/*
1640 * ":write" and ":saveas".
1641 */
1642void ex_write(exarg_T *eap)
1643{
1644 if (eap->usefilter) /* input lines to shell command */
1645 do_bang(1, eap, FALSE, TRUE, FALSE);
1646 else
1647 (void)do_write(eap);
1648}
1649
1650/*
1651 * write current buffer to file 'eap->arg'
1652 * if 'eap->append' is TRUE, append to the file
1653 *
1654 * if *eap->arg == NUL write to current file
1655 *
1656 * return FAIL for failure, OK otherwise
1657 */
1658int do_write(exarg_T *eap)
1659{
1660 int other;
1661 char_u *fname = NULL; /* init to shut up gcc */
1662 char_u *ffname;
1663 int retval = FAIL;
1664 char_u *free_fname = NULL;
1665 buf_T *alt_buf = NULL;
1666 int name_was_missing;
1667
1668 if (not_writing()) /* check 'write' option */
1669 return FAIL;
1670
1671 ffname = eap->arg;
1672 if (*ffname == NUL) {
1673 if (eap->cmdidx == CMD_saveas) {
1674 EMSG(_(e_argreq));
1675 goto theend;
1676 }
1677 other = FALSE;
1678 } else {
1679 fname = ffname;
1680 free_fname = (char_u *)fix_fname((char *)ffname);
1681 /*
1682 * When out-of-memory, keep unexpanded file name, because we MUST be
1683 * able to write the file in this situation.
1684 */
1685 if (free_fname != NULL)
1686 ffname = free_fname;
1687 other = otherfile(ffname);
1688 }
1689
1690 /*
1691 * If we have a new file, put its name in the list of alternate file names.
1692 */
1693 if (other) {
1694 if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL
1695 || eap->cmdidx == CMD_saveas)
1696 alt_buf = setaltfname(ffname, fname, (linenr_T)1);
1697 else
1698 alt_buf = buflist_findname(ffname);
1699 if (alt_buf != NULL && alt_buf->b_ml.ml_mfp != NULL) {
1700 /* Overwriting a file that is loaded in another buffer is not a
1701 * good idea. */
1702 EMSG(_(e_bufloaded));
1703 goto theend;
1704 }
1705 }
1706
1707 // Writing to the current file is not allowed in readonly mode
1708 // and a file name is required.
1709 // "nofile" and "nowrite" buffers cannot be written implicitly either.
1710 if (!other && (bt_dontwrite_msg(curbuf)
1711 || check_fname() == FAIL
1712 || check_readonly(&eap->forceit, curbuf))) {
1713 goto theend;
1714 }
1715
1716 if (!other) {
1717 ffname = curbuf->b_ffname;
1718 fname = curbuf->b_fname;
1719 /*
1720 * Not writing the whole file is only allowed with '!'.
1721 */
1722 if ( (eap->line1 != 1
1723 || eap->line2 != curbuf->b_ml.ml_line_count)
1724 && !eap->forceit
1725 && !eap->append
1726 && !p_wa) {
1727 if (p_confirm || cmdmod.confirm) {
1728 if (vim_dialog_yesno(VIM_QUESTION, NULL,
1729 (char_u *)_("Write partial file?"), 2) != VIM_YES)
1730 goto theend;
1731 eap->forceit = TRUE;
1732 } else {
1733 EMSG(_("E140: Use ! to write partial buffer"));
1734 goto theend;
1735 }
1736 }
1737 }
1738
1739 if (check_overwrite(eap, curbuf, fname, ffname, other) == OK) {
1740 if (eap->cmdidx == CMD_saveas && alt_buf != NULL) {
1741 buf_T *was_curbuf = curbuf;
1742
1743 apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf);
1744 apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, alt_buf);
1745 if (curbuf != was_curbuf || aborting()) {
1746 /* buffer changed, don't change name now */
1747 retval = FAIL;
1748 goto theend;
1749 }
1750 /* Exchange the file names for the current and the alternate
1751 * buffer. This makes it look like we are now editing the buffer
1752 * under the new name. Must be done before buf_write(), because
1753 * if there is no file name and 'cpo' contains 'F', it will set
1754 * the file name. */
1755 fname = alt_buf->b_fname;
1756 alt_buf->b_fname = curbuf->b_fname;
1757 curbuf->b_fname = fname;
1758 fname = alt_buf->b_ffname;
1759 alt_buf->b_ffname = curbuf->b_ffname;
1760 curbuf->b_ffname = fname;
1761 fname = alt_buf->b_sfname;
1762 alt_buf->b_sfname = curbuf->b_sfname;
1763 curbuf->b_sfname = fname;
1764 buf_name_changed(curbuf);
1765 apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf);
1766 apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, alt_buf);
1767 if (!alt_buf->b_p_bl) {
1768 alt_buf->b_p_bl = TRUE;
1769 apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, alt_buf);
1770 }
1771 if (curbuf != was_curbuf || aborting()) {
1772 /* buffer changed, don't write the file */
1773 retval = FAIL;
1774 goto theend;
1775 }
1776
1777 // If 'filetype' was empty try detecting it now.
1778 if (*curbuf->b_p_ft == NUL) {
1779 if (au_has_group((char_u *)"filetypedetect")) {
1780 (void)do_doautocmd((char_u *)"filetypedetect BufRead", true, NULL);
1781 }
1782 do_modelines(0);
1783 }
1784
1785 /* Autocommands may have changed buffer names, esp. when
1786 * 'autochdir' is set. */
1787 fname = curbuf->b_sfname;
1788 }
1789
1790 name_was_missing = curbuf->b_ffname == NULL;
1791 retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2,
1792 eap, eap->append, eap->forceit, TRUE, FALSE);
1793
1794 /* After ":saveas fname" reset 'readonly'. */
1795 if (eap->cmdidx == CMD_saveas) {
1796 if (retval == OK) {
1797 curbuf->b_p_ro = FALSE;
1798 redraw_tabline = TRUE;
1799 }
1800 }
1801
1802 // Change directories when the 'acd' option is set and the file name
1803 // got changed or set.
1804 if (eap->cmdidx == CMD_saveas || name_was_missing) {
1805 do_autochdir();
1806 }
1807 }
1808
1809theend:
1810 xfree(free_fname);
1811 return retval;
1812}
1813
1814/*
1815 * Check if it is allowed to overwrite a file. If b_flags has BF_NOTEDITED,
1816 * BF_NEW or BF_READERR, check for overwriting current file.
1817 * May set eap->forceit if a dialog says it's OK to overwrite.
1818 * Return OK if it's OK, FAIL if it is not.
1819 */
1820int
1821check_overwrite(
1822 exarg_T *eap,
1823 buf_T *buf,
1824 char_u *fname, // file name to be used (can differ from
1825 // buf->ffname)
1826 char_u *ffname, // full path version of fname
1827 int other // writing under other name
1828)
1829{
1830 /*
1831 * write to other file or b_flags set or not writing the whole file:
1832 * overwriting only allowed with '!'
1833 */
1834 if ((other
1835 || (buf->b_flags & BF_NOTEDITED)
1836 || ((buf->b_flags & BF_NEW)
1837 && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
1838 || (buf->b_flags & BF_READERR))
1839 && !p_wa
1840 && !bt_nofile(buf)
1841 && os_path_exists(ffname)) {
1842 if (!eap->forceit && !eap->append) {
1843#ifdef UNIX
1844 // It is possible to open a directory on Unix.
1845 if (os_isdir(ffname)) {
1846 EMSG2(_(e_isadir2), ffname);
1847 return FAIL;
1848 }
1849#endif
1850 if (p_confirm || cmdmod.confirm) {
1851 char_u buff[DIALOG_MSG_SIZE];
1852
1853 dialog_msg(buff, _("Overwrite existing file \"%s\"?"), fname);
1854 if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES)
1855 return FAIL;
1856 eap->forceit = TRUE;
1857 } else {
1858 EMSG(_(e_exists));
1859 return FAIL;
1860 }
1861 }
1862
1863 /* For ":w! filename" check that no swap file exists for "filename". */
1864 if (other && !emsg_silent) {
1865 char_u *dir;
1866 char_u *p;
1867 char_u *swapname;
1868
1869 /* We only try the first entry in 'directory', without checking if
1870 * it's writable. If the "." directory is not writable the write
1871 * will probably fail anyway.
1872 * Use 'shortname' of the current buffer, since there is no buffer
1873 * for the written file. */
1874 if (*p_dir == NUL) {
1875 dir = xmalloc(5);
1876 STRCPY(dir, ".");
1877 } else {
1878 dir = xmalloc(MAXPATHL);
1879 p = p_dir;
1880 copy_option_part(&p, dir, MAXPATHL, ",");
1881 }
1882 swapname = makeswapname(fname, ffname, curbuf, dir);
1883 xfree(dir);
1884 if (os_path_exists(swapname)) {
1885 if (p_confirm || cmdmod.confirm) {
1886 char_u buff[DIALOG_MSG_SIZE];
1887
1888 dialog_msg(buff,
1889 _("Swap file \"%s\" exists, overwrite anyway?"),
1890 swapname);
1891 if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2)
1892 != VIM_YES) {
1893 xfree(swapname);
1894 return FAIL;
1895 }
1896 eap->forceit = TRUE;
1897 } else {
1898 EMSG2(_("E768: Swap file exists: %s (:silent! overrides)"),
1899 swapname);
1900 xfree(swapname);
1901 return FAIL;
1902 }
1903 }
1904 xfree(swapname);
1905 }
1906 }
1907 return OK;
1908}
1909
1910/*
1911 * Handle ":wnext", ":wNext" and ":wprevious" commands.
1912 */
1913void ex_wnext(exarg_T *eap)
1914{
1915 int i;
1916
1917 if (eap->cmd[1] == 'n')
1918 i = curwin->w_arg_idx + (int)eap->line2;
1919 else
1920 i = curwin->w_arg_idx - (int)eap->line2;
1921 eap->line1 = 1;
1922 eap->line2 = curbuf->b_ml.ml_line_count;
1923 if (do_write(eap) != FAIL)
1924 do_argfile(eap, i);
1925}
1926
1927/*
1928 * ":wall", ":wqall" and ":xall": Write all changed files (and exit).
1929 */
1930void do_wqall(exarg_T *eap)
1931{
1932 int error = 0;
1933 int save_forceit = eap->forceit;
1934
1935 if (eap->cmdidx == CMD_xall || eap->cmdidx == CMD_wqall) {
1936 exiting = true;
1937 }
1938
1939 FOR_ALL_BUFFERS(buf) {
1940 if (!bufIsChanged(buf) || bt_dontwrite(buf)) {
1941 continue;
1942 }
1943 /*
1944 * Check if there is a reason the buffer cannot be written:
1945 * 1. if the 'write' option is set
1946 * 2. if there is no file name (even after browsing)
1947 * 3. if the 'readonly' is set (even after a dialog)
1948 * 4. if overwriting is allowed (even after a dialog)
1949 */
1950 if (not_writing()) {
1951 ++error;
1952 break;
1953 }
1954 if (buf->b_ffname == NULL) {
1955 EMSGN(_("E141: No file name for buffer %" PRId64), buf->b_fnum);
1956 ++error;
1957 } else if (check_readonly(&eap->forceit, buf)
1958 || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname,
1959 FALSE) == FAIL) {
1960 ++error;
1961 } else {
1962 bufref_T bufref;
1963 set_bufref(&bufref, buf);
1964 if (buf_write_all(buf, eap->forceit) == FAIL) {
1965 error++;
1966 }
1967 // An autocommand may have deleted the buffer.
1968 if (!bufref_valid(&bufref)) {
1969 buf = firstbuf;
1970 }
1971 }
1972 eap->forceit = save_forceit; /* check_overwrite() may set it */
1973 }
1974 if (exiting) {
1975 if (!error)
1976 getout(0); /* exit Vim */
1977 not_exiting();
1978 }
1979}
1980
1981/*
1982 * Check the 'write' option.
1983 * Return TRUE and give a message when it's not st.
1984 */
1985int not_writing(void)
1986{
1987 if (p_write)
1988 return FALSE;
1989 EMSG(_("E142: File not written: Writing is disabled by 'write' option"));
1990 return TRUE;
1991}
1992
1993/*
1994 * Check if a buffer is read-only (either 'readonly' option is set or file is
1995 * read-only). Ask for overruling in a dialog. Return TRUE and give an error
1996 * message when the buffer is readonly.
1997 */
1998static int check_readonly(int *forceit, buf_T *buf)
1999{
2000 /* Handle a file being readonly when the 'readonly' option is set or when
2001 * the file exists and permissions are read-only. */
2002 if (!*forceit && (buf->b_p_ro
2003 || (os_path_exists(buf->b_ffname)
2004 && !os_file_is_writable((char *)buf->b_ffname)))) {
2005 if ((p_confirm || cmdmod.confirm) && buf->b_fname != NULL) {
2006 char_u buff[DIALOG_MSG_SIZE];
2007
2008 if (buf->b_p_ro)
2009 dialog_msg(buff,
2010 _(
2011 "'readonly' option is set for \"%s\".\nDo you wish to write anyway?"),
2012 buf->b_fname);
2013 else
2014 dialog_msg(buff,
2015 _(
2016 "File permissions of \"%s\" are read-only.\nIt may still be possible to write it.\nDo you wish to try?"),
2017 buf->b_fname);
2018
2019 if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) == VIM_YES) {
2020 /* Set forceit, to force the writing of a readonly file */
2021 *forceit = TRUE;
2022 return FALSE;
2023 } else
2024 return TRUE;
2025 } else if (buf->b_p_ro)
2026 EMSG(_(e_readonly));
2027 else
2028 EMSG2(_("E505: \"%s\" is read-only (add ! to override)"),
2029 buf->b_fname);
2030 return TRUE;
2031 }
2032
2033 return FALSE;
2034}
2035
2036/*
2037 * Try to abandon current file and edit a new or existing file.
2038 * "fnum" is the number of the file, if zero use ffname/sfname.
2039 * "lnum" is the line number for the cursor in the new file (if non-zero).
2040 *
2041 * Return:
2042 * GETFILE_ERROR for "normal" error,
2043 * GETFILE_NOT_WRITTEN for "not written" error,
2044 * GETFILE_SAME_FILE for success
2045 * GETFILE_OPEN_OTHER for successfully opening another file.
2046 */
2047int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum, int forceit)
2048{
2049 int other;
2050 int retval;
2051 char_u *free_me = NULL;
2052
2053 if (text_locked()) {
2054 return GETFILE_ERROR;
2055 }
2056 if (curbuf_locked()) {
2057 return GETFILE_ERROR;
2058 }
2059
2060 if (fnum == 0) {
2061 /* make ffname full path, set sfname */
2062 fname_expand(curbuf, &ffname, &sfname);
2063 other = otherfile(ffname);
2064 free_me = ffname; /* has been allocated, free() later */
2065 } else
2066 other = (fnum != curbuf->b_fnum);
2067
2068 if (other) {
2069 no_wait_return++; // don't wait for autowrite message
2070 }
2071 if (other && !forceit && curbuf->b_nwindows == 1 && !buf_hide(curbuf)
2072 && curbufIsChanged() && autowrite(curbuf, forceit) == FAIL) {
2073 if (p_confirm && p_write) {
2074 dialog_changed(curbuf, false);
2075 }
2076 if (curbufIsChanged()) {
2077 no_wait_return--;
2078 no_write_message();
2079 retval = GETFILE_NOT_WRITTEN; // File has been changed.
2080 goto theend;
2081 }
2082 }
2083 if (other)
2084 --no_wait_return;
2085 if (setpm)
2086 setpcmark();
2087 if (!other) {
2088 if (lnum != 0) {
2089 curwin->w_cursor.lnum = lnum;
2090 }
2091 check_cursor_lnum();
2092 beginline(BL_SOL | BL_FIX);
2093 retval = GETFILE_SAME_FILE; // it's in the same file
2094 } else if (do_ecmd(fnum, ffname, sfname, NULL, lnum,
2095 (buf_hide(curbuf) ? ECMD_HIDE : 0)
2096 + (forceit ? ECMD_FORCEIT : 0), curwin) == OK) {
2097 retval = GETFILE_OPEN_OTHER; // opened another file
2098 } else {
2099 retval = GETFILE_ERROR; // error encountered
2100 }
2101
2102theend:
2103 xfree(free_me);
2104 return retval;
2105}
2106
2107/// start editing a new file
2108///
2109/// @param fnum file number; if zero use ffname/sfname
2110/// @param ffname the file name
2111/// - full path if sfname used,
2112/// - any file name if sfname is NULL
2113/// - empty string to re-edit with the same file name (but may
2114/// be in a different directory)
2115/// - NULL to start an empty buffer
2116/// @param sfname the short file name (or NULL)
2117/// @param eap contains the command to be executed after loading the file
2118/// and forced 'ff' and 'fenc'
2119/// @param newlnum if > 0: put cursor on this line number (if possible)
2120/// ECMD_LASTL: use last position in loaded file
2121/// ECMD_LAST: use last position in all files
2122/// ECMD_ONE: use first line
2123/// @param flags ECMD_HIDE: if TRUE don't free the current buffer
2124/// ECMD_SET_HELP: set b_help flag of (new) buffer before
2125/// opening file
2126/// ECMD_OLDBUF: use existing buffer if it exists
2127/// ECMD_FORCEIT: ! used for Ex command
2128/// ECMD_ADDBUF: don't edit, just add to buffer list
2129/// @param oldwin Should be "curwin" when editing a new buffer in the current
2130/// window, NULL when splitting the window first. When not NULL
2131/// info of the previous buffer for "oldwin" is stored.
2132///
2133/// @return FAIL for failure, OK otherwise
2134int do_ecmd(
2135 int fnum,
2136 char_u *ffname,
2137 char_u *sfname,
2138 exarg_T *eap, /* can be NULL! */
2139 linenr_T newlnum,
2140 int flags,
2141 win_T *oldwin
2142)
2143{
2144 int other_file; /* TRUE if editing another file */
2145 int oldbuf; /* TRUE if using existing buffer */
2146 int auto_buf = FALSE; /* TRUE if autocommands brought us
2147 into the buffer unexpectedly */
2148 char_u *new_name = NULL;
2149 int did_set_swapcommand = FALSE;
2150 buf_T *buf;
2151 bufref_T bufref;
2152 bufref_T old_curbuf;
2153 char_u *free_fname = NULL;
2154 int retval = FAIL;
2155 long n;
2156 pos_T orig_pos;
2157 linenr_T topline = 0;
2158 int newcol = -1;
2159 int solcol = -1;
2160 pos_T *pos;
2161 char_u *command = NULL;
2162 int did_get_winopts = FALSE;
2163 int readfile_flags = 0;
2164 bool did_inc_redrawing_disabled = false;
2165
2166 if (eap != NULL)
2167 command = eap->do_ecmd_cmd;
2168
2169 set_bufref(&old_curbuf, curbuf);
2170
2171 if (fnum != 0) {
2172 if (fnum == curbuf->b_fnum) /* file is already being edited */
2173 return OK; /* nothing to do */
2174 other_file = TRUE;
2175 } else {
2176 /* if no short name given, use ffname for short name */
2177 if (sfname == NULL)
2178 sfname = ffname;
2179#ifdef USE_FNAME_CASE
2180 if (sfname != NULL)
2181 path_fix_case(sfname); // set correct case for sfname
2182#endif
2183
2184 if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL))
2185 goto theend;
2186
2187 if (ffname == NULL)
2188 other_file = TRUE;
2189 /* there is no file name */
2190 else if (*ffname == NUL && curbuf->b_ffname == NULL)
2191 other_file = FALSE;
2192 else {
2193 if (*ffname == NUL) { /* re-edit with same file name */
2194 ffname = curbuf->b_ffname;
2195 sfname = curbuf->b_fname;
2196 }
2197 free_fname = (char_u *)fix_fname((char *)ffname); /* may expand to full path name */
2198 if (free_fname != NULL)
2199 ffname = free_fname;
2200 other_file = otherfile(ffname);
2201 }
2202 }
2203
2204 // Re-editing a terminal buffer: skip most buffer re-initialization.
2205 if (!other_file && curbuf->terminal) {
2206 check_arg_idx(curwin); // Needed when called from do_argfile().
2207 maketitle(); // Title may show the arg index, e.g. "(2 of 5)".
2208 retval = OK;
2209 goto theend;
2210 }
2211
2212 /*
2213 * if the file was changed we may not be allowed to abandon it
2214 * - if we are going to re-edit the same file
2215 * - or if we are the only window on this file and if ECMD_HIDE is FALSE
2216 */
2217 if ( ((!other_file && !(flags & ECMD_OLDBUF))
2218 || (curbuf->b_nwindows == 1
2219 && !(flags & (ECMD_HIDE | ECMD_ADDBUF))))
2220 && check_changed(curbuf, (p_awa ? CCGD_AW : 0)
2221 | (other_file ? 0 : CCGD_MULTWIN)
2222 | ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0)
2223 | (eap == NULL ? 0 : CCGD_EXCMD))) {
2224 if (fnum == 0 && other_file && ffname != NULL)
2225 (void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum);
2226 goto theend;
2227 }
2228
2229 /*
2230 * End Visual mode before switching to another buffer, so the text can be
2231 * copied into the GUI selection buffer.
2232 */
2233 reset_VIsual();
2234
2235 if ((command != NULL || newlnum > (linenr_T)0)
2236 && *get_vim_var_str(VV_SWAPCOMMAND) == NUL) {
2237 // Set v:swapcommand for the SwapExists autocommands.
2238 const size_t len = (command != NULL) ? STRLEN(command) + 3 : 30;
2239 char *const p = xmalloc(len);
2240 if (command != NULL) {
2241 vim_snprintf(p, len, ":%s\r", command);
2242 } else {
2243 vim_snprintf(p, len, "%" PRId64 "G", (int64_t)newlnum);
2244 }
2245 set_vim_var_string(VV_SWAPCOMMAND, p, -1);
2246 did_set_swapcommand = TRUE;
2247 xfree(p);
2248 }
2249
2250 /*
2251 * If we are starting to edit another file, open a (new) buffer.
2252 * Otherwise we re-use the current buffer.
2253 */
2254 if (other_file) {
2255 if (!(flags & ECMD_ADDBUF)) {
2256 if (!cmdmod.keepalt)
2257 curwin->w_alt_fnum = curbuf->b_fnum;
2258 if (oldwin != NULL)
2259 buflist_altfpos(oldwin);
2260 }
2261
2262 if (fnum) {
2263 buf = buflist_findnr(fnum);
2264 } else {
2265 if (flags & ECMD_ADDBUF) {
2266 linenr_T tlnum = 1L;
2267
2268 if (command != NULL) {
2269 tlnum = atol((char *)command);
2270 if (tlnum <= 0)
2271 tlnum = 1L;
2272 }
2273 (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED);
2274 goto theend;
2275 }
2276 buf = buflist_new(ffname, sfname, 0L,
2277 BLN_CURBUF | (flags & ECMD_SET_HELP ? 0 : BLN_LISTED));
2278 // Autocmds may change curwin and curbuf.
2279 if (oldwin != NULL) {
2280 oldwin = curwin;
2281 }
2282 set_bufref(&old_curbuf, curbuf);
2283 }
2284 if (buf == NULL)
2285 goto theend;
2286 if (buf->b_ml.ml_mfp == NULL) {
2287 // No memfile yet.
2288 oldbuf = false;
2289 } else {
2290 // Existing memfile.
2291 oldbuf = true;
2292 set_bufref(&bufref, buf);
2293 (void)buf_check_timestamp(buf, false);
2294 // Check if autocommands made buffer invalid or changed the current
2295 // buffer.
2296 if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf) {
2297 goto theend;
2298 }
2299 if (aborting()) {
2300 // Autocmds may abort script processing.
2301 goto theend;
2302 }
2303 }
2304
2305 /* May jump to last used line number for a loaded buffer or when asked
2306 * for explicitly */
2307 if ((oldbuf && newlnum == ECMD_LASTL) || newlnum == ECMD_LAST) {
2308 pos = buflist_findfpos(buf);
2309 newlnum = pos->lnum;
2310 solcol = pos->col;
2311 }
2312
2313 /*
2314 * Make the (new) buffer the one used by the current window.
2315 * If the old buffer becomes unused, free it if ECMD_HIDE is FALSE.
2316 * If the current buffer was empty and has no file name, curbuf
2317 * is returned by buflist_new(), nothing to do here.
2318 */
2319 if (buf != curbuf) {
2320 /*
2321 * Be careful: The autocommands may delete any buffer and change
2322 * the current buffer.
2323 * - If the buffer we are going to edit is deleted, give up.
2324 * - If the current buffer is deleted, prefer to load the new
2325 * buffer when loading a buffer is required. This avoids
2326 * loading another buffer which then must be closed again.
2327 * - If we ended up in the new buffer already, need to skip a few
2328 * things, set auto_buf.
2329 */
2330 if (buf->b_fname != NULL) {
2331 new_name = vim_strsave(buf->b_fname);
2332 }
2333 set_bufref(&au_new_curbuf, buf);
2334 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf);
2335 if (!bufref_valid(&au_new_curbuf)) {
2336 // New buffer has been deleted.
2337 delbuf_msg(new_name); // Frees new_name.
2338 goto theend;
2339 }
2340 if (aborting()) { /* autocmds may abort script processing */
2341 xfree(new_name);
2342 goto theend;
2343 }
2344 if (buf == curbuf) { // already in new buffer
2345 auto_buf = true;
2346 } else {
2347 win_T *the_curwin = curwin;
2348
2349 // Set w_closing to avoid that autocommands close the window.
2350 // Set b_locked for the same reason.
2351 the_curwin->w_closing = true;
2352 buf->b_locked++;
2353
2354 if (curbuf == old_curbuf.br_buf) {
2355 buf_copy_options(buf, BCO_ENTER);
2356 }
2357
2358 // Close the link to the current buffer. This will set
2359 // oldwin->w_buffer to NULL.
2360 u_sync(false);
2361 close_buffer(oldwin, curbuf,
2362 (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
2363 false);
2364
2365 the_curwin->w_closing = false;
2366 buf->b_locked--;
2367
2368 // autocmds may abort script processing
2369 if (aborting() && curwin->w_buffer != NULL) {
2370 xfree(new_name);
2371 goto theend;
2372 }
2373 // Be careful again, like above.
2374 if (!bufref_valid(&au_new_curbuf)) {
2375 // New buffer has been deleted.
2376 delbuf_msg(new_name); // Frees new_name.
2377 goto theend;
2378 }
2379 if (buf == curbuf) { // already in new buffer
2380 auto_buf = true;
2381 } else {
2382 // <VN> We could instead free the synblock
2383 // and re-attach to buffer, perhaps.
2384 if (curwin->w_buffer == NULL
2385 || curwin->w_s == &(curwin->w_buffer->b_s)) {
2386 curwin->w_s = &(buf->b_s);
2387 }
2388
2389 curwin->w_buffer = buf;
2390 curbuf = buf;
2391 ++curbuf->b_nwindows;
2392
2393 /* Set 'fileformat', 'binary' and 'fenc' when forced. */
2394 if (!oldbuf && eap != NULL) {
2395 set_file_options(TRUE, eap);
2396 set_forced_fenc(eap);
2397 }
2398 }
2399
2400 /* May get the window options from the last time this buffer
2401 * was in this window (or another window). If not used
2402 * before, reset the local window options to the global
2403 * values. Also restores old folding stuff. */
2404 get_winopts(curbuf);
2405 did_get_winopts = TRUE;
2406
2407 }
2408 xfree(new_name);
2409 au_new_curbuf.br_buf = NULL;
2410 au_new_curbuf.br_buf_free_count = 0;
2411 }
2412
2413 curwin->w_pcmark.lnum = 1;
2414 curwin->w_pcmark.col = 0;
2415 } else { // !other_file
2416 if ((flags & ECMD_ADDBUF)
2417 || check_fname() == FAIL) {
2418 goto theend;
2419 }
2420 oldbuf = (flags & ECMD_OLDBUF);
2421 }
2422
2423 // Don't redraw until the cursor is in the right line, otherwise
2424 // autocommands may cause ml_get errors.
2425 RedrawingDisabled++;
2426 did_inc_redrawing_disabled = true;
2427
2428 buf = curbuf;
2429 if ((flags & ECMD_SET_HELP) || keep_help_flag) {
2430 prepare_help_buffer();
2431 } else if (!curbuf->b_help) {
2432 // Don't make a buffer listed if it's a help buffer. Useful when using
2433 // CTRL-O to go back to a help file.
2434 set_buflisted(TRUE);
2435 }
2436
2437 /* If autocommands change buffers under our fingers, forget about
2438 * editing the file. */
2439 if (buf != curbuf)
2440 goto theend;
2441 if (aborting()) /* autocmds may abort script processing */
2442 goto theend;
2443
2444 /* Since we are starting to edit a file, consider the filetype to be
2445 * unset. Helps for when an autocommand changes files and expects syntax
2446 * highlighting to work in the other file. */
2447 did_filetype = FALSE;
2448
2449 /*
2450 * other_file oldbuf
2451 * FALSE FALSE re-edit same file, buffer is re-used
2452 * FALSE TRUE re-edit same file, nothing changes
2453 * TRUE FALSE start editing new file, new buffer
2454 * TRUE TRUE start editing in existing buffer (nothing to do)
2455 */
2456 if (!other_file && !oldbuf) { /* re-use the buffer */
2457 set_last_cursor(curwin); /* may set b_last_cursor */
2458 if (newlnum == ECMD_LAST || newlnum == ECMD_LASTL) {
2459 newlnum = curwin->w_cursor.lnum;
2460 solcol = curwin->w_cursor.col;
2461 }
2462 buf = curbuf;
2463 if (buf->b_fname != NULL) {
2464 new_name = vim_strsave(buf->b_fname);
2465 } else {
2466 new_name = NULL;
2467 }
2468 set_bufref(&bufref, buf);
2469 if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) {
2470 // Save all the text, so that the reload can be undone.
2471 // Sync first so that this is a separate undo-able action.
2472 u_sync(false);
2473 if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, true)
2474 == FAIL) {
2475 xfree(new_name);
2476 goto theend;
2477 }
2478 u_unchanged(curbuf);
2479 buf_updates_unregister_all(curbuf);
2480 buf_freeall(curbuf, BFA_KEEP_UNDO);
2481
2482 // Tell readfile() not to clear or reload undo info.
2483 readfile_flags = READ_KEEP_UNDO;
2484 } else {
2485 buf_updates_unregister_all(curbuf);
2486 buf_freeall(curbuf, 0); // Free all things for buffer.
2487 }
2488 // If autocommands deleted the buffer we were going to re-edit, give
2489 // up and jump to the end.
2490 if (!bufref_valid(&bufref)) {
2491 delbuf_msg(new_name); // Frees new_name.
2492 goto theend;
2493 }
2494 xfree(new_name);
2495
2496 /* If autocommands change buffers under our fingers, forget about
2497 * re-editing the file. Should do the buf_clear_file(), but perhaps
2498 * the autocommands changed the buffer... */
2499 if (buf != curbuf)
2500 goto theend;
2501 if (aborting()) /* autocmds may abort script processing */
2502 goto theend;
2503 buf_clear_file(curbuf);
2504 curbuf->b_op_start.lnum = 0; /* clear '[ and '] marks */
2505 curbuf->b_op_end.lnum = 0;
2506 }
2507
2508 /*
2509 * If we get here we are sure to start editing
2510 */
2511
2512 /* Assume success now */
2513 retval = OK;
2514
2515 /*
2516 * Check if we are editing the w_arg_idx file in the argument list.
2517 */
2518 check_arg_idx(curwin);
2519
2520 if (!auto_buf) {
2521 /*
2522 * Set cursor and init window before reading the file and executing
2523 * autocommands. This allows for the autocommands to position the
2524 * cursor.
2525 */
2526 curwin_init();
2527
2528 /* It's possible that all lines in the buffer changed. Need to update
2529 * automatic folding for all windows where it's used. */
2530 FOR_ALL_TAB_WINDOWS(tp, win) {
2531 if (win->w_buffer == curbuf) {
2532 foldUpdateAll(win);
2533 }
2534 }
2535
2536 /* Change directories when the 'acd' option is set. */
2537 do_autochdir();
2538
2539 /*
2540 * Careful: open_buffer() and apply_autocmds() may change the current
2541 * buffer and window.
2542 */
2543 orig_pos = curwin->w_cursor;
2544 topline = curwin->w_topline;
2545 if (!oldbuf) { /* need to read the file */
2546 swap_exists_action = SEA_DIALOG;
2547 curbuf->b_flags |= BF_CHECK_RO; /* set/reset 'ro' flag */
2548
2549 /*
2550 * Open the buffer and read the file.
2551 */
2552 if (should_abort(open_buffer(FALSE, eap, readfile_flags)))
2553 retval = FAIL;
2554
2555 if (swap_exists_action == SEA_QUIT)
2556 retval = FAIL;
2557 handle_swap_exists(&old_curbuf);
2558 } else {
2559 /* Read the modelines, but only to set window-local options. Any
2560 * buffer-local options have already been set and may have been
2561 * changed by the user. */
2562 do_modelines(OPT_WINONLY);
2563
2564 apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf,
2565 &retval);
2566 apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf,
2567 &retval);
2568 }
2569 check_arg_idx(curwin);
2570
2571 // If autocommands change the cursor position or topline, we should
2572 // keep it. Also when it moves within a line. But not when it moves
2573 // to the first non-blank.
2574 if (!equalpos(curwin->w_cursor, orig_pos)) {
2575 const char_u *text = get_cursor_line_ptr();
2576
2577 if (curwin->w_cursor.lnum != orig_pos.lnum
2578 || curwin->w_cursor.col != (int)(skipwhite(text) - text)) {
2579 newlnum = curwin->w_cursor.lnum;
2580 newcol = curwin->w_cursor.col;
2581 }
2582 }
2583 if (curwin->w_topline == topline)
2584 topline = 0;
2585
2586 /* Even when cursor didn't move we need to recompute topline. */
2587 changed_line_abv_curs();
2588
2589 maketitle();
2590 }
2591
2592 /* Tell the diff stuff that this buffer is new and/or needs updating.
2593 * Also needed when re-editing the same buffer, because unloading will
2594 * have removed it as a diff buffer. */
2595 if (curwin->w_p_diff) {
2596 diff_buf_add(curbuf);
2597 diff_invalidate(curbuf);
2598 }
2599
2600 /* If the window options were changed may need to set the spell language.
2601 * Can only do this after the buffer has been properly setup. */
2602 if (did_get_winopts && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
2603 (void)did_set_spelllang(curwin);
2604
2605 if (command == NULL) {
2606 if (newcol >= 0) { /* position set by autocommands */
2607 curwin->w_cursor.lnum = newlnum;
2608 curwin->w_cursor.col = newcol;
2609 check_cursor();
2610 } else if (newlnum > 0) { /* line number from caller or old position */
2611 curwin->w_cursor.lnum = newlnum;
2612 check_cursor_lnum();
2613 if (solcol >= 0 && !p_sol) {
2614 /* 'sol' is off: Use last known column. */
2615 curwin->w_cursor.col = solcol;
2616 check_cursor_col();
2617 curwin->w_cursor.coladd = 0;
2618 curwin->w_set_curswant = TRUE;
2619 } else
2620 beginline(BL_SOL | BL_FIX);
2621 } else { /* no line number, go to last line in Ex mode */
2622 if (exmode_active)
2623 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
2624 beginline(BL_WHITE | BL_FIX);
2625 }
2626 }
2627
2628 /* Check if cursors in other windows on the same buffer are still valid */
2629 check_lnums(FALSE);
2630
2631 /*
2632 * Did not read the file, need to show some info about the file.
2633 * Do this after setting the cursor.
2634 */
2635 if (oldbuf
2636 && !auto_buf
2637 ) {
2638 int msg_scroll_save = msg_scroll;
2639
2640 /* Obey the 'O' flag in 'cpoptions': overwrite any previous file
2641 * message. */
2642 if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0)
2643 msg_scroll = FALSE;
2644 if (!msg_scroll) /* wait a bit when overwriting an error msg */
2645 check_for_delay(FALSE);
2646 msg_start();
2647 msg_scroll = msg_scroll_save;
2648 msg_scrolled_ign = TRUE;
2649
2650 if (!shortmess(SHM_FILEINFO)) {
2651 fileinfo(false, true, false);
2652 }
2653
2654 msg_scrolled_ign = FALSE;
2655 }
2656
2657 if (command != NULL)
2658 do_cmdline(command, NULL, NULL, DOCMD_VERBOSE);
2659
2660 if (curbuf->b_kmap_state & KEYMAP_INIT)
2661 (void)keymap_init();
2662
2663 RedrawingDisabled--;
2664 did_inc_redrawing_disabled = false;
2665 if (!skip_redraw) {
2666 n = p_so;
2667 if (topline == 0 && command == NULL)
2668 p_so = 999; // force cursor to be vertically centered in the window
2669 update_topline();
2670 curwin->w_scbind_pos = curwin->w_topline;
2671 p_so = n;
2672 redraw_curbuf_later(NOT_VALID); /* redraw this buffer later */
2673 }
2674
2675 if (p_im)
2676 need_start_insertmode = TRUE;
2677
2678 /* Change directories when the 'acd' option is set. */
2679 do_autochdir();
2680
2681
2682theend:
2683 if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->terminal != NULL) {
2684 terminal_check_size(old_curbuf.br_buf->terminal);
2685 }
2686
2687 if (did_inc_redrawing_disabled) {
2688 RedrawingDisabled--;
2689 }
2690 if (did_set_swapcommand) {
2691 set_vim_var_string(VV_SWAPCOMMAND, NULL, -1);
2692 }
2693 xfree(free_fname);
2694 return retval;
2695}
2696
2697static void delbuf_msg(char_u *name)
2698{
2699 EMSG2(_("E143: Autocommands unexpectedly deleted new buffer %s"),
2700 name == NULL ? (char_u *)"" : name);
2701 xfree(name);
2702 au_new_curbuf.br_buf = NULL;
2703 au_new_curbuf.br_buf_free_count = 0;
2704}
2705
2706static int append_indent = 0; /* autoindent for first line */
2707
2708/*
2709 * ":insert" and ":append", also used by ":change"
2710 */
2711void ex_append(exarg_T *eap)
2712{
2713 char_u *theline;
2714 bool did_undo = false;
2715 linenr_T lnum = eap->line2;
2716 int indent = 0;
2717 char_u *p;
2718 int vcol;
2719 int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
2720
2721 /* the ! flag toggles autoindent */
2722 if (eap->forceit)
2723 curbuf->b_p_ai = !curbuf->b_p_ai;
2724
2725 /* First autoindent comes from the line we start on */
2726 if (eap->cmdidx != CMD_change && curbuf->b_p_ai && lnum > 0)
2727 append_indent = get_indent_lnum(lnum);
2728
2729 if (eap->cmdidx != CMD_append)
2730 --lnum;
2731
2732 // when the buffer is empty need to delete the dummy line
2733 if (empty && lnum == 1)
2734 lnum = 0;
2735
2736 State = INSERT; /* behave like in Insert mode */
2737 if (curbuf->b_p_iminsert == B_IMODE_LMAP)
2738 State |= LANGMAP;
2739
2740 for (;; ) {
2741 msg_scroll = TRUE;
2742 need_wait_return = FALSE;
2743 if (curbuf->b_p_ai) {
2744 if (append_indent >= 0) {
2745 indent = append_indent;
2746 append_indent = -1;
2747 } else if (lnum > 0)
2748 indent = get_indent_lnum(lnum);
2749 }
2750 ex_keep_indent = FALSE;
2751 if (eap->getline == NULL) {
2752 /* No getline() function, use the lines that follow. This ends
2753 * when there is no more. */
2754 if (eap->nextcmd == NULL || *eap->nextcmd == NUL)
2755 break;
2756 p = vim_strchr(eap->nextcmd, NL);
2757 if (p == NULL)
2758 p = eap->nextcmd + STRLEN(eap->nextcmd);
2759 theline = vim_strnsave(eap->nextcmd, (int)(p - eap->nextcmd));
2760 if (*p != NUL)
2761 ++p;
2762 eap->nextcmd = p;
2763 } else {
2764 // Set State to avoid the cursor shape to be set to INSERT mode
2765 // when getline() returns.
2766 int save_State = State;
2767 State = CMDLINE;
2768 theline = eap->getline(
2769 eap->cstack->cs_looplevel > 0 ? -1 :
2770 NUL, eap->cookie, indent);
2771 State = save_State;
2772 }
2773 lines_left = Rows - 1;
2774 if (theline == NULL)
2775 break;
2776
2777 /* Using ^ CTRL-D in getexmodeline() makes us repeat the indent. */
2778 if (ex_keep_indent)
2779 append_indent = indent;
2780
2781 /* Look for the "." after automatic indent. */
2782 vcol = 0;
2783 for (p = theline; indent > vcol; ++p) {
2784 if (*p == ' ')
2785 ++vcol;
2786 else if (*p == TAB)
2787 vcol += 8 - vcol % 8;
2788 else
2789 break;
2790 }
2791 if ((p[0] == '.' && p[1] == NUL)
2792 || (!did_undo && u_save(lnum, lnum + 1 + (empty ? 1 : 0))
2793 == FAIL)) {
2794 xfree(theline);
2795 break;
2796 }
2797
2798 /* don't use autoindent if nothing was typed. */
2799 if (p[0] == NUL)
2800 theline[0] = NUL;
2801
2802 did_undo = true;
2803 ml_append(lnum, theline, (colnr_T)0, false);
2804 appended_lines_mark(lnum + (empty ? 1 : 0), 1L);
2805
2806 xfree(theline);
2807 ++lnum;
2808
2809 if (empty) {
2810 ml_delete(2L, false);
2811 empty = 0;
2812 }
2813 }
2814 State = NORMAL;
2815
2816 if (eap->forceit)
2817 curbuf->b_p_ai = !curbuf->b_p_ai;
2818
2819 /* "start" is set to eap->line2+1 unless that position is invalid (when
2820 * eap->line2 pointed to the end of the buffer and nothing was appended)
2821 * "end" is set to lnum when something has been appended, otherwise
2822 * it is the same than "start" -- Acevedo */
2823 curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ?
2824 eap->line2 + 1 : curbuf->b_ml.ml_line_count;
2825 if (eap->cmdidx != CMD_append)
2826 --curbuf->b_op_start.lnum;
2827 curbuf->b_op_end.lnum = (eap->line2 < lnum)
2828 ? lnum : curbuf->b_op_start.lnum;
2829 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
2830 curwin->w_cursor.lnum = lnum;
2831 check_cursor_lnum();
2832 beginline(BL_SOL | BL_FIX);
2833
2834 need_wait_return = FALSE; /* don't use wait_return() now */
2835 ex_no_reprint = TRUE;
2836}
2837
2838/*
2839 * ":change"
2840 */
2841void ex_change(exarg_T *eap)
2842{
2843 linenr_T lnum;
2844
2845 if (eap->line2 >= eap->line1
2846 && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL)
2847 return;
2848
2849 /* the ! flag toggles autoindent */
2850 if (eap->forceit ? !curbuf->b_p_ai : curbuf->b_p_ai)
2851 append_indent = get_indent_lnum(eap->line1);
2852
2853 for (lnum = eap->line2; lnum >= eap->line1; --lnum) {
2854 if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
2855 break;
2856 ml_delete(eap->line1, false);
2857 }
2858
2859 /* make sure the cursor is not beyond the end of the file now */
2860 check_cursor_lnum();
2861 deleted_lines_mark(eap->line1, (long)(eap->line2 - lnum));
2862
2863 /* ":append" on the line above the deleted lines. */
2864 eap->line2 = eap->line1;
2865 ex_append(eap);
2866}
2867
2868void ex_z(exarg_T *eap)
2869{
2870 char_u *x;
2871 int64_t bigness;
2872 char_u *kind;
2873 int minus = 0;
2874 linenr_T start, end, curs, i;
2875 int j;
2876 linenr_T lnum = eap->line2;
2877
2878 // Vi compatible: ":z!" uses display height, without a count uses
2879 // 'scroll'
2880 if (eap->forceit) {
2881 bigness = curwin->w_height_inner;
2882 } else if (ONE_WINDOW) {
2883 bigness = curwin->w_p_scr * 2;
2884 } else {
2885 bigness = curwin->w_height_inner - 3;
2886 }
2887 if (bigness < 1) {
2888 bigness = 1;
2889 }
2890
2891 x = eap->arg;
2892 kind = x;
2893 if (*kind == '-' || *kind == '+' || *kind == '='
2894 || *kind == '^' || *kind == '.')
2895 ++x;
2896 while (*x == '-' || *x == '+')
2897 ++x;
2898
2899 if (*x != 0) {
2900 if (!ascii_isdigit(*x)) {
2901 EMSG(_("E144: non-numeric argument to :z"));
2902 return;
2903 }
2904 bigness = atol((char *)x);
2905
2906 // bigness could be < 0 if atol(x) overflows.
2907 if (bigness > 2 * curbuf->b_ml.ml_line_count || bigness < 0) {
2908 bigness = 2 * curbuf->b_ml.ml_line_count;
2909 }
2910
2911 p_window = bigness;
2912 if (*kind == '=') {
2913 bigness += 2;
2914 }
2915 }
2916
2917 /* the number of '-' and '+' multiplies the distance */
2918 if (*kind == '-' || *kind == '+')
2919 for (x = kind + 1; *x == *kind; ++x)
2920 ;
2921
2922 switch (*kind) {
2923 case '-':
2924 start = lnum - bigness * (linenr_T)(x - kind) + 1;
2925 end = start + bigness - 1;
2926 curs = end;
2927 break;
2928
2929 case '=':
2930 start = lnum - (bigness + 1) / 2 + 1;
2931 end = lnum + (bigness + 1) / 2 - 1;
2932 curs = lnum;
2933 minus = 1;
2934 break;
2935
2936 case '^':
2937 start = lnum - bigness * 2;
2938 end = lnum - bigness;
2939 curs = lnum - bigness;
2940 break;
2941
2942 case '.':
2943 start = lnum - (bigness + 1) / 2 + 1;
2944 end = lnum + (bigness + 1) / 2 - 1;
2945 curs = end;
2946 break;
2947
2948 default: /* '+' */
2949 start = lnum;
2950 if (*kind == '+')
2951 start += bigness * (linenr_T)(x - kind - 1) + 1;
2952 else if (eap->addr_count == 0)
2953 ++start;
2954 end = start + bigness - 1;
2955 curs = end;
2956 break;
2957 }
2958
2959 if (start < 1)
2960 start = 1;
2961
2962 if (end > curbuf->b_ml.ml_line_count)
2963 end = curbuf->b_ml.ml_line_count;
2964
2965 if (curs > curbuf->b_ml.ml_line_count) {
2966 curs = curbuf->b_ml.ml_line_count;
2967 } else if (curs < 1) {
2968 curs = 1;
2969 }
2970
2971 for (i = start; i <= end; i++) {
2972 if (minus && i == lnum) {
2973 msg_putchar('\n');
2974
2975 for (j = 1; j < Columns; j++)
2976 msg_putchar('-');
2977 }
2978
2979 print_line(i, eap->flags & EXFLAG_NR, eap->flags & EXFLAG_LIST);
2980
2981 if (minus && i == lnum) {
2982 msg_putchar('\n');
2983
2984 for (j = 1; j < Columns; j++)
2985 msg_putchar('-');
2986 }
2987 }
2988
2989 if (curwin->w_cursor.lnum != curs) {
2990 curwin->w_cursor.lnum = curs;
2991 curwin->w_cursor.col = 0;
2992 }
2993 ex_no_reprint = true;
2994}
2995
2996/*
2997 * Check if the restricted flag is set.
2998 * If so, give an error message and return TRUE.
2999 * Otherwise, return FALSE.
3000 */
3001int check_restricted(void)
3002{
3003 if (restricted) {
3004 EMSG(_("E145: Shell commands not allowed in restricted mode"));
3005 return TRUE;
3006 }
3007 return FALSE;
3008}
3009
3010/*
3011 * Check if the secure flag is set (.exrc or .vimrc in current directory).
3012 * If so, give an error message and return TRUE.
3013 * Otherwise, return FALSE.
3014 */
3015int check_secure(void)
3016{
3017 if (secure) {
3018 secure = 2;
3019 EMSG(_(e_curdir));
3020 return TRUE;
3021 }
3022
3023 // In the sandbox more things are not allowed, including the things
3024 // disallowed in secure mode.
3025 if (sandbox != 0) {
3026 EMSG(_(e_sandbox));
3027 return TRUE;
3028 }
3029 return FALSE;
3030}
3031
3032/// Previous substitute replacement string
3033static SubReplacementString old_sub = {NULL, 0, NULL};
3034
3035static int global_need_beginline; // call beginline() after ":g"
3036
3037/// Get old substitute replacement string
3038///
3039/// @param[out] ret_sub Location where old string will be saved.
3040void sub_get_replacement(SubReplacementString *const ret_sub)
3041 FUNC_ATTR_NONNULL_ALL
3042{
3043 *ret_sub = old_sub;
3044}
3045
3046/// Set substitute string and timestamp
3047///
3048/// @warning `sub` must be in allocated memory. It is not copied.
3049///
3050/// @param[in] sub New replacement string.
3051void sub_set_replacement(SubReplacementString sub)
3052{
3053 xfree(old_sub.sub);
3054 if (sub.additional_elements != old_sub.additional_elements) {
3055 tv_list_unref(old_sub.additional_elements);
3056 }
3057 old_sub = sub;
3058}
3059
3060/// Recognize ":%s/\n//" and turn it into a join command, which is much
3061/// more efficient.
3062///
3063/// @param[in] eap Ex arguments
3064/// @param[in] pat Search pattern
3065/// @param[in] sub Replacement string
3066/// @param[in] cmd Command from :s_flags
3067/// @param[in] save Save pattern to options, history
3068///
3069/// @returns true if :substitute can be replaced with a join command
3070static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub,
3071 char_u *cmd, bool save)
3072 FUNC_ATTR_NONNULL_ARG(1, 3, 4)
3073{
3074 // TODO(vim): find a generic solution to make line-joining operations more
3075 // efficient, avoid allocating a string that grows in size.
3076 if (pat != NULL
3077 && strcmp((const char *)pat, "\\n") == 0
3078 && *sub == NUL
3079 && (*cmd == NUL || (cmd[1] == NUL
3080 && (*cmd == 'g'
3081 || *cmd == 'l'
3082 || *cmd == 'p'
3083 || *cmd == '#')))) {
3084 curwin->w_cursor.lnum = eap->line1;
3085 if (*cmd == 'l') {
3086 eap->flags = EXFLAG_LIST;
3087 } else if (*cmd == '#') {
3088 eap->flags = EXFLAG_NR;
3089 } else if (*cmd == 'p') {
3090 eap->flags = EXFLAG_PRINT;
3091 }
3092
3093 // The number of lines joined is the number of lines in the range
3094 linenr_T joined_lines_count = eap->line2 - eap->line1 + 1
3095 // plus one extra line if not at the end of file.
3096 + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0);
3097 if (joined_lines_count > 1) {
3098 do_join(joined_lines_count, FALSE, TRUE, FALSE, true);
3099 sub_nsubs = joined_lines_count - 1;
3100 sub_nlines = 1;
3101 do_sub_msg(false);
3102 ex_may_print(eap);
3103 }
3104
3105 if (save) {
3106 if (!cmdmod.keeppatterns) {
3107 save_re_pat(RE_SUBST, pat, p_magic);
3108 }
3109 add_to_history(HIST_SEARCH, pat, true, NUL);
3110 }
3111
3112 return true;
3113 }
3114
3115 return false;
3116}
3117
3118/// Allocate memory to store the replacement text for :substitute.
3119///
3120/// Slightly more memory that is strictly necessary is allocated to reduce the
3121/// frequency of memory (re)allocation.
3122///
3123/// @param[in,out] new_start pointer to the memory for the replacement text
3124/// @param[in] needed_len amount of memory needed
3125///
3126/// @returns pointer to the end of the allocated memory
3127static char_u *sub_grow_buf(char_u **new_start, int needed_len)
3128 FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_RET
3129{
3130 int new_start_len = 0;
3131 char_u *new_end;
3132 if (*new_start == NULL) {
3133 // Get some space for a temporary buffer to do the
3134 // substitution into (and some extra space to avoid
3135 // too many calls to xmalloc()/free()).
3136 new_start_len = needed_len + 50;
3137 *new_start = xmalloc(new_start_len);
3138 **new_start = NUL;
3139 new_end = *new_start;
3140 } else {
3141 // Check if the temporary buffer is long enough to do the
3142 // substitution into. If not, make it larger (with a bit
3143 // extra to avoid too many calls to xmalloc()/free()).
3144 size_t len = STRLEN(*new_start);
3145 needed_len += len;
3146 if (needed_len > new_start_len) {
3147 new_start_len = needed_len + 50;
3148 *new_start = xrealloc(*new_start, new_start_len);
3149 }
3150 new_end = *new_start + len;
3151 }
3152
3153 return new_end;
3154}
3155
3156/// Parse cmd string for :substitute's {flags} and update subflags accordingly
3157///
3158/// @param[in] cmd command string
3159/// @param[in,out] subflags current flags defined for the :substitute command
3160/// @param[in,out] which_pat pattern type from which to get default search
3161///
3162/// @returns pointer to the end of the flags, which may be the end of the string
3163static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
3164 int *which_pat)
3165 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
3166{
3167 // Find trailing options. When '&' is used, keep old options.
3168 if (*cmd == '&') {
3169 cmd++;
3170 } else {
3171 subflags->do_all = p_gd;
3172 subflags->do_ask = false;
3173 subflags->do_error = true;
3174 subflags->do_print = false;
3175 subflags->do_list = false;
3176 subflags->do_count = false;
3177 subflags->do_number = false;
3178 subflags->do_ic = kSubHonorOptions;
3179 }
3180 while (*cmd) {
3181 // Note that 'g' and 'c' are always inverted.
3182 // 'r' is never inverted.
3183 if (*cmd == 'g') {
3184 subflags->do_all = !subflags->do_all;
3185 } else if (*cmd == 'c') {
3186 subflags->do_ask = !subflags->do_ask;
3187 } else if (*cmd == 'n') {
3188 subflags->do_count = true;
3189 } else if (*cmd == 'e') {
3190 subflags->do_error = !subflags->do_error;
3191 } else if (*cmd == 'r') { // use last used regexp
3192 *which_pat = RE_LAST;
3193 } else if (*cmd == 'p') {
3194 subflags->do_print = true;
3195 } else if (*cmd == '#') {
3196 subflags->do_print = true;
3197 subflags->do_number = true;
3198 } else if (*cmd == 'l') {
3199 subflags->do_print = true;
3200 subflags->do_list = true;
3201 } else if (*cmd == 'i') { // ignore case
3202 subflags->do_ic = kSubIgnoreCase;
3203 } else if (*cmd == 'I') { // don't ignore case
3204 subflags->do_ic = kSubMatchCase;
3205 } else {
3206 break;
3207 }
3208 cmd++;
3209 }
3210 if (subflags->do_count) {
3211 subflags->do_ask = false;
3212 }
3213
3214 return cmd;
3215}
3216
3217/// Perform a substitution from line eap->line1 to line eap->line2 using the
3218/// command pointed to by eap->arg which should be of the form:
3219///
3220/// /pattern/substitution/{flags}
3221///
3222/// The usual escapes are supported as described in the regexp docs.
3223///
3224/// @param do_buf_event If `true`, send buffer updates.
3225/// @return buffer used for 'inccommand' preview
3226static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
3227 bool do_buf_event)
3228{
3229 long i = 0;
3230 regmmatch_T regmatch;
3231 static subflags_T subflags = {
3232 .do_all = false,
3233 .do_ask = false,
3234 .do_count = false,
3235 .do_error = true,
3236 .do_print = false,
3237 .do_list = false,
3238 .do_number = false,
3239 .do_ic = kSubHonorOptions
3240 };
3241 char_u *pat = NULL, *sub = NULL; // init for GCC
3242 int delimiter;
3243 bool has_second_delim = false;
3244 int sublen;
3245 int got_quit = false;
3246 int got_match = false;
3247 int which_pat;
3248 char_u *cmd = eap->arg;
3249 linenr_T first_line = 0; // first changed line
3250 linenr_T last_line= 0; // below last changed line AFTER the change
3251 linenr_T old_line_count = curbuf->b_ml.ml_line_count;
3252 char_u *sub_firstline; // allocated copy of first sub line
3253 bool endcolumn = false; // cursor in last column when done
3254 PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 };
3255 static int pre_src_id = 0; // Source id for the preview highlight
3256 static int pre_hl_id = 0;
3257 buf_T *orig_buf = curbuf; // save to reset highlighting
3258 pos_T old_cursor = curwin->w_cursor;
3259 int start_nsubs;
3260 int save_ma = 0;
3261 int save_b_changed = curbuf->b_changed;
3262 bool preview = (State & CMDPREVIEW);
3263
3264 if (!global_busy) {
3265 sub_nsubs = 0;
3266 sub_nlines = 0;
3267 }
3268 start_nsubs = sub_nsubs;
3269
3270 if (eap->cmdidx == CMD_tilde)
3271 which_pat = RE_LAST; /* use last used regexp */
3272 else
3273 which_pat = RE_SUBST; /* use last substitute regexp */
3274
3275 /* new pattern and substitution */
3276 if (eap->cmd[0] == 's' && *cmd != NUL && !ascii_iswhite(*cmd)
3277 && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) {
3278 /* don't accept alphanumeric for separator */
3279 if (isalpha(*cmd)) {
3280 EMSG(_("E146: Regular expressions can't be delimited by letters"));
3281 return NULL;
3282 }
3283 /*
3284 * undocumented vi feature:
3285 * "\/sub/" and "\?sub?" use last used search pattern (almost like
3286 * //sub/r). "\&sub&" use last substitute pattern (like //sub/).
3287 */
3288 if (*cmd == '\\') {
3289 ++cmd;
3290 if (vim_strchr((char_u *)"/?&", *cmd) == NULL) {
3291 EMSG(_(e_backslash));
3292 return NULL;
3293 }
3294 if (*cmd != '&') {
3295 which_pat = RE_SEARCH; // use last '/' pattern
3296 }
3297 pat = (char_u *)""; // empty search pattern
3298 delimiter = *cmd++; // remember delimiter character
3299 has_second_delim = true;
3300 } else { // find the end of the regexp
3301 which_pat = RE_LAST; // use last used regexp
3302 delimiter = *cmd++; // remember delimiter character
3303 pat = cmd; // remember start of search pat
3304 cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg);
3305 if (cmd[0] == delimiter) { // end delimiter found
3306 *cmd++ = NUL; // replace it with a NUL
3307 has_second_delim = true;
3308 }
3309 }
3310
3311 /*
3312 * Small incompatibility: vi sees '\n' as end of the command, but in
3313 * Vim we want to use '\n' to find/substitute a NUL.
3314 */
3315 sub = cmd; /* remember the start of the substitution */
3316
3317 while (cmd[0]) {
3318 if (cmd[0] == delimiter) { /* end delimiter found */
3319 *cmd++ = NUL; /* replace it with a NUL */
3320 break;
3321 }
3322 if (cmd[0] == '\\' && cmd[1] != 0) { // skip escaped characters
3323 cmd++;
3324 }
3325 MB_PTR_ADV(cmd);
3326 }
3327
3328 if (!eap->skip && !preview) {
3329 sub_set_replacement((SubReplacementString) {
3330 .sub = xstrdup((char *) sub),
3331 .timestamp = os_time(),
3332 .additional_elements = NULL,
3333 });
3334 }
3335 } else if (!eap->skip) { /* use previous pattern and substitution */
3336 if (old_sub.sub == NULL) { /* there is no previous command */
3337 EMSG(_(e_nopresub));
3338 return NULL;
3339 }
3340 pat = NULL; /* search_regcomp() will use previous pattern */
3341 sub = (char_u *) old_sub.sub;
3342
3343 /* Vi compatibility quirk: repeating with ":s" keeps the cursor in the
3344 * last column after using "$". */
3345 endcolumn = (curwin->w_curswant == MAXCOL);
3346 }
3347
3348 if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !preview)) {
3349 return NULL;
3350 }
3351
3352 cmd = sub_parse_flags(cmd, &subflags, &which_pat);
3353
3354 bool save_do_all = subflags.do_all; // remember user specified 'g' flag
3355 bool save_do_ask = subflags.do_ask; // remember user specified 'c' flag
3356
3357 // check for a trailing count
3358 cmd = skipwhite(cmd);
3359 if (ascii_isdigit(*cmd)) {
3360 i = getdigits_long(&cmd, true, 0);
3361 if (i <= 0 && !eap->skip && subflags.do_error) {
3362 EMSG(_(e_zerocount));
3363 return NULL;
3364 }
3365 eap->line1 = eap->line2;
3366 eap->line2 += i - 1;
3367 if (eap->line2 > curbuf->b_ml.ml_line_count)
3368 eap->line2 = curbuf->b_ml.ml_line_count;
3369 }
3370
3371 /*
3372 * check for trailing command or garbage
3373 */
3374 cmd = skipwhite(cmd);
3375 if (*cmd && *cmd != '"') { /* if not end-of-line or comment */
3376 eap->nextcmd = check_nextcmd(cmd);
3377 if (eap->nextcmd == NULL) {
3378 EMSG(_(e_trailing));
3379 return NULL;
3380 }
3381 }
3382
3383 if (eap->skip) { // not executing commands, only parsing
3384 return NULL;
3385 }
3386
3387 if (!subflags.do_count && !MODIFIABLE(curbuf)) {
3388 // Substitution is not allowed in non-'modifiable' buffer
3389 EMSG(_(e_modifiable));
3390 return NULL;
3391 }
3392
3393 if (search_regcomp(pat, RE_SUBST, which_pat, (preview ? 0 : SEARCH_HIS),
3394 &regmatch) == FAIL) {
3395 if (subflags.do_error) {
3396 EMSG(_(e_invcmd));
3397 }
3398 return NULL;
3399 }
3400
3401 // the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase'
3402 if (subflags.do_ic == kSubIgnoreCase) {
3403 regmatch.rmm_ic = true;
3404 } else if (subflags.do_ic == kSubMatchCase) {
3405 regmatch.rmm_ic = false;
3406 }
3407
3408 sub_firstline = NULL;
3409
3410 /*
3411 * ~ in the substitute pattern is replaced with the old pattern.
3412 * We do it here once to avoid it to be replaced over and over again.
3413 * But don't do it when it starts with "\=", then it's an expression.
3414 */
3415 if (!(sub[0] == '\\' && sub[1] == '='))
3416 sub = regtilde(sub, p_magic);
3417
3418 // Check for a match on each line.
3419 // If preview: limit to max('cmdwinheight', viewport).
3420 linenr_T line2 = eap->line2;
3421 for (linenr_T lnum = eap->line1;
3422 lnum <= line2 && !got_quit && !aborting()
3423 && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh
3424 || lnum <= curwin->w_botline);
3425 lnum++) {
3426 long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
3427 (colnr_T)0, NULL, NULL);
3428 if (nmatch) {
3429 colnr_T copycol;
3430 colnr_T matchcol;
3431 colnr_T prev_matchcol = MAXCOL;
3432 char_u *new_end, *new_start = NULL;
3433 char_u *p1;
3434 int did_sub = FALSE;
3435 int lastone;
3436 long nmatch_tl = 0; // nr of lines matched below lnum
3437 int do_again; // do it again after joining lines
3438 int skip_match = false;
3439 linenr_T sub_firstlnum; // nr of first sub line
3440
3441 /*
3442 * The new text is build up step by step, to avoid too much
3443 * copying. There are these pieces:
3444 * sub_firstline The old text, unmodified.
3445 * copycol Column in the old text where we started
3446 * looking for a match; from here old text still
3447 * needs to be copied to the new text.
3448 * matchcol Column number of the old text where to look
3449 * for the next match. It's just after the
3450 * previous match or one further.
3451 * prev_matchcol Column just after the previous match (if any).
3452 * Mostly equal to matchcol, except for the first
3453 * match and after skipping an empty match.
3454 * regmatch.*pos Where the pattern matched in the old text.
3455 * new_start The new text, all that has been produced so
3456 * far.
3457 * new_end The new text, where to append new text.
3458 *
3459 * lnum The line number where we found the start of
3460 * the match. Can be below the line we searched
3461 * when there is a \n before a \zs in the
3462 * pattern.
3463 * sub_firstlnum The line number in the buffer where to look
3464 * for a match. Can be different from "lnum"
3465 * when the pattern or substitute string contains
3466 * line breaks.
3467 *
3468 * Special situations:
3469 * - When the substitute string contains a line break, the part up
3470 * to the line break is inserted in the text, but the copy of
3471 * the original line is kept. "sub_firstlnum" is adjusted for
3472 * the inserted lines.
3473 * - When the matched pattern contains a line break, the old line
3474 * is taken from the line at the end of the pattern. The lines
3475 * in the match are deleted later, "sub_firstlnum" is adjusted
3476 * accordingly.
3477 *
3478 * The new text is built up in new_start[]. It has some extra
3479 * room to avoid using xmalloc()/free() too often.
3480 *
3481 * Make a copy of the old line, so it won't be taken away when
3482 * updating the screen or handling a multi-line match. The "old_"
3483 * pointers point into this copy.
3484 */
3485 sub_firstlnum = lnum;
3486 copycol = 0;
3487 matchcol = 0;
3488
3489 /* At first match, remember current cursor position. */
3490 if (!got_match) {
3491 setpcmark();
3492 got_match = TRUE;
3493 }
3494
3495 /*
3496 * Loop until nothing more to replace in this line.
3497 * 1. Handle match with empty string.
3498 * 2. If subflags.do_ask is set, ask for confirmation.
3499 * 3. substitute the string.
3500 * 4. if subflags.do_all is set, find next match
3501 * 5. break if there isn't another match in this line
3502 */
3503 for (;; ) {
3504 SubResult current_match = {
3505 .start = { 0, 0 },
3506 .end = { 0, 0 },
3507 .pre_match = 0,
3508 };
3509 // lnum is where the match start, but maybe not the pattern match,
3510 // since we can have \n before \zs in the pattern
3511
3512 // Advance "lnum" to the line where the match starts. The
3513 // match does not start in the first line when there is a line
3514 // break before \zs.
3515 if (regmatch.startpos[0].lnum > 0) {
3516 current_match.pre_match = lnum;
3517 lnum += regmatch.startpos[0].lnum;
3518 sub_firstlnum += regmatch.startpos[0].lnum;
3519 nmatch -= regmatch.startpos[0].lnum;
3520 XFREE_CLEAR(sub_firstline);
3521 }
3522
3523 // Now we're at the line where the pattern match starts
3524 // Note: If not first match on a line, column can't be known here
3525 current_match.start.lnum = sub_firstlnum;
3526
3527 if (sub_firstline == NULL) {
3528 sub_firstline = vim_strsave(ml_get(sub_firstlnum));
3529 }
3530
3531 /* Save the line number of the last change for the final
3532 * cursor position (just like Vi). */
3533 curwin->w_cursor.lnum = lnum;
3534 do_again = FALSE;
3535
3536 /*
3537 * 1. Match empty string does not count, except for first
3538 * match. This reproduces the strange vi behaviour.
3539 * This also catches endless loops.
3540 */
3541 if (matchcol == prev_matchcol
3542 && regmatch.endpos[0].lnum == 0
3543 && matchcol == regmatch.endpos[0].col) {
3544 if (sub_firstline[matchcol] == NUL)
3545 /* We already were at the end of the line. Don't look
3546 * for a match in this line again. */
3547 skip_match = TRUE;
3548 else {
3549 /* search for a match at next column */
3550 if (has_mbyte)
3551 matchcol += mb_ptr2len(sub_firstline + matchcol);
3552 else
3553 ++matchcol;
3554 }
3555 // match will be pushed to preview_lines, bring it into a proper state
3556 current_match.start.col = matchcol;
3557 current_match.end.lnum = sub_firstlnum;
3558 current_match.end.col = matchcol;
3559 goto skip;
3560 }
3561
3562 /* Normally we continue searching for a match just after the
3563 * previous match. */
3564 matchcol = regmatch.endpos[0].col;
3565 prev_matchcol = matchcol;
3566
3567 // 2. If subflags.do_count is set only increase the counter.
3568 // If do_ask is set, ask for confirmation.
3569 if (subflags.do_count) {
3570 // For a multi-line match, put matchcol at the NUL at
3571 // the end of the line and set nmatch to one, so that
3572 // we continue looking for a match on the next line.
3573 // Avoids that ":s/\nB\@=//gc" get stuck.
3574 if (nmatch > 1) {
3575 matchcol = (colnr_T)STRLEN(sub_firstline);
3576 nmatch = 1;
3577 skip_match = TRUE;
3578 }
3579 sub_nsubs++;
3580 did_sub = TRUE;
3581 /* Skip the substitution, unless an expression is used,
3582 * then it is evaluated in the sandbox. */
3583 if (!(sub[0] == '\\' && sub[1] == '='))
3584 goto skip;
3585 }
3586
3587 if (subflags.do_ask && !preview) {
3588 int typed = 0;
3589
3590 /* change State to CONFIRM, so that the mouse works
3591 * properly */
3592 int save_State = State;
3593 State = CONFIRM;
3594 setmouse(); /* disable mouse in xterm */
3595 curwin->w_cursor.col = regmatch.startpos[0].col;
3596
3597 if (curwin->w_p_crb) {
3598 do_check_cursorbind();
3599 }
3600
3601 /* When 'cpoptions' contains "u" don't sync undo when
3602 * asking for confirmation. */
3603 if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
3604 ++no_u_sync;
3605
3606 /*
3607 * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
3608 */
3609 while (subflags.do_ask) {
3610 if (exmode_active) {
3611 char_u *resp;
3612 colnr_T sc, ec;
3613
3614 print_line_no_prefix(lnum, subflags.do_number, subflags.do_list);
3615
3616 getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
3617 curwin->w_cursor.col = regmatch.endpos[0].col - 1;
3618 if (curwin->w_cursor.col < 0) {
3619 curwin->w_cursor.col = 0;
3620 }
3621 getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec);
3622 if (subflags.do_number || curwin->w_p_nu) {
3623 int numw = number_width(curwin) + 1;
3624 sc += numw;
3625 ec += numw;
3626 }
3627 msg_start();
3628 for (i = 0; i < (long)sc; ++i)
3629 msg_putchar(' ');
3630 for (; i <= (long)ec; ++i)
3631 msg_putchar('^');
3632
3633 resp = getexmodeline('?', NULL, 0);
3634 if (resp != NULL) {
3635 typed = *resp;
3636 xfree(resp);
3637 }
3638 } else {
3639 char_u *orig_line = NULL;
3640 int len_change = 0;
3641 int save_p_fen = curwin->w_p_fen;
3642
3643 curwin->w_p_fen = FALSE;
3644 /* Invert the matched string.
3645 * Remove the inversion afterwards. */
3646 int temp = RedrawingDisabled;
3647 RedrawingDisabled = 0;
3648
3649 if (new_start != NULL) {
3650 /* There already was a substitution, we would
3651 * like to show this to the user. We cannot
3652 * really update the line, it would change
3653 * what matches. Temporarily replace the line
3654 * and change it back afterwards. */
3655 orig_line = vim_strsave(ml_get(lnum));
3656 char_u *new_line = concat_str(new_start, sub_firstline + copycol);
3657
3658 // Position the cursor relative to the end of the line, the
3659 // previous substitute may have inserted or deleted characters
3660 // before the cursor.
3661 len_change = (int)STRLEN(new_line) - (int)STRLEN(orig_line);
3662 curwin->w_cursor.col += len_change;
3663 ml_replace(lnum, new_line, false);
3664 }
3665
3666 search_match_lines = regmatch.endpos[0].lnum
3667 - regmatch.startpos[0].lnum;
3668 search_match_endcol = regmatch.endpos[0].col
3669 + len_change;
3670 highlight_match = TRUE;
3671
3672 update_topline();
3673 validate_cursor();
3674 update_screen(SOME_VALID);
3675 highlight_match = FALSE;
3676 redraw_later(SOME_VALID);
3677
3678 curwin->w_p_fen = save_p_fen;
3679 if (msg_row == Rows - 1)
3680 msg_didout = FALSE; /* avoid a scroll-up */
3681 msg_starthere();
3682 i = msg_scroll;
3683 msg_scroll = 0; /* truncate msg when
3684 needed */
3685 msg_no_more = true;
3686 msg_ext_set_kind("confirm_sub");
3687 smsg_attr(HL_ATTR(HLF_R), // Same highlight as wait_return().
3688 _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
3689 msg_no_more = FALSE;
3690 msg_scroll = i;
3691 showruler(TRUE);
3692 ui_cursor_goto(msg_row, msg_col);
3693 RedrawingDisabled = temp;
3694
3695 no_mapping++; // don't map this key
3696 typed = plain_vgetc();
3697 no_mapping--;
3698
3699 /* clear the question */
3700 msg_didout = FALSE; /* don't scroll up */
3701 msg_col = 0;
3702 gotocmdline(TRUE);
3703
3704 // restore the line
3705 if (orig_line != NULL) {
3706 ml_replace(lnum, orig_line, false);
3707 }
3708 }
3709
3710 need_wait_return = false; // no hit-return prompt
3711 if (typed == 'q' || typed == ESC || typed == Ctrl_C) {
3712 got_quit = true;
3713 break;
3714 }
3715 if (typed == 'n')
3716 break;
3717 if (typed == 'y')
3718 break;
3719 if (typed == 'l') {
3720 // last: replace and then stop
3721 subflags.do_all = false;
3722 line2 = lnum;
3723 break;
3724 }
3725 if (typed == 'a') {
3726 subflags.do_ask = false;
3727 break;
3728 }
3729 if (typed == Ctrl_E)
3730 scrollup_clamp();
3731 else if (typed == Ctrl_Y)
3732 scrolldown_clamp();
3733 }
3734 State = save_State;
3735 setmouse();
3736 if (vim_strchr(p_cpo, CPO_UNDO) != NULL)
3737 --no_u_sync;
3738
3739 if (typed == 'n') {
3740 /* For a multi-line match, put matchcol at the NUL at
3741 * the end of the line and set nmatch to one, so that
3742 * we continue looking for a match on the next line.
3743 * Avoids that ":%s/\nB\@=//gc" and ":%s/\n/,\r/gc"
3744 * get stuck when pressing 'n'. */
3745 if (nmatch > 1) {
3746 matchcol = (colnr_T)STRLEN(sub_firstline);
3747 skip_match = TRUE;
3748 }
3749 goto skip;
3750 }
3751 if (got_quit)
3752 goto skip;
3753 }
3754
3755 /* Move the cursor to the start of the match, so that we can
3756 * use "\=col("."). */
3757 curwin->w_cursor.col = regmatch.startpos[0].col;
3758
3759 // When the match included the "$" of the last line it may
3760 // go beyond the last line of the buffer.
3761 if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) {
3762 nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1;
3763 current_match.end.lnum = sub_firstlnum + nmatch;
3764 skip_match = true;
3765 }
3766
3767#define ADJUST_SUB_FIRSTLNUM() \
3768 do { \
3769 /* For a multi-line match, make a copy of the last matched */ \
3770 /* line and continue in that one. */ \
3771 if (nmatch > 1) { \
3772 sub_firstlnum += nmatch - 1; \
3773 xfree(sub_firstline); \
3774 sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \
3775 /* When going beyond the last line, stop substituting. */ \
3776 if (sub_firstlnum <= line2) { \
3777 do_again = true; \
3778 } else { \
3779 subflags.do_all = false; \
3780 } \
3781 } \
3782 if (skip_match) { \
3783 /* Already hit end of the buffer, sub_firstlnum is one */ \
3784 /* less than what it ought to be. */ \
3785 xfree(sub_firstline); \
3786 sub_firstline = vim_strsave((char_u *)""); \
3787 copycol = 0; \
3788 } \
3789 } while (0)
3790
3791 // Save the line numbers for the preview buffer
3792 // NOTE: If the pattern matches a final newline, the next line will
3793 // be shown also, but should not be highlighted. Intentional for now.
3794 if (preview && !has_second_delim) {
3795 current_match.start.col = regmatch.startpos[0].col;
3796 if (current_match.end.lnum == 0) {
3797 current_match.end.lnum = sub_firstlnum + nmatch - 1;
3798 }
3799 current_match.end.col = regmatch.endpos[0].col;
3800
3801 ADJUST_SUB_FIRSTLNUM();
3802 lnum += nmatch - 1;
3803
3804 goto skip;
3805 }
3806
3807 // 3. Substitute the string. During 'inccommand' preview only do this if
3808 // there is a replace pattern.
3809 if (!preview || has_second_delim) {
3810 save_ma = curbuf->b_p_ma;
3811 if (subflags.do_count) {
3812 // prevent accidentally changing the buffer by a function
3813 curbuf->b_p_ma = false;
3814 sandbox++;
3815 }
3816 // Save flags for recursion. They can change for e.g.
3817 // :s/^/\=execute("s#^##gn")
3818 subflags_T subflags_save = subflags;
3819 // get length of substitution part
3820 sublen = vim_regsub_multi(&regmatch,
3821 sub_firstlnum - regmatch.startpos[0].lnum,
3822 sub, sub_firstline, false, p_magic, true);
3823 // If getting the substitute string caused an error, don't do
3824 // the replacement.
3825 // Don't keep flags set by a recursive call
3826 subflags = subflags_save;
3827 if (aborting() || subflags.do_count) {
3828 curbuf->b_p_ma = save_ma;
3829 if (sandbox > 0) {
3830 sandbox--;
3831 }
3832 goto skip;
3833 }
3834
3835 // Need room for:
3836 // - result so far in new_start (not for first sub in line)
3837 // - original text up to match
3838 // - length of substituted part
3839 // - original text after match
3840 if (nmatch == 1) {
3841 p1 = sub_firstline;
3842 } else {
3843 p1 = ml_get(sub_firstlnum + nmatch - 1);
3844 nmatch_tl += nmatch - 1;
3845 }
3846 size_t copy_len = regmatch.startpos[0].col - copycol;
3847 new_end = sub_grow_buf(&new_start,
3848 (STRLEN(p1) - regmatch.endpos[0].col)
3849 + copy_len + sublen + 1);
3850
3851 // copy the text up to the part that matched
3852 memmove(new_end, sub_firstline + copycol, (size_t)copy_len);
3853 new_end += copy_len;
3854
3855 // Finally, at this point we can know where the match actually will
3856 // start in the new text
3857 current_match.start.col = new_end - new_start;
3858
3859 (void)vim_regsub_multi(&regmatch,
3860 sub_firstlnum - regmatch.startpos[0].lnum,
3861 sub, new_end, true, p_magic, true);
3862 sub_nsubs++;
3863 did_sub = true;
3864
3865 // Move the cursor to the start of the line, to avoid that it
3866 // is beyond the end of the line after the substitution.
3867 curwin->w_cursor.col = 0;
3868
3869 // Remember next character to be copied.
3870 copycol = regmatch.endpos[0].col;
3871
3872 ADJUST_SUB_FIRSTLNUM();
3873
3874 // Now the trick is to replace CTRL-M chars with a real line
3875 // break. This would make it impossible to insert a CTRL-M in
3876 // the text. The line break can be avoided by preceding the
3877 // CTRL-M with a backslash. To be able to insert a backslash,
3878 // they must be doubled in the string and are halved here.
3879 // That is Vi compatible.
3880 for (p1 = new_end; *p1; p1++) {
3881 if (p1[0] == '\\' && p1[1] != NUL) { // remove backslash
3882 STRMOVE(p1, p1 + 1);
3883 } else if (*p1 == CAR) {
3884 if (u_inssub(lnum) == OK) { // prepare for undo
3885 *p1 = NUL; // truncate up to the CR
3886 ml_append(lnum - 1, new_start,
3887 (colnr_T)(p1 - new_start + 1), false);
3888 mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false);
3889 if (subflags.do_ask) {
3890 appended_lines(lnum - 1, 1L);
3891 } else {
3892 if (first_line == 0) {
3893 first_line = lnum;
3894 }
3895 last_line = lnum + 1;
3896 }
3897 // All line numbers increase.
3898 sub_firstlnum++;
3899 lnum++;
3900 line2++;
3901 // move the cursor to the new line, like Vi
3902 curwin->w_cursor.lnum++;
3903 // copy the rest
3904 STRMOVE(new_start, p1 + 1);
3905 p1 = new_start - 1;
3906 }
3907 } else if (has_mbyte) {
3908 p1 += (*mb_ptr2len)(p1) - 1;
3909 }
3910 }
3911 current_match.end.col = STRLEN(new_start);
3912 current_match.end.lnum = lnum;
3913 }
3914
3915 // 4. If subflags.do_all is set, find next match.
3916 // Prevent endless loop with patterns that match empty
3917 // strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.
3918 // But ":s/\n/#/" is OK.
3919skip:
3920 /* We already know that we did the last subst when we are at
3921 * the end of the line, except that a pattern like
3922 * "bar\|\nfoo" may match at the NUL. "lnum" can be below
3923 * "line2" when there is a \zs in the pattern after a line
3924 * break. */
3925 lastone = (skip_match
3926 || got_int
3927 || got_quit
3928 || lnum > line2
3929 || !(subflags.do_all || do_again)
3930 || (sub_firstline[matchcol] == NUL && nmatch <= 1
3931 && !re_multiline(regmatch.regprog)));
3932 nmatch = -1;
3933
3934 /*
3935 * Replace the line in the buffer when needed. This is
3936 * skipped when there are more matches.
3937 * The check for nmatch_tl is needed for when multi-line
3938 * matching must replace the lines before trying to do another
3939 * match, otherwise "\@<=" won't work.
3940 * When the match starts below where we start searching also
3941 * need to replace the line first (using \zs after \n).
3942 */
3943 if (lastone
3944 || nmatch_tl > 0
3945 || (nmatch = vim_regexec_multi(&regmatch, curwin,
3946 curbuf, sub_firstlnum,
3947 matchcol, NULL, NULL)) == 0
3948 || regmatch.startpos[0].lnum > 0) {
3949 if (new_start != NULL) {
3950 /*
3951 * Copy the rest of the line, that didn't match.
3952 * "matchcol" has to be adjusted, we use the end of
3953 * the line as reference, because the substitute may
3954 * have changed the number of characters. Same for
3955 * "prev_matchcol".
3956 */
3957 STRCAT(new_start, sub_firstline + copycol);
3958 matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
3959 prev_matchcol = (colnr_T)STRLEN(sub_firstline)
3960 - prev_matchcol;
3961
3962 if (u_savesub(lnum) != OK) {
3963 break;
3964 }
3965 ml_replace(lnum, new_start, true);
3966
3967 if (nmatch_tl > 0) {
3968 /*
3969 * Matched lines have now been substituted and are
3970 * useless, delete them. The part after the match
3971 * has been appended to new_start, we don't need
3972 * it in the buffer.
3973 */
3974 ++lnum;
3975 if (u_savedel(lnum, nmatch_tl) != OK)
3976 break;
3977 for (i = 0; i < nmatch_tl; i++) {
3978 ml_delete(lnum, false);
3979 }
3980 mark_adjust(lnum, lnum + nmatch_tl - 1,
3981 (long)MAXLNUM, -nmatch_tl, false);
3982 if (subflags.do_ask) {
3983 deleted_lines(lnum, nmatch_tl);
3984 }
3985 lnum--;
3986 line2 -= nmatch_tl; // nr of lines decreases
3987 nmatch_tl = 0;
3988 }
3989
3990 /* When asking, undo is saved each time, must also set
3991 * changed flag each time. */
3992 if (subflags.do_ask) {
3993 changed_bytes(lnum, 0);
3994 } else {
3995 if (first_line == 0) {
3996 first_line = lnum;
3997 }
3998 last_line = lnum + 1;
3999 }
4000
4001 sub_firstlnum = lnum;
4002 xfree(sub_firstline); /* free the temp buffer */
4003 sub_firstline = new_start;
4004 new_start = NULL;
4005 matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
4006 prev_matchcol = (colnr_T)STRLEN(sub_firstline)
4007 - prev_matchcol;
4008 copycol = 0;
4009 }
4010 if (nmatch == -1 && !lastone)
4011 nmatch = vim_regexec_multi(&regmatch, curwin, curbuf,
4012 sub_firstlnum, matchcol, NULL, NULL);
4013
4014 /*
4015 * 5. break if there isn't another match in this line
4016 */
4017 if (nmatch <= 0) {
4018 /* If the match found didn't start where we were
4019 * searching, do the next search in the line where we
4020 * found the match. */
4021 if (nmatch == -1)
4022 lnum -= regmatch.startpos[0].lnum;
4023
4024#define PUSH_PREVIEW_LINES() \
4025 do { \
4026 linenr_T match_lines = current_match.end.lnum \
4027 - current_match.start.lnum +1; \
4028 if (preview_lines.subresults.size > 0) { \
4029 linenr_T last = kv_last(preview_lines.subresults).end.lnum; \
4030 if (last == current_match.start.lnum) { \
4031 preview_lines.lines_needed += match_lines - 1; \
4032 } \
4033 } else { \
4034 preview_lines.lines_needed += match_lines; \
4035 } \
4036 kv_push(preview_lines.subresults, current_match); \
4037 } while (0)
4038
4039 // Push the match to preview_lines.
4040 PUSH_PREVIEW_LINES();
4041
4042 break;
4043 }
4044 }
4045 // Push the match to preview_lines.
4046 PUSH_PREVIEW_LINES();
4047
4048 line_breakcheck();
4049 }
4050
4051 if (did_sub) {
4052 sub_nlines++;
4053 }
4054 xfree(new_start); // for when substitute was cancelled
4055 XFREE_CLEAR(sub_firstline); // free the copy of the original line
4056 }
4057
4058 line_breakcheck();
4059
4060 if (profile_passed_limit(timeout)) {
4061 got_quit = true;
4062 }
4063 }
4064
4065 if (first_line != 0) {
4066 /* Need to subtract the number of added lines from "last_line" to get
4067 * the line number before the change (same as adding the number of
4068 * deleted lines). */
4069 i = curbuf->b_ml.ml_line_count - old_line_count;
4070 changed_lines(first_line, 0, last_line - i, i, false);
4071
4072 int64_t num_added = last_line - first_line;
4073 int64_t num_removed = num_added - i;
4074 buf_updates_send_changes(curbuf, first_line, num_added, num_removed,
4075 do_buf_event);
4076 }
4077
4078 xfree(sub_firstline); /* may have to free allocated copy of the line */
4079
4080 // ":s/pat//n" doesn't move the cursor
4081 if (subflags.do_count) {
4082 curwin->w_cursor = old_cursor;
4083 }
4084
4085 if (sub_nsubs > start_nsubs) {
4086 /* Set the '[ and '] marks. */
4087 curbuf->b_op_start.lnum = eap->line1;
4088 curbuf->b_op_end.lnum = line2;
4089 curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
4090
4091 if (!global_busy) {
4092 // when interactive leave cursor on the match
4093 if (!subflags.do_ask) {
4094 if (endcolumn) {
4095 coladvance((colnr_T)MAXCOL);
4096 } else {
4097 beginline(BL_WHITE | BL_FIX);
4098 }
4099 }
4100 if (!preview && !do_sub_msg(subflags.do_count) && subflags.do_ask) {
4101 MSG("");
4102 }
4103 } else {
4104 global_need_beginline = true;
4105 }
4106 if (subflags.do_print) {
4107 print_line(curwin->w_cursor.lnum, subflags.do_number, subflags.do_list);
4108 }
4109 } else if (!global_busy) {
4110 if (got_int) {
4111 // interrupted
4112 EMSG(_(e_interr));
4113 } else if (got_match) {
4114 // did find something but nothing substituted
4115 MSG("");
4116 } else if (subflags.do_error) {
4117 // nothing found
4118 EMSG2(_(e_patnotf2), get_search_pat());
4119 }
4120 }
4121
4122 if (subflags.do_ask && hasAnyFolding(curwin)) {
4123 // Cursor position may require updating
4124 changed_window_setting();
4125 }
4126
4127 vim_regfree(regmatch.regprog);
4128
4129 // Restore the flag values, they can be used for ":&&".
4130 subflags.do_all = save_do_all;
4131 subflags.do_ask = save_do_ask;
4132
4133 // Show 'inccommand' preview if there are matched lines.
4134 buf_T *preview_buf = NULL;
4135 size_t subsize = preview_lines.subresults.size;
4136 if (preview && !aborting()) {
4137 if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
4138 set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE,
4139 SID_NONE);
4140 } else if (*p_icm != NUL && pat != NULL) {
4141 if (pre_src_id == 0) {
4142 // Get a unique new src_id, saved in a static
4143 pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0);
4144 }
4145 if (pre_hl_id == 0) {
4146 pre_hl_id = syn_check_group((char_u *)S_LEN("Substitute"));
4147 }
4148 curbuf->b_changed = save_b_changed; // preserve 'modified' during preview
4149 preview_buf = show_sub(eap, old_cursor, &preview_lines,
4150 pre_hl_id, pre_src_id);
4151 if (subsize > 0) {
4152 bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1,
4153 kv_last(preview_lines.subresults).end.lnum);
4154 }
4155 }
4156 }
4157
4158 kv_destroy(preview_lines.subresults);
4159
4160 return preview_buf;
4161#undef ADJUST_SUB_FIRSTLNUM
4162#undef PUSH_PREVIEW_LINES
4163} // NOLINT(readability/fn_size)
4164
4165/*
4166 * Give message for number of substitutions.
4167 * Can also be used after a ":global" command.
4168 * Return TRUE if a message was given.
4169 */
4170bool
4171do_sub_msg (
4172 bool count_only /* used 'n' flag for ":s" */
4173)
4174{
4175 /*
4176 * Only report substitutions when:
4177 * - more than 'report' substitutions
4178 * - command was typed by user, or number of changed lines > 'report'
4179 * - giving messages is not disabled by 'lazyredraw'
4180 */
4181 if (((sub_nsubs > p_report && (KeyTyped || sub_nlines > 1 || p_report < 1))
4182 || count_only)
4183 && messaging()) {
4184 if (got_int)
4185 STRCPY(msg_buf, _("(Interrupted) "));
4186 else
4187 *msg_buf = NUL;
4188 if (sub_nsubs == 1)
4189 vim_snprintf_add((char *)msg_buf, sizeof(msg_buf),
4190 "%s", count_only ? _("1 match") : _("1 substitution"));
4191 else
4192 vim_snprintf_add((char *)msg_buf, sizeof(msg_buf),
4193 count_only ? _("%" PRId64 " matches")
4194 : _("%" PRId64 " substitutions"),
4195 (int64_t)sub_nsubs);
4196 if (sub_nlines == 1)
4197 vim_snprintf_add((char *)msg_buf, sizeof(msg_buf),
4198 "%s", _(" on 1 line"));
4199 else
4200 vim_snprintf_add((char *)msg_buf, sizeof(msg_buf),
4201 _(" on %" PRId64 " lines"), (int64_t)sub_nlines);
4202 if (msg(msg_buf))
4203 /* save message to display it after redraw */
4204 set_keep_msg(msg_buf, 0);
4205 return true;
4206 }
4207 if (got_int) {
4208 EMSG(_(e_interr));
4209 return true;
4210 }
4211 return false;
4212}
4213
4214static void global_exe_one(char_u *const cmd, const linenr_T lnum)
4215{
4216 curwin->w_cursor.lnum = lnum;
4217 curwin->w_cursor.col = 0;
4218 if (*cmd == NUL || *cmd == '\n') {
4219 do_cmdline((char_u *)"p", NULL, NULL, DOCMD_NOWAIT);
4220 } else {
4221 do_cmdline(cmd, NULL, NULL, DOCMD_NOWAIT);
4222 }
4223}
4224
4225/*
4226 * Execute a global command of the form:
4227 *
4228 * g/pattern/X : execute X on all lines where pattern matches
4229 * v/pattern/X : execute X on all lines where pattern does not match
4230 *
4231 * where 'X' is an EX command
4232 *
4233 * The command character (as well as the trailing slash) is optional, and
4234 * is assumed to be 'p' if missing.
4235 *
4236 * This is implemented in two passes: first we scan the file for the pattern and
4237 * set a mark for each line that (not) matches. Secondly we execute the command
4238 * for each line that has a mark. This is required because after deleting
4239 * lines we do not know where to search for the next match.
4240 */
4241void ex_global(exarg_T *eap)
4242{
4243 linenr_T lnum; /* line number according to old situation */
4244 int ndone = 0;
4245 int type; /* first char of cmd: 'v' or 'g' */
4246 char_u *cmd; /* command argument */
4247
4248 char_u delim; /* delimiter, normally '/' */
4249 char_u *pat;
4250 regmmatch_T regmatch;
4251 int match;
4252 int which_pat;
4253
4254 // When nesting the command works on one line. This allows for
4255 // ":g/found/v/notfound/command".
4256 if (global_busy && (eap->line1 != 1
4257 || eap->line2 != curbuf->b_ml.ml_line_count)) {
4258 // will increment global_busy to break out of the loop
4259 EMSG(_("E147: Cannot do :global recursive with a range"));
4260 return;
4261 }
4262
4263 if (eap->forceit) /* ":global!" is like ":vglobal" */
4264 type = 'v';
4265 else
4266 type = *eap->cmd;
4267 cmd = eap->arg;
4268 which_pat = RE_LAST; /* default: use last used regexp */
4269
4270 /*
4271 * undocumented vi feature:
4272 * "\/" and "\?": use previous search pattern.
4273 * "\&": use previous substitute pattern.
4274 */
4275 if (*cmd == '\\') {
4276 ++cmd;
4277 if (vim_strchr((char_u *)"/?&", *cmd) == NULL) {
4278 EMSG(_(e_backslash));
4279 return;
4280 }
4281 if (*cmd == '&')
4282 which_pat = RE_SUBST; /* use previous substitute pattern */
4283 else
4284 which_pat = RE_SEARCH; /* use previous search pattern */
4285 ++cmd;
4286 pat = (char_u *)"";
4287 } else if (*cmd == NUL) {
4288 EMSG(_("E148: Regular expression missing from global"));
4289 return;
4290 } else {
4291 delim = *cmd; /* get the delimiter */
4292 if (delim)
4293 ++cmd; /* skip delimiter if there is one */
4294 pat = cmd; /* remember start of pattern */
4295 cmd = skip_regexp(cmd, delim, p_magic, &eap->arg);
4296 if (cmd[0] == delim) /* end delimiter found */
4297 *cmd++ = NUL; /* replace it with a NUL */
4298 }
4299
4300 if (search_regcomp(pat, RE_BOTH, which_pat, SEARCH_HIS, &regmatch) == FAIL) {
4301 EMSG(_(e_invcmd));
4302 return;
4303 }
4304
4305 if (global_busy) {
4306 lnum = curwin->w_cursor.lnum;
4307 match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
4308 (colnr_T)0, NULL, NULL);
4309 if ((type == 'g' && match) || (type == 'v' && !match)) {
4310 global_exe_one(cmd, lnum);
4311 }
4312 } else {
4313 // pass 1: set marks for each (not) matching line
4314 for (lnum = eap->line1; lnum <= eap->line2 && !got_int; lnum++) {
4315 // a match on this line?
4316 match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
4317 (colnr_T)0, NULL, NULL);
4318 if ((type == 'g' && match) || (type == 'v' && !match)) {
4319 ml_setmarked(lnum);
4320 ndone++;
4321 }
4322 line_breakcheck();
4323 }
4324
4325 // pass 2: execute the command for each line that has been marked
4326 if (got_int) {
4327 MSG(_(e_interr));
4328 } else if (ndone == 0) {
4329 if (type == 'v') {
4330 smsg(_("Pattern found in every line: %s"), pat);
4331 } else {
4332 smsg(_("Pattern not found: %s"), pat);
4333 }
4334 } else {
4335 global_exe(cmd);
4336 }
4337 ml_clearmarked(); // clear rest of the marks
4338 }
4339 vim_regfree(regmatch.regprog);
4340}
4341
4342/// Execute `cmd` on lines marked with ml_setmarked().
4343void global_exe(char_u *cmd)
4344{
4345 linenr_T old_lcount; // b_ml.ml_line_count before the command
4346 buf_T *old_buf = curbuf; // remember what buffer we started in
4347 linenr_T lnum; // line number according to old situation
4348 int save_mapped_ctrl_c = mapped_ctrl_c;
4349
4350 // Set current position only once for a global command.
4351 // If global_busy is set, setpcmark() will not do anything.
4352 // If there is an error, global_busy will be incremented.
4353 setpcmark();
4354
4355 // When the command writes a message, don't overwrite the command.
4356 msg_didout = true;
4357 // Disable CTRL-C mapping, let it interrupt (potentially long output).
4358 mapped_ctrl_c = 0;
4359
4360 sub_nsubs = 0;
4361 sub_nlines = 0;
4362 global_need_beginline = false;
4363 global_busy = 1;
4364 old_lcount = curbuf->b_ml.ml_line_count;
4365
4366 while (!got_int && (lnum = ml_firstmarked()) != 0 && global_busy == 1) {
4367 global_exe_one(cmd, lnum);
4368 os_breakcheck();
4369 }
4370
4371 mapped_ctrl_c = save_mapped_ctrl_c;
4372 global_busy = 0;
4373 if (global_need_beginline) {
4374 beginline(BL_WHITE | BL_FIX);
4375 } else {
4376 check_cursor(); // cursor may be beyond the end of the line
4377 }
4378
4379 // the cursor may not have moved in the text but a change in a previous
4380 // line may move it on the screen
4381 changed_line_abv_curs();
4382
4383 // If it looks like no message was written, allow overwriting the
4384 // command with the report for number of changes.
4385 if (msg_col == 0 && msg_scrolled == 0) {
4386 msg_didout = false;
4387 }
4388
4389 // If substitutes done, report number of substitutes, otherwise report
4390 // number of extra or deleted lines.
4391 // Don't report extra or deleted lines in the edge case where the buffer
4392 // we are in after execution is different from the buffer we started in.
4393 if (!do_sub_msg(false) && curbuf == old_buf) {
4394 msgmore(curbuf->b_ml.ml_line_count - old_lcount);
4395 }
4396}
4397
4398#if defined(EXITFREE)
4399void free_old_sub(void)
4400{
4401 sub_set_replacement((SubReplacementString) {NULL, 0, NULL});
4402}
4403
4404#endif
4405
4406/*
4407 * Set up for a tagpreview.
4408 * Return TRUE when it was created.
4409 */
4410bool
4411prepare_tagpreview (
4412 bool undo_sync /* sync undo when leaving the window */
4413)
4414{
4415 /*
4416 * If there is already a preview window open, use that one.
4417 */
4418 if (!curwin->w_p_pvw) {
4419 bool found_win = false;
4420 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
4421 if (wp->w_p_pvw) {
4422 win_enter(wp, undo_sync);
4423 found_win = true;
4424 break;
4425 }
4426 }
4427 if (!found_win) {
4428 /*
4429 * There is no preview window open yet. Create one.
4430 */
4431 if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0)
4432 == FAIL)
4433 return false;
4434 curwin->w_p_pvw = TRUE;
4435 curwin->w_p_wfh = TRUE;
4436 RESET_BINDING(curwin); /* don't take over 'scrollbind'
4437 and 'cursorbind' */
4438 curwin->w_p_diff = FALSE; /* no 'diff' */
4439 curwin->w_p_fdc = 0; /* no 'foldcolumn' */
4440 return true;
4441 }
4442 }
4443 return false;
4444}
4445
4446
4447
4448/*
4449 * ":help": open a read-only window on a help file
4450 */
4451void ex_help(exarg_T *eap)
4452{
4453 char_u *arg;
4454 char_u *tag;
4455 FILE *helpfd; /* file descriptor of help file */
4456 int n;
4457 int i;
4458 win_T *wp;
4459 int num_matches;
4460 char_u **matches;
4461 char_u *p;
4462 int empty_fnum = 0;
4463 int alt_fnum = 0;
4464 buf_T *buf;
4465 int len;
4466 char_u *lang;
4467 const bool old_KeyTyped = KeyTyped;
4468
4469 if (eap != NULL) {
4470 /*
4471 * A ":help" command ends at the first LF, or at a '|' that is
4472 * followed by some text. Set nextcmd to the following command.
4473 */
4474 for (arg = eap->arg; *arg; ++arg) {
4475 if (*arg == '\n' || *arg == '\r'
4476 || (*arg == '|' && arg[1] != NUL && arg[1] != '|')) {
4477 *arg++ = NUL;
4478 eap->nextcmd = arg;
4479 break;
4480 }
4481 }
4482 arg = eap->arg;
4483
4484 if (eap->forceit && *arg == NUL && !curbuf->b_help) {
4485 EMSG(_("E478: Don't panic!"));
4486 return;
4487 }
4488
4489 if (eap->skip) /* not executing commands */
4490 return;
4491 } else
4492 arg = (char_u *)"";
4493
4494 /* remove trailing blanks */
4495 p = arg + STRLEN(arg) - 1;
4496 while (p > arg && ascii_iswhite(*p) && p[-1] != '\\')
4497 *p-- = NUL;
4498
4499 /* Check for a specified language */
4500 lang = check_help_lang(arg);
4501
4502 /* When no argument given go to the index. */
4503 if (*arg == NUL)
4504 arg = (char_u *)"help.txt";
4505
4506 /*
4507 * Check if there is a match for the argument.
4508 */
4509 n = find_help_tags(arg, &num_matches, &matches,
4510 eap != NULL && eap->forceit);
4511
4512 i = 0;
4513 if (n != FAIL && lang != NULL)
4514 /* Find first item with the requested language. */
4515 for (i = 0; i < num_matches; ++i) {
4516 len = (int)STRLEN(matches[i]);
4517 if (len > 3 && matches[i][len - 3] == '@'
4518 && STRICMP(matches[i] + len - 2, lang) == 0)
4519 break;
4520 }
4521 if (i >= num_matches || n == FAIL) {
4522 if (lang != NULL)
4523 EMSG3(_("E661: Sorry, no '%s' help for %s"), lang, arg);
4524 else
4525 EMSG2(_("E149: Sorry, no help for %s"), arg);
4526 if (n != FAIL)
4527 FreeWild(num_matches, matches);
4528 return;
4529 }
4530
4531 /* The first match (in the requested language) is the best match. */
4532 tag = vim_strsave(matches[i]);
4533 FreeWild(num_matches, matches);
4534
4535 /*
4536 * Re-use an existing help window or open a new one.
4537 * Always open a new one for ":tab help".
4538 */
4539 if (!bt_help(curwin->w_buffer)
4540 || cmdmod.tab != 0
4541 ) {
4542 if (cmdmod.tab != 0) {
4543 wp = NULL;
4544 } else {
4545 wp = NULL;
4546 FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
4547 if (bt_help(wp2->w_buffer)) {
4548 wp = wp2;
4549 break;
4550 }
4551 }
4552 }
4553 if (wp != NULL && wp->w_buffer->b_nwindows > 0) {
4554 win_enter(wp, true);
4555 } else {
4556 // There is no help window yet.
4557 // Try to open the file specified by the "helpfile" option.
4558 if ((helpfd = os_fopen((char *)p_hf, READBIN)) == NULL) {
4559 smsg(_("Sorry, help file \"%s\" not found"), p_hf);
4560 goto erret;
4561 }
4562 fclose(helpfd);
4563
4564 /* Split off help window; put it at far top if no position
4565 * specified, the current window is vertically split and
4566 * narrow. */
4567 n = WSP_HELP;
4568 if (cmdmod.split == 0 && curwin->w_width != Columns
4569 && curwin->w_width < 80)
4570 n |= WSP_TOP;
4571 if (win_split(0, n) == FAIL)
4572 goto erret;
4573
4574 if (curwin->w_height < p_hh)
4575 win_setheight((int)p_hh);
4576
4577 /*
4578 * Open help file (do_ecmd() will set b_help flag, readfile() will
4579 * set b_p_ro flag).
4580 * Set the alternate file to the previously edited file.
4581 */
4582 alt_fnum = curbuf->b_fnum;
4583 (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
4584 ECMD_HIDE + ECMD_SET_HELP,
4585 NULL /* buffer is still open, don't store info */
4586 );
4587 if (!cmdmod.keepalt)
4588 curwin->w_alt_fnum = alt_fnum;
4589 empty_fnum = curbuf->b_fnum;
4590 }
4591 }
4592
4593 if (!p_im)
4594 restart_edit = 0; /* don't want insert mode in help file */
4595
4596 /* Restore KeyTyped, setting 'filetype=help' may reset it.
4597 * It is needed for do_tag top open folds under the cursor. */
4598 KeyTyped = old_KeyTyped;
4599
4600 do_tag(tag, DT_HELP, 1, FALSE, TRUE);
4601
4602 /* Delete the empty buffer if we're not using it. Careful: autocommands
4603 * may have jumped to another window, check that the buffer is not in a
4604 * window. */
4605 if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) {
4606 buf = buflist_findnr(empty_fnum);
4607 if (buf != NULL && buf->b_nwindows == 0)
4608 wipe_buffer(buf, TRUE);
4609 }
4610
4611 /* keep the previous alternate file */
4612 if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum && !cmdmod.keepalt)
4613 curwin->w_alt_fnum = alt_fnum;
4614
4615erret:
4616 xfree(tag);
4617}
4618
4619
4620/*
4621 * In an argument search for a language specifiers in the form "@xx".
4622 * Changes the "@" to NUL if found, and returns a pointer to "xx".
4623 * Returns NULL if not found.
4624 */
4625char_u *check_help_lang(char_u *arg)
4626{
4627 int len = (int)STRLEN(arg);
4628
4629 if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
4630 && ASCII_ISALPHA(arg[len - 1])) {
4631 arg[len - 3] = NUL; /* remove the '@' */
4632 return arg + len - 2;
4633 }
4634 return NULL;
4635}
4636
4637/*
4638 * Return a heuristic indicating how well the given string matches. The
4639 * smaller the number, the better the match. This is the order of priorities,
4640 * from best match to worst match:
4641 * - Match with least alpha-numeric characters is better.
4642 * - Match with least total characters is better.
4643 * - Match towards the start is better.
4644 * - Match starting with "+" is worse (feature instead of command)
4645 * Assumption is made that the matched_string passed has already been found to
4646 * match some string for which help is requested. webb.
4647 */
4648int
4649help_heuristic(
4650 char_u *matched_string,
4651 int offset, // offset for match
4652 int wrong_case // no matching case
4653)
4654{
4655 int num_letters;
4656 char_u *p;
4657
4658 num_letters = 0;
4659 for (p = matched_string; *p; p++)
4660 if (ASCII_ISALNUM(*p))
4661 num_letters++;
4662
4663 /*
4664 * Multiply the number of letters by 100 to give it a much bigger
4665 * weighting than the number of characters.
4666 * If there only is a match while ignoring case, add 5000.
4667 * If the match starts in the middle of a word, add 10000 to put it
4668 * somewhere in the last half.
4669 * If the match is more than 2 chars from the start, multiply by 200 to
4670 * put it after matches at the start.
4671 */
4672 if (ASCII_ISALNUM(matched_string[offset]) && offset > 0
4673 && ASCII_ISALNUM(matched_string[offset - 1]))
4674 offset += 10000;
4675 else if (offset > 2)
4676 offset *= 200;
4677 if (wrong_case)
4678 offset += 5000;
4679 /* Features are less interesting than the subjects themselves, but "+"
4680 * alone is not a feature. */
4681 if (matched_string[0] == '+' && matched_string[1] != NUL)
4682 offset += 100;
4683 return (int)(100 * num_letters + STRLEN(matched_string) + offset);
4684}
4685
4686/*
4687 * Compare functions for qsort() below, that checks the help heuristics number
4688 * that has been put after the tagname by find_tags().
4689 */
4690static int help_compare(const void *s1, const void *s2)
4691{
4692 char *p1;
4693 char *p2;
4694
4695 p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
4696 p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
4697 return strcmp(p1, p2);
4698}
4699
4700// Find all help tags matching "arg", sort them and return in matches[], with
4701// the number of matches in num_matches.
4702// The matches will be sorted with a "best" match algorithm.
4703// When "keep_lang" is true try keeping the language of the current buffer.
4704int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches,
4705 bool keep_lang)
4706{
4707 int i;
4708 static const char *(mtable[]) = {
4709 "*", "g*", "[*", "]*",
4710 "/*", "/\\*", "\"*", "**",
4711 "/\\(\\)", "/\\%(\\)",
4712 "?", ":?", "?<CR>", "g?", "g?g?", "g??",
4713 "-?", "q?", "v_g?",
4714 "/\\?", "/\\z(\\)", "\\=", ":s\\=",
4715 "[count]", "[quotex]",
4716 "[range]", ":[range]",
4717 "[pattern]", "\\|", "\\%$",
4718 "s/\\~", "s/\\U", "s/\\L",
4719 "s/\\1", "s/\\2", "s/\\3", "s/\\9"
4720 };
4721 static const char *(rtable[]) = {
4722 "star", "gstar", "[star", "]star",
4723 "/star", "/\\\\star", "quotestar", "starstar",
4724 "/\\\\(\\\\)", "/\\\\%(\\\\)",
4725 "?", ":?", "?<CR>", "g?", "g?g?", "g??",
4726 "-?", "q?", "v_g?",
4727 "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=",
4728 "\\[count]", "\\[quotex]",
4729 "\\[range]", ":\\[range]",
4730 "\\[pattern]", "\\\\bar", "/\\\\%\\$",
4731 "s/\\\\\\~", "s/\\\\U", "s/\\\\L",
4732 "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"
4733 };
4734 static const char *(expr_table[]) = {
4735 "!=?", "!~?", "<=?", "<?", "==?", "=~?",
4736 ">=?", ">?", "is?", "isnot?"
4737 };
4738 char_u *d = IObuff; // assume IObuff is long enough!
4739
4740 if (STRNICMP(arg, "expr-", 5) == 0) {
4741 // When the string starting with "expr-" and containing '?' and matches
4742 // the table, it is taken literally. Otherwise '?' is recognized as a
4743 // wildcard.
4744 for (i = (int)ARRAY_SIZE(expr_table); --i >= 0; ) {
4745 if (STRCMP(arg + 5, expr_table[i]) == 0) {
4746 STRCPY(d, arg);
4747 break;
4748 }
4749 }
4750 } else {
4751 // Recognize a few exceptions to the rule. Some strings that contain
4752 // '*' with "star". Otherwise '*' is recognized as a wildcard.
4753 for (i = (int)ARRAY_SIZE(mtable); --i >= 0; ) {
4754 if (STRCMP(arg, mtable[i]) == 0) {
4755 STRCPY(d, rtable[i]);
4756 break;
4757 }
4758 }
4759 }
4760
4761 if (i < 0) { /* no match in table */
4762 /* Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
4763 * Also replace "\%^" and "\%(", they match every tag too.
4764 * Also "\zs", "\z1", etc.
4765 * Also "\@<", "\@=", "\@<=", etc.
4766 * And also "\_$" and "\_^". */
4767 if (arg[0] == '\\'
4768 && ((arg[1] != NUL && arg[2] == NUL)
4769 || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL
4770 && arg[2] != NUL))) {
4771 STRCPY(d, "/\\\\");
4772 STRCPY(d + 3, arg + 1);
4773 /* Check for "/\\_$", should be "/\\_\$" */
4774 if (d[3] == '_' && d[4] == '$')
4775 STRCPY(d + 4, "\\$");
4776 } else {
4777 /* Replace:
4778 * "[:...:]" with "\[:...:]"
4779 * "[++...]" with "\[++...]"
4780 * "\{" with "\\{" -- matching "} \}"
4781 */
4782 if ((arg[0] == '[' && (arg[1] == ':'
4783 || (arg[1] == '+' && arg[2] == '+')))
4784 || (arg[0] == '\\' && arg[1] == '{'))
4785 *d++ = '\\';
4786
4787 // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'.
4788 if (*arg == '(' && arg[1] == '\'') {
4789 arg++;
4790 }
4791 for (const char_u *s = arg; *s; s++) {
4792 // Replace "|" with "bar" and '"' with "quote" to match the name of
4793 // the tags for these commands.
4794 // Replace "*" with ".*" and "?" with "." to match command line
4795 // completion.
4796 // Insert a backslash before '~', '$' and '.' to avoid their
4797 // special meaning.
4798 if (d - IObuff > IOSIZE - 10) { // getting too long!?
4799 break;
4800 }
4801 switch (*s) {
4802 case '|': STRCPY(d, "bar");
4803 d += 3;
4804 continue;
4805 case '"': STRCPY(d, "quote");
4806 d += 5;
4807 continue;
4808 case '*': *d++ = '.';
4809 break;
4810 case '?': *d++ = '.';
4811 continue;
4812 case '$':
4813 case '.':
4814 case '~': *d++ = '\\';
4815 break;
4816 }
4817
4818 /*
4819 * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
4820 * ":help i_^_CTRL-D" work.
4821 * Insert '-' before and after "CTRL-X" when applicable.
4822 */
4823 if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1])
4824 || vim_strchr((char_u *)
4825 "?@[\\]^",
4826 s[1]) != NULL))) {
4827 if (d > IObuff && d[-1] != '_' && d[-1] != '\\')
4828 *d++ = '_'; /* prepend a '_' to make x_CTRL-x */
4829 STRCPY(d, "CTRL-");
4830 d += 5;
4831 if (*s < ' ') {
4832 *d++ = *s + '@';
4833 if (d[-1] == '\\')
4834 *d++ = '\\'; /* double a backslash */
4835 } else
4836 *d++ = *++s;
4837 if (s[1] != NUL && s[1] != '_')
4838 *d++ = '_'; /* append a '_' */
4839 continue;
4840 } else if (*s == '^') /* "^" or "CTRL-^" or "^_" */
4841 *d++ = '\\';
4842
4843 /*
4844 * Insert a backslash before a backslash after a slash, for search
4845 * pattern tags: "/\|" --> "/\\|".
4846 */
4847 else if (s[0] == '\\' && s[1] != '\\'
4848 && *arg == '/' && s == arg + 1)
4849 *d++ = '\\';
4850
4851 /* "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in
4852 * "CTRL-\_CTRL-N" */
4853 if (STRNICMP(s, "CTRL-\\_", 7) == 0) {
4854 STRCPY(d, "CTRL-\\\\");
4855 d += 7;
4856 s += 6;
4857 }
4858
4859 *d++ = *s;
4860
4861 // If tag contains "({" or "([", tag terminates at the "(".
4862 // This is for help on functions, e.g.: abs({expr}).
4863 if (*s == '(' && (s[1] == '{' || s[1] =='[')) {
4864 break;
4865 }
4866
4867 // If tag starts with ', toss everything after a second '. Fixes
4868 // CTRL-] on 'option'. (would include the trailing '.').
4869 if (*s == '\'' && s > arg && *arg == '\'') {
4870 break;
4871 }
4872 // Also '{' and '}'. Fixes CTRL-] on '{address}'.
4873 if (*s == '}' && s > arg && *arg == '{') {
4874 break;
4875 }
4876 }
4877 *d = NUL;
4878
4879 if (*IObuff == '`') {
4880 if (d > IObuff + 2 && d[-1] == '`') {
4881 /* remove the backticks from `command` */
4882 memmove(IObuff, IObuff + 1, STRLEN(IObuff));
4883 d[-2] = NUL;
4884 } else if (d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
4885 /* remove the backticks and comma from `command`, */
4886 memmove(IObuff, IObuff + 1, STRLEN(IObuff));
4887 d[-3] = NUL;
4888 } else if (d > IObuff + 4 && d[-3] == '`'
4889 && d[-2] == '\\' && d[-1] == '.') {
4890 /* remove the backticks and dot from `command`\. */
4891 memmove(IObuff, IObuff + 1, STRLEN(IObuff));
4892 d[-4] = NUL;
4893 }
4894 }
4895 }
4896 }
4897
4898 *matches = NULL;
4899 *num_matches = 0;
4900 int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE;
4901 if (keep_lang) {
4902 flags |= TAG_KEEP_LANG;
4903 }
4904 if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK
4905 && *num_matches > 0) {
4906 /* Sort the matches found on the heuristic number that is after the
4907 * tag name. */
4908 qsort((void *)*matches, (size_t)*num_matches,
4909 sizeof(char_u *), help_compare);
4910 /* Delete more than TAG_MANY to reduce the size of the listing. */
4911 while (*num_matches > TAG_MANY)
4912 xfree((*matches)[--*num_matches]);
4913 }
4914 return OK;
4915}
4916
4917/// Called when starting to edit a buffer for a help file.
4918static void prepare_help_buffer(void)
4919{
4920 curbuf->b_help = true;
4921 set_string_option_direct((char_u *)"buftype", -1, (char_u *)"help",
4922 OPT_FREE|OPT_LOCAL, 0);
4923
4924 // Always set these options after jumping to a help tag, because the
4925 // user may have an autocommand that gets in the way.
4926 // Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
4927 // latin1 word characters (for translated help files).
4928 // Only set it when needed, buf_init_chartab() is some work.
4929 char_u *p = (char_u *)"!-~,^*,^|,^\",192-255";
4930 if (STRCMP(curbuf->b_p_isk, p) != 0) {
4931 set_string_option_direct((char_u *)"isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
4932 check_buf_options(curbuf);
4933 (void)buf_init_chartab(curbuf, FALSE);
4934 }
4935
4936 // Don't use the global foldmethod.
4937 set_string_option_direct((char_u *)"fdm", -1, (char_u *)"manual",
4938 OPT_FREE|OPT_LOCAL, 0);
4939
4940 curbuf->b_p_ts = 8; // 'tabstop' is 8.
4941 curwin->w_p_list = FALSE; // No list mode.
4942
4943 curbuf->b_p_ma = FALSE; // Not modifiable.
4944 curbuf->b_p_bin = FALSE; // Reset 'bin' before reading file.
4945 curwin->w_p_nu = 0; // No line numbers.
4946 curwin->w_p_rnu = 0; // No relative line numbers.
4947 RESET_BINDING(curwin); // No scroll or cursor binding.
4948 curwin->w_p_arab = FALSE; // No arabic mode.
4949 curwin->w_p_rl = FALSE; // Help window is left-to-right.
4950 curwin->w_p_fen = FALSE; // No folding in the help window.
4951 curwin->w_p_diff = FALSE; // No 'diff'.
4952 curwin->w_p_spell = FALSE; // No spell checking.
4953
4954 set_buflisted(FALSE);
4955}
4956
4957/*
4958 * After reading a help file: May cleanup a help buffer when syntax
4959 * highlighting is not used.
4960 */
4961void fix_help_buffer(void)
4962{
4963 linenr_T lnum;
4964 char_u *line;
4965 bool in_example = false;
4966
4967 // Set filetype to "help".
4968 if (STRCMP(curbuf->b_p_ft, "help") != 0) {
4969 curbuf_lock++;
4970 set_option_value("ft", 0L, "help", OPT_LOCAL);
4971 curbuf_lock--;
4972 }
4973
4974 if (!syntax_present(curwin)) {
4975 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
4976 line = ml_get_buf(curbuf, lnum, false);
4977 const size_t len = STRLEN(line);
4978 if (in_example && len > 0 && !ascii_iswhite(line[0])) {
4979 /* End of example: non-white or '<' in first column. */
4980 if (line[0] == '<') {
4981 /* blank-out a '<' in the first column */
4982 line = ml_get_buf(curbuf, lnum, TRUE);
4983 line[0] = ' ';
4984 }
4985 in_example = false;
4986 }
4987 if (!in_example && len > 0) {
4988 if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) {
4989 /* blank-out a '>' in the last column (start of example) */
4990 line = ml_get_buf(curbuf, lnum, TRUE);
4991 line[len - 1] = ' ';
4992 in_example = true;
4993 } else if (line[len - 1] == '~') {
4994 /* blank-out a '~' at the end of line (header marker) */
4995 line = ml_get_buf(curbuf, lnum, TRUE);
4996 line[len - 1] = ' ';
4997 }
4998 }
4999 }
5000 }
5001
5002 /*
5003 * In the "help.txt" and "help.abx" file, add the locally added help
5004 * files. This uses the very first line in the help file.
5005 */
5006 char_u *const fname = path_tail(curbuf->b_fname);
5007 if (fnamecmp(fname, "help.txt") == 0
5008 || (fnamencmp(fname, "help.", 5) == 0
5009 && ASCII_ISALPHA(fname[5])
5010 && ASCII_ISALPHA(fname[6])
5011 && TOLOWER_ASC(fname[7]) == 'x'
5012 && fname[8] == NUL)
5013 ) {
5014 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum) {
5015 line = ml_get_buf(curbuf, lnum, FALSE);
5016 if (strstr((char *)line, "*local-additions*") == NULL)
5017 continue;
5018
5019 /* Go through all directories in 'runtimepath', skipping
5020 * $VIMRUNTIME. */
5021 char_u *p = p_rtp;
5022 while (*p != NUL) {
5023 copy_option_part(&p, NameBuff, MAXPATHL, ",");
5024 char_u *const rt = (char_u *)vim_getenv("VIMRUNTIME");
5025 if (rt != NULL
5026 && path_full_compare(rt, NameBuff, false) != kEqualFiles) {
5027 int fcount;
5028 char_u **fnames;
5029 char_u *s;
5030 vimconv_T vc;
5031 char_u *cp;
5032
5033 // Find all "doc/ *.txt" files in this directory.
5034 if (!add_pathsep((char *)NameBuff)
5035 || STRLCAT(NameBuff, "doc/*.??[tx]",
5036 sizeof(NameBuff)) >= MAXPATHL) {
5037 EMSG(_(e_fnametoolong));
5038 continue;
5039 }
5040
5041 // Note: We cannot just do `&NameBuff` because it is a statically sized array
5042 // so `NameBuff == &NameBuff` according to C semantics.
5043 char_u *buff_list[1] = {NameBuff};
5044 if (gen_expand_wildcards(1, buff_list, &fcount,
5045 &fnames, EW_FILE|EW_SILENT) == OK
5046 && fcount > 0) {
5047 // If foo.abx is found use it instead of foo.txt in
5048 // the same directory.
5049 for (int i1 = 0; i1 < fcount; i1++) {
5050 for (int i2 = 0; i2 < fcount; i2++) {
5051 if (i1 == i2) {
5052 continue;
5053 }
5054 if (fnames[i1] == NULL || fnames[i2] == NULL) {
5055 continue;
5056 }
5057 const char_u *const f1 = fnames[i1];
5058 const char_u *const f2 = fnames[i2];
5059 const char_u *const t1 = path_tail(f1);
5060 const char_u *const t2 = path_tail(f2);
5061 const char_u *const e1 = STRRCHR(t1, '.');
5062 const char_u *const e2 = STRRCHR(t2, '.');
5063 if (e1 == NULL || e2 == NULL) {
5064 continue;
5065 }
5066 if (fnamecmp(e1, ".txt") != 0
5067 && fnamecmp(e1, fname + 4) != 0) {
5068 // Not .txt and not .abx, remove it.
5069 XFREE_CLEAR(fnames[i1]);
5070 continue;
5071 }
5072 if (e1 - f1 != e2 - f2
5073 || fnamencmp(f1, f2, e1 - f1) != 0) {
5074 continue;
5075 }
5076 if (fnamecmp(e1, ".txt") == 0
5077 && fnamecmp(e2, fname + 4) == 0) {
5078 // use .abx instead of .txt
5079 XFREE_CLEAR(fnames[i1]);
5080 }
5081 }
5082 }
5083 for (int fi = 0; fi < fcount; fi++) {
5084 if (fnames[fi] == NULL) {
5085 continue;
5086 }
5087
5088 FILE *const fd = os_fopen((char *)fnames[fi], "r");
5089 if (fd == NULL) {
5090 continue;
5091 }
5092 vim_fgets(IObuff, IOSIZE, fd);
5093 if (IObuff[0] == '*'
5094 && (s = vim_strchr(IObuff + 1, '*'))
5095 != NULL) {
5096 TriState this_utf = kNone;
5097 // Change tag definition to a
5098 // reference and remove <CR>/<NL>.
5099 IObuff[0] = '|';
5100 *s = '|';
5101 while (*s != NUL) {
5102 if (*s == '\r' || *s == '\n')
5103 *s = NUL;
5104 /* The text is utf-8 when a byte
5105 * above 127 is found and no
5106 * illegal byte sequence is found.
5107 */
5108 if (*s >= 0x80 && this_utf != kFalse) {
5109 this_utf = kTrue;
5110 const int l = utf_ptr2len(s);
5111 if (l == 1) {
5112 this_utf = kFalse;
5113 }
5114 s += l - 1;
5115 }
5116 ++s;
5117 }
5118 /* The help file is latin1 or utf-8;
5119 * conversion to the current
5120 * 'encoding' may be required. */
5121 vc.vc_type = CONV_NONE;
5122 convert_setup(
5123 &vc,
5124 (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"),
5125 p_enc);
5126 if (vc.vc_type == CONV_NONE) {
5127 // No conversion needed.
5128 cp = IObuff;
5129 } else {
5130 // Do the conversion. If it fails
5131 // use the unconverted text.
5132 cp = string_convert(&vc, IObuff, NULL);
5133 if (cp == NULL) {
5134 cp = IObuff;
5135 }
5136 }
5137 convert_setup(&vc, NULL, NULL);
5138
5139 ml_append(lnum, cp, (colnr_T)0, false);
5140 if (cp != IObuff) {
5141 xfree(cp);
5142 }
5143 lnum++;
5144 }
5145 fclose(fd);
5146 }
5147 FreeWild(fcount, fnames);
5148 }
5149 }
5150 xfree(rt);
5151 }
5152 break;
5153 }
5154 }
5155}
5156
5157/*
5158 * ":exusage"
5159 */
5160void ex_exusage(exarg_T *eap)
5161{
5162 do_cmdline_cmd("help ex-cmd-index");
5163}
5164
5165/*
5166 * ":viusage"
5167 */
5168void ex_viusage(exarg_T *eap)
5169{
5170 do_cmdline_cmd("help normal-index");
5171}
5172
5173
5174/// Generate tags in one help directory
5175///
5176/// @param dir Path to the doc directory
5177/// @param ext Suffix of the help files (".txt", ".itx", ".frx", etc.)
5178/// @param tagname Name of the tags file ("tags" for English, "tags-fr" for
5179/// French)
5180/// @param add_help_tags Whether to add the "help-tags" tag
5181static void helptags_one(char_u *const dir, const char_u *const ext,
5182 const char_u *const tagfname, const bool add_help_tags)
5183{
5184 garray_T ga;
5185 int filecount;
5186 char_u **files;
5187 char_u *p1, *p2;
5188 char_u *s;
5189 TriState utf8 = kNone;
5190 bool mix = false; // detected mixed encodings
5191
5192 // Find all *.txt files.
5193 size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff));
5194 if (dirlen >= MAXPATHL
5195 || STRLCAT(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT
5196 || STRLCAT(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) {
5197 EMSG(_(e_fnametoolong));
5198 return;
5199 }
5200
5201 // Note: We cannot just do `&NameBuff` because it is a statically sized array
5202 // so `NameBuff == &NameBuff` according to C semantics.
5203 char_u *buff_list[1] = {NameBuff};
5204 if (gen_expand_wildcards(1, buff_list, &filecount, &files,
5205 EW_FILE|EW_SILENT) == FAIL
5206 || filecount == 0) {
5207 if (!got_int) {
5208 EMSG2(_("E151: No match: %s"), NameBuff);
5209 }
5210 return;
5211 }
5212
5213 //
5214 // Open the tags file for writing.
5215 // Do this before scanning through all the files.
5216 //
5217 memcpy(NameBuff, dir, dirlen + 1);
5218 if (!add_pathsep((char *)NameBuff)
5219 || STRLCAT(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) {
5220 EMSG(_(e_fnametoolong));
5221 return;
5222 }
5223
5224 FILE *const fd_tags = os_fopen((char *)NameBuff, "w");
5225 if (fd_tags == NULL) {
5226 EMSG2(_("E152: Cannot open %s for writing"), NameBuff);
5227 FreeWild(filecount, files);
5228 return;
5229 }
5230
5231 // If using the "++t" argument or generating tags for "$VIMRUNTIME/doc"
5232 // add the "help-tags" tag.
5233 ga_init(&ga, (int)sizeof(char_u *), 100);
5234 if (add_help_tags
5235 || path_full_compare((char_u *)"$VIMRUNTIME/doc",
5236 dir, false) == kEqualFiles) {
5237 s = xmalloc(18 + STRLEN(tagfname));
5238 sprintf((char *)s, "help-tags\t%s\t1\n", tagfname);
5239 GA_APPEND(char_u *, &ga, s);
5240 }
5241
5242 // Go over all the files and extract the tags.
5243 for (int fi = 0; fi < filecount && !got_int; fi++) {
5244 FILE *const fd = os_fopen((char *)files[fi], "r");
5245 if (fd == NULL) {
5246 EMSG2(_("E153: Unable to open %s for reading"), files[fi]);
5247 continue;
5248 }
5249 const char_u *const fname = files[fi] + dirlen + 1;
5250
5251 bool firstline = true;
5252 while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
5253 if (firstline) {
5254 // Detect utf-8 file by a non-ASCII char in the first line.
5255 TriState this_utf8 = kNone;
5256 for (s = IObuff; *s != NUL; s++) {
5257 if (*s >= 0x80) {
5258 this_utf8 = kTrue;
5259 const int l = utf_ptr2len(s);
5260 if (l == 1) {
5261 // Illegal UTF-8 byte sequence.
5262 this_utf8 = kFalse;
5263 break;
5264 }
5265 s += l - 1;
5266 }
5267 }
5268 if (this_utf8 == kNone) { // only ASCII characters found
5269 this_utf8 = kFalse;
5270 }
5271 if (utf8 == kNone) { // first file
5272 utf8 = this_utf8;
5273 } else if (utf8 != this_utf8) {
5274 EMSG2(_(
5275 "E670: Mix of help file encodings within a language: %s"),
5276 files[fi]);
5277 mix = !got_int;
5278 got_int = TRUE;
5279 }
5280 firstline = false;
5281 }
5282 p1 = vim_strchr(IObuff, '*'); // find first '*'
5283 while (p1 != NULL) {
5284 p2 = (char_u *)strchr((const char *)p1 + 1, '*'); // Find second '*'.
5285 if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**".
5286 for (s = p1 + 1; s < p2; s++) {
5287 if (*s == ' ' || *s == '\t' || *s == '|') {
5288 break;
5289 }
5290 }
5291
5292 // Only accept a *tag* when it consists of valid
5293 // characters, there is white space before it and is
5294 // followed by a white character or end-of-line.
5295 if (s == p2
5296 && (p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
5297 && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL
5298 || s[1] == '\0')) {
5299 *p2 = '\0';
5300 ++p1;
5301 s = xmalloc((p2 - p1) + STRLEN(fname) + 2);
5302 GA_APPEND(char_u *, &ga, s);
5303 sprintf((char *)s, "%s\t%s", p1, fname);
5304
5305 // find next '*'
5306 p2 = vim_strchr(p2 + 1, '*');
5307 }
5308 }
5309 p1 = p2;
5310 }
5311 line_breakcheck();
5312 }
5313
5314 fclose(fd);
5315 }
5316
5317 FreeWild(filecount, files);
5318
5319 if (!got_int && ga.ga_data != NULL) {
5320 // Sort the tags.
5321 sort_strings((char_u **)ga.ga_data, ga.ga_len);
5322
5323 // Check for duplicates.
5324 for (int i = 1; i < ga.ga_len; i++) {
5325 p1 = ((char_u **)ga.ga_data)[i - 1];
5326 p2 = ((char_u **)ga.ga_data)[i];
5327 while (*p1 == *p2) {
5328 if (*p2 == '\t') {
5329 *p2 = NUL;
5330 vim_snprintf((char *)NameBuff, MAXPATHL,
5331 _("E154: Duplicate tag \"%s\" in file %s/%s"),
5332 ((char_u **)ga.ga_data)[i], dir, p2 + 1);
5333 EMSG(NameBuff);
5334 *p2 = '\t';
5335 break;
5336 }
5337 ++p1;
5338 ++p2;
5339 }
5340 }
5341
5342 if (utf8 == kTrue) {
5343 fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n");
5344 }
5345
5346 // Write the tags into the file.
5347 for (int i = 0; i < ga.ga_len; i++) {
5348 s = ((char_u **)ga.ga_data)[i];
5349 if (STRNCMP(s, "help-tags\t", 10) == 0) {
5350 // help-tags entry was added in formatted form
5351 fputs((char *)s, fd_tags);
5352 } else {
5353 fprintf(fd_tags, "%s\t/" "*", s);
5354 for (p1 = s; *p1 != '\t'; p1++) {
5355 // insert backslash before '\\' and '/'
5356 if (*p1 == '\\' || *p1 == '/') {
5357 putc('\\', fd_tags);
5358 }
5359 putc(*p1, fd_tags);
5360 }
5361 fprintf(fd_tags, "*\n");
5362 }
5363 }
5364 }
5365 if (mix) {
5366 got_int = false; // continue with other languages
5367 }
5368
5369 GA_DEEP_CLEAR_PTR(&ga);
5370 fclose(fd_tags); // there is no check for an error...
5371}
5372
5373/// Generate tags in one help directory, taking care of translations.
5374static void do_helptags(char_u *dirname, bool add_help_tags)
5375{
5376 int len;
5377 garray_T ga;
5378 char_u lang[2];
5379 char_u ext[5];
5380 char_u fname[8];
5381 int filecount;
5382 char_u **files;
5383
5384 // Get a list of all files in the help directory and in subdirectories.
5385 STRLCPY(NameBuff, dirname, sizeof(NameBuff));
5386 if (!add_pathsep((char *)NameBuff)
5387 || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
5388 EMSG(_(e_fnametoolong));
5389 return;
5390 }
5391
5392 // Note: We cannot just do `&NameBuff` because it is a statically sized array
5393 // so `NameBuff == &NameBuff` according to C semantics.
5394 char_u *buff_list[1] = {NameBuff};
5395 if (gen_expand_wildcards(1, buff_list, &filecount, &files,
5396 EW_FILE|EW_SILENT) == FAIL
5397 || filecount == 0) {
5398 EMSG2(_("E151: No match: %s"), NameBuff);
5399 return;
5400 }
5401
5402 /* Go over all files in the directory to find out what languages are
5403 * present. */
5404 int j;
5405 ga_init(&ga, 1, 10);
5406 for (int i = 0; i < filecount; i++) {
5407 len = (int)STRLEN(files[i]);
5408 if (len <= 4) {
5409 continue;
5410 }
5411 if (STRICMP(files[i] + len - 4, ".txt") == 0) {
5412 /* ".txt" -> language "en" */
5413 lang[0] = 'e';
5414 lang[1] = 'n';
5415 } else if (files[i][len - 4] == '.'
5416 && ASCII_ISALPHA(files[i][len - 3])
5417 && ASCII_ISALPHA(files[i][len - 2])
5418 && TOLOWER_ASC(files[i][len - 1]) == 'x') {
5419 /* ".abx" -> language "ab" */
5420 lang[0] = TOLOWER_ASC(files[i][len - 3]);
5421 lang[1] = TOLOWER_ASC(files[i][len - 2]);
5422 } else
5423 continue;
5424
5425 // Did we find this language already?
5426 for (j = 0; j < ga.ga_len; j += 2) {
5427 if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) {
5428 break;
5429 }
5430 }
5431 if (j == ga.ga_len) {
5432 // New language, add it.
5433 ga_grow(&ga, 2);
5434 ((char_u *)ga.ga_data)[ga.ga_len++] = lang[0];
5435 ((char_u *)ga.ga_data)[ga.ga_len++] = lang[1];
5436 }
5437 }
5438
5439 /*
5440 * Loop over the found languages to generate a tags file for each one.
5441 */
5442 for (j = 0; j < ga.ga_len; j += 2) {
5443 STRCPY(fname, "tags-xx");
5444 fname[5] = ((char_u *)ga.ga_data)[j];
5445 fname[6] = ((char_u *)ga.ga_data)[j + 1];
5446 if (fname[5] == 'e' && fname[6] == 'n') {
5447 /* English is an exception: use ".txt" and "tags". */
5448 fname[4] = NUL;
5449 STRCPY(ext, ".txt");
5450 } else {
5451 /* Language "ab" uses ".abx" and "tags-ab". */
5452 STRCPY(ext, ".xxx");
5453 ext[1] = fname[5];
5454 ext[2] = fname[6];
5455 }
5456 helptags_one(dirname, ext, fname, add_help_tags);
5457 }
5458
5459 ga_clear(&ga);
5460 FreeWild(filecount, files);
5461}
5462
5463 static void
5464helptags_cb(char_u *fname, void *cookie)
5465{
5466 do_helptags(fname, *(bool *)cookie);
5467}
5468
5469/*
5470 * ":helptags"
5471 */
5472void ex_helptags(exarg_T *eap)
5473{
5474 expand_T xpc;
5475 char_u *dirname;
5476 bool add_help_tags = false;
5477
5478 /* Check for ":helptags ++t {dir}". */
5479 if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) {
5480 add_help_tags = true;
5481 eap->arg = skipwhite(eap->arg + 3);
5482 }
5483
5484 if (STRCMP(eap->arg, "ALL") == 0) {
5485 do_in_path(p_rtp, (char_u *)"doc", DIP_ALL + DIP_DIR,
5486 helptags_cb, &add_help_tags);
5487 } else {
5488 ExpandInit(&xpc);
5489 xpc.xp_context = EXPAND_DIRECTORIES;
5490 dirname = ExpandOne(&xpc, eap->arg, NULL,
5491 WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
5492 if (dirname == NULL || !os_isdir(dirname)) {
5493 EMSG2(_("E150: Not a directory: %s"), eap->arg);
5494 } else {
5495 do_helptags(dirname, add_help_tags);
5496 }
5497 xfree(dirname);
5498 }
5499}
5500
5501/*
5502 * ":helpclose": Close one help window
5503 */
5504void ex_helpclose(exarg_T *eap)
5505{
5506 FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
5507 if (bt_help(win->w_buffer)) {
5508 win_close(win, false);
5509 return;
5510 }
5511 }
5512}
5513
5514/// Shows the effects of the :substitute command being typed ('inccommand').
5515/// If inccommand=split, shows a preview window and later restores the layout.
5516static buf_T *show_sub(exarg_T *eap, pos_T old_cusr,
5517 PreviewLines *preview_lines, int hl_id, int src_id)
5518 FUNC_ATTR_NONNULL_ALL
5519{
5520 static handle_T bufnr = 0; // special buffer, re-used on each visit
5521
5522 win_T *save_curwin = curwin;
5523 cmdmod_T save_cmdmod = cmdmod;
5524 char_u *save_shm_p = vim_strsave(p_shm);
5525 PreviewLines lines = *preview_lines;
5526 buf_T *orig_buf = curbuf;
5527
5528 // We keep a special-purpose buffer around, but don't assume it exists.
5529 buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0;
5530 cmdmod.tab = 0; // disable :tab modifier
5531 cmdmod.noswapfile = true; // disable swap for preview buffer
5532 // disable file info message
5533 set_string_option_direct((char_u *)"shm", -1, (char_u *)"F", OPT_FREE,
5534 SID_NONE);
5535
5536 bool outside_curline = (eap->line1 != old_cusr.lnum
5537 || eap->line2 != old_cusr.lnum);
5538 bool split = outside_curline && (*p_icm != 'n');
5539 if (preview_buf == curbuf) { // Preview buffer cannot preview itself!
5540 split = false;
5541 preview_buf = NULL;
5542 }
5543
5544 // Place cursor on nearest matching line, to undo do_sub() cursor placement.
5545 for (size_t i = 0; i < lines.subresults.size; i++) {
5546 SubResult curres = lines.subresults.items[i];
5547 if (curres.start.lnum >= old_cusr.lnum) {
5548 curwin->w_cursor.lnum = curres.start.lnum;
5549 curwin->w_cursor.col = curres.start.col;
5550 break;
5551 } // Else: All matches are above, do_sub() already placed cursor.
5552 }
5553
5554 // Width of the "| lnum|..." column which displays the line numbers.
5555 linenr_T highest_num_line = 0;
5556 int col_width = 0;
5557
5558 if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) {
5559 buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]");
5560 buf_clear();
5561 preview_buf = curbuf;
5562 bufnr = preview_buf->handle;
5563 curbuf->b_p_bl = false;
5564 curbuf->b_p_ma = true;
5565 curbuf->b_p_ul = -1;
5566 curbuf->b_p_tw = 0; // Reset 'textwidth' (was set by ftplugin)
5567 curwin->w_p_cul = false;
5568 curwin->w_p_cuc = false;
5569 curwin->w_p_spell = false;
5570 curwin->w_p_fen = false;
5571
5572 if (lines.subresults.size > 0) {
5573 highest_num_line = kv_last(lines.subresults).end.lnum;
5574 col_width = log10(highest_num_line) + 1 + 3;
5575 }
5576 }
5577
5578 char *str = NULL; // construct the line to show in here
5579 size_t old_line_size = 0;
5580 size_t line_size = 0;
5581 linenr_T linenr_preview = 0; // last line added to preview buffer
5582 linenr_T linenr_origbuf = 0; // last line added to original buffer
5583 linenr_T next_linenr = 0; // next line to show for the match
5584
5585 for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) {
5586 SubResult match = lines.subresults.items[matchidx];
5587
5588 if (split && preview_buf) {
5589 lpos_T p_start = { 0, match.start.col }; // match starts here in preview
5590 lpos_T p_end = { 0, match.end.col }; // ... and ends here
5591
5592 if (match.pre_match == 0) {
5593 next_linenr = match.start.lnum;
5594 } else {
5595 next_linenr = match.pre_match;
5596 }
5597 // Don't add a line twice
5598 if (next_linenr == linenr_origbuf) {
5599 next_linenr++;
5600 p_start.lnum = linenr_preview; // might be redefined below
5601 p_end.lnum = linenr_preview; // might be redefined below
5602 }
5603
5604 for (; next_linenr <= match.end.lnum; next_linenr++) {
5605 if (next_linenr == match.start.lnum) {
5606 p_start.lnum = linenr_preview + 1;
5607 }
5608 if (next_linenr == match.end.lnum) {
5609 p_end.lnum = linenr_preview + 1;
5610 }
5611 char *line;
5612 if (next_linenr == orig_buf->b_ml.ml_line_count + 1) {
5613 line = "";
5614 } else {
5615 line = (char *)ml_get_buf(orig_buf, next_linenr, false);
5616 line_size = strlen(line) + col_width + 1;
5617
5618 // Reallocate if line not long enough
5619 if (line_size > old_line_size) {
5620 str = xrealloc(str, line_size * sizeof(char));
5621 old_line_size = line_size;
5622 }
5623 }
5624 // Put "|lnum| line" into `str` and append it to the preview buffer.
5625 snprintf(str, line_size, "|%*ld| %s", col_width - 3,
5626 next_linenr, line);
5627 if (linenr_preview == 0) {
5628 ml_replace(1, (char_u *)str, true);
5629 } else {
5630 ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false);
5631 }
5632 linenr_preview += 1;
5633 }
5634 linenr_origbuf = match.end.lnum;
5635
5636 bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start,
5637 p_end, col_width);
5638 }
5639 bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start,
5640 match.end, 0);
5641 }
5642 xfree(str);
5643
5644 redraw_later(SOME_VALID);
5645 win_enter(save_curwin, false); // Return to original window
5646 update_topline();
5647
5648 // Update screen now. Must do this _before_ close_windows().
5649 int save_rd = RedrawingDisabled;
5650 RedrawingDisabled = 0;
5651 update_screen(SOME_VALID);
5652 RedrawingDisabled = save_rd;
5653
5654 set_string_option_direct((char_u *)"shm", -1, save_shm_p, OPT_FREE, SID_NONE);
5655 xfree(save_shm_p);
5656
5657 cmdmod = save_cmdmod;
5658
5659 return preview_buf;
5660}
5661
5662/// :substitute command
5663///
5664/// If 'inccommand' is empty: calls do_sub().
5665/// If 'inccommand' is set: shows a "live" preview then removes the changes.
5666/// from undo history.
5667void ex_substitute(exarg_T *eap)
5668{
5669 bool preview = (State & CMDPREVIEW);
5670 if (*p_icm == NUL || !preview) { // 'inccommand' is disabled
5671 (void)do_sub(eap, profile_zero(), true);
5672 return;
5673 }
5674
5675 block_autocmds(); // Disable events during command preview.
5676
5677 char_u *save_eap = eap->arg;
5678 garray_T save_view;
5679 win_size_save(&save_view); // Save current window sizes.
5680 save_search_patterns();
5681 int save_changedtick = buf_get_changedtick(curbuf);
5682 time_t save_b_u_time_cur = curbuf->b_u_time_cur;
5683 u_header_T *save_b_u_newhead = curbuf->b_u_newhead;
5684 long save_b_p_ul = curbuf->b_p_ul;
5685 int save_w_p_cul = curwin->w_p_cul;
5686 int save_w_p_cuc = curwin->w_p_cuc;
5687
5688 curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes
5689 curwin->w_p_cul = false; // Disable 'cursorline'
5690 curwin->w_p_cuc = false; // Disable 'cursorcolumn'
5691
5692 // Don't show search highlighting during live substitution
5693 bool save_hls = p_hls;
5694 p_hls = false;
5695 buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt), false);
5696 p_hls = save_hls;
5697
5698 if (save_changedtick != buf_get_changedtick(curbuf)) {
5699 // Undo invisibly. This also moves the cursor!
5700 if (!u_undo_and_forget(1)) { abort(); }
5701 // Restore newhead. It is meaningless when curhead is valid, but we must
5702 // restore it so that undotree() is identical before/after the preview.
5703 curbuf->b_u_newhead = save_b_u_newhead;
5704 curbuf->b_u_time_cur = save_b_u_time_cur;
5705 buf_set_changedtick(curbuf, save_changedtick);
5706 }
5707 if (buf_valid(preview_buf)) {
5708 // XXX: Must do this *after* u_undo_and_forget(), why?
5709 close_windows(preview_buf, false);
5710 }
5711 curbuf->b_p_ul = save_b_p_ul;
5712 curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline'
5713 curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn'
5714 eap->arg = save_eap;
5715 restore_search_patterns();
5716 win_size_restore(&save_view);
5717 ga_clear(&save_view);
5718 unblock_autocmds();
5719}
5720
5721/// Skip over the pattern argument of ":vimgrep /pat/[g][j]".
5722/// Put the start of the pattern in "*s", unless "s" is NULL.
5723/// If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP.
5724/// If "s" is not NULL terminate the pattern with a NUL.
5725/// Return a pointer to the char just past the pattern plus flags.
5726char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags)
5727{
5728 int c;
5729
5730 if (vim_isIDc(*p)) {
5731 // ":vimgrep pattern fname"
5732 if (s != NULL) {
5733 *s = p;
5734 }
5735 p = skiptowhite(p);
5736 if (s != NULL && *p != NUL) {
5737 *p++ = NUL;
5738 }
5739 } else {
5740 // ":vimgrep /pattern/[g][j] fname"
5741 if (s != NULL) {
5742 *s = p + 1;
5743 }
5744 c = *p;
5745 p = skip_regexp(p + 1, c, true, NULL);
5746 if (*p != c) {
5747 return NULL;
5748 }
5749
5750 // Truncate the pattern.
5751 if (s != NULL) {
5752 *p = NUL;
5753 }
5754 p++;
5755
5756 // Find the flags
5757 while (*p == 'g' || *p == 'j') {
5758 if (flags != NULL) {
5759 if (*p == 'g') {
5760 *flags |= VGR_GLOBAL;
5761 } else {
5762 *flags |= VGR_NOJUMP;
5763 }
5764 }
5765 p++;
5766 }
5767 }
5768 return p;
5769}
5770
5771/// List v:oldfiles in a nice way.
5772void ex_oldfiles(exarg_T *eap)
5773{
5774 list_T *l = get_vim_var_list(VV_OLDFILES);
5775 long nr = 0;
5776
5777 if (l == NULL) {
5778 msg((char_u *)_("No old files"));
5779 } else {
5780 msg_start();
5781 msg_scroll = true;
5782 TV_LIST_ITER(l, li, {
5783 if (got_int) {
5784 break;
5785 }
5786 nr++;
5787 const char *fname = tv_get_string(TV_LIST_ITEM_TV(li));
5788 if (!message_filtered((char_u *)fname)) {
5789 msg_outnum(nr);
5790 MSG_PUTS(": ");
5791 msg_outtrans((char_u *)tv_get_string(TV_LIST_ITEM_TV(li)));
5792 msg_clr_eos();
5793 msg_putchar('\n');
5794 ui_flush(); // output one line at a time
5795 os_breakcheck();
5796 }
5797 });
5798
5799 // Assume "got_int" was set to truncate the listing.
5800 got_int = false;
5801
5802 // File selection prompt on ":browse oldfiles"
5803 if (cmdmod.browse) {
5804 quit_more = false;
5805 nr = prompt_for_number(false);
5806 msg_starthere();
5807 if (nr > 0 && nr <= tv_list_len(l)) {
5808 const char *const p = tv_list_find_str(l, nr - 1);
5809 if (p == NULL) {
5810 return;
5811 }
5812 char *const s = (char *)expand_env_save((char_u *)p);
5813 eap->arg = (char_u *)s;
5814 eap->cmdidx = CMD_edit;
5815 cmdmod.browse = false;
5816 do_exedit(eap, NULL);
5817 xfree(s);
5818 }
5819 }
5820 }
5821}
5822