xref: /vim-8.2.3635/src/ops.c (revision 5be4ceec)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * ops.c: implementation of various operators: op_shift, op_delete, op_tilde,
12  *	  op_change, op_yank, do_put, do_join
13  */
14 
15 #include "vim.h"
16 
17 static void shift_block(oparg_T *oap, int amount);
18 static void	mb_adjust_opend(oparg_T *oap);
19 static int	do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1);
20 static int	ends_in_white(linenr_T lnum);
21 #ifdef FEAT_COMMENTS
22 static int	fmt_check_par(linenr_T, int *, char_u **, int do_comments);
23 #else
24 static int	fmt_check_par(linenr_T);
25 #endif
26 
27 // Flags for third item in "opchars".
28 #define OPF_LINES  1	// operator always works on lines
29 #define OPF_CHANGE 2	// operator changes text
30 
31 /*
32  * The names of operators.
33  * IMPORTANT: Index must correspond with defines in vim.h!!!
34  * The third field holds OPF_ flags.
35  */
36 static char opchars[][3] =
37 {
38     {NUL, NUL, 0},			// OP_NOP
39     {'d', NUL, OPF_CHANGE},		// OP_DELETE
40     {'y', NUL, 0},			// OP_YANK
41     {'c', NUL, OPF_CHANGE},		// OP_CHANGE
42     {'<', NUL, OPF_LINES | OPF_CHANGE},	// OP_LSHIFT
43     {'>', NUL, OPF_LINES | OPF_CHANGE},	// OP_RSHIFT
44     {'!', NUL, OPF_LINES | OPF_CHANGE},	// OP_FILTER
45     {'g', '~', OPF_CHANGE},		// OP_TILDE
46     {'=', NUL, OPF_LINES | OPF_CHANGE},	// OP_INDENT
47     {'g', 'q', OPF_LINES | OPF_CHANGE},	// OP_FORMAT
48     {':', NUL, OPF_LINES},		// OP_COLON
49     {'g', 'U', OPF_CHANGE},		// OP_UPPER
50     {'g', 'u', OPF_CHANGE},		// OP_LOWER
51     {'J', NUL, OPF_LINES | OPF_CHANGE},	// DO_JOIN
52     {'g', 'J', OPF_LINES | OPF_CHANGE},	// DO_JOIN_NS
53     {'g', '?', OPF_CHANGE},		// OP_ROT13
54     {'r', NUL, OPF_CHANGE},		// OP_REPLACE
55     {'I', NUL, OPF_CHANGE},		// OP_INSERT
56     {'A', NUL, OPF_CHANGE},		// OP_APPEND
57     {'z', 'f', OPF_LINES},		// OP_FOLD
58     {'z', 'o', OPF_LINES},		// OP_FOLDOPEN
59     {'z', 'O', OPF_LINES},		// OP_FOLDOPENREC
60     {'z', 'c', OPF_LINES},		// OP_FOLDCLOSE
61     {'z', 'C', OPF_LINES},		// OP_FOLDCLOSEREC
62     {'z', 'd', OPF_LINES},		// OP_FOLDDEL
63     {'z', 'D', OPF_LINES},		// OP_FOLDDELREC
64     {'g', 'w', OPF_LINES | OPF_CHANGE},	// OP_FORMAT2
65     {'g', '@', OPF_CHANGE},		// OP_FUNCTION
66     {Ctrl_A, NUL, OPF_CHANGE},		// OP_NR_ADD
67     {Ctrl_X, NUL, OPF_CHANGE},		// OP_NR_SUB
68 };
69 
70 /*
71  * Translate a command name into an operator type.
72  * Must only be called with a valid operator name!
73  */
74     int
75 get_op_type(int char1, int char2)
76 {
77     int		i;
78 
79     if (char1 == 'r')		/* ignore second character */
80 	return OP_REPLACE;
81     if (char1 == '~')		/* when tilde is an operator */
82 	return OP_TILDE;
83     if (char1 == 'g' && char2 == Ctrl_A)	/* add */
84 	return OP_NR_ADD;
85     if (char1 == 'g' && char2 == Ctrl_X)	/* subtract */
86 	return OP_NR_SUB;
87     for (i = 0; ; ++i)
88     {
89 	if (opchars[i][0] == char1 && opchars[i][1] == char2)
90 	    break;
91 	if (i == (int)(sizeof(opchars) / sizeof(char [3]) - 1))
92 	{
93 	    internal_error("get_op_type()");
94 	    break;
95 	}
96     }
97     return i;
98 }
99 
100 /*
101  * Return TRUE if operator "op" always works on whole lines.
102  */
103     int
104 op_on_lines(int op)
105 {
106     return opchars[op][2] & OPF_LINES;
107 }
108 
109 #if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
110 /*
111  * Return TRUE if operator "op" changes text.
112  */
113     int
114 op_is_change(int op)
115 {
116     return opchars[op][2] & OPF_CHANGE;
117 }
118 #endif
119 
120 /*
121  * Get first operator command character.
122  * Returns 'g' or 'z' if there is another command character.
123  */
124     int
125 get_op_char(int optype)
126 {
127     return opchars[optype][0];
128 }
129 
130 /*
131  * Get second operator command character.
132  */
133     int
134 get_extra_op_char(int optype)
135 {
136     return opchars[optype][1];
137 }
138 
139 /*
140  * op_shift - handle a shift operation
141  */
142     void
143 op_shift(oparg_T *oap, int curs_top, int amount)
144 {
145     long	    i;
146     int		    first_char;
147     int		    block_col = 0;
148 
149     if (u_save((linenr_T)(oap->start.lnum - 1),
150 				       (linenr_T)(oap->end.lnum + 1)) == FAIL)
151 	return;
152 
153     if (oap->block_mode)
154 	block_col = curwin->w_cursor.col;
155 
156     for (i = oap->line_count; --i >= 0; )
157     {
158 	first_char = *ml_get_curline();
159 	if (first_char == NUL)				/* empty line */
160 	    curwin->w_cursor.col = 0;
161 	else if (oap->block_mode)
162 	    shift_block(oap, amount);
163 	else
164 	    /* Move the line right if it doesn't start with '#', 'smartindent'
165 	     * isn't set or 'cindent' isn't set or '#' isn't in 'cino'. */
166 #if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT)
167 	    if (first_char != '#' || !preprocs_left())
168 #endif
169 	    shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, FALSE);
170 	++curwin->w_cursor.lnum;
171     }
172 
173     changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
174     if (oap->block_mode)
175     {
176 	curwin->w_cursor.lnum = oap->start.lnum;
177 	curwin->w_cursor.col = block_col;
178     }
179     else if (curs_top)	    /* put cursor on first line, for ">>" */
180     {
181 	curwin->w_cursor.lnum = oap->start.lnum;
182 	beginline(BL_SOL | BL_FIX);   /* shift_line() may have set cursor.col */
183     }
184     else
185 	--curwin->w_cursor.lnum;	/* put cursor on last line, for ":>" */
186 
187 #ifdef FEAT_FOLDING
188     /* The cursor line is not in a closed fold */
189     foldOpenCursor();
190 #endif
191 
192 
193     if (oap->line_count > p_report)
194     {
195 	char	    *op;
196 	char	    *msg_line_single;
197 	char	    *msg_line_plural;
198 
199 	if (oap->op_type == OP_RSHIFT)
200 	    op = ">";
201 	else
202 	    op = "<";
203 	msg_line_single = NGETTEXT("%ld line %sed %d time",
204 					     "%ld line %sed %d times", amount);
205 	msg_line_plural = NGETTEXT("%ld lines %sed %d time",
206 					    "%ld lines %sed %d times", amount);
207 	vim_snprintf((char *)IObuff, IOSIZE,
208 		NGETTEXT(msg_line_single, msg_line_plural, oap->line_count),
209 		oap->line_count, op, amount);
210 	msg((char *)IObuff);
211     }
212 
213     /*
214      * Set "'[" and "']" marks.
215      */
216     curbuf->b_op_start = oap->start;
217     curbuf->b_op_end.lnum = oap->end.lnum;
218     curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
219     if (curbuf->b_op_end.col > 0)
220 	--curbuf->b_op_end.col;
221 }
222 
223 /*
224  * Shift the current line one shiftwidth left (if left != 0) or right
225  * leaves cursor on first blank in the line.
226  */
227     void
228 shift_line(
229     int	left,
230     int	round,
231     int	amount,
232     int call_changed_bytes)	/* call changed_bytes() */
233 {
234     int		count;
235     int		i, j;
236     int		sw_val = (int)get_sw_value_indent(curbuf);
237 
238     count = get_indent();	/* get current indent */
239 
240     if (round)			/* round off indent */
241     {
242 	i = count / sw_val;	/* number of p_sw rounded down */
243 	j = count % sw_val;	/* extra spaces */
244 	if (j && left)		/* first remove extra spaces */
245 	    --amount;
246 	if (left)
247 	{
248 	    i -= amount;
249 	    if (i < 0)
250 		i = 0;
251 	}
252 	else
253 	    i += amount;
254 	count = i * sw_val;
255     }
256     else		/* original vi indent */
257     {
258 	if (left)
259 	{
260 	    count -= sw_val * amount;
261 	    if (count < 0)
262 		count = 0;
263 	}
264 	else
265 	    count += sw_val * amount;
266     }
267 
268     /* Set new indent */
269     if (State & VREPLACE_FLAG)
270 	change_indent(INDENT_SET, count, FALSE, NUL, call_changed_bytes);
271     else
272 	(void)set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
273 }
274 
275 /*
276  * Shift one line of the current block one shiftwidth right or left.
277  * Leaves cursor on first character in block.
278  */
279     static void
280 shift_block(oparg_T *oap, int amount)
281 {
282     int			left = (oap->op_type == OP_LSHIFT);
283     int			oldstate = State;
284     int			total;
285     char_u		*newp, *oldp;
286     int			oldcol = curwin->w_cursor.col;
287     int			sw_val = (int)get_sw_value_indent(curbuf);
288     int			ts_val = (int)curbuf->b_p_ts;
289     struct block_def	bd;
290     int			incr;
291     colnr_T		ws_vcol;
292     int			i = 0, j = 0;
293     int			len;
294 #ifdef FEAT_RIGHTLEFT
295     int			old_p_ri = p_ri;
296 
297     p_ri = 0;			/* don't want revins in indent */
298 #endif
299 
300     State = INSERT;		/* don't want REPLACE for State */
301     block_prep(oap, &bd, curwin->w_cursor.lnum, TRUE);
302     if (bd.is_short)
303 	return;
304 
305     /* total is number of screen columns to be inserted/removed */
306     total = (int)((unsigned)amount * (unsigned)sw_val);
307     if ((total / sw_val) != amount)
308 	return; /* multiplication overflow */
309 
310     oldp = ml_get_curline();
311 
312     if (!left)
313     {
314 	/*
315 	 *  1. Get start vcol
316 	 *  2. Total ws vcols
317 	 *  3. Divvy into TABs & spp
318 	 *  4. Construct new string
319 	 */
320 	total += bd.pre_whitesp; /* all virtual WS upto & incl a split TAB */
321 	ws_vcol = bd.start_vcol - bd.pre_whitesp;
322 	if (bd.startspaces)
323 	{
324 	    if (has_mbyte)
325 	    {
326 		if ((*mb_ptr2len)(bd.textstart) == 1)
327 		    ++bd.textstart;
328 		else
329 		{
330 		    ws_vcol = 0;
331 		    bd.startspaces = 0;
332 		}
333 	    }
334 	    else
335 		++bd.textstart;
336 	}
337 	for ( ; VIM_ISWHITE(*bd.textstart); )
338 	{
339 	    /* TODO: is passing bd.textstart for start of the line OK? */
340 	    incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart,
341 						    (colnr_T)(bd.start_vcol));
342 	    total += incr;
343 	    bd.start_vcol += incr;
344 	}
345 	/* OK, now total=all the VWS reqd, and textstart points at the 1st
346 	 * non-ws char in the block. */
347 #ifdef FEAT_VARTABS
348 	if (!curbuf->b_p_et)
349 	    tabstop_fromto(ws_vcol, ws_vcol + total,
350 					ts_val, curbuf->b_p_vts_array, &i, &j);
351 	else
352 	    j = total;
353 #else
354 	if (!curbuf->b_p_et)
355 	    i = ((ws_vcol % ts_val) + total) / ts_val; /* number of tabs */
356 	if (i)
357 	    j = ((ws_vcol % ts_val) + total) % ts_val; /* number of spp */
358 	else
359 	    j = total;
360 #endif
361 	/* if we're splitting a TAB, allow for it */
362 	bd.textcol -= bd.pre_whitesp_c - (bd.startspaces != 0);
363 	len = (int)STRLEN(bd.textstart) + 1;
364 	newp = alloc(bd.textcol + i + j + len);
365 	if (newp == NULL)
366 	    return;
367 	vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + len));
368 	mch_memmove(newp, oldp, (size_t)bd.textcol);
369 	vim_memset(newp + bd.textcol, TAB, (size_t)i);
370 	vim_memset(newp + bd.textcol + i, ' ', (size_t)j);
371 	/* the end */
372 	mch_memmove(newp + bd.textcol + i + j, bd.textstart, (size_t)len);
373     }
374     else /* left */
375     {
376 	colnr_T	    destination_col;	/* column to which text in block will
377 					   be shifted */
378 	char_u	    *verbatim_copy_end;	/* end of the part of the line which is
379 					   copied verbatim */
380 	colnr_T	    verbatim_copy_width;/* the (displayed) width of this part
381 					   of line */
382 	unsigned    fill;		/* nr of spaces that replace a TAB */
383 	unsigned    new_line_len;	/* the length of the line after the
384 					   block shift */
385 	size_t	    block_space_width;
386 	size_t	    shift_amount;
387 	char_u	    *non_white = bd.textstart;
388 	colnr_T	    non_white_col;
389 
390 	/*
391 	 * Firstly, let's find the first non-whitespace character that is
392 	 * displayed after the block's start column and the character's column
393 	 * number. Also, let's calculate the width of all the whitespace
394 	 * characters that are displayed in the block and precede the searched
395 	 * non-whitespace character.
396 	 */
397 
398 	/* If "bd.startspaces" is set, "bd.textstart" points to the character,
399 	 * the part of which is displayed at the block's beginning. Let's start
400 	 * searching from the next character. */
401 	if (bd.startspaces)
402 	    MB_PTR_ADV(non_white);
403 
404 	/* The character's column is in "bd.start_vcol".  */
405 	non_white_col = bd.start_vcol;
406 
407 	while (VIM_ISWHITE(*non_white))
408 	{
409 	    incr = lbr_chartabsize_adv(bd.textstart, &non_white, non_white_col);
410 	    non_white_col += incr;
411 	}
412 
413 	block_space_width = non_white_col - oap->start_vcol;
414 	/* We will shift by "total" or "block_space_width", whichever is less.
415 	 */
416 	shift_amount = (block_space_width < (size_t)total
417 					 ? block_space_width : (size_t)total);
418 
419 	/* The column to which we will shift the text.  */
420 	destination_col = (colnr_T)(non_white_col - shift_amount);
421 
422 	/* Now let's find out how much of the beginning of the line we can
423 	 * reuse without modification.  */
424 	verbatim_copy_end = bd.textstart;
425 	verbatim_copy_width = bd.start_vcol;
426 
427 	/* If "bd.startspaces" is set, "bd.textstart" points to the character
428 	 * preceding the block. We have to subtract its width to obtain its
429 	 * column number.  */
430 	if (bd.startspaces)
431 	    verbatim_copy_width -= bd.start_char_vcols;
432 	while (verbatim_copy_width < destination_col)
433 	{
434 	    char_u *line = verbatim_copy_end;
435 
436 	    /* TODO: is passing verbatim_copy_end for start of the line OK? */
437 	    incr = lbr_chartabsize(line, verbatim_copy_end,
438 							 verbatim_copy_width);
439 	    if (verbatim_copy_width + incr > destination_col)
440 		break;
441 	    verbatim_copy_width += incr;
442 	    MB_PTR_ADV(verbatim_copy_end);
443 	}
444 
445 	/* If "destination_col" is different from the width of the initial
446 	 * part of the line that will be copied, it means we encountered a tab
447 	 * character, which we will have to partly replace with spaces.  */
448 	fill = destination_col - verbatim_copy_width;
449 
450 	/* The replacement line will consist of:
451 	 * - the beginning of the original line up to "verbatim_copy_end",
452 	 * - "fill" number of spaces,
453 	 * - the rest of the line, pointed to by non_white.  */
454 	new_line_len = (unsigned)(verbatim_copy_end - oldp)
455 		       + fill
456 		       + (unsigned)STRLEN(non_white) + 1;
457 
458 	newp = alloc(new_line_len);
459 	if (newp == NULL)
460 	    return;
461 	mch_memmove(newp, oldp, (size_t)(verbatim_copy_end - oldp));
462 	vim_memset(newp + (verbatim_copy_end - oldp), ' ', (size_t)fill);
463 	STRMOVE(newp + (verbatim_copy_end - oldp) + fill, non_white);
464     }
465     /* replace the line */
466     ml_replace(curwin->w_cursor.lnum, newp, FALSE);
467     changed_bytes(curwin->w_cursor.lnum, (colnr_T)bd.textcol);
468     State = oldstate;
469     curwin->w_cursor.col = oldcol;
470 #ifdef FEAT_RIGHTLEFT
471     p_ri = old_p_ri;
472 #endif
473 }
474 
475 /*
476  * Insert string "s" (b_insert ? before : after) block :AKelly
477  * Caller must prepare for undo.
478  */
479     static void
480 block_insert(
481     oparg_T		*oap,
482     char_u		*s,
483     int			b_insert,
484     struct block_def	*bdp)
485 {
486     int		ts_val;
487     int		count = 0;	/* extra spaces to replace a cut TAB */
488     int		spaces = 0;	/* non-zero if cutting a TAB */
489     colnr_T	offset;		/* pointer along new line */
490     unsigned	s_len;		/* STRLEN(s) */
491     char_u	*newp, *oldp;	/* new, old lines */
492     linenr_T	lnum;		/* loop var */
493     int		oldstate = State;
494 
495     State = INSERT;		/* don't want REPLACE for State */
496     s_len = (unsigned)STRLEN(s);
497 
498     for (lnum = oap->start.lnum + 1; lnum <= oap->end.lnum; lnum++)
499     {
500 	block_prep(oap, bdp, lnum, TRUE);
501 	if (bdp->is_short && b_insert)
502 	    continue;	/* OP_INSERT, line ends before block start */
503 
504 	oldp = ml_get(lnum);
505 
506 	if (b_insert)
507 	{
508 	    ts_val = bdp->start_char_vcols;
509 	    spaces = bdp->startspaces;
510 	    if (spaces != 0)
511 		count = ts_val - 1; /* we're cutting a TAB */
512 	    offset = bdp->textcol;
513 	}
514 	else /* append */
515 	{
516 	    ts_val = bdp->end_char_vcols;
517 	    if (!bdp->is_short) /* spaces = padding after block */
518 	    {
519 		spaces = (bdp->endspaces ? ts_val - bdp->endspaces : 0);
520 		if (spaces != 0)
521 		    count = ts_val - 1; /* we're cutting a TAB */
522 		offset = bdp->textcol + bdp->textlen - (spaces != 0);
523 	    }
524 	    else /* spaces = padding to block edge */
525 	    {
526 		/* if $ used, just append to EOL (ie spaces==0) */
527 		if (!bdp->is_MAX)
528 		    spaces = (oap->end_vcol - bdp->end_vcol) + 1;
529 		count = spaces;
530 		offset = bdp->textcol + bdp->textlen;
531 	    }
532 	}
533 
534 	if (has_mbyte && spaces > 0)
535 	{
536 	    int off;
537 
538 	    /* Avoid starting halfway a multi-byte character. */
539 	    if (b_insert)
540 	    {
541 		off = (*mb_head_off)(oldp, oldp + offset + spaces);
542 	    }
543 	    else
544 	    {
545 		off = (*mb_off_next)(oldp, oldp + offset);
546 		offset += off;
547 	    }
548 	    spaces -= off;
549 	    count -= off;
550 	}
551 
552 	newp = alloc(STRLEN(oldp) + s_len + count + 1);
553 	if (newp == NULL)
554 	    continue;
555 
556 	/* copy up to shifted part */
557 	mch_memmove(newp, oldp, (size_t)(offset));
558 	oldp += offset;
559 
560 	/* insert pre-padding */
561 	vim_memset(newp + offset, ' ', (size_t)spaces);
562 
563 	/* copy the new text */
564 	mch_memmove(newp + offset + spaces, s, (size_t)s_len);
565 	offset += s_len;
566 
567 	if (spaces && !bdp->is_short)
568 	{
569 	    /* insert post-padding */
570 	    vim_memset(newp + offset + spaces, ' ', (size_t)(ts_val - spaces));
571 	    /* We're splitting a TAB, don't copy it. */
572 	    oldp++;
573 	    /* We allowed for that TAB, remember this now */
574 	    count++;
575 	}
576 
577 	if (spaces > 0)
578 	    offset += count;
579 	STRMOVE(newp + offset, oldp);
580 
581 	ml_replace(lnum, newp, FALSE);
582 
583 	if (lnum == oap->end.lnum)
584 	{
585 	    /* Set "']" mark to the end of the block instead of the end of
586 	     * the insert in the first line.  */
587 	    curbuf->b_op_end.lnum = oap->end.lnum;
588 	    curbuf->b_op_end.col = offset;
589 	}
590     } /* for all lnum */
591 
592     changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L);
593 
594     State = oldstate;
595 }
596 
597 #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(PROTO)
598 /*
599  * op_reindent - handle reindenting a block of lines.
600  */
601     void
602 op_reindent(oparg_T *oap, int (*how)(void))
603 {
604     long	i;
605     char_u	*l;
606     int		amount;
607     linenr_T	first_changed = 0;
608     linenr_T	last_changed = 0;
609     linenr_T	start_lnum = curwin->w_cursor.lnum;
610 
611     /* Don't even try when 'modifiable' is off. */
612     if (!curbuf->b_p_ma)
613     {
614 	emsg(_(e_modifiable));
615 	return;
616     }
617 
618     for (i = oap->line_count; --i >= 0 && !got_int; )
619     {
620 	/* it's a slow thing to do, so give feedback so there's no worry that
621 	 * the computer's just hung. */
622 
623 	if (i > 1
624 		&& (i % 50 == 0 || i == oap->line_count - 1)
625 		&& oap->line_count > p_report)
626 	    smsg(_("%ld lines to indent... "), i);
627 
628 	/*
629 	 * Be vi-compatible: For lisp indenting the first line is not
630 	 * indented, unless there is only one line.
631 	 */
632 #ifdef FEAT_LISP
633 	if (i != oap->line_count - 1 || oap->line_count == 1
634 						    || how != get_lisp_indent)
635 #endif
636 	{
637 	    l = skipwhite(ml_get_curline());
638 	    if (*l == NUL)		    /* empty or blank line */
639 		amount = 0;
640 	    else
641 		amount = how();		    /* get the indent for this line */
642 
643 	    if (amount >= 0 && set_indent(amount, SIN_UNDO))
644 	    {
645 		/* did change the indent, call changed_lines() later */
646 		if (first_changed == 0)
647 		    first_changed = curwin->w_cursor.lnum;
648 		last_changed = curwin->w_cursor.lnum;
649 	    }
650 	}
651 	++curwin->w_cursor.lnum;
652 	curwin->w_cursor.col = 0;  /* make sure it's valid */
653     }
654 
655     /* put cursor on first non-blank of indented line */
656     curwin->w_cursor.lnum = start_lnum;
657     beginline(BL_SOL | BL_FIX);
658 
659     /* Mark changed lines so that they will be redrawn.  When Visual
660      * highlighting was present, need to continue until the last line.  When
661      * there is no change still need to remove the Visual highlighting. */
662     if (last_changed != 0)
663 	changed_lines(first_changed, 0,
664 		oap->is_VIsual ? start_lnum + oap->line_count :
665 		last_changed + 1, 0L);
666     else if (oap->is_VIsual)
667 	redraw_curbuf_later(INVERTED);
668 
669     if (oap->line_count > p_report)
670     {
671 	i = oap->line_count - (i + 1);
672 	smsg(NGETTEXT("%ld line indented ",
673 						 "%ld lines indented ", i), i);
674     }
675     /* set '[ and '] marks */
676     curbuf->b_op_start = oap->start;
677     curbuf->b_op_end = oap->end;
678 }
679 #endif /* defined(FEAT_LISP) || defined(FEAT_CINDENT) */
680 
681 /*
682  * Stuff a string into the typeahead buffer, such that edit() will insert it
683  * literally ("literally" TRUE) or interpret is as typed characters.
684  */
685     void
686 stuffescaped(char_u *arg, int literally)
687 {
688     int		c;
689     char_u	*start;
690 
691     while (*arg != NUL)
692     {
693 	/* Stuff a sequence of normal ASCII characters, that's fast.  Also
694 	 * stuff K_SPECIAL to get the effect of a special key when "literally"
695 	 * is TRUE. */
696 	start = arg;
697 	while ((*arg >= ' '
698 #ifndef EBCDIC
699 		    && *arg < DEL /* EBCDIC: chars above space are normal */
700 #endif
701 		    )
702 		|| (*arg == K_SPECIAL && !literally))
703 	    ++arg;
704 	if (arg > start)
705 	    stuffReadbuffLen(start, (long)(arg - start));
706 
707 	/* stuff a single special character */
708 	if (*arg != NUL)
709 	{
710 	    if (has_mbyte)
711 		c = mb_cptr2char_adv(&arg);
712 	    else
713 		c = *arg++;
714 	    if (literally && ((c < ' ' && c != TAB) || c == DEL))
715 		stuffcharReadbuff(Ctrl_V);
716 	    stuffcharReadbuff(c);
717 	}
718     }
719 }
720 
721 /*
722  * Handle a delete operation.
723  *
724  * Return FAIL if undo failed, OK otherwise.
725  */
726     int
727 op_delete(oparg_T *oap)
728 {
729     int			n;
730     linenr_T		lnum;
731     char_u		*ptr;
732     char_u		*newp, *oldp;
733     struct block_def	bd;
734     linenr_T		old_lcount = curbuf->b_ml.ml_line_count;
735     int			did_yank = FALSE;
736 
737     if (curbuf->b_ml.ml_flags & ML_EMPTY)	    /* nothing to do */
738 	return OK;
739 
740     /* Nothing to delete, return here.	Do prepare undo, for op_change(). */
741     if (oap->empty)
742 	return u_save_cursor();
743 
744     if (!curbuf->b_p_ma)
745     {
746 	emsg(_(e_modifiable));
747 	return FAIL;
748     }
749 
750 #ifdef FEAT_CLIPBOARD
751     adjust_clip_reg(&oap->regname);
752 #endif
753 
754     if (has_mbyte)
755 	mb_adjust_opend(oap);
756 
757     /*
758      * Imitate the strange Vi behaviour: If the delete spans more than one
759      * line and motion_type == MCHAR and the result is a blank line, make the
760      * delete linewise.  Don't do this for the change command or Visual mode.
761      */
762     if (       oap->motion_type == MCHAR
763 	    && !oap->is_VIsual
764 	    && !oap->block_mode
765 	    && oap->line_count > 1
766 	    && oap->motion_force == NUL
767 	    && oap->op_type == OP_DELETE)
768     {
769 	ptr = ml_get(oap->end.lnum) + oap->end.col;
770 	if (*ptr != NUL)
771 	    ptr += oap->inclusive;
772 	ptr = skipwhite(ptr);
773 	if (*ptr == NUL && inindent(0))
774 	    oap->motion_type = MLINE;
775     }
776 
777     /*
778      * Check for trying to delete (e.g. "D") in an empty line.
779      * Note: For the change operator it is ok.
780      */
781     if (       oap->motion_type == MCHAR
782 	    && oap->line_count == 1
783 	    && oap->op_type == OP_DELETE
784 	    && *ml_get(oap->start.lnum) == NUL)
785     {
786 	/*
787 	 * It's an error to operate on an empty region, when 'E' included in
788 	 * 'cpoptions' (Vi compatible).
789 	 */
790 	if (virtual_op)
791 	    /* Virtual editing: Nothing gets deleted, but we set the '[ and ']
792 	     * marks as if it happened. */
793 	    goto setmarks;
794 	if (vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL)
795 	    beep_flush();
796 	return OK;
797     }
798 
799     /*
800      * Do a yank of whatever we're about to delete.
801      * If a yank register was specified, put the deleted text into that
802      * register.  For the black hole register '_' don't yank anything.
803      */
804     if (oap->regname != '_')
805     {
806 	if (oap->regname != 0)
807 	{
808 	    /* check for read-only register */
809 	    if (!valid_yank_reg(oap->regname, TRUE))
810 	    {
811 		beep_flush();
812 		return OK;
813 	    }
814 	    get_yank_register(oap->regname, TRUE); /* yank into specif'd reg. */
815 	    if (op_yank(oap, TRUE, FALSE) == OK)   /* yank without message */
816 		did_yank = TRUE;
817 	}
818 
819 	/*
820 	 * Put deleted text into register 1 and shift number registers if the
821 	 * delete contains a line break, or when using a specific operator (Vi
822 	 * compatible)
823 	 * Use the register name from before adjust_clip_reg() may have
824 	 * changed it.
825 	 */
826 	if (oap->motion_type == MLINE || oap->line_count > 1
827 							   || oap->use_reg_one)
828 	{
829 	    shift_delete_registers();
830 	    if (op_yank(oap, TRUE, FALSE) == OK)
831 		did_yank = TRUE;
832 	}
833 
834 	/* Yank into small delete register when no named register specified
835 	 * and the delete is within one line. */
836 	if ((
837 #ifdef FEAT_CLIPBOARD
838 	    ((clip_unnamed & CLIP_UNNAMED) && oap->regname == '*') ||
839 	    ((clip_unnamed & CLIP_UNNAMED_PLUS) && oap->regname == '+') ||
840 #endif
841 	    oap->regname == 0) && oap->motion_type != MLINE
842 						      && oap->line_count == 1)
843 	{
844 	    oap->regname = '-';
845 	    get_yank_register(oap->regname, TRUE);
846 	    if (op_yank(oap, TRUE, FALSE) == OK)
847 		did_yank = TRUE;
848 	    oap->regname = 0;
849 	}
850 
851 	/*
852 	 * If there's too much stuff to fit in the yank register, then get a
853 	 * confirmation before doing the delete. This is crude, but simple.
854 	 * And it avoids doing a delete of something we can't put back if we
855 	 * want.
856 	 */
857 	if (!did_yank)
858 	{
859 	    int msg_silent_save = msg_silent;
860 
861 	    msg_silent = 0;	/* must display the prompt */
862 	    n = ask_yesno((char_u *)_("cannot yank; delete anyway"), TRUE);
863 	    msg_silent = msg_silent_save;
864 	    if (n != 'y')
865 	    {
866 		emsg(_(e_abort));
867 		return FAIL;
868 	    }
869 	}
870 
871 #if defined(FEAT_EVAL)
872 	if (did_yank && has_textyankpost())
873 	    yank_do_autocmd(oap, get_y_current());
874 #endif
875     }
876 
877     /*
878      * block mode delete
879      */
880     if (oap->block_mode)
881     {
882 	if (u_save((linenr_T)(oap->start.lnum - 1),
883 			       (linenr_T)(oap->end.lnum + 1)) == FAIL)
884 	    return FAIL;
885 
886 	for (lnum = curwin->w_cursor.lnum; lnum <= oap->end.lnum; ++lnum)
887 	{
888 	    block_prep(oap, &bd, lnum, TRUE);
889 	    if (bd.textlen == 0)	/* nothing to delete */
890 		continue;
891 
892 	    /* Adjust cursor position for tab replaced by spaces and 'lbr'. */
893 	    if (lnum == curwin->w_cursor.lnum)
894 	    {
895 		curwin->w_cursor.col = bd.textcol + bd.startspaces;
896 		curwin->w_cursor.coladd = 0;
897 	    }
898 
899 	    // "n" == number of chars deleted
900 	    // If we delete a TAB, it may be replaced by several characters.
901 	    // Thus the number of characters may increase!
902 	    n = bd.textlen - bd.startspaces - bd.endspaces;
903 	    oldp = ml_get(lnum);
904 	    newp = alloc(STRLEN(oldp) + 1 - n);
905 	    if (newp == NULL)
906 		continue;
907 	    /* copy up to deleted part */
908 	    mch_memmove(newp, oldp, (size_t)bd.textcol);
909 	    /* insert spaces */
910 	    vim_memset(newp + bd.textcol, ' ',
911 				     (size_t)(bd.startspaces + bd.endspaces));
912 	    /* copy the part after the deleted part */
913 	    oldp += bd.textcol + bd.textlen;
914 	    STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp);
915 	    /* replace the line */
916 	    ml_replace(lnum, newp, FALSE);
917 
918 #ifdef FEAT_TEXT_PROP
919 	    if (curbuf->b_has_textprop && n != 0)
920 		adjust_prop_columns(lnum, bd.textcol, -n, 0);
921 #endif
922 	}
923 
924 	check_cursor_col();
925 	changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col,
926 						       oap->end.lnum + 1, 0L);
927 	oap->line_count = 0;	    /* no lines deleted */
928     }
929     else if (oap->motion_type == MLINE)
930     {
931 	if (oap->op_type == OP_CHANGE)
932 	{
933 	    /* Delete the lines except the first one.  Temporarily move the
934 	     * cursor to the next line.  Save the current line number, if the
935 	     * last line is deleted it may be changed.
936 	     */
937 	    if (oap->line_count > 1)
938 	    {
939 		lnum = curwin->w_cursor.lnum;
940 		++curwin->w_cursor.lnum;
941 		del_lines((long)(oap->line_count - 1), TRUE);
942 		curwin->w_cursor.lnum = lnum;
943 	    }
944 	    if (u_save_cursor() == FAIL)
945 		return FAIL;
946 	    if (curbuf->b_p_ai)		    /* don't delete indent */
947 	    {
948 		beginline(BL_WHITE);	    /* cursor on first non-white */
949 		did_ai = TRUE;		    /* delete the indent when ESC hit */
950 		ai_col = curwin->w_cursor.col;
951 	    }
952 	    else
953 		beginline(0);		    /* cursor in column 0 */
954 	    truncate_line(FALSE);   /* delete the rest of the line */
955 				    /* leave cursor past last char in line */
956 	    if (oap->line_count > 1)
957 		u_clearline();	    /* "U" command not possible after "2cc" */
958 	}
959 	else
960 	{
961 	    del_lines(oap->line_count, TRUE);
962 	    beginline(BL_WHITE | BL_FIX);
963 	    u_clearline();	/* "U" command not possible after "dd" */
964 	}
965     }
966     else
967     {
968 	if (virtual_op)
969 	{
970 	    int		endcol = 0;
971 
972 	    /* For virtualedit: break the tabs that are partly included. */
973 	    if (gchar_pos(&oap->start) == '\t')
974 	    {
975 		if (u_save_cursor() == FAIL)	/* save first line for undo */
976 		    return FAIL;
977 		if (oap->line_count == 1)
978 		    endcol = getviscol2(oap->end.col, oap->end.coladd);
979 		coladvance_force(getviscol2(oap->start.col, oap->start.coladd));
980 		oap->start = curwin->w_cursor;
981 		if (oap->line_count == 1)
982 		{
983 		    coladvance(endcol);
984 		    oap->end.col = curwin->w_cursor.col;
985 		    oap->end.coladd = curwin->w_cursor.coladd;
986 		    curwin->w_cursor = oap->start;
987 		}
988 	    }
989 
990 	    /* Break a tab only when it's included in the area. */
991 	    if (gchar_pos(&oap->end) == '\t'
992 				     && (int)oap->end.coladd < oap->inclusive)
993 	    {
994 		/* save last line for undo */
995 		if (u_save((linenr_T)(oap->end.lnum - 1),
996 				       (linenr_T)(oap->end.lnum + 1)) == FAIL)
997 		    return FAIL;
998 		curwin->w_cursor = oap->end;
999 		coladvance_force(getviscol2(oap->end.col, oap->end.coladd));
1000 		oap->end = curwin->w_cursor;
1001 		curwin->w_cursor = oap->start;
1002 	    }
1003 	}
1004 
1005 	if (oap->line_count == 1)	/* delete characters within one line */
1006 	{
1007 	    if (u_save_cursor() == FAIL)	/* save line for undo */
1008 		return FAIL;
1009 
1010 	    /* if 'cpoptions' contains '$', display '$' at end of change */
1011 	    if (       vim_strchr(p_cpo, CPO_DOLLAR) != NULL
1012 		    && oap->op_type == OP_CHANGE
1013 		    && oap->end.lnum == curwin->w_cursor.lnum
1014 		    && !oap->is_VIsual)
1015 		display_dollar(oap->end.col - !oap->inclusive);
1016 
1017 	    n = oap->end.col - oap->start.col + 1 - !oap->inclusive;
1018 
1019 	    if (virtual_op)
1020 	    {
1021 		/* fix up things for virtualedit-delete:
1022 		 * break the tabs which are going to get in our way
1023 		 */
1024 		char_u		*curline = ml_get_curline();
1025 		int		len = (int)STRLEN(curline);
1026 
1027 		if (oap->end.coladd != 0
1028 			&& (int)oap->end.col >= len - 1
1029 			&& !(oap->start.coladd && (int)oap->end.col >= len - 1))
1030 		    n++;
1031 		/* Delete at least one char (e.g, when on a control char). */
1032 		if (n == 0 && oap->start.coladd != oap->end.coladd)
1033 		    n = 1;
1034 
1035 		/* When deleted a char in the line, reset coladd. */
1036 		if (gchar_cursor() != NUL)
1037 		    curwin->w_cursor.coladd = 0;
1038 	    }
1039 	    (void)del_bytes((long)n, !virtual_op,
1040 			    oap->op_type == OP_DELETE && !oap->is_VIsual);
1041 	}
1042 	else				/* delete characters between lines */
1043 	{
1044 	    pos_T   curpos;
1045 
1046 	    /* save deleted and changed lines for undo */
1047 	    if (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
1048 		 (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL)
1049 		return FAIL;
1050 
1051 	    truncate_line(TRUE);	/* delete from cursor to end of line */
1052 
1053 	    curpos = curwin->w_cursor;	/* remember curwin->w_cursor */
1054 	    ++curwin->w_cursor.lnum;
1055 	    del_lines((long)(oap->line_count - 2), FALSE);
1056 
1057 	    /* delete from start of line until op_end */
1058 	    n = (oap->end.col + 1 - !oap->inclusive);
1059 	    curwin->w_cursor.col = 0;
1060 	    (void)del_bytes((long)n, !virtual_op,
1061 			    oap->op_type == OP_DELETE && !oap->is_VIsual);
1062 	    curwin->w_cursor = curpos;	/* restore curwin->w_cursor */
1063 	    (void)do_join(2, FALSE, FALSE, FALSE, FALSE);
1064 	}
1065     }
1066 
1067     msgmore(curbuf->b_ml.ml_line_count - old_lcount);
1068 
1069 setmarks:
1070     if (oap->block_mode)
1071     {
1072 	curbuf->b_op_end.lnum = oap->end.lnum;
1073 	curbuf->b_op_end.col = oap->start.col;
1074     }
1075     else
1076 	curbuf->b_op_end = oap->start;
1077     curbuf->b_op_start = oap->start;
1078 
1079     return OK;
1080 }
1081 
1082 /*
1083  * Adjust end of operating area for ending on a multi-byte character.
1084  * Used for deletion.
1085  */
1086     static void
1087 mb_adjust_opend(oparg_T *oap)
1088 {
1089     char_u	*p;
1090 
1091     if (oap->inclusive)
1092     {
1093 	p = ml_get(oap->end.lnum);
1094 	oap->end.col += mb_tail_off(p, p + oap->end.col);
1095     }
1096 }
1097 
1098 /*
1099  * Replace the character under the cursor with "c".
1100  * This takes care of multi-byte characters.
1101  */
1102     static void
1103 replace_character(int c)
1104 {
1105     int n = State;
1106 
1107     State = REPLACE;
1108     ins_char(c);
1109     State = n;
1110     /* Backup to the replaced character. */
1111     dec_cursor();
1112 }
1113 
1114 /*
1115  * Replace a whole area with one character.
1116  */
1117     int
1118 op_replace(oparg_T *oap, int c)
1119 {
1120     int			n, numc;
1121     int			num_chars;
1122     char_u		*newp, *oldp;
1123     size_t		oldlen;
1124     struct block_def	bd;
1125     char_u		*after_p = NULL;
1126     int			had_ctrl_v_cr = FALSE;
1127 
1128     if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty)
1129 	return OK;	    /* nothing to do */
1130 
1131     if (c == REPLACE_CR_NCHAR)
1132     {
1133 	had_ctrl_v_cr = TRUE;
1134 	c = CAR;
1135     }
1136     else if (c == REPLACE_NL_NCHAR)
1137     {
1138 	had_ctrl_v_cr = TRUE;
1139 	c = NL;
1140     }
1141 
1142     if (has_mbyte)
1143 	mb_adjust_opend(oap);
1144 
1145     if (u_save((linenr_T)(oap->start.lnum - 1),
1146 				       (linenr_T)(oap->end.lnum + 1)) == FAIL)
1147 	return FAIL;
1148 
1149     /*
1150      * block mode replace
1151      */
1152     if (oap->block_mode)
1153     {
1154 	bd.is_MAX = (curwin->w_curswant == MAXCOL);
1155 	for ( ; curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum)
1156 	{
1157 	    curwin->w_cursor.col = 0;  /* make sure cursor position is valid */
1158 	    block_prep(oap, &bd, curwin->w_cursor.lnum, TRUE);
1159 	    if (bd.textlen == 0 && (!virtual_op || bd.is_MAX))
1160 		continue;	    /* nothing to replace */
1161 
1162 	    /* n == number of extra chars required
1163 	     * If we split a TAB, it may be replaced by several characters.
1164 	     * Thus the number of characters may increase!
1165 	     */
1166 	    /* If the range starts in virtual space, count the initial
1167 	     * coladd offset as part of "startspaces" */
1168 	    if (virtual_op && bd.is_short && *bd.textstart == NUL)
1169 	    {
1170 		pos_T vpos;
1171 
1172 		vpos.lnum = curwin->w_cursor.lnum;
1173 		getvpos(&vpos, oap->start_vcol);
1174 		bd.startspaces += vpos.coladd;
1175 		n = bd.startspaces;
1176 	    }
1177 	    else
1178 		/* allow for pre spaces */
1179 		n = (bd.startspaces ? bd.start_char_vcols - 1 : 0);
1180 
1181 	    /* allow for post spp */
1182 	    n += (bd.endspaces
1183 		    && !bd.is_oneChar
1184 		    && bd.end_char_vcols > 0) ? bd.end_char_vcols - 1 : 0;
1185 	    /* Figure out how many characters to replace. */
1186 	    numc = oap->end_vcol - oap->start_vcol + 1;
1187 	    if (bd.is_short && (!virtual_op || bd.is_MAX))
1188 		numc -= (oap->end_vcol - bd.end_vcol) + 1;
1189 
1190 	    /* A double-wide character can be replaced only up to half the
1191 	     * times. */
1192 	    if ((*mb_char2cells)(c) > 1)
1193 	    {
1194 		if ((numc & 1) && !bd.is_short)
1195 		{
1196 		    ++bd.endspaces;
1197 		    ++n;
1198 		}
1199 		numc = numc / 2;
1200 	    }
1201 
1202 	    /* Compute bytes needed, move character count to num_chars. */
1203 	    num_chars = numc;
1204 	    numc *= (*mb_char2len)(c);
1205 	    /* oldlen includes textlen, so don't double count */
1206 	    n += numc - bd.textlen;
1207 
1208 	    oldp = ml_get_curline();
1209 	    oldlen = STRLEN(oldp);
1210 	    newp = alloc(oldlen + 1 + n);
1211 	    if (newp == NULL)
1212 		continue;
1213 	    vim_memset(newp, NUL, (size_t)(oldlen + 1 + n));
1214 	    /* copy up to deleted part */
1215 	    mch_memmove(newp, oldp, (size_t)bd.textcol);
1216 	    oldp += bd.textcol + bd.textlen;
1217 	    /* insert pre-spaces */
1218 	    vim_memset(newp + bd.textcol, ' ', (size_t)bd.startspaces);
1219 	    /* insert replacement chars CHECK FOR ALLOCATED SPACE */
1220 	    /* REPLACE_CR_NCHAR/REPLACE_NL_NCHAR is used for entering CR
1221 	     * literally. */
1222 	    if (had_ctrl_v_cr || (c != '\r' && c != '\n'))
1223 	    {
1224 		if (has_mbyte)
1225 		{
1226 		    n = (int)STRLEN(newp);
1227 		    while (--num_chars >= 0)
1228 			n += (*mb_char2bytes)(c, newp + n);
1229 		}
1230 		else
1231 		    vim_memset(newp + STRLEN(newp), c, (size_t)numc);
1232 		if (!bd.is_short)
1233 		{
1234 		    /* insert post-spaces */
1235 		    vim_memset(newp + STRLEN(newp), ' ', (size_t)bd.endspaces);
1236 		    /* copy the part after the changed part */
1237 		    STRMOVE(newp + STRLEN(newp), oldp);
1238 		}
1239 	    }
1240 	    else
1241 	    {
1242 		/* Replacing with \r or \n means splitting the line. */
1243 		after_p = alloc(oldlen + 1 + n - STRLEN(newp));
1244 		if (after_p != NULL)
1245 		    STRMOVE(after_p, oldp);
1246 	    }
1247 	    /* replace the line */
1248 	    ml_replace(curwin->w_cursor.lnum, newp, FALSE);
1249 	    if (after_p != NULL)
1250 	    {
1251 		ml_append(curwin->w_cursor.lnum++, after_p, 0, FALSE);
1252 		appended_lines_mark(curwin->w_cursor.lnum, 1L);
1253 		oap->end.lnum++;
1254 		vim_free(after_p);
1255 	    }
1256 	}
1257     }
1258     else
1259     {
1260 	/*
1261 	 * MCHAR and MLINE motion replace.
1262 	 */
1263 	if (oap->motion_type == MLINE)
1264 	{
1265 	    oap->start.col = 0;
1266 	    curwin->w_cursor.col = 0;
1267 	    oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
1268 	    if (oap->end.col)
1269 		--oap->end.col;
1270 	}
1271 	else if (!oap->inclusive)
1272 	    dec(&(oap->end));
1273 
1274 	while (LTOREQ_POS(curwin->w_cursor, oap->end))
1275 	{
1276 	    n = gchar_cursor();
1277 	    if (n != NUL)
1278 	    {
1279 		if ((*mb_char2len)(c) > 1 || (*mb_char2len)(n) > 1)
1280 		{
1281 		    /* This is slow, but it handles replacing a single-byte
1282 		     * with a multi-byte and the other way around. */
1283 		    if (curwin->w_cursor.lnum == oap->end.lnum)
1284 			oap->end.col += (*mb_char2len)(c) - (*mb_char2len)(n);
1285 		    replace_character(c);
1286 		}
1287 		else
1288 		{
1289 		    if (n == TAB)
1290 		    {
1291 			int end_vcol = 0;
1292 
1293 			if (curwin->w_cursor.lnum == oap->end.lnum)
1294 			{
1295 			    /* oap->end has to be recalculated when
1296 			     * the tab breaks */
1297 			    end_vcol = getviscol2(oap->end.col,
1298 							     oap->end.coladd);
1299 			}
1300 			coladvance_force(getviscol());
1301 			if (curwin->w_cursor.lnum == oap->end.lnum)
1302 			    getvpos(&oap->end, end_vcol);
1303 		    }
1304 		    PBYTE(curwin->w_cursor, c);
1305 		}
1306 	    }
1307 	    else if (virtual_op && curwin->w_cursor.lnum == oap->end.lnum)
1308 	    {
1309 		int virtcols = oap->end.coladd;
1310 
1311 		if (curwin->w_cursor.lnum == oap->start.lnum
1312 			&& oap->start.col == oap->end.col && oap->start.coladd)
1313 		    virtcols -= oap->start.coladd;
1314 
1315 		/* oap->end has been trimmed so it's effectively inclusive;
1316 		 * as a result an extra +1 must be counted so we don't
1317 		 * trample the NUL byte. */
1318 		coladvance_force(getviscol2(oap->end.col, oap->end.coladd) + 1);
1319 		curwin->w_cursor.col -= (virtcols + 1);
1320 		for (; virtcols >= 0; virtcols--)
1321 		{
1322                    if ((*mb_char2len)(c) > 1)
1323 		       replace_character(c);
1324                    else
1325 			PBYTE(curwin->w_cursor, c);
1326 		   if (inc(&curwin->w_cursor) == -1)
1327 		       break;
1328 		}
1329 	    }
1330 
1331 	    /* Advance to next character, stop at the end of the file. */
1332 	    if (inc_cursor() == -1)
1333 		break;
1334 	}
1335     }
1336 
1337     curwin->w_cursor = oap->start;
1338     check_cursor();
1339     changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L);
1340 
1341     /* Set "'[" and "']" marks. */
1342     curbuf->b_op_start = oap->start;
1343     curbuf->b_op_end = oap->end;
1344 
1345     return OK;
1346 }
1347 
1348 static int swapchars(int op_type, pos_T *pos, int length);
1349 
1350 /*
1351  * Handle the (non-standard vi) tilde operator.  Also for "gu", "gU" and "g?".
1352  */
1353     void
1354 op_tilde(oparg_T *oap)
1355 {
1356     pos_T		pos;
1357     struct block_def	bd;
1358     int			did_change = FALSE;
1359 
1360     if (u_save((linenr_T)(oap->start.lnum - 1),
1361 				       (linenr_T)(oap->end.lnum + 1)) == FAIL)
1362 	return;
1363 
1364     pos = oap->start;
1365     if (oap->block_mode)		    /* Visual block mode */
1366     {
1367 	for (; pos.lnum <= oap->end.lnum; ++pos.lnum)
1368 	{
1369 	    int one_change;
1370 
1371 	    block_prep(oap, &bd, pos.lnum, FALSE);
1372 	    pos.col = bd.textcol;
1373 	    one_change = swapchars(oap->op_type, &pos, bd.textlen);
1374 	    did_change |= one_change;
1375 
1376 #ifdef FEAT_NETBEANS_INTG
1377 	    if (netbeans_active() && one_change)
1378 	    {
1379 		char_u *ptr = ml_get_buf(curbuf, pos.lnum, FALSE);
1380 
1381 		netbeans_removed(curbuf, pos.lnum, bd.textcol,
1382 							    (long)bd.textlen);
1383 		netbeans_inserted(curbuf, pos.lnum, bd.textcol,
1384 						&ptr[bd.textcol], bd.textlen);
1385 	    }
1386 #endif
1387 	}
1388 	if (did_change)
1389 	    changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
1390     }
1391     else				    /* not block mode */
1392     {
1393 	if (oap->motion_type == MLINE)
1394 	{
1395 	    oap->start.col = 0;
1396 	    pos.col = 0;
1397 	    oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
1398 	    if (oap->end.col)
1399 		--oap->end.col;
1400 	}
1401 	else if (!oap->inclusive)
1402 	    dec(&(oap->end));
1403 
1404 	if (pos.lnum == oap->end.lnum)
1405 	    did_change = swapchars(oap->op_type, &pos,
1406 						  oap->end.col - pos.col + 1);
1407 	else
1408 	    for (;;)
1409 	    {
1410 		did_change |= swapchars(oap->op_type, &pos,
1411 				pos.lnum == oap->end.lnum ? oap->end.col + 1:
1412 					   (int)STRLEN(ml_get_pos(&pos)));
1413 		if (LTOREQ_POS(oap->end, pos) || inc(&pos) == -1)
1414 		    break;
1415 	    }
1416 	if (did_change)
1417 	{
1418 	    changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1,
1419 									  0L);
1420 #ifdef FEAT_NETBEANS_INTG
1421 	    if (netbeans_active() && did_change)
1422 	    {
1423 		char_u *ptr;
1424 		int count;
1425 
1426 		pos = oap->start;
1427 		while (pos.lnum < oap->end.lnum)
1428 		{
1429 		    ptr = ml_get_buf(curbuf, pos.lnum, FALSE);
1430 		    count = (int)STRLEN(ptr) - pos.col;
1431 		    netbeans_removed(curbuf, pos.lnum, pos.col, (long)count);
1432 		    netbeans_inserted(curbuf, pos.lnum, pos.col,
1433 							&ptr[pos.col], count);
1434 		    pos.col = 0;
1435 		    pos.lnum++;
1436 		}
1437 		ptr = ml_get_buf(curbuf, pos.lnum, FALSE);
1438 		count = oap->end.col - pos.col + 1;
1439 		netbeans_removed(curbuf, pos.lnum, pos.col, (long)count);
1440 		netbeans_inserted(curbuf, pos.lnum, pos.col,
1441 							&ptr[pos.col], count);
1442 	    }
1443 #endif
1444 	}
1445     }
1446 
1447     if (!did_change && oap->is_VIsual)
1448 	/* No change: need to remove the Visual selection */
1449 	redraw_curbuf_later(INVERTED);
1450 
1451     /*
1452      * Set '[ and '] marks.
1453      */
1454     curbuf->b_op_start = oap->start;
1455     curbuf->b_op_end = oap->end;
1456 
1457     if (oap->line_count > p_report)
1458 	smsg(NGETTEXT("%ld line changed", "%ld lines changed",
1459 					    oap->line_count), oap->line_count);
1460 }
1461 
1462 /*
1463  * Invoke swapchar() on "length" bytes at position "pos".
1464  * "pos" is advanced to just after the changed characters.
1465  * "length" is rounded up to include the whole last multi-byte character.
1466  * Also works correctly when the number of bytes changes.
1467  * Returns TRUE if some character was changed.
1468  */
1469     static int
1470 swapchars(int op_type, pos_T *pos, int length)
1471 {
1472     int todo;
1473     int	did_change = 0;
1474 
1475     for (todo = length; todo > 0; --todo)
1476     {
1477 	if (has_mbyte)
1478 	{
1479 	    int len = (*mb_ptr2len)(ml_get_pos(pos));
1480 
1481 	    /* we're counting bytes, not characters */
1482 	    if (len > 0)
1483 		todo -= len - 1;
1484 	}
1485 	did_change |= swapchar(op_type, pos);
1486 	if (inc(pos) == -1)    /* at end of file */
1487 	    break;
1488     }
1489     return did_change;
1490 }
1491 
1492 /*
1493  * If op_type == OP_UPPER: make uppercase,
1494  * if op_type == OP_LOWER: make lowercase,
1495  * if op_type == OP_ROT13: do rot13 encoding,
1496  * else swap case of character at 'pos'
1497  * returns TRUE when something actually changed.
1498  */
1499     int
1500 swapchar(int op_type, pos_T *pos)
1501 {
1502     int	    c;
1503     int	    nc;
1504 
1505     c = gchar_pos(pos);
1506 
1507     /* Only do rot13 encoding for ASCII characters. */
1508     if (c >= 0x80 && op_type == OP_ROT13)
1509 	return FALSE;
1510 
1511     if (op_type == OP_UPPER && c == 0xdf
1512 		      && (enc_latin1like || STRCMP(p_enc, "iso-8859-2") == 0))
1513     {
1514 	pos_T   sp = curwin->w_cursor;
1515 
1516 	/* Special handling of German sharp s: change to "SS". */
1517 	curwin->w_cursor = *pos;
1518 	del_char(FALSE);
1519 	ins_char('S');
1520 	ins_char('S');
1521 	curwin->w_cursor = sp;
1522 	inc(pos);
1523     }
1524 
1525     if (enc_dbcs != 0 && c >= 0x100)	/* No lower/uppercase letter */
1526 	return FALSE;
1527     nc = c;
1528     if (MB_ISLOWER(c))
1529     {
1530 	if (op_type == OP_ROT13)
1531 	    nc = ROT13(c, 'a');
1532 	else if (op_type != OP_LOWER)
1533 	    nc = MB_TOUPPER(c);
1534     }
1535     else if (MB_ISUPPER(c))
1536     {
1537 	if (op_type == OP_ROT13)
1538 	    nc = ROT13(c, 'A');
1539 	else if (op_type != OP_UPPER)
1540 	    nc = MB_TOLOWER(c);
1541     }
1542     if (nc != c)
1543     {
1544 	if (enc_utf8 && (c >= 0x80 || nc >= 0x80))
1545 	{
1546 	    pos_T   sp = curwin->w_cursor;
1547 
1548 	    curwin->w_cursor = *pos;
1549 	    /* don't use del_char(), it also removes composing chars */
1550 	    del_bytes(utf_ptr2len(ml_get_cursor()), FALSE, FALSE);
1551 	    ins_char(nc);
1552 	    curwin->w_cursor = sp;
1553 	}
1554 	else
1555 	    PBYTE(*pos, nc);
1556 	return TRUE;
1557     }
1558     return FALSE;
1559 }
1560 
1561 /*
1562  * op_insert - Insert and append operators for Visual mode.
1563  */
1564     void
1565 op_insert(oparg_T *oap, long count1)
1566 {
1567     long		ins_len, pre_textlen = 0;
1568     char_u		*firstline, *ins_text;
1569     colnr_T		ind_pre = 0, ind_post;
1570     struct block_def	bd;
1571     int			i;
1572     pos_T		t1;
1573 
1574     /* edit() changes this - record it for OP_APPEND */
1575     bd.is_MAX = (curwin->w_curswant == MAXCOL);
1576 
1577     /* vis block is still marked. Get rid of it now. */
1578     curwin->w_cursor.lnum = oap->start.lnum;
1579     update_screen(INVERTED);
1580 
1581     if (oap->block_mode)
1582     {
1583 	/* When 'virtualedit' is used, need to insert the extra spaces before
1584 	 * doing block_prep().  When only "block" is used, virtual edit is
1585 	 * already disabled, but still need it when calling
1586 	 * coladvance_force(). */
1587 	if (curwin->w_cursor.coladd > 0)
1588 	{
1589 	    int		old_ve_flags = ve_flags;
1590 
1591 	    ve_flags = VE_ALL;
1592 	    if (u_save_cursor() == FAIL)
1593 		return;
1594 	    coladvance_force(oap->op_type == OP_APPEND
1595 					   ? oap->end_vcol + 1 : getviscol());
1596 	    if (oap->op_type == OP_APPEND)
1597 		--curwin->w_cursor.col;
1598 	    ve_flags = old_ve_flags;
1599 	}
1600 	/* Get the info about the block before entering the text */
1601 	block_prep(oap, &bd, oap->start.lnum, TRUE);
1602 	/* Get indent information */
1603 	ind_pre = (colnr_T)getwhitecols_curline();
1604 	firstline = ml_get(oap->start.lnum) + bd.textcol;
1605 
1606 	if (oap->op_type == OP_APPEND)
1607 	    firstline += bd.textlen;
1608 	pre_textlen = (long)STRLEN(firstline);
1609     }
1610 
1611     if (oap->op_type == OP_APPEND)
1612     {
1613 	if (oap->block_mode && curwin->w_cursor.coladd == 0)
1614 	{
1615 	    /* Move the cursor to the character right of the block. */
1616 	    curwin->w_set_curswant = TRUE;
1617 	    while (*ml_get_cursor() != NUL
1618 		    && (curwin->w_cursor.col < bd.textcol + bd.textlen))
1619 		++curwin->w_cursor.col;
1620 	    if (bd.is_short && !bd.is_MAX)
1621 	    {
1622 		/* First line was too short, make it longer and adjust the
1623 		 * values in "bd". */
1624 		if (u_save_cursor() == FAIL)
1625 		    return;
1626 		for (i = 0; i < bd.endspaces; ++i)
1627 		    ins_char(' ');
1628 		bd.textlen += bd.endspaces;
1629 	    }
1630 	}
1631 	else
1632 	{
1633 	    curwin->w_cursor = oap->end;
1634 	    check_cursor_col();
1635 
1636 	    /* Works just like an 'i'nsert on the next character. */
1637 	    if (!LINEEMPTY(curwin->w_cursor.lnum)
1638 		    && oap->start_vcol != oap->end_vcol)
1639 		inc_cursor();
1640 	}
1641     }
1642 
1643     t1 = oap->start;
1644     (void)edit(NUL, FALSE, (linenr_T)count1);
1645 
1646     /* When a tab was inserted, and the characters in front of the tab
1647      * have been converted to a tab as well, the column of the cursor
1648      * might have actually been reduced, so need to adjust here. */
1649     if (t1.lnum == curbuf->b_op_start_orig.lnum
1650 	    && LT_POS(curbuf->b_op_start_orig, t1))
1651 	oap->start = curbuf->b_op_start_orig;
1652 
1653     /* If user has moved off this line, we don't know what to do, so do
1654      * nothing.
1655      * Also don't repeat the insert when Insert mode ended with CTRL-C. */
1656     if (curwin->w_cursor.lnum != oap->start.lnum || got_int)
1657 	return;
1658 
1659     if (oap->block_mode)
1660     {
1661 	struct block_def	bd2;
1662 	int			did_indent = FALSE;
1663 	size_t			len;
1664 	int			add;
1665 
1666 	/* If indent kicked in, the firstline might have changed
1667 	 * but only do that, if the indent actually increased. */
1668 	ind_post = (colnr_T)getwhitecols_curline();
1669 	if (curbuf->b_op_start.col > ind_pre && ind_post > ind_pre)
1670 	{
1671 	    bd.textcol += ind_post - ind_pre;
1672 	    bd.start_vcol += ind_post - ind_pre;
1673 	    did_indent = TRUE;
1674 	}
1675 
1676 	/* The user may have moved the cursor before inserting something, try
1677 	 * to adjust the block for that.  But only do it, if the difference
1678 	 * does not come from indent kicking in. */
1679 	if (oap->start.lnum == curbuf->b_op_start_orig.lnum
1680 						  && !bd.is_MAX && !did_indent)
1681 	{
1682 	    if (oap->op_type == OP_INSERT
1683 		    && oap->start.col + oap->start.coladd
1684 			!= curbuf->b_op_start_orig.col
1685 					      + curbuf->b_op_start_orig.coladd)
1686 	    {
1687 		int t = getviscol2(curbuf->b_op_start_orig.col,
1688 					      curbuf->b_op_start_orig.coladd);
1689 		oap->start.col = curbuf->b_op_start_orig.col;
1690 		pre_textlen -= t - oap->start_vcol;
1691 		oap->start_vcol = t;
1692 	    }
1693 	    else if (oap->op_type == OP_APPEND
1694 		      && oap->end.col + oap->end.coladd
1695 			>= curbuf->b_op_start_orig.col
1696 					      + curbuf->b_op_start_orig.coladd)
1697 	    {
1698 		int t = getviscol2(curbuf->b_op_start_orig.col,
1699 					      curbuf->b_op_start_orig.coladd);
1700 		oap->start.col = curbuf->b_op_start_orig.col;
1701 		/* reset pre_textlen to the value of OP_INSERT */
1702 		pre_textlen += bd.textlen;
1703 		pre_textlen -= t - oap->start_vcol;
1704 		oap->start_vcol = t;
1705 		oap->op_type = OP_INSERT;
1706 	    }
1707 	}
1708 
1709 	/*
1710 	 * Spaces and tabs in the indent may have changed to other spaces and
1711 	 * tabs.  Get the starting column again and correct the length.
1712 	 * Don't do this when "$" used, end-of-line will have changed.
1713 	 */
1714 	block_prep(oap, &bd2, oap->start.lnum, TRUE);
1715 	if (!bd.is_MAX || bd2.textlen < bd.textlen)
1716 	{
1717 	    if (oap->op_type == OP_APPEND)
1718 	    {
1719 		pre_textlen += bd2.textlen - bd.textlen;
1720 		if (bd2.endspaces)
1721 		    --bd2.textlen;
1722 	    }
1723 	    bd.textcol = bd2.textcol;
1724 	    bd.textlen = bd2.textlen;
1725 	}
1726 
1727 	/*
1728 	 * Subsequent calls to ml_get() flush the firstline data - take a
1729 	 * copy of the required string.
1730 	 */
1731 	firstline = ml_get(oap->start.lnum);
1732 	len = STRLEN(firstline);
1733 	add = bd.textcol;
1734 	if (oap->op_type == OP_APPEND)
1735 	    add += bd.textlen;
1736 	if ((size_t)add > len)
1737 	    firstline += len;  // short line, point to the NUL
1738 	else
1739 	    firstline += add;
1740 	if (pre_textlen >= 0
1741 		     && (ins_len = (long)STRLEN(firstline) - pre_textlen) > 0)
1742 	{
1743 	    ins_text = vim_strnsave(firstline, (int)ins_len);
1744 	    if (ins_text != NULL)
1745 	    {
1746 		/* block handled here */
1747 		if (u_save(oap->start.lnum,
1748 					 (linenr_T)(oap->end.lnum + 1)) == OK)
1749 		    block_insert(oap, ins_text, (oap->op_type == OP_INSERT),
1750 									 &bd);
1751 
1752 		curwin->w_cursor.col = oap->start.col;
1753 		check_cursor();
1754 		vim_free(ins_text);
1755 	    }
1756 	}
1757     }
1758 }
1759 
1760 /*
1761  * op_change - handle a change operation
1762  *
1763  * return TRUE if edit() returns because of a CTRL-O command
1764  */
1765     int
1766 op_change(oparg_T *oap)
1767 {
1768     colnr_T		l;
1769     int			retval;
1770     long		offset;
1771     linenr_T		linenr;
1772     long		ins_len;
1773     long		pre_textlen = 0;
1774     long		pre_indent = 0;
1775     char_u		*firstline;
1776     char_u		*ins_text, *newp, *oldp;
1777     struct block_def	bd;
1778 
1779     l = oap->start.col;
1780     if (oap->motion_type == MLINE)
1781     {
1782 	l = 0;
1783 #ifdef FEAT_SMARTINDENT
1784 	if (!p_paste && curbuf->b_p_si
1785 # ifdef FEAT_CINDENT
1786 		&& !curbuf->b_p_cin
1787 # endif
1788 		)
1789 	    can_si = TRUE;	/* It's like opening a new line, do si */
1790 #endif
1791     }
1792 
1793     /* First delete the text in the region.  In an empty buffer only need to
1794      * save for undo */
1795     if (curbuf->b_ml.ml_flags & ML_EMPTY)
1796     {
1797 	if (u_save_cursor() == FAIL)
1798 	    return FALSE;
1799     }
1800     else if (op_delete(oap) == FAIL)
1801 	return FALSE;
1802 
1803     if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum)
1804 							 && !virtual_op)
1805 	inc_cursor();
1806 
1807     /* check for still on same line (<CR> in inserted text meaningless) */
1808     /* skip blank lines too */
1809     if (oap->block_mode)
1810     {
1811 	/* Add spaces before getting the current line length. */
1812 	if (virtual_op && (curwin->w_cursor.coladd > 0
1813 						    || gchar_cursor() == NUL))
1814 	    coladvance_force(getviscol());
1815 	firstline = ml_get(oap->start.lnum);
1816 	pre_textlen = (long)STRLEN(firstline);
1817 	pre_indent = (long)getwhitecols(firstline);
1818 	bd.textcol = curwin->w_cursor.col;
1819     }
1820 
1821 #if defined(FEAT_LISP) || defined(FEAT_CINDENT)
1822     if (oap->motion_type == MLINE)
1823 	fix_indent();
1824 #endif
1825 
1826     retval = edit(NUL, FALSE, (linenr_T)1);
1827 
1828     /*
1829      * In Visual block mode, handle copying the new text to all lines of the
1830      * block.
1831      * Don't repeat the insert when Insert mode ended with CTRL-C.
1832      */
1833     if (oap->block_mode && oap->start.lnum != oap->end.lnum && !got_int)
1834     {
1835 	/* Auto-indenting may have changed the indent.  If the cursor was past
1836 	 * the indent, exclude that indent change from the inserted text. */
1837 	firstline = ml_get(oap->start.lnum);
1838 	if (bd.textcol > (colnr_T)pre_indent)
1839 	{
1840 	    long new_indent = (long)getwhitecols(firstline);
1841 
1842 	    pre_textlen += new_indent - pre_indent;
1843 	    bd.textcol += new_indent - pre_indent;
1844 	}
1845 
1846 	ins_len = (long)STRLEN(firstline) - pre_textlen;
1847 	if (ins_len > 0)
1848 	{
1849 	    /* Subsequent calls to ml_get() flush the firstline data - take a
1850 	     * copy of the inserted text.  */
1851 	    if ((ins_text = alloc(ins_len + 1)) != NULL)
1852 	    {
1853 		vim_strncpy(ins_text, firstline + bd.textcol, (size_t)ins_len);
1854 		for (linenr = oap->start.lnum + 1; linenr <= oap->end.lnum;
1855 								     linenr++)
1856 		{
1857 		    block_prep(oap, &bd, linenr, TRUE);
1858 		    if (!bd.is_short || virtual_op)
1859 		    {
1860 			pos_T vpos;
1861 
1862 			/* If the block starts in virtual space, count the
1863 			 * initial coladd offset as part of "startspaces" */
1864 			if (bd.is_short)
1865 			{
1866 			    vpos.lnum = linenr;
1867 			    (void)getvpos(&vpos, oap->start_vcol);
1868 			}
1869 			else
1870 			    vpos.coladd = 0;
1871 			oldp = ml_get(linenr);
1872 			newp = alloc(STRLEN(oldp) + vpos.coladd + ins_len + 1);
1873 			if (newp == NULL)
1874 			    continue;
1875 			/* copy up to block start */
1876 			mch_memmove(newp, oldp, (size_t)bd.textcol);
1877 			offset = bd.textcol;
1878 			vim_memset(newp + offset, ' ', (size_t)vpos.coladd);
1879 			offset += vpos.coladd;
1880 			mch_memmove(newp + offset, ins_text, (size_t)ins_len);
1881 			offset += ins_len;
1882 			oldp += bd.textcol;
1883 			STRMOVE(newp + offset, oldp);
1884 			ml_replace(linenr, newp, FALSE);
1885 		    }
1886 		}
1887 		check_cursor();
1888 
1889 		changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L);
1890 	    }
1891 	    vim_free(ins_text);
1892 	}
1893     }
1894 
1895     return retval;
1896 }
1897 
1898 /*
1899  * When the cursor is on the NUL past the end of the line and it should not be
1900  * there move it left.
1901  */
1902     void
1903 adjust_cursor_eol(void)
1904 {
1905     if (curwin->w_cursor.col > 0
1906 	    && gchar_cursor() == NUL
1907 	    && (ve_flags & VE_ONEMORE) == 0
1908 	    && !(restart_edit || (State & INSERT)))
1909     {
1910 	/* Put the cursor on the last character in the line. */
1911 	dec_cursor();
1912 
1913 	if (ve_flags == VE_ALL)
1914 	{
1915 	    colnr_T	    scol, ecol;
1916 
1917 	    /* Coladd is set to the width of the last character. */
1918 	    getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol);
1919 	    curwin->w_cursor.coladd = ecol - scol + 1;
1920 	}
1921     }
1922 }
1923 
1924 #if defined(FEAT_SMARTINDENT) || defined(FEAT_CINDENT) || defined(PROTO)
1925 /*
1926  * Return TRUE if lines starting with '#' should be left aligned.
1927  */
1928     int
1929 preprocs_left(void)
1930 {
1931     return
1932 # ifdef FEAT_SMARTINDENT
1933 #  ifdef FEAT_CINDENT
1934 	(curbuf->b_p_si && !curbuf->b_p_cin) ||
1935 #  else
1936 	curbuf->b_p_si
1937 #  endif
1938 # endif
1939 # ifdef FEAT_CINDENT
1940 	(curbuf->b_p_cin && in_cinkeys('#', ' ', TRUE)
1941 					   && curbuf->b_ind_hash_comment == 0)
1942 # endif
1943 	;
1944 }
1945 #endif
1946 
1947 #if defined(FEAT_COMMENTS) || defined(PROTO)
1948 /*
1949  * If "process" is TRUE and the line begins with a comment leader (possibly
1950  * after some white space), return a pointer to the text after it. Put a boolean
1951  * value indicating whether the line ends with an unclosed comment in
1952  * "is_comment".
1953  * line - line to be processed,
1954  * process - if FALSE, will only check whether the line ends with an unclosed
1955  *	     comment,
1956  * include_space - whether to also skip space following the comment leader,
1957  * is_comment - will indicate whether the current line ends with an unclosed
1958  *		comment.
1959  */
1960     char_u *
1961 skip_comment(
1962     char_u   *line,
1963     int      process,
1964     int	     include_space,
1965     int      *is_comment)
1966 {
1967     char_u *comment_flags = NULL;
1968     int    lead_len;
1969     int    leader_offset = get_last_leader_offset(line, &comment_flags);
1970 
1971     *is_comment = FALSE;
1972     if (leader_offset != -1)
1973     {
1974 	/* Let's check whether the line ends with an unclosed comment.
1975 	 * If the last comment leader has COM_END in flags, there's no comment.
1976 	 */
1977 	while (*comment_flags)
1978 	{
1979 	    if (*comment_flags == COM_END
1980 		    || *comment_flags == ':')
1981 		break;
1982 	    ++comment_flags;
1983 	}
1984 	if (*comment_flags != COM_END)
1985 	    *is_comment = TRUE;
1986     }
1987 
1988     if (process == FALSE)
1989 	return line;
1990 
1991     lead_len = get_leader_len(line, &comment_flags, FALSE, include_space);
1992 
1993     if (lead_len == 0)
1994 	return line;
1995 
1996     /* Find:
1997      * - COM_END,
1998      * - colon,
1999      * whichever comes first.
2000      */
2001     while (*comment_flags)
2002     {
2003 	if (*comment_flags == COM_END
2004 		|| *comment_flags == ':')
2005 	    break;
2006 	++comment_flags;
2007     }
2008 
2009     /* If we found a colon, it means that we are not processing a line
2010      * starting with a closing part of a three-part comment. That's good,
2011      * because we don't want to remove those as this would be annoying.
2012      */
2013     if (*comment_flags == ':' || *comment_flags == NUL)
2014 	line += lead_len;
2015 
2016     return line;
2017 }
2018 #endif
2019 
2020 /*
2021  * Join 'count' lines (minimal 2) at cursor position.
2022  * When "save_undo" is TRUE save lines for undo first.
2023  * Set "use_formatoptions" to FALSE when e.g. processing backspace and comment
2024  * leaders should not be removed.
2025  * When setmark is TRUE, sets the '[ and '] mark, else, the caller is expected
2026  * to set those marks.
2027  *
2028  * return FAIL for failure, OK otherwise
2029  */
2030     int
2031 do_join(
2032     long    count,
2033     int	    insert_space,
2034     int	    save_undo,
2035     int	    use_formatoptions UNUSED,
2036     int	    setmark)
2037 {
2038     char_u	*curr = NULL;
2039     char_u      *curr_start = NULL;
2040     char_u	*cend;
2041     char_u	*newp;
2042     char_u	*spaces;	/* number of spaces inserted before a line */
2043     int		endcurr1 = NUL;
2044     int		endcurr2 = NUL;
2045     int		currsize = 0;	/* size of the current line */
2046     int		sumsize = 0;	/* size of the long new line */
2047     linenr_T	t;
2048     colnr_T	col = 0;
2049     int		ret = OK;
2050 #if defined(FEAT_COMMENTS) || defined(PROTO)
2051     int		*comments = NULL;
2052     int		remove_comments = (use_formatoptions == TRUE)
2053 				  && has_format_option(FO_REMOVE_COMS);
2054     int		prev_was_comment;
2055 #endif
2056 #ifdef FEAT_TEXT_PROP
2057     textprop_T	**prop_lines = NULL;
2058     int		*prop_lengths = NULL;
2059 #endif
2060 
2061     if (save_undo && u_save((linenr_T)(curwin->w_cursor.lnum - 1),
2062 			    (linenr_T)(curwin->w_cursor.lnum + count)) == FAIL)
2063 	return FAIL;
2064 
2065     /* Allocate an array to store the number of spaces inserted before each
2066      * line.  We will use it to pre-compute the length of the new line and the
2067      * proper placement of each original line in the new one. */
2068     spaces = lalloc_clear(count, TRUE);
2069     if (spaces == NULL)
2070 	return FAIL;
2071 #if defined(FEAT_COMMENTS) || defined(PROTO)
2072     if (remove_comments)
2073     {
2074 	comments = lalloc_clear(count * sizeof(int), TRUE);
2075 	if (comments == NULL)
2076 	{
2077 	    vim_free(spaces);
2078 	    return FAIL;
2079 	}
2080     }
2081 #endif
2082 
2083     /*
2084      * Don't move anything yet, just compute the final line length
2085      * and setup the array of space strings lengths
2086      * This loops forward over the joined lines.
2087      */
2088     for (t = 0; t < count; ++t)
2089     {
2090 	curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t));
2091 	if (t == 0 && setmark)
2092 	{
2093 	    /* Set the '[ mark. */
2094 	    curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum;
2095 	    curwin->w_buffer->b_op_start.col  = (colnr_T)STRLEN(curr);
2096 	}
2097 #if defined(FEAT_COMMENTS) || defined(PROTO)
2098 	if (remove_comments)
2099 	{
2100 	    /* We don't want to remove the comment leader if the
2101 	     * previous line is not a comment. */
2102 	    if (t > 0 && prev_was_comment)
2103 	    {
2104 
2105 		char_u *new_curr = skip_comment(curr, TRUE, insert_space,
2106 							   &prev_was_comment);
2107 		comments[t] = (int)(new_curr - curr);
2108 		curr = new_curr;
2109 	    }
2110 	    else
2111 		curr = skip_comment(curr, FALSE, insert_space,
2112 							   &prev_was_comment);
2113 	}
2114 #endif
2115 
2116 	if (insert_space && t > 0)
2117 	{
2118 	    curr = skipwhite(curr);
2119 	    if (*curr != ')' && currsize != 0 && endcurr1 != TAB
2120 		    && (!has_format_option(FO_MBYTE_JOIN)
2121 			|| (mb_ptr2char(curr) < 0x100 && endcurr1 < 0x100))
2122 		    && (!has_format_option(FO_MBYTE_JOIN2)
2123 			|| mb_ptr2char(curr) < 0x100 || endcurr1 < 0x100)
2124 	       )
2125 	    {
2126 		/* don't add a space if the line is ending in a space */
2127 		if (endcurr1 == ' ')
2128 		    endcurr1 = endcurr2;
2129 		else
2130 		    ++spaces[t];
2131 		/* extra space when 'joinspaces' set and line ends in '.' */
2132 		if (       p_js
2133 			&& (endcurr1 == '.'
2134 			    || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
2135 				&& (endcurr1 == '?' || endcurr1 == '!'))))
2136 		    ++spaces[t];
2137 	    }
2138 	}
2139 	currsize = (int)STRLEN(curr);
2140 	sumsize += currsize + spaces[t];
2141 	endcurr1 = endcurr2 = NUL;
2142 	if (insert_space && currsize > 0)
2143 	{
2144 	    if (has_mbyte)
2145 	    {
2146 		cend = curr + currsize;
2147 		MB_PTR_BACK(curr, cend);
2148 		endcurr1 = (*mb_ptr2char)(cend);
2149 		if (cend > curr)
2150 		{
2151 		    MB_PTR_BACK(curr, cend);
2152 		    endcurr2 = (*mb_ptr2char)(cend);
2153 		}
2154 	    }
2155 	    else
2156 	    {
2157 		endcurr1 = *(curr + currsize - 1);
2158 		if (currsize > 1)
2159 		    endcurr2 = *(curr + currsize - 2);
2160 	    }
2161 	}
2162 	line_breakcheck();
2163 	if (got_int)
2164 	{
2165 	    ret = FAIL;
2166 	    goto theend;
2167 	}
2168     }
2169 
2170     /* store the column position before last line */
2171     col = sumsize - currsize - spaces[count - 1];
2172 
2173     /* allocate the space for the new line */
2174     newp = alloc(sumsize + 1);
2175     if (newp == NULL)
2176     {
2177 	ret = FAIL;
2178 	goto theend;
2179     }
2180     cend = newp + sumsize;
2181     *cend = 0;
2182 
2183 #ifdef FEAT_TEXT_PROP
2184     // We need to move properties of the lines that are going to be deleted to
2185     // the new long one.
2186     if (curbuf->b_has_textprop && !text_prop_frozen)
2187     {
2188 	// Allocate an array to copy the text properties of joined lines into.
2189 	// And another array to store the number of properties in each line.
2190 	prop_lines = ALLOC_CLEAR_MULT(textprop_T *, count - 1);
2191 	prop_lengths = ALLOC_CLEAR_MULT(int, count - 1);
2192 	if (prop_lengths == NULL)
2193 	    VIM_CLEAR(prop_lines);
2194     }
2195 #endif
2196 
2197     /*
2198      * Move affected lines to the new long one.
2199      * This loops backwards over the joined lines, including the original line.
2200      *
2201      * Move marks from each deleted line to the joined line, adjusting the
2202      * column.  This is not Vi compatible, but Vi deletes the marks, thus that
2203      * should not really be a problem.
2204      */
2205     for (t = count - 1; ; --t)
2206     {
2207 	int spaces_removed;
2208 
2209 	cend -= currsize;
2210 	mch_memmove(cend, curr, (size_t)currsize);
2211 	if (spaces[t] > 0)
2212 	{
2213 	    cend -= spaces[t];
2214 	    vim_memset(cend, ' ', (size_t)(spaces[t]));
2215 	}
2216 
2217 	// If deleting more spaces than adding, the cursor moves no more than
2218 	// what is added if it is inside these spaces.
2219 	spaces_removed = (curr - curr_start) - spaces[t];
2220 
2221 	mark_col_adjust(curwin->w_cursor.lnum + t, (colnr_T)0, (linenr_T)-t,
2222 			 (long)(cend - newp - spaces_removed), spaces_removed);
2223 	if (t == 0)
2224 	    break;
2225 #ifdef FEAT_TEXT_PROP
2226 	if (prop_lines != NULL)
2227 	    adjust_props_for_join(curwin->w_cursor.lnum + t,
2228 				      prop_lines + t - 1, prop_lengths + t - 1,
2229 			 (long)(cend - newp - spaces_removed), spaces_removed);
2230 #endif
2231 
2232 	curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1));
2233 #if defined(FEAT_COMMENTS)
2234 	if (remove_comments)
2235 	    curr += comments[t - 1];
2236 #endif
2237 	if (insert_space && t > 1)
2238 	    curr = skipwhite(curr);
2239 	currsize = (int)STRLEN(curr);
2240     }
2241 
2242 #ifdef FEAT_TEXT_PROP
2243     if (prop_lines != NULL)
2244 	join_prop_lines(curwin->w_cursor.lnum, newp,
2245 					      prop_lines, prop_lengths, count);
2246     else
2247 #endif
2248 	ml_replace(curwin->w_cursor.lnum, newp, FALSE);
2249 
2250     if (setmark)
2251     {
2252 	/* Set the '] mark. */
2253 	curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum;
2254 	curwin->w_buffer->b_op_end.col  = (colnr_T)sumsize;
2255     }
2256 
2257     /* Only report the change in the first line here, del_lines() will report
2258      * the deleted line. */
2259     changed_lines(curwin->w_cursor.lnum, currsize,
2260 					       curwin->w_cursor.lnum + 1, 0L);
2261     /*
2262      * Delete following lines. To do this we move the cursor there
2263      * briefly, and then move it back. After del_lines() the cursor may
2264      * have moved up (last line deleted), so the current lnum is kept in t.
2265      */
2266     t = curwin->w_cursor.lnum;
2267     ++curwin->w_cursor.lnum;
2268     del_lines(count - 1, FALSE);
2269     curwin->w_cursor.lnum = t;
2270 
2271     /*
2272      * Set the cursor column:
2273      * Vi compatible: use the column of the first join
2274      * vim:	      use the column of the last join
2275      */
2276     curwin->w_cursor.col =
2277 		    (vim_strchr(p_cpo, CPO_JOINCOL) != NULL ? currsize : col);
2278     check_cursor_col();
2279 
2280     curwin->w_cursor.coladd = 0;
2281     curwin->w_set_curswant = TRUE;
2282 
2283 theend:
2284     vim_free(spaces);
2285 #if defined(FEAT_COMMENTS) || defined(PROTO)
2286     if (remove_comments)
2287 	vim_free(comments);
2288 #endif
2289     return ret;
2290 }
2291 
2292 #ifdef FEAT_COMMENTS
2293 /*
2294  * Return TRUE if the two comment leaders given are the same.  "lnum" is
2295  * the first line.  White-space is ignored.  Note that the whole of
2296  * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
2297  */
2298     static int
2299 same_leader(
2300     linenr_T lnum,
2301     int	    leader1_len,
2302     char_u  *leader1_flags,
2303     int	    leader2_len,
2304     char_u  *leader2_flags)
2305 {
2306     int	    idx1 = 0, idx2 = 0;
2307     char_u  *p;
2308     char_u  *line1;
2309     char_u  *line2;
2310 
2311     if (leader1_len == 0)
2312 	return (leader2_len == 0);
2313 
2314     /*
2315      * If first leader has 'f' flag, the lines can be joined only if the
2316      * second line does not have a leader.
2317      * If first leader has 'e' flag, the lines can never be joined.
2318      * If fist leader has 's' flag, the lines can only be joined if there is
2319      * some text after it and the second line has the 'm' flag.
2320      */
2321     if (leader1_flags != NULL)
2322     {
2323 	for (p = leader1_flags; *p && *p != ':'; ++p)
2324 	{
2325 	    if (*p == COM_FIRST)
2326 		return (leader2_len == 0);
2327 	    if (*p == COM_END)
2328 		return FALSE;
2329 	    if (*p == COM_START)
2330 	    {
2331 		if (*(ml_get(lnum) + leader1_len) == NUL)
2332 		    return FALSE;
2333 		if (leader2_flags == NULL || leader2_len == 0)
2334 		    return FALSE;
2335 		for (p = leader2_flags; *p && *p != ':'; ++p)
2336 		    if (*p == COM_MIDDLE)
2337 			return TRUE;
2338 		return FALSE;
2339 	    }
2340 	}
2341     }
2342 
2343     /*
2344      * Get current line and next line, compare the leaders.
2345      * The first line has to be saved, only one line can be locked at a time.
2346      */
2347     line1 = vim_strsave(ml_get(lnum));
2348     if (line1 != NULL)
2349     {
2350 	for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1)
2351 	    ;
2352 	line2 = ml_get(lnum + 1);
2353 	for (idx2 = 0; idx2 < leader2_len; ++idx2)
2354 	{
2355 	    if (!VIM_ISWHITE(line2[idx2]))
2356 	    {
2357 		if (line1[idx1++] != line2[idx2])
2358 		    break;
2359 	    }
2360 	    else
2361 		while (VIM_ISWHITE(line1[idx1]))
2362 		    ++idx1;
2363 	}
2364 	vim_free(line1);
2365     }
2366     return (idx2 == leader2_len && idx1 == leader1_len);
2367 }
2368 #endif
2369 
2370 /*
2371  * Implementation of the format operator 'gq'.
2372  */
2373     void
2374 op_format(
2375     oparg_T	*oap,
2376     int		keep_cursor)		/* keep cursor on same text char */
2377 {
2378     long	old_line_count = curbuf->b_ml.ml_line_count;
2379 
2380     /* Place the cursor where the "gq" or "gw" command was given, so that "u"
2381      * can put it back there. */
2382     curwin->w_cursor = oap->cursor_start;
2383 
2384     if (u_save((linenr_T)(oap->start.lnum - 1),
2385 				       (linenr_T)(oap->end.lnum + 1)) == FAIL)
2386 	return;
2387     curwin->w_cursor = oap->start;
2388 
2389     if (oap->is_VIsual)
2390 	/* When there is no change: need to remove the Visual selection */
2391 	redraw_curbuf_later(INVERTED);
2392 
2393     /* Set '[ mark at the start of the formatted area */
2394     curbuf->b_op_start = oap->start;
2395 
2396     /* For "gw" remember the cursor position and put it back below (adjusted
2397      * for joined and split lines). */
2398     if (keep_cursor)
2399 	saved_cursor = oap->cursor_start;
2400 
2401     format_lines(oap->line_count, keep_cursor);
2402 
2403     /*
2404      * Leave the cursor at the first non-blank of the last formatted line.
2405      * If the cursor was moved one line back (e.g. with "Q}") go to the next
2406      * line, so "." will do the next lines.
2407      */
2408     if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2409 	++curwin->w_cursor.lnum;
2410     beginline(BL_WHITE | BL_FIX);
2411     old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
2412     msgmore(old_line_count);
2413 
2414     /* put '] mark on the end of the formatted area */
2415     curbuf->b_op_end = curwin->w_cursor;
2416 
2417     if (keep_cursor)
2418     {
2419 	curwin->w_cursor = saved_cursor;
2420 	saved_cursor.lnum = 0;
2421     }
2422 
2423     if (oap->is_VIsual)
2424     {
2425 	win_T	*wp;
2426 
2427 	FOR_ALL_WINDOWS(wp)
2428 	{
2429 	    if (wp->w_old_cursor_lnum != 0)
2430 	    {
2431 		/* When lines have been inserted or deleted, adjust the end of
2432 		 * the Visual area to be redrawn. */
2433 		if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum)
2434 		    wp->w_old_cursor_lnum += old_line_count;
2435 		else
2436 		    wp->w_old_visual_lnum += old_line_count;
2437 	    }
2438 	}
2439     }
2440 }
2441 
2442 #if defined(FEAT_EVAL) || defined(PROTO)
2443 /*
2444  * Implementation of the format operator 'gq' for when using 'formatexpr'.
2445  */
2446     void
2447 op_formatexpr(oparg_T *oap)
2448 {
2449     if (oap->is_VIsual)
2450 	/* When there is no change: need to remove the Visual selection */
2451 	redraw_curbuf_later(INVERTED);
2452 
2453     if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0)
2454 	/* As documented: when 'formatexpr' returns non-zero fall back to
2455 	 * internal formatting. */
2456 	op_format(oap, FALSE);
2457 }
2458 
2459     int
2460 fex_format(
2461     linenr_T	lnum,
2462     long	count,
2463     int		c)	/* character to be inserted */
2464 {
2465     int		use_sandbox = was_set_insecurely((char_u *)"formatexpr",
2466 								   OPT_LOCAL);
2467     int		r;
2468     char_u	*fex;
2469 
2470     /*
2471      * Set v:lnum to the first line number and v:count to the number of lines.
2472      * Set v:char to the character to be inserted (can be NUL).
2473      */
2474     set_vim_var_nr(VV_LNUM, lnum);
2475     set_vim_var_nr(VV_COUNT, count);
2476     set_vim_var_char(c);
2477 
2478     /* Make a copy, the option could be changed while calling it. */
2479     fex = vim_strsave(curbuf->b_p_fex);
2480     if (fex == NULL)
2481 	return 0;
2482 
2483     /*
2484      * Evaluate the function.
2485      */
2486     if (use_sandbox)
2487 	++sandbox;
2488     r = (int)eval_to_number(fex);
2489     if (use_sandbox)
2490 	--sandbox;
2491 
2492     set_vim_var_string(VV_CHAR, NULL, -1);
2493     vim_free(fex);
2494 
2495     return r;
2496 }
2497 #endif
2498 
2499 /*
2500  * Format "line_count" lines, starting at the cursor position.
2501  * When "line_count" is negative, format until the end of the paragraph.
2502  * Lines after the cursor line are saved for undo, caller must have saved the
2503  * first line.
2504  */
2505     void
2506 format_lines(
2507     linenr_T	line_count,
2508     int		avoid_fex)		/* don't use 'formatexpr' */
2509 {
2510     int		max_len;
2511     int		is_not_par;		/* current line not part of parag. */
2512     int		next_is_not_par;	/* next line not part of paragraph */
2513     int		is_end_par;		/* at end of paragraph */
2514     int		prev_is_end_par = FALSE;/* prev. line not part of parag. */
2515     int		next_is_start_par = FALSE;
2516 #ifdef FEAT_COMMENTS
2517     int		leader_len = 0;		/* leader len of current line */
2518     int		next_leader_len;	/* leader len of next line */
2519     char_u	*leader_flags = NULL;	/* flags for leader of current line */
2520     char_u	*next_leader_flags;	/* flags for leader of next line */
2521     int		do_comments;		/* format comments */
2522     int		do_comments_list = 0;	/* format comments with 'n' or '2' */
2523 #endif
2524     int		advance = TRUE;
2525     int		second_indent = -1;	/* indent for second line (comment
2526 					 * aware) */
2527     int		do_second_indent;
2528     int		do_number_indent;
2529     int		do_trail_white;
2530     int		first_par_line = TRUE;
2531     int		smd_save;
2532     long	count;
2533     int		need_set_indent = TRUE;	/* set indent of next paragraph */
2534     int		force_format = FALSE;
2535     int		old_State = State;
2536 
2537     /* length of a line to force formatting: 3 * 'tw' */
2538     max_len = comp_textwidth(TRUE) * 3;
2539 
2540     /* check for 'q', '2' and '1' in 'formatoptions' */
2541 #ifdef FEAT_COMMENTS
2542     do_comments = has_format_option(FO_Q_COMS);
2543 #endif
2544     do_second_indent = has_format_option(FO_Q_SECOND);
2545     do_number_indent = has_format_option(FO_Q_NUMBER);
2546     do_trail_white = has_format_option(FO_WHITE_PAR);
2547 
2548     /*
2549      * Get info about the previous and current line.
2550      */
2551     if (curwin->w_cursor.lnum > 1)
2552 	is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1
2553 #ifdef FEAT_COMMENTS
2554 				, &leader_len, &leader_flags, do_comments
2555 #endif
2556 				);
2557     else
2558 	is_not_par = TRUE;
2559     next_is_not_par = fmt_check_par(curwin->w_cursor.lnum
2560 #ifdef FEAT_COMMENTS
2561 			   , &next_leader_len, &next_leader_flags, do_comments
2562 #endif
2563 				);
2564     is_end_par = (is_not_par || next_is_not_par);
2565     if (!is_end_par && do_trail_white)
2566 	is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
2567 
2568     curwin->w_cursor.lnum--;
2569     for (count = line_count; count != 0 && !got_int; --count)
2570     {
2571 	/*
2572 	 * Advance to next paragraph.
2573 	 */
2574 	if (advance)
2575 	{
2576 	    curwin->w_cursor.lnum++;
2577 	    prev_is_end_par = is_end_par;
2578 	    is_not_par = next_is_not_par;
2579 #ifdef FEAT_COMMENTS
2580 	    leader_len = next_leader_len;
2581 	    leader_flags = next_leader_flags;
2582 #endif
2583 	}
2584 
2585 	/*
2586 	 * The last line to be formatted.
2587 	 */
2588 	if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
2589 	{
2590 	    next_is_not_par = TRUE;
2591 #ifdef FEAT_COMMENTS
2592 	    next_leader_len = 0;
2593 	    next_leader_flags = NULL;
2594 #endif
2595 	}
2596 	else
2597 	{
2598 	    next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1
2599 #ifdef FEAT_COMMENTS
2600 			   , &next_leader_len, &next_leader_flags, do_comments
2601 #endif
2602 					);
2603 	    if (do_number_indent)
2604 		next_is_start_par =
2605 			   (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
2606 	}
2607 	advance = TRUE;
2608 	is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
2609 	if (!is_end_par && do_trail_white)
2610 	    is_end_par = !ends_in_white(curwin->w_cursor.lnum);
2611 
2612 	/*
2613 	 * Skip lines that are not in a paragraph.
2614 	 */
2615 	if (is_not_par)
2616 	{
2617 	    if (line_count < 0)
2618 		break;
2619 	}
2620 	else
2621 	{
2622 	    /*
2623 	     * For the first line of a paragraph, check indent of second line.
2624 	     * Don't do this for comments and empty lines.
2625 	     */
2626 	    if (first_par_line
2627 		    && (do_second_indent || do_number_indent)
2628 		    && prev_is_end_par
2629 		    && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2630 	    {
2631 		if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1))
2632 		{
2633 #ifdef FEAT_COMMENTS
2634 		    if (leader_len == 0 && next_leader_len == 0)
2635 		    {
2636 			/* no comment found */
2637 #endif
2638 			second_indent =
2639 				   get_indent_lnum(curwin->w_cursor.lnum + 1);
2640 #ifdef FEAT_COMMENTS
2641 		    }
2642 		    else
2643 		    {
2644 			second_indent = next_leader_len;
2645 			do_comments_list = 1;
2646 		    }
2647 #endif
2648 		}
2649 		else if (do_number_indent)
2650 		{
2651 #ifdef FEAT_COMMENTS
2652 		    if (leader_len == 0 && next_leader_len == 0)
2653 		    {
2654 			/* no comment found */
2655 #endif
2656 			second_indent =
2657 				     get_number_indent(curwin->w_cursor.lnum);
2658 #ifdef FEAT_COMMENTS
2659 		    }
2660 		    else
2661 		    {
2662 			/* get_number_indent() is now "comment aware"... */
2663 			second_indent =
2664 				     get_number_indent(curwin->w_cursor.lnum);
2665 			do_comments_list = 1;
2666 		    }
2667 #endif
2668 		}
2669 	    }
2670 
2671 	    /*
2672 	     * When the comment leader changes, it's the end of the paragraph.
2673 	     */
2674 	    if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
2675 #ifdef FEAT_COMMENTS
2676 		    || !same_leader(curwin->w_cursor.lnum,
2677 					leader_len, leader_flags,
2678 					  next_leader_len, next_leader_flags)
2679 #endif
2680 		    )
2681 		is_end_par = TRUE;
2682 
2683 	    /*
2684 	     * If we have got to the end of a paragraph, or the line is
2685 	     * getting long, format it.
2686 	     */
2687 	    if (is_end_par || force_format)
2688 	    {
2689 		if (need_set_indent)
2690 		    /* replace indent in first line with minimal number of
2691 		     * tabs and spaces, according to current options */
2692 		    (void)set_indent(get_indent(), SIN_CHANGED);
2693 
2694 		/* put cursor on last non-space */
2695 		State = NORMAL;	/* don't go past end-of-line */
2696 		coladvance((colnr_T)MAXCOL);
2697 		while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
2698 		    dec_cursor();
2699 
2700 		/* do the formatting, without 'showmode' */
2701 		State = INSERT;	/* for open_line() */
2702 		smd_save = p_smd;
2703 		p_smd = FALSE;
2704 		insertchar(NUL, INSCHAR_FORMAT
2705 #ifdef FEAT_COMMENTS
2706 			+ (do_comments ? INSCHAR_DO_COM : 0)
2707 			+ (do_comments && do_comments_list
2708 						       ? INSCHAR_COM_LIST : 0)
2709 #endif
2710 			+ (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
2711 		State = old_State;
2712 		p_smd = smd_save;
2713 		second_indent = -1;
2714 		/* at end of par.: need to set indent of next par. */
2715 		need_set_indent = is_end_par;
2716 		if (is_end_par)
2717 		{
2718 		    /* When called with a negative line count, break at the
2719 		     * end of the paragraph. */
2720 		    if (line_count < 0)
2721 			break;
2722 		    first_par_line = TRUE;
2723 		}
2724 		force_format = FALSE;
2725 	    }
2726 
2727 	    /*
2728 	     * When still in same paragraph, join the lines together.  But
2729 	     * first delete the leader from the second line.
2730 	     */
2731 	    if (!is_end_par)
2732 	    {
2733 		advance = FALSE;
2734 		curwin->w_cursor.lnum++;
2735 		curwin->w_cursor.col = 0;
2736 		if (line_count < 0 && u_save_cursor() == FAIL)
2737 		    break;
2738 #ifdef FEAT_COMMENTS
2739 		if (next_leader_len > 0)
2740 		{
2741 		    (void)del_bytes((long)next_leader_len, FALSE, FALSE);
2742 		    mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
2743 						      (long)-next_leader_len, 0);
2744 		} else
2745 #endif
2746 		    if (second_indent > 0)  /* the "leader" for FO_Q_SECOND */
2747 		{
2748 		    int indent = getwhitecols_curline();
2749 
2750 		    if (indent > 0)
2751 		    {
2752 			(void)del_bytes(indent, FALSE, FALSE);
2753 			mark_col_adjust(curwin->w_cursor.lnum,
2754 					       (colnr_T)0, 0L, (long)-indent, 0);
2755 		    }
2756 		}
2757 		curwin->w_cursor.lnum--;
2758 		if (do_join(2, TRUE, FALSE, FALSE, FALSE) == FAIL)
2759 		{
2760 		    beep_flush();
2761 		    break;
2762 		}
2763 		first_par_line = FALSE;
2764 		/* If the line is getting long, format it next time */
2765 		if (STRLEN(ml_get_curline()) > (size_t)max_len)
2766 		    force_format = TRUE;
2767 		else
2768 		    force_format = FALSE;
2769 	    }
2770 	}
2771 	line_breakcheck();
2772     }
2773 }
2774 
2775 /*
2776  * Return TRUE if line "lnum" ends in a white character.
2777  */
2778     static int
2779 ends_in_white(linenr_T lnum)
2780 {
2781     char_u	*s = ml_get(lnum);
2782     size_t	l;
2783 
2784     if (*s == NUL)
2785 	return FALSE;
2786     /* Don't use STRLEN() inside VIM_ISWHITE(), SAS/C complains: "macro
2787      * invocation may call function multiple times". */
2788     l = STRLEN(s) - 1;
2789     return VIM_ISWHITE(s[l]);
2790 }
2791 
2792 /*
2793  * Blank lines, and lines containing only the comment leader, are left
2794  * untouched by the formatting.  The function returns TRUE in this
2795  * case.  It also returns TRUE when a line starts with the end of a comment
2796  * ('e' in comment flags), so that this line is skipped, and not joined to the
2797  * previous line.  A new paragraph starts after a blank line, or when the
2798  * comment leader changes -- webb.
2799  */
2800 #ifdef FEAT_COMMENTS
2801     static int
2802 fmt_check_par(
2803     linenr_T	lnum,
2804     int		*leader_len,
2805     char_u	**leader_flags,
2806     int		do_comments)
2807 {
2808     char_u	*flags = NULL;	    /* init for GCC */
2809     char_u	*ptr;
2810 
2811     ptr = ml_get(lnum);
2812     if (do_comments)
2813 	*leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE);
2814     else
2815 	*leader_len = 0;
2816 
2817     if (*leader_len > 0)
2818     {
2819 	/*
2820 	 * Search for 'e' flag in comment leader flags.
2821 	 */
2822 	flags = *leader_flags;
2823 	while (*flags && *flags != ':' && *flags != COM_END)
2824 	    ++flags;
2825     }
2826 
2827     return (*skipwhite(ptr + *leader_len) == NUL
2828 	    || (*leader_len > 0 && *flags == COM_END)
2829 	    || startPS(lnum, NUL, FALSE));
2830 }
2831 #else
2832     static int
2833 fmt_check_par(linenr_T lnum)
2834 {
2835     return (*skipwhite(ml_get(lnum)) == NUL || startPS(lnum, NUL, FALSE));
2836 }
2837 #endif
2838 
2839 /*
2840  * Return TRUE when a paragraph starts in line "lnum".  Return FALSE when the
2841  * previous line is in the same paragraph.  Used for auto-formatting.
2842  */
2843     int
2844 paragraph_start(linenr_T lnum)
2845 {
2846     char_u	*p;
2847 #ifdef FEAT_COMMENTS
2848     int		leader_len = 0;		/* leader len of current line */
2849     char_u	*leader_flags = NULL;	/* flags for leader of current line */
2850     int		next_leader_len;	/* leader len of next line */
2851     char_u	*next_leader_flags;	/* flags for leader of next line */
2852     int		do_comments;		/* format comments */
2853 #endif
2854 
2855     if (lnum <= 1)
2856 	return TRUE;		/* start of the file */
2857 
2858     p = ml_get(lnum - 1);
2859     if (*p == NUL)
2860 	return TRUE;		/* after empty line */
2861 
2862 #ifdef FEAT_COMMENTS
2863     do_comments = has_format_option(FO_Q_COMS);
2864 #endif
2865     if (fmt_check_par(lnum - 1
2866 #ifdef FEAT_COMMENTS
2867 				, &leader_len, &leader_flags, do_comments
2868 #endif
2869 		))
2870 	return TRUE;		/* after non-paragraph line */
2871 
2872     if (fmt_check_par(lnum
2873 #ifdef FEAT_COMMENTS
2874 			   , &next_leader_len, &next_leader_flags, do_comments
2875 #endif
2876 		))
2877 	return TRUE;		/* "lnum" is not a paragraph line */
2878 
2879     if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1))
2880 	return TRUE;		/* missing trailing space in previous line. */
2881 
2882     if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0))
2883 	return TRUE;		/* numbered item starts in "lnum". */
2884 
2885 #ifdef FEAT_COMMENTS
2886     if (!same_leader(lnum - 1, leader_len, leader_flags,
2887 					  next_leader_len, next_leader_flags))
2888 	return TRUE;		/* change of comment leader. */
2889 #endif
2890 
2891     return FALSE;
2892 }
2893 
2894 /*
2895  * prepare a few things for block mode yank/delete/tilde
2896  *
2897  * for delete:
2898  * - textlen includes the first/last char to be (partly) deleted
2899  * - start/endspaces is the number of columns that are taken by the
2900  *   first/last deleted char minus the number of columns that have to be
2901  *   deleted.
2902  * for yank and tilde:
2903  * - textlen includes the first/last char to be wholly yanked
2904  * - start/endspaces is the number of columns of the first/last yanked char
2905  *   that are to be yanked.
2906  */
2907     void
2908 block_prep(
2909     oparg_T		*oap,
2910     struct block_def	*bdp,
2911     linenr_T		lnum,
2912     int			is_del)
2913 {
2914     int		incr = 0;
2915     char_u	*pend;
2916     char_u	*pstart;
2917     char_u	*line;
2918     char_u	*prev_pstart;
2919     char_u	*prev_pend;
2920 
2921     bdp->startspaces = 0;
2922     bdp->endspaces = 0;
2923     bdp->textlen = 0;
2924     bdp->start_vcol = 0;
2925     bdp->end_vcol = 0;
2926     bdp->is_short = FALSE;
2927     bdp->is_oneChar = FALSE;
2928     bdp->pre_whitesp = 0;
2929     bdp->pre_whitesp_c = 0;
2930     bdp->end_char_vcols = 0;
2931     bdp->start_char_vcols = 0;
2932 
2933     line = ml_get(lnum);
2934     pstart = line;
2935     prev_pstart = line;
2936     while (bdp->start_vcol < oap->start_vcol && *pstart)
2937     {
2938 	/* Count a tab for what it's worth (if list mode not on) */
2939 	incr = lbr_chartabsize(line, pstart, (colnr_T)bdp->start_vcol);
2940 	bdp->start_vcol += incr;
2941 	if (VIM_ISWHITE(*pstart))
2942 	{
2943 	    bdp->pre_whitesp += incr;
2944 	    bdp->pre_whitesp_c++;
2945 	}
2946 	else
2947 	{
2948 	    bdp->pre_whitesp = 0;
2949 	    bdp->pre_whitesp_c = 0;
2950 	}
2951 	prev_pstart = pstart;
2952 	MB_PTR_ADV(pstart);
2953     }
2954     bdp->start_char_vcols = incr;
2955     if (bdp->start_vcol < oap->start_vcol)	/* line too short */
2956     {
2957 	bdp->end_vcol = bdp->start_vcol;
2958 	bdp->is_short = TRUE;
2959 	if (!is_del || oap->op_type == OP_APPEND)
2960 	    bdp->endspaces = oap->end_vcol - oap->start_vcol + 1;
2961     }
2962     else
2963     {
2964 	/* notice: this converts partly selected Multibyte characters to
2965 	 * spaces, too. */
2966 	bdp->startspaces = bdp->start_vcol - oap->start_vcol;
2967 	if (is_del && bdp->startspaces)
2968 	    bdp->startspaces = bdp->start_char_vcols - bdp->startspaces;
2969 	pend = pstart;
2970 	bdp->end_vcol = bdp->start_vcol;
2971 	if (bdp->end_vcol > oap->end_vcol)	/* it's all in one character */
2972 	{
2973 	    bdp->is_oneChar = TRUE;
2974 	    if (oap->op_type == OP_INSERT)
2975 		bdp->endspaces = bdp->start_char_vcols - bdp->startspaces;
2976 	    else if (oap->op_type == OP_APPEND)
2977 	    {
2978 		bdp->startspaces += oap->end_vcol - oap->start_vcol + 1;
2979 		bdp->endspaces = bdp->start_char_vcols - bdp->startspaces;
2980 	    }
2981 	    else
2982 	    {
2983 		bdp->startspaces = oap->end_vcol - oap->start_vcol + 1;
2984 		if (is_del && oap->op_type != OP_LSHIFT)
2985 		{
2986 		    /* just putting the sum of those two into
2987 		     * bdp->startspaces doesn't work for Visual replace,
2988 		     * so we have to split the tab in two */
2989 		    bdp->startspaces = bdp->start_char_vcols
2990 					- (bdp->start_vcol - oap->start_vcol);
2991 		    bdp->endspaces = bdp->end_vcol - oap->end_vcol - 1;
2992 		}
2993 	    }
2994 	}
2995 	else
2996 	{
2997 	    prev_pend = pend;
2998 	    while (bdp->end_vcol <= oap->end_vcol && *pend != NUL)
2999 	    {
3000 		/* Count a tab for what it's worth (if list mode not on) */
3001 		prev_pend = pend;
3002 		incr = lbr_chartabsize_adv(line, &pend, (colnr_T)bdp->end_vcol);
3003 		bdp->end_vcol += incr;
3004 	    }
3005 	    if (bdp->end_vcol <= oap->end_vcol
3006 		    && (!is_del
3007 			|| oap->op_type == OP_APPEND
3008 			|| oap->op_type == OP_REPLACE)) /* line too short */
3009 	    {
3010 		bdp->is_short = TRUE;
3011 		/* Alternative: include spaces to fill up the block.
3012 		 * Disadvantage: can lead to trailing spaces when the line is
3013 		 * short where the text is put */
3014 		/* if (!is_del || oap->op_type == OP_APPEND) */
3015 		if (oap->op_type == OP_APPEND || virtual_op)
3016 		    bdp->endspaces = oap->end_vcol - bdp->end_vcol
3017 							     + oap->inclusive;
3018 		else
3019 		    bdp->endspaces = 0; /* replace doesn't add characters */
3020 	    }
3021 	    else if (bdp->end_vcol > oap->end_vcol)
3022 	    {
3023 		bdp->endspaces = bdp->end_vcol - oap->end_vcol - 1;
3024 		if (!is_del && bdp->endspaces)
3025 		{
3026 		    bdp->endspaces = incr - bdp->endspaces;
3027 		    if (pend != pstart)
3028 			pend = prev_pend;
3029 		}
3030 	    }
3031 	}
3032 	bdp->end_char_vcols = incr;
3033 	if (is_del && bdp->startspaces)
3034 	    pstart = prev_pstart;
3035 	bdp->textlen = (int)(pend - pstart);
3036     }
3037     bdp->textcol = (colnr_T) (pstart - line);
3038     bdp->textstart = pstart;
3039 }
3040 
3041 /*
3042  * Handle the add/subtract operator.
3043  */
3044     void
3045 op_addsub(
3046     oparg_T	*oap,
3047     linenr_T	Prenum1,	    /* Amount of add/subtract */
3048     int		g_cmd)		    /* was g<c-a>/g<c-x> */
3049 {
3050     pos_T		pos;
3051     struct block_def	bd;
3052     int			change_cnt = 0;
3053     linenr_T		amount = Prenum1;
3054 
3055    // do_addsub() might trigger re-evaluation of 'foldexpr' halfway, when the
3056    // buffer is not completely updated yet. Postpone updating folds until before
3057    // the call to changed_lines().
3058 #ifdef FEAT_FOLDING
3059    disable_fold_update++;
3060 #endif
3061 
3062     if (!VIsual_active)
3063     {
3064 	pos = curwin->w_cursor;
3065 	if (u_save_cursor() == FAIL)
3066 	{
3067 #ifdef FEAT_FOLDING
3068 	    disable_fold_update--;
3069 #endif
3070 	    return;
3071 	}
3072 	change_cnt = do_addsub(oap->op_type, &pos, 0, amount);
3073 #ifdef FEAT_FOLDING
3074 	disable_fold_update--;
3075 #endif
3076 	if (change_cnt)
3077 	    changed_lines(pos.lnum, 0, pos.lnum + 1, 0L);
3078     }
3079     else
3080     {
3081 	int	one_change;
3082 	int	length;
3083 	pos_T	startpos;
3084 
3085 	if (u_save((linenr_T)(oap->start.lnum - 1),
3086 					(linenr_T)(oap->end.lnum + 1)) == FAIL)
3087 	{
3088 #ifdef FEAT_FOLDING
3089 	    disable_fold_update--;
3090 #endif
3091 	    return;
3092 	}
3093 
3094 	pos = oap->start;
3095 	for (; pos.lnum <= oap->end.lnum; ++pos.lnum)
3096 	{
3097 	    if (oap->block_mode)		    /* Visual block mode */
3098 	    {
3099 		block_prep(oap, &bd, pos.lnum, FALSE);
3100 		pos.col = bd.textcol;
3101 		length = bd.textlen;
3102 	    }
3103 	    else if (oap->motion_type == MLINE)
3104 	    {
3105 		curwin->w_cursor.col = 0;
3106 		pos.col = 0;
3107 		length = (colnr_T)STRLEN(ml_get(pos.lnum));
3108 	    }
3109 	    else /* oap->motion_type == MCHAR */
3110 	    {
3111 		if (pos.lnum == oap->start.lnum && !oap->inclusive)
3112 		    dec(&(oap->end));
3113 		length = (colnr_T)STRLEN(ml_get(pos.lnum));
3114 		pos.col = 0;
3115 		if (pos.lnum == oap->start.lnum)
3116 		{
3117 		    pos.col += oap->start.col;
3118 		    length -= oap->start.col;
3119 		}
3120 		if (pos.lnum == oap->end.lnum)
3121 		{
3122 		    length = (int)STRLEN(ml_get(oap->end.lnum));
3123 		    if (oap->end.col >= length)
3124 			oap->end.col = length - 1;
3125 		    length = oap->end.col - pos.col + 1;
3126 		}
3127 	    }
3128 	    one_change = do_addsub(oap->op_type, &pos, length, amount);
3129 	    if (one_change)
3130 	    {
3131 		/* Remember the start position of the first change. */
3132 		if (change_cnt == 0)
3133 		    startpos = curbuf->b_op_start;
3134 		++change_cnt;
3135 	    }
3136 
3137 #ifdef FEAT_NETBEANS_INTG
3138 	    if (netbeans_active() && one_change)
3139 	    {
3140 		char_u *ptr = ml_get_buf(curbuf, pos.lnum, FALSE);
3141 
3142 		netbeans_removed(curbuf, pos.lnum, pos.col, (long)length);
3143 		netbeans_inserted(curbuf, pos.lnum, pos.col,
3144 						&ptr[pos.col], length);
3145 	    }
3146 #endif
3147 	    if (g_cmd && one_change)
3148 		amount += Prenum1;
3149 	}
3150 
3151 #ifdef FEAT_FOLDING
3152 	disable_fold_update--;
3153 #endif
3154 	if (change_cnt)
3155 	    changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L);
3156 
3157 	if (!change_cnt && oap->is_VIsual)
3158 	    /* No change: need to remove the Visual selection */
3159 	    redraw_curbuf_later(INVERTED);
3160 
3161 	/* Set '[ mark if something changed. Keep the last end
3162 	 * position from do_addsub(). */
3163 	if (change_cnt > 0)
3164 	    curbuf->b_op_start = startpos;
3165 
3166 	if (change_cnt > p_report)
3167 	    smsg(NGETTEXT("%ld line changed", "%ld lines changed",
3168 						      change_cnt), change_cnt);
3169     }
3170 }
3171 
3172 /*
3173  * Add or subtract 'Prenum1' from a number in a line
3174  * op_type is OP_NR_ADD or OP_NR_SUB
3175  *
3176  * Returns TRUE if some character was changed.
3177  */
3178     static int
3179 do_addsub(
3180     int		op_type,
3181     pos_T	*pos,
3182     int		length,
3183     linenr_T	Prenum1)
3184 {
3185     int		col;
3186     char_u	*buf1;
3187     char_u	buf2[NUMBUFLEN];
3188     int		pre;		/* 'X'/'x': hex; '0': octal; 'B'/'b': bin */
3189     static int	hexupper = FALSE;	/* 0xABC */
3190     uvarnumber_T	n;
3191     uvarnumber_T	oldn;
3192     char_u	*ptr;
3193     int		c;
3194     int		todel;
3195     int		dohex;
3196     int		dooct;
3197     int		dobin;
3198     int		doalp;
3199     int		firstdigit;
3200     int		subtract;
3201     int		negative = FALSE;
3202     int		was_positive = TRUE;
3203     int		visual = VIsual_active;
3204     int		did_change = FALSE;
3205     pos_T	save_cursor = curwin->w_cursor;
3206     int		maxlen = 0;
3207     pos_T	startpos;
3208     pos_T	endpos;
3209 
3210     dohex = (vim_strchr(curbuf->b_p_nf, 'x') != NULL);	// "heX"
3211     dooct = (vim_strchr(curbuf->b_p_nf, 'o') != NULL);	// "Octal"
3212     dobin = (vim_strchr(curbuf->b_p_nf, 'b') != NULL);	// "Bin"
3213     doalp = (vim_strchr(curbuf->b_p_nf, 'p') != NULL);	// "alPha"
3214 
3215     curwin->w_cursor = *pos;
3216     ptr = ml_get(pos->lnum);
3217     col = pos->col;
3218 
3219     if (*ptr == NUL)
3220 	goto theend;
3221 
3222     /*
3223      * First check if we are on a hexadecimal number, after the "0x".
3224      */
3225     if (!VIsual_active)
3226     {
3227 	if (dobin)
3228 	    while (col > 0 && vim_isbdigit(ptr[col]))
3229 	    {
3230 		--col;
3231 		if (has_mbyte)
3232 		    col -= (*mb_head_off)(ptr, ptr + col);
3233 	    }
3234 
3235 	if (dohex)
3236 	    while (col > 0 && vim_isxdigit(ptr[col]))
3237 	    {
3238 		--col;
3239 		if (has_mbyte)
3240 		    col -= (*mb_head_off)(ptr, ptr + col);
3241 	    }
3242 
3243 	if (       dobin
3244 		&& dohex
3245 		&& ! ((col > 0
3246 		    && (ptr[col] == 'X'
3247 			|| ptr[col] == 'x')
3248 		    && ptr[col - 1] == '0'
3249 		    && (!has_mbyte ||
3250 			!(*mb_head_off)(ptr, ptr + col - 1))
3251 		    && vim_isxdigit(ptr[col + 1]))))
3252 	{
3253 
3254 	    /* In case of binary/hexadecimal pattern overlap match, rescan */
3255 
3256 	    col = pos->col;
3257 
3258 	    while (col > 0 && vim_isdigit(ptr[col]))
3259 	    {
3260 		col--;
3261 		if (has_mbyte)
3262 		    col -= (*mb_head_off)(ptr, ptr + col);
3263 	    }
3264 	}
3265 
3266 	if ((       dohex
3267 		&& col > 0
3268 		&& (ptr[col] == 'X'
3269 		    || ptr[col] == 'x')
3270 		&& ptr[col - 1] == '0'
3271 		&& (!has_mbyte ||
3272 		    !(*mb_head_off)(ptr, ptr + col - 1))
3273 		&& vim_isxdigit(ptr[col + 1])) ||
3274 	    (       dobin
3275 		&& col > 0
3276 		&& (ptr[col] == 'B'
3277 		    || ptr[col] == 'b')
3278 		&& ptr[col - 1] == '0'
3279 		&& (!has_mbyte ||
3280 		    !(*mb_head_off)(ptr, ptr + col - 1))
3281 		&& vim_isbdigit(ptr[col + 1])))
3282 	{
3283 	    /* Found hexadecimal or binary number, move to its start. */
3284 	    --col;
3285 	    if (has_mbyte)
3286 		col -= (*mb_head_off)(ptr, ptr + col);
3287 	}
3288 	else
3289 	{
3290 	    /*
3291 	     * Search forward and then backward to find the start of number.
3292 	     */
3293 	    col = pos->col;
3294 
3295 	    while (ptr[col] != NUL
3296 		    && !vim_isdigit(ptr[col])
3297 		    && !(doalp && ASCII_ISALPHA(ptr[col])))
3298 		col += MB_PTR2LEN(ptr + col);
3299 
3300 	    while (col > 0
3301 		    && vim_isdigit(ptr[col - 1])
3302 		    && !(doalp && ASCII_ISALPHA(ptr[col])))
3303 	    {
3304 		--col;
3305 		if (has_mbyte)
3306 		    col -= (*mb_head_off)(ptr, ptr + col);
3307 	    }
3308 	}
3309     }
3310 
3311     if (visual)
3312     {
3313 	while (ptr[col] != NUL && length > 0
3314 		&& !vim_isdigit(ptr[col])
3315 		&& !(doalp && ASCII_ISALPHA(ptr[col])))
3316 	{
3317 	    int mb_len = MB_PTR2LEN(ptr + col);
3318 
3319 	    col += mb_len;
3320 	    length -= mb_len;
3321 	}
3322 
3323 	if (length == 0)
3324 	    goto theend;
3325 
3326 	if (col > pos->col && ptr[col - 1] == '-'
3327 		&& (!has_mbyte || !(*mb_head_off)(ptr, ptr + col - 1)))
3328 	{
3329 	    negative = TRUE;
3330 	    was_positive = FALSE;
3331 	}
3332     }
3333 
3334     /*
3335      * If a number was found, and saving for undo works, replace the number.
3336      */
3337     firstdigit = ptr[col];
3338     if (!VIM_ISDIGIT(firstdigit) && !(doalp && ASCII_ISALPHA(firstdigit)))
3339     {
3340 	beep_flush();
3341 	goto theend;
3342     }
3343 
3344     if (doalp && ASCII_ISALPHA(firstdigit))
3345     {
3346 	/* decrement or increment alphabetic character */
3347 	if (op_type == OP_NR_SUB)
3348 	{
3349 	    if (CharOrd(firstdigit) < Prenum1)
3350 	    {
3351 		if (isupper(firstdigit))
3352 		    firstdigit = 'A';
3353 		else
3354 		    firstdigit = 'a';
3355 	    }
3356 	    else
3357 #ifdef EBCDIC
3358 		firstdigit = EBCDIC_CHAR_ADD(firstdigit, -Prenum1);
3359 #else
3360 		firstdigit -= Prenum1;
3361 #endif
3362 	}
3363 	else
3364 	{
3365 	    if (26 - CharOrd(firstdigit) - 1 < Prenum1)
3366 	    {
3367 		if (isupper(firstdigit))
3368 		    firstdigit = 'Z';
3369 		else
3370 		    firstdigit = 'z';
3371 	    }
3372 	    else
3373 #ifdef EBCDIC
3374 		firstdigit = EBCDIC_CHAR_ADD(firstdigit, Prenum1);
3375 #else
3376 		firstdigit += Prenum1;
3377 #endif
3378 	}
3379 	curwin->w_cursor.col = col;
3380 	if (!did_change)
3381 	    startpos = curwin->w_cursor;
3382 	did_change = TRUE;
3383 	(void)del_char(FALSE);
3384 	ins_char(firstdigit);
3385 	endpos = curwin->w_cursor;
3386 	curwin->w_cursor.col = col;
3387     }
3388     else
3389     {
3390 	if (col > 0 && ptr[col - 1] == '-'
3391 		&& (!has_mbyte ||
3392 		    !(*mb_head_off)(ptr, ptr + col - 1))
3393 		&& !visual)
3394 	{
3395 	    /* negative number */
3396 	    --col;
3397 	    negative = TRUE;
3398 	}
3399 	/* get the number value (unsigned) */
3400 	if (visual && VIsual_mode != 'V')
3401 	    maxlen = (curbuf->b_visual.vi_curswant == MAXCOL
3402 		    ? (int)STRLEN(ptr) - col
3403 		    : length);
3404 
3405 	vim_str2nr(ptr + col, &pre, &length,
3406 		0 + (dobin ? STR2NR_BIN : 0)
3407 		    + (dooct ? STR2NR_OCT : 0)
3408 		    + (dohex ? STR2NR_HEX : 0),
3409 		NULL, &n, maxlen, FALSE);
3410 
3411 	/* ignore leading '-' for hex and octal and bin numbers */
3412 	if (pre && negative)
3413 	{
3414 	    ++col;
3415 	    --length;
3416 	    negative = FALSE;
3417 	}
3418 	/* add or subtract */
3419 	subtract = FALSE;
3420 	if (op_type == OP_NR_SUB)
3421 	    subtract ^= TRUE;
3422 	if (negative)
3423 	    subtract ^= TRUE;
3424 
3425 	oldn = n;
3426 	if (subtract)
3427 	    n -= (uvarnumber_T)Prenum1;
3428 	else
3429 	    n += (uvarnumber_T)Prenum1;
3430 	/* handle wraparound for decimal numbers */
3431 	if (!pre)
3432 	{
3433 	    if (subtract)
3434 	    {
3435 		if (n > oldn)
3436 		{
3437 		    n = 1 + (n ^ (uvarnumber_T)-1);
3438 		    negative ^= TRUE;
3439 		}
3440 	    }
3441 	    else
3442 	    {
3443 		/* add */
3444 		if (n < oldn)
3445 		{
3446 		    n = (n ^ (uvarnumber_T)-1);
3447 		    negative ^= TRUE;
3448 		}
3449 	    }
3450 	    if (n == 0)
3451 		negative = FALSE;
3452 	}
3453 
3454 	if (visual && !was_positive && !negative && col > 0)
3455 	{
3456 	    /* need to remove the '-' */
3457 	    col--;
3458 	    length++;
3459 	}
3460 
3461 	/*
3462 	 * Delete the old number.
3463 	 */
3464 	curwin->w_cursor.col = col;
3465 	if (!did_change)
3466 	    startpos = curwin->w_cursor;
3467 	did_change = TRUE;
3468 	todel = length;
3469 	c = gchar_cursor();
3470 	/*
3471 	 * Don't include the '-' in the length, only the length of the
3472 	 * part after it is kept the same.
3473 	 */
3474 	if (c == '-')
3475 	    --length;
3476 	while (todel-- > 0)
3477 	{
3478 	    if (c < 0x100 && isalpha(c))
3479 	    {
3480 		if (isupper(c))
3481 		    hexupper = TRUE;
3482 		else
3483 		    hexupper = FALSE;
3484 	    }
3485 	    /* del_char() will mark line needing displaying */
3486 	    (void)del_char(FALSE);
3487 	    c = gchar_cursor();
3488 	}
3489 
3490 	/*
3491 	 * Prepare the leading characters in buf1[].
3492 	 * When there are many leading zeros it could be very long.
3493 	 * Allocate a bit too much.
3494 	 */
3495 	buf1 = alloc(length + NUMBUFLEN);
3496 	if (buf1 == NULL)
3497 	    goto theend;
3498 	ptr = buf1;
3499 	if (negative && (!visual || was_positive))
3500 	    *ptr++ = '-';
3501 	if (pre)
3502 	{
3503 	    *ptr++ = '0';
3504 	    --length;
3505 	}
3506 	if (pre == 'b' || pre == 'B' ||
3507 	    pre == 'x' || pre == 'X')
3508 	{
3509 	    *ptr++ = pre;
3510 	    --length;
3511 	}
3512 
3513 	/*
3514 	 * Put the number characters in buf2[].
3515 	 */
3516 	if (pre == 'b' || pre == 'B')
3517 	{
3518 	    int i;
3519 	    int bit = 0;
3520 	    int bits = sizeof(uvarnumber_T) * 8;
3521 
3522 	    /* leading zeros */
3523 	    for (bit = bits; bit > 0; bit--)
3524 		if ((n >> (bit - 1)) & 0x1) break;
3525 
3526 	    for (i = 0; bit > 0; bit--)
3527 		buf2[i++] = ((n >> (bit - 1)) & 0x1) ? '1' : '0';
3528 
3529 	    buf2[i] = '\0';
3530 	}
3531 	else if (pre == 0)
3532 	    vim_snprintf((char *)buf2, NUMBUFLEN, "%llu",
3533 							(long_long_u_T)n);
3534 	else if (pre == '0')
3535 	    vim_snprintf((char *)buf2, NUMBUFLEN, "%llo",
3536 							(long_long_u_T)n);
3537 	else if (pre && hexupper)
3538 	    vim_snprintf((char *)buf2, NUMBUFLEN, "%llX",
3539 							(long_long_u_T)n);
3540 	else
3541 	    vim_snprintf((char *)buf2, NUMBUFLEN, "%llx",
3542 							(long_long_u_T)n);
3543 	length -= (int)STRLEN(buf2);
3544 
3545 	/*
3546 	 * Adjust number of zeros to the new number of digits, so the
3547 	 * total length of the number remains the same.
3548 	 * Don't do this when
3549 	 * the result may look like an octal number.
3550 	 */
3551 	if (firstdigit == '0' && !(dooct && pre == 0))
3552 	    while (length-- > 0)
3553 		*ptr++ = '0';
3554 	*ptr = NUL;
3555 	STRCAT(buf1, buf2);
3556 	ins_str(buf1);		/* insert the new number */
3557 	vim_free(buf1);
3558 	endpos = curwin->w_cursor;
3559 	if (did_change && curwin->w_cursor.col)
3560 	    --curwin->w_cursor.col;
3561     }
3562 
3563     if (did_change)
3564     {
3565 	/* set the '[ and '] marks */
3566 	curbuf->b_op_start = startpos;
3567 	curbuf->b_op_end = endpos;
3568 	if (curbuf->b_op_end.col > 0)
3569 	    --curbuf->b_op_end.col;
3570     }
3571 
3572 theend:
3573     if (visual)
3574 	curwin->w_cursor = save_cursor;
3575     else if (did_change)
3576 	curwin->w_set_curswant = TRUE;
3577 
3578     return did_change;
3579 }
3580 
3581 #if defined(FEAT_CLIPBOARD) || defined(PROTO)
3582 /*
3583  * SELECTION / PRIMARY ('*')
3584  *
3585  * Text selection stuff that uses the GUI selection register '*'.  When using a
3586  * GUI this may be text from another window, otherwise it is the last text we
3587  * had highlighted with VIsual mode.  With mouse support, clicking the middle
3588  * button performs the paste, otherwise you will need to do <"*p>. "
3589  * If not under X, it is synonymous with the clipboard register '+'.
3590  *
3591  * X CLIPBOARD ('+')
3592  *
3593  * Text selection stuff that uses the GUI clipboard register '+'.
3594  * Under X, this matches the standard cut/paste buffer CLIPBOARD selection.
3595  * It will be used for unnamed cut/pasting is 'clipboard' contains "unnamed",
3596  * otherwise you will need to do <"+p>. "
3597  * If not under X, it is synonymous with the selection register '*'.
3598  */
3599 
3600 /*
3601  * Routine to export any final X selection we had to the environment
3602  * so that the text is still available after Vim has exited. X selections
3603  * only exist while the owning application exists, so we write to the
3604  * permanent (while X runs) store CUT_BUFFER0.
3605  * Dump the CLIPBOARD selection if we own it (it's logically the more
3606  * 'permanent' of the two), otherwise the PRIMARY one.
3607  * For now, use a hard-coded sanity limit of 1Mb of data.
3608  */
3609 #if (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO)
3610     void
3611 x11_export_final_selection(void)
3612 {
3613     Display	*dpy;
3614     char_u	*str = NULL;
3615     long_u	len = 0;
3616     int		motion_type = -1;
3617 
3618 # ifdef FEAT_GUI
3619     if (gui.in_use)
3620 	dpy = X_DISPLAY;
3621     else
3622 # endif
3623 # ifdef FEAT_XCLIPBOARD
3624 	dpy = xterm_dpy;
3625 # else
3626 	return;
3627 # endif
3628 
3629     /* Get selection to export */
3630     if (clip_plus.owned)
3631 	motion_type = clip_convert_selection(&str, &len, &clip_plus);
3632     else if (clip_star.owned)
3633 	motion_type = clip_convert_selection(&str, &len, &clip_star);
3634 
3635     /* Check it's OK */
3636     if (dpy != NULL && str != NULL && motion_type >= 0
3637 					       && len < 1024*1024 && len > 0)
3638     {
3639 	int ok = TRUE;
3640 
3641 	/* The CUT_BUFFER0 is supposed to always contain latin1.  Convert from
3642 	 * 'enc' when it is a multi-byte encoding.  When 'enc' is an 8-bit
3643 	 * encoding conversion usually doesn't work, so keep the text as-is.
3644 	 */
3645 	if (has_mbyte)
3646 	{
3647 	    vimconv_T	vc;
3648 
3649 	    vc.vc_type = CONV_NONE;
3650 	    if (convert_setup(&vc, p_enc, (char_u *)"latin1") == OK)
3651 	    {
3652 		int	intlen = len;
3653 		char_u	*conv_str;
3654 
3655 		vc.vc_fail = TRUE;
3656 		conv_str = string_convert(&vc, str, &intlen);
3657 		len = intlen;
3658 		if (conv_str != NULL)
3659 		{
3660 		    vim_free(str);
3661 		    str = conv_str;
3662 		}
3663 		else
3664 		{
3665 		    ok = FALSE;
3666 		}
3667 		convert_setup(&vc, NULL, NULL);
3668 	    }
3669 	    else
3670 	    {
3671 		ok = FALSE;
3672 	    }
3673 	}
3674 
3675 	/* Do not store the string if conversion failed.  Better to use any
3676 	 * other selection than garbled text. */
3677 	if (ok)
3678 	{
3679 	    XStoreBuffer(dpy, (char *)str, (int)len, 0);
3680 	    XFlush(dpy);
3681 	}
3682     }
3683 
3684     vim_free(str);
3685 }
3686 #endif
3687 #endif /* FEAT_CLIPBOARD || PROTO */
3688 
3689     void
3690 clear_oparg(oparg_T *oap)
3691 {
3692     vim_memset(oap, 0, sizeof(oparg_T));
3693 }
3694 
3695 /*
3696  *  Count the number of bytes, characters and "words" in a line.
3697  *
3698  *  "Words" are counted by looking for boundaries between non-space and
3699  *  space characters.  (it seems to produce results that match 'wc'.)
3700  *
3701  *  Return value is byte count; word count for the line is added to "*wc".
3702  *  Char count is added to "*cc".
3703  *
3704  *  The function will only examine the first "limit" characters in the
3705  *  line, stopping if it encounters an end-of-line (NUL byte).  In that
3706  *  case, eol_size will be added to the character count to account for
3707  *  the size of the EOL character.
3708  */
3709     static varnumber_T
3710 line_count_info(
3711     char_u	*line,
3712     varnumber_T	*wc,
3713     varnumber_T	*cc,
3714     varnumber_T	limit,
3715     int		eol_size)
3716 {
3717     varnumber_T	i;
3718     varnumber_T	words = 0;
3719     varnumber_T	chars = 0;
3720     int		is_word = 0;
3721 
3722     for (i = 0; i < limit && line[i] != NUL; )
3723     {
3724 	if (is_word)
3725 	{
3726 	    if (vim_isspace(line[i]))
3727 	    {
3728 		words++;
3729 		is_word = 0;
3730 	    }
3731 	}
3732 	else if (!vim_isspace(line[i]))
3733 	    is_word = 1;
3734 	++chars;
3735 	i += (*mb_ptr2len)(line + i);
3736     }
3737 
3738     if (is_word)
3739 	words++;
3740     *wc += words;
3741 
3742     /* Add eol_size if the end of line was reached before hitting limit. */
3743     if (i < limit && line[i] == NUL)
3744     {
3745 	i += eol_size;
3746 	chars += eol_size;
3747     }
3748     *cc += chars;
3749     return i;
3750 }
3751 
3752 /*
3753  * Give some info about the position of the cursor (for "g CTRL-G").
3754  * In Visual mode, give some info about the selected region.  (In this case,
3755  * the *_count_cursor variables store running totals for the selection.)
3756  * When "dict" is not NULL store the info there instead of showing it.
3757  */
3758     void
3759 cursor_pos_info(dict_T *dict)
3760 {
3761     char_u	*p;
3762     char_u	buf1[50];
3763     char_u	buf2[40];
3764     linenr_T	lnum;
3765     varnumber_T	byte_count = 0;
3766     varnumber_T	bom_count  = 0;
3767     varnumber_T	byte_count_cursor = 0;
3768     varnumber_T	char_count = 0;
3769     varnumber_T	char_count_cursor = 0;
3770     varnumber_T	word_count = 0;
3771     varnumber_T	word_count_cursor = 0;
3772     int		eol_size;
3773     varnumber_T	last_check = 100000L;
3774     long	line_count_selected = 0;
3775     pos_T	min_pos, max_pos;
3776     oparg_T	oparg;
3777     struct block_def	bd;
3778 
3779     /*
3780      * Compute the length of the file in characters.
3781      */
3782     if (curbuf->b_ml.ml_flags & ML_EMPTY)
3783     {
3784 	if (dict == NULL)
3785 	{
3786 	    msg(_(no_lines_msg));
3787 	    return;
3788 	}
3789     }
3790     else
3791     {
3792 	if (get_fileformat(curbuf) == EOL_DOS)
3793 	    eol_size = 2;
3794 	else
3795 	    eol_size = 1;
3796 
3797 	if (VIsual_active)
3798 	{
3799 	    if (LT_POS(VIsual, curwin->w_cursor))
3800 	    {
3801 		min_pos = VIsual;
3802 		max_pos = curwin->w_cursor;
3803 	    }
3804 	    else
3805 	    {
3806 		min_pos = curwin->w_cursor;
3807 		max_pos = VIsual;
3808 	    }
3809 	    if (*p_sel == 'e' && max_pos.col > 0)
3810 		--max_pos.col;
3811 
3812 	    if (VIsual_mode == Ctrl_V)
3813 	    {
3814 #ifdef FEAT_LINEBREAK
3815 		char_u * saved_sbr = p_sbr;
3816 
3817 		/* Make 'sbr' empty for a moment to get the correct size. */
3818 		p_sbr = empty_option;
3819 #endif
3820 		oparg.is_VIsual = 1;
3821 		oparg.block_mode = TRUE;
3822 		oparg.op_type = OP_NOP;
3823 		getvcols(curwin, &min_pos, &max_pos,
3824 					  &oparg.start_vcol, &oparg.end_vcol);
3825 #ifdef FEAT_LINEBREAK
3826 		p_sbr = saved_sbr;
3827 #endif
3828 		if (curwin->w_curswant == MAXCOL)
3829 		    oparg.end_vcol = MAXCOL;
3830 		/* Swap the start, end vcol if needed */
3831 		if (oparg.end_vcol < oparg.start_vcol)
3832 		{
3833 		    oparg.end_vcol += oparg.start_vcol;
3834 		    oparg.start_vcol = oparg.end_vcol - oparg.start_vcol;
3835 		    oparg.end_vcol -= oparg.start_vcol;
3836 		}
3837 	    }
3838 	    line_count_selected = max_pos.lnum - min_pos.lnum + 1;
3839 	}
3840 
3841 	for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
3842 	{
3843 	    /* Check for a CTRL-C every 100000 characters. */
3844 	    if (byte_count > last_check)
3845 	    {
3846 		ui_breakcheck();
3847 		if (got_int)
3848 		    return;
3849 		last_check = byte_count + 100000L;
3850 	    }
3851 
3852 	    /* Do extra processing for VIsual mode. */
3853 	    if (VIsual_active
3854 		    && lnum >= min_pos.lnum && lnum <= max_pos.lnum)
3855 	    {
3856 		char_u	    *s = NULL;
3857 		long	    len = 0L;
3858 
3859 		switch (VIsual_mode)
3860 		{
3861 		    case Ctrl_V:
3862 			virtual_op = virtual_active();
3863 			block_prep(&oparg, &bd, lnum, 0);
3864 			virtual_op = MAYBE;
3865 			s = bd.textstart;
3866 			len = (long)bd.textlen;
3867 			break;
3868 		    case 'V':
3869 			s = ml_get(lnum);
3870 			len = MAXCOL;
3871 			break;
3872 		    case 'v':
3873 			{
3874 			    colnr_T start_col = (lnum == min_pos.lnum)
3875 							   ? min_pos.col : 0;
3876 			    colnr_T end_col = (lnum == max_pos.lnum)
3877 				      ? max_pos.col - start_col + 1 : MAXCOL;
3878 
3879 			    s = ml_get(lnum) + start_col;
3880 			    len = end_col;
3881 			}
3882 			break;
3883 		}
3884 		if (s != NULL)
3885 		{
3886 		    byte_count_cursor += line_count_info(s, &word_count_cursor,
3887 					   &char_count_cursor, len, eol_size);
3888 		    if (lnum == curbuf->b_ml.ml_line_count
3889 			    && !curbuf->b_p_eol
3890 			    && (curbuf->b_p_bin || !curbuf->b_p_fixeol)
3891 			    && (long)STRLEN(s) < len)
3892 			byte_count_cursor -= eol_size;
3893 		}
3894 	    }
3895 	    else
3896 	    {
3897 		/* In non-visual mode, check for the line the cursor is on */
3898 		if (lnum == curwin->w_cursor.lnum)
3899 		{
3900 		    word_count_cursor += word_count;
3901 		    char_count_cursor += char_count;
3902 		    byte_count_cursor = byte_count +
3903 			line_count_info(ml_get(lnum),
3904 				&word_count_cursor, &char_count_cursor,
3905 				(varnumber_T)(curwin->w_cursor.col + 1),
3906 				eol_size);
3907 		}
3908 	    }
3909 	    /* Add to the running totals */
3910 	    byte_count += line_count_info(ml_get(lnum), &word_count,
3911 					 &char_count, (varnumber_T)MAXCOL,
3912 					 eol_size);
3913 	}
3914 
3915 	/* Correction for when last line doesn't have an EOL. */
3916 	if (!curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol))
3917 	    byte_count -= eol_size;
3918 
3919 	if (dict == NULL)
3920 	{
3921 	    if (VIsual_active)
3922 	    {
3923 		if (VIsual_mode == Ctrl_V && curwin->w_curswant < MAXCOL)
3924 		{
3925 		    getvcols(curwin, &min_pos, &max_pos, &min_pos.col,
3926 								    &max_pos.col);
3927 		    vim_snprintf((char *)buf1, sizeof(buf1), _("%ld Cols; "),
3928 			    (long)(oparg.end_vcol - oparg.start_vcol + 1));
3929 		}
3930 		else
3931 		    buf1[0] = NUL;
3932 
3933 		if (char_count_cursor == byte_count_cursor
3934 						    && char_count == byte_count)
3935 		    vim_snprintf((char *)IObuff, IOSIZE,
3936 			    _("Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Bytes"),
3937 			    buf1, line_count_selected,
3938 			    (long)curbuf->b_ml.ml_line_count,
3939 			    (long_long_T)word_count_cursor,
3940 			    (long_long_T)word_count,
3941 			    (long_long_T)byte_count_cursor,
3942 			    (long_long_T)byte_count);
3943 		else
3944 		    vim_snprintf((char *)IObuff, IOSIZE,
3945 			    _("Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Chars; %lld of %lld Bytes"),
3946 			    buf1, line_count_selected,
3947 			    (long)curbuf->b_ml.ml_line_count,
3948 			    (long_long_T)word_count_cursor,
3949 			    (long_long_T)word_count,
3950 			    (long_long_T)char_count_cursor,
3951 			    (long_long_T)char_count,
3952 			    (long_long_T)byte_count_cursor,
3953 			    (long_long_T)byte_count);
3954 	    }
3955 	    else
3956 	    {
3957 		p = ml_get_curline();
3958 		validate_virtcol();
3959 		col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1,
3960 			(int)curwin->w_virtcol + 1);
3961 		col_print(buf2, sizeof(buf2), (int)STRLEN(p),
3962 				    linetabsize(p));
3963 
3964 		if (char_count_cursor == byte_count_cursor
3965 			&& char_count == byte_count)
3966 		    vim_snprintf((char *)IObuff, IOSIZE,
3967 			_("Col %s of %s; Line %ld of %ld; Word %lld of %lld; Byte %lld of %lld"),
3968 			(char *)buf1, (char *)buf2,
3969 			(long)curwin->w_cursor.lnum,
3970 			(long)curbuf->b_ml.ml_line_count,
3971 			(long_long_T)word_count_cursor, (long_long_T)word_count,
3972 			(long_long_T)byte_count_cursor, (long_long_T)byte_count);
3973 		else
3974 		    vim_snprintf((char *)IObuff, IOSIZE,
3975 			_("Col %s of %s; Line %ld of %ld; Word %lld of %lld; Char %lld of %lld; Byte %lld of %lld"),
3976 			(char *)buf1, (char *)buf2,
3977 			(long)curwin->w_cursor.lnum,
3978 			(long)curbuf->b_ml.ml_line_count,
3979 			(long_long_T)word_count_cursor, (long_long_T)word_count,
3980 			(long_long_T)char_count_cursor, (long_long_T)char_count,
3981 			(long_long_T)byte_count_cursor, (long_long_T)byte_count);
3982 	    }
3983 	}
3984 
3985 	bom_count = bomb_size();
3986 	if (dict == NULL && bom_count > 0)
3987 	    vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE,
3988 				 _("(+%lld for BOM)"), (long_long_T)bom_count);
3989 	if (dict == NULL)
3990 	{
3991 	    // Don't shorten this message, the user asked for it.
3992 	    p = p_shm;
3993 	    p_shm = (char_u *)"";
3994 	    msg((char *)IObuff);
3995 	    p_shm = p;
3996 	}
3997     }
3998 #if defined(FEAT_EVAL)
3999     if (dict != NULL)
4000     {
4001 	dict_add_number(dict, "words", word_count);
4002 	dict_add_number(dict, "chars", char_count);
4003 	dict_add_number(dict, "bytes", byte_count + bom_count);
4004 	dict_add_number(dict, VIsual_active ? "visual_bytes" : "cursor_bytes",
4005 		byte_count_cursor);
4006 	dict_add_number(dict, VIsual_active ? "visual_chars" : "cursor_chars",
4007 		char_count_cursor);
4008 	dict_add_number(dict, VIsual_active ? "visual_words" : "cursor_words",
4009 		word_count_cursor);
4010     }
4011 #endif
4012 }
4013