1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4/// @file diff.c
5///
6/// Code for diff'ing two, three or four buffers.
7///
8/// There are three ways to diff:
9/// - Shell out to an external diff program, using files.
10/// - Use the compiled-in xdiff library.
11/// - Let 'diffexpr' do the work, using files.
12
13#include <inttypes.h>
14#include <stdbool.h>
15
16#include "nvim/vim.h"
17#include "xdiff/xdiff.h"
18#include "nvim/ascii.h"
19#include "nvim/diff.h"
20#include "nvim/buffer.h"
21#include "nvim/change.h"
22#include "nvim/charset.h"
23#include "nvim/cursor.h"
24#include "nvim/eval.h"
25#include "nvim/ex_cmds.h"
26#include "nvim/ex_docmd.h"
27#include "nvim/fileio.h"
28#include "nvim/fold.h"
29#include "nvim/mark.h"
30#include "nvim/mbyte.h"
31#include "nvim/memline.h"
32#include "nvim/message.h"
33#include "nvim/misc1.h"
34#include "nvim/memory.h"
35#include "nvim/move.h"
36#include "nvim/normal.h"
37#include "nvim/option.h"
38#include "nvim/path.h"
39#include "nvim/screen.h"
40#include "nvim/strings.h"
41#include "nvim/undo.h"
42#include "nvim/window.h"
43#include "nvim/os/os.h"
44#include "nvim/os/shell.h"
45
46static int diff_busy = false; // using diff structs, don't change them
47static int diff_need_update = false; // ex_diffupdate needs to be called
48
49// Flags obtained from the 'diffopt' option
50#define DIFF_FILLER 0x001 // display filler lines
51#define DIFF_IBLANK 0x002 // ignore empty lines
52#define DIFF_ICASE 0x004 // ignore case
53#define DIFF_IWHITE 0x008 // ignore change in white space
54#define DIFF_IWHITEALL 0x010 // ignore all white space changes
55#define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL
56#define DIFF_HORIZONTAL 0x040 // horizontal splits
57#define DIFF_VERTICAL 0x080 // vertical splits
58#define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden
59#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm
60#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL)
61static int diff_flags = DIFF_INTERNAL | DIFF_FILLER;
62
63static long diff_algorithm = 0;
64
65#define LBUFLEN 50 // length of line in diff file
66
67// kTrue when "diff -a" works, kFalse when it doesn't work,
68// kNone when not checked yet
69static TriState diff_a_works = kNone;
70
71// used for diff input
72typedef struct {
73 char_u *din_fname; // used for external diff
74 mmfile_t din_mmfile; // used for internal diff
75} diffin_T;
76
77// used for diff result
78typedef struct {
79 char_u *dout_fname; // used for external diff
80 garray_T dout_ga; // used for internal diff
81} diffout_T;
82
83// two diff inputs and one result
84typedef struct {
85 diffin_T dio_orig; // original file input
86 diffin_T dio_new; // new file input
87 diffout_T dio_diff; // diff result
88 int dio_internal; // using internal diff
89} diffio_T;
90
91#ifdef INCLUDE_GENERATED_DECLARATIONS
92# include "diff.c.generated.h"
93#endif
94
95/// Called when deleting or unloading a buffer: No longer make a diff with it.
96///
97/// @param buf
98void diff_buf_delete(buf_T *buf)
99{
100 FOR_ALL_TABS(tp) {
101 int i = diff_buf_idx_tp(buf, tp);
102
103 if (i != DB_COUNT) {
104 tp->tp_diffbuf[i] = NULL;
105 tp->tp_diff_invalid = true;
106
107 if (tp == curtab) {
108 diff_redraw(true);
109 }
110 }
111 }
112}
113
114/// Check if the current buffer should be added to or removed from the list of
115/// diff buffers.
116///
117/// @param win
118void diff_buf_adjust(win_T *win)
119{
120
121 if (!win->w_p_diff) {
122 // When there is no window showing a diff for this buffer, remove
123 // it from the diffs.
124 bool found_win = false;
125 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
126 if ((wp->w_buffer == win->w_buffer) && wp->w_p_diff) {
127 found_win = true;
128 }
129 }
130
131 if (!found_win) {
132 int i = diff_buf_idx(win->w_buffer);
133 if (i != DB_COUNT) {
134 curtab->tp_diffbuf[i] = NULL;
135 curtab->tp_diff_invalid = true;
136 diff_redraw(true);
137 }
138 }
139 } else {
140 diff_buf_add(win->w_buffer);
141 }
142}
143
144/// Add a buffer to make diffs for.
145///
146/// Call this when a new buffer is being edited in the current window where
147/// 'diff' is set.
148/// Marks the current buffer as being part of the diff and requiring updating.
149/// This must be done before any autocmd, because a command may use info
150/// about the screen contents.
151///
152/// @param buf The buffer to add.
153void diff_buf_add(buf_T *buf)
154{
155 if (diff_buf_idx(buf) != DB_COUNT) {
156 // It's already there.
157 return;
158 }
159
160 int i;
161 for (i = 0; i < DB_COUNT; ++i) {
162 if (curtab->tp_diffbuf[i] == NULL) {
163 curtab->tp_diffbuf[i] = buf;
164 curtab->tp_diff_invalid = true;
165 diff_redraw(true);
166 return;
167 }
168 }
169
170 EMSGN(_("E96: Cannot diff more than %" PRId64 " buffers"), DB_COUNT);
171}
172
173///
174/// Remove all buffers to make diffs for.
175///
176static void diff_buf_clear(void)
177{
178 for (int i = 0; i < DB_COUNT; i++) {
179 if (curtab->tp_diffbuf[i] != NULL) {
180 curtab->tp_diffbuf[i] = NULL;
181 curtab->tp_diff_invalid = true;
182 diff_redraw(true);
183 }
184 }
185}
186
187/// Find buffer "buf" in the list of diff buffers for the current tab page.
188///
189/// @param buf The buffer to find.
190///
191/// @return Its index or DB_COUNT if not found.
192static int diff_buf_idx(buf_T *buf)
193{
194 int idx;
195 for (idx = 0; idx < DB_COUNT; ++idx) {
196 if (curtab->tp_diffbuf[idx] == buf) {
197 break;
198 }
199 }
200 return idx;
201}
202
203/// Find buffer "buf" in the list of diff buffers for tab page "tp".
204///
205/// @param buf
206/// @param tp
207///
208/// @return its index or DB_COUNT if not found.
209static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp)
210{
211 int idx;
212 for (idx = 0; idx < DB_COUNT; ++idx) {
213 if (tp->tp_diffbuf[idx] == buf) {
214 break;
215 }
216 }
217 return idx;
218}
219
220/// Mark the diff info involving buffer "buf" as invalid, it will be updated
221/// when info is requested.
222///
223/// @param buf
224void diff_invalidate(buf_T *buf)
225{
226 FOR_ALL_TABS(tp) {
227 int i = diff_buf_idx_tp(buf, tp);
228 if (i != DB_COUNT) {
229 tp->tp_diff_invalid = true;
230 if (tp == curtab) {
231 diff_redraw(true);
232 }
233 }
234 }
235}
236
237/// Called by mark_adjust(): update line numbers in "curbuf".
238///
239/// @param line1
240/// @param line2
241/// @param amount
242/// @param amount_after
243void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount,
244 long amount_after)
245{
246 // Handle all tab pages that use the current buffer in a diff.
247 FOR_ALL_TABS(tp) {
248 int idx = diff_buf_idx_tp(curbuf, tp);
249 if (idx != DB_COUNT) {
250 diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after);
251 }
252 }
253}
254
255/// Update line numbers in tab page "tp" for "curbuf" with index "idx".
256///
257/// This attempts to update the changes as much as possible:
258/// When inserting/deleting lines outside of existing change blocks, create a
259/// new change block and update the line numbers in following blocks.
260/// When inserting/deleting lines in existing change blocks, update them.
261///
262/// @param tp
263/// @param idx
264/// @param line1
265/// @param line2
266/// @param amount
267/// @amount_after
268static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1,
269 linenr_T line2, long amount, long amount_after)
270{
271 if (diff_internal()) {
272 // Will update diffs before redrawing. Set _invalid to update the
273 // diffs themselves, set _update to also update folds properly just
274 // before redrawing.
275 // Do update marks here, it is needed for :%diffput.
276 tp->tp_diff_invalid = true;
277 tp->tp_diff_update = true;
278 }
279
280 int inserted;
281 int deleted;
282 if (line2 == MAXLNUM) {
283 // mark_adjust(99, MAXLNUM, 9, 0): insert lines
284 inserted = amount;
285 deleted = 0;
286 } else if (amount_after > 0) {
287 // mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines
288 inserted = amount_after;
289 deleted = 0;
290 } else {
291 // mark_adjust(98, 99, MAXLNUM, -2): delete lines
292 inserted = 0;
293 deleted = -amount_after;
294 }
295
296 diff_T *dprev = NULL;
297 diff_T *dp = tp->tp_first_diff;
298
299 linenr_T last;
300 linenr_T lnum_deleted = line1; // lnum of remaining deletion
301 int n;
302 int off;
303 for (;;) {
304 // If the change is after the previous diff block and before the next
305 // diff block, thus not touching an existing change, create a new diff
306 // block. Don't do this when ex_diffgetput() is busy.
307 if (((dp == NULL)
308 || (dp->df_lnum[idx] - 1 > line2)
309 || ((line2 == MAXLNUM) && (dp->df_lnum[idx] > line1)))
310 && ((dprev == NULL)
311 || (dprev->df_lnum[idx] + dprev->df_count[idx] < line1))
312 && !diff_busy) {
313 diff_T *dnext = diff_alloc_new(tp, dprev, dp);
314
315 dnext->df_lnum[idx] = line1;
316 dnext->df_count[idx] = inserted;
317 int i;
318 for (i = 0; i < DB_COUNT; ++i) {
319 if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) {
320 if (dprev == NULL) {
321 dnext->df_lnum[i] = line1;
322 } else {
323 dnext->df_lnum[i] = line1
324 + (dprev->df_lnum[i] + dprev->df_count[i])
325 - (dprev->df_lnum[idx] + dprev->df_count[idx]);
326 }
327 dnext->df_count[i] = deleted;
328 }
329 }
330 }
331
332 // if at end of the list, quit
333 if (dp == NULL) {
334 break;
335 }
336
337 //
338 // Check for these situations:
339 // 1 2 3
340 // 1 2 3
341 // line1 2 3 4 5
342 // 2 3 4 5
343 // 2 3 4 5
344 // line2 2 3 4 5
345 // 3 5 6
346 // 3 5 6
347
348 // compute last line of this change
349 last = dp->df_lnum[idx] + dp->df_count[idx] - 1;
350
351 // 1. change completely above line1: nothing to do
352 if (last >= line1 - 1) {
353 // 6. change below line2: only adjust for amount_after; also when
354 // "deleted" became zero when deleted all lines between two diffs.
355 if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2) {
356 if (amount_after == 0) {
357 // nothing left to change
358 break;
359 }
360 dp->df_lnum[idx] += amount_after;
361 } else {
362 int check_unchanged = false;
363
364 // 2. 3. 4. 5.: inserted/deleted lines touching this diff.
365 if (deleted > 0) {
366 if (dp->df_lnum[idx] >= line1) {
367 off = dp->df_lnum[idx] - lnum_deleted;
368
369 if (last <= line2) {
370 // 4. delete all lines of diff
371 if ((dp->df_next != NULL)
372 && (dp->df_next->df_lnum[idx] - 1 <= line2)) {
373 // delete continues in next diff, only do
374 // lines until that one
375 n = dp->df_next->df_lnum[idx] - lnum_deleted;
376 deleted -= n;
377 n -= dp->df_count[idx];
378 lnum_deleted = dp->df_next->df_lnum[idx];
379 } else {
380 n = deleted - dp->df_count[idx];
381 }
382 dp->df_count[idx] = 0;
383 } else {
384 // 5. delete lines at or just before top of diff
385 n = off;
386 dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1;
387 check_unchanged = true;
388 }
389 dp->df_lnum[idx] = line1;
390 } else {
391 off = 0;
392
393 if (last < line2) {
394 // 2. delete at end of of diff
395 dp->df_count[idx] -= last - lnum_deleted + 1;
396
397 if ((dp->df_next != NULL)
398 && (dp->df_next->df_lnum[idx] - 1 <= line2)) {
399 // delete continues in next diff, only do
400 // lines until that one
401 n = dp->df_next->df_lnum[idx] - 1 - last;
402 deleted -= dp->df_next->df_lnum[idx] - lnum_deleted;
403 lnum_deleted = dp->df_next->df_lnum[idx];
404 } else {
405 n = line2 - last;
406 }
407 check_unchanged = true;
408 } else {
409 // 3. delete lines inside the diff
410 n = 0;
411 dp->df_count[idx] -= deleted;
412 }
413 }
414
415 int i;
416 for (i = 0; i < DB_COUNT; ++i) {
417 if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) {
418 dp->df_lnum[i] -= off;
419 dp->df_count[i] += n;
420 }
421 }
422 } else {
423 if (dp->df_lnum[idx] <= line1) {
424 // inserted lines somewhere in this diff
425 dp->df_count[idx] += inserted;
426 check_unchanged = true;
427 } else {
428 // inserted lines somewhere above this diff
429 dp->df_lnum[idx] += inserted;
430 }
431 }
432
433 if (check_unchanged) {
434 // Check if inserted lines are equal, may reduce the size of the
435 // diff.
436 //
437 // TODO: also check for equal lines in the middle and perhaps split
438 // the block.
439 diff_check_unchanged(tp, dp);
440 }
441 }
442 }
443
444 // check if this block touches the previous one, may merge them.
445 if ((dprev != NULL)
446 && (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) {
447 int i;
448 for (i = 0; i < DB_COUNT; ++i) {
449 if (tp->tp_diffbuf[i] != NULL) {
450 dprev->df_count[i] += dp->df_count[i];
451 }
452 }
453 dprev->df_next = dp->df_next;
454 xfree(dp);
455 dp = dprev->df_next;
456 } else {
457 // Advance to next entry.
458 dprev = dp;
459 dp = dp->df_next;
460 }
461 }
462
463 dprev = NULL;
464 dp = tp->tp_first_diff;
465
466 while (dp != NULL) {
467 // All counts are zero, remove this entry.
468 int i;
469 for (i = 0; i < DB_COUNT; ++i) {
470 if ((tp->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) {
471 break;
472 }
473 }
474
475 if (i == DB_COUNT) {
476 diff_T *dnext = dp->df_next;
477 xfree(dp);
478 dp = dnext;
479
480 if (dprev == NULL) {
481 tp->tp_first_diff = dnext;
482 } else {
483 dprev->df_next = dnext;
484 }
485 } else {
486 // Advance to next entry.
487 dprev = dp;
488 dp = dp->df_next;
489 }
490 }
491
492 if (tp == curtab) {
493 diff_redraw(true);
494
495 // Need to recompute the scroll binding, may remove or add filler
496 // lines (e.g., when adding lines above w_topline). But it's slow when
497 // making many changes, postpone until redrawing.
498 diff_need_scrollbind = true;
499 }
500}
501
502/// Allocate a new diff block and link it between "dprev" and "dp".
503///
504/// @param tp
505/// @param dprev
506/// @param dp
507///
508/// @return The new diff block.
509static diff_T* diff_alloc_new(tabpage_T *tp, diff_T *dprev, diff_T *dp)
510{
511 diff_T *dnew = xmalloc(sizeof(*dnew));
512
513 dnew->df_next = dp;
514 if (dprev == NULL) {
515 tp->tp_first_diff = dnew;
516 } else {
517 dprev->df_next = dnew;
518 }
519
520 return dnew;
521}
522
523/// Check if the diff block "dp" can be made smaller for lines at the start and
524/// end that are equal. Called after inserting lines.
525///
526/// This may result in a change where all buffers have zero lines, the caller
527/// must take care of removing it.
528///
529/// @param tp
530/// @param dp
531static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
532{
533 // Find the first buffers, use it as the original, compare the other
534 // buffer lines against this one.
535 int i_org;
536 for (i_org = 0; i_org < DB_COUNT; ++i_org) {
537 if (tp->tp_diffbuf[i_org] != NULL) {
538 break;
539 }
540 }
541
542 // safety check
543 if (i_org == DB_COUNT) {
544 return;
545 }
546
547 if (diff_check_sanity(tp, dp) == FAIL) {
548 return;
549 }
550
551 // First check lines at the top, then at the bottom.
552 int off_org = 0;
553 int off_new = 0;
554 int dir = FORWARD;
555 for (;;) {
556 // Repeat until a line is found which is different or the number of
557 // lines has become zero.
558 while (dp->df_count[i_org] > 0) {
559 // Copy the line, the next ml_get() will invalidate it.
560 if (dir == BACKWARD) {
561 off_org = dp->df_count[i_org] - 1;
562 }
563 char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org],
564 dp->df_lnum[i_org] + off_org,
565 false));
566
567 int i_new;
568 for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) {
569 if (tp->tp_diffbuf[i_new] == NULL) {
570 continue;
571 }
572
573 if (dir == BACKWARD) {
574 off_new = dp->df_count[i_new] - 1;
575 }
576
577 // if other buffer doesn't have this line, it was inserted
578 if ((off_new < 0) || (off_new >= dp->df_count[i_new])) {
579 break;
580 }
581
582 if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new],
583 dp->df_lnum[i_new] + off_new,
584 false)) != 0) {
585 break;
586 }
587 }
588 xfree(line_org);
589
590 // Stop when a line isn't equal in all diff buffers.
591 if (i_new != DB_COUNT) {
592 break;
593 }
594
595 // Line matched in all buffers, remove it from the diff.
596 for (i_new = i_org; i_new < DB_COUNT; ++i_new) {
597 if (tp->tp_diffbuf[i_new] != NULL) {
598 if (dir == FORWARD) {
599 dp->df_lnum[i_new]++;
600 }
601 dp->df_count[i_new]--;
602 }
603 }
604 }
605
606 if (dir == BACKWARD) {
607 break;
608 }
609 dir = BACKWARD;
610 }
611}
612
613/// Check if a diff block doesn't contain invalid line numbers.
614/// This can happen when the diff program returns invalid results.
615///
616/// @param tp
617/// @param dp
618///
619/// @return OK if the diff block doesn't contain invalid line numbers.
620static int diff_check_sanity(tabpage_T *tp, diff_T *dp)
621{
622 int i;
623 for (i = 0; i < DB_COUNT; ++i) {
624 if (tp->tp_diffbuf[i] != NULL) {
625 if (dp->df_lnum[i] + dp->df_count[i] - 1
626 > tp->tp_diffbuf[i]->b_ml.ml_line_count) {
627 return FAIL;
628 }
629 }
630 }
631 return OK;
632}
633
634/// Mark all diff buffers in the current tab page for redraw.
635///
636/// @param dofold Also recompute the folds
637static void diff_redraw(int dofold)
638{
639 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
640 if (!wp->w_p_diff) {
641 continue;
642 }
643 redraw_win_later(wp, SOME_VALID);
644 if (dofold && foldmethodIsDiff(wp)) {
645 foldUpdateAll(wp);
646 }
647
648 /* A change may have made filler lines invalid, need to take care
649 * of that for other windows. */
650 int n = diff_check(wp, wp->w_topline);
651
652 if (((wp != curwin) && (wp->w_topfill > 0)) || (n > 0)) {
653 if (wp->w_topfill > n) {
654 wp->w_topfill = (n < 0 ? 0 : n);
655 } else if ((n > 0) && (n > wp->w_topfill)) {
656 wp->w_topfill = n;
657 }
658 check_topfill(wp, false);
659 }
660 }
661}
662
663static void clear_diffin(diffin_T *din)
664{
665 if (din->din_fname == NULL) {
666 XFREE_CLEAR(din->din_mmfile.ptr);
667 } else {
668 os_remove((char *)din->din_fname);
669 }
670}
671
672static void clear_diffout(diffout_T *dout)
673{
674 if (dout->dout_fname == NULL) {
675 ga_clear_strings(&dout->dout_ga);
676 } else {
677 os_remove((char *)dout->dout_fname);
678 }
679}
680
681/// Write buffer "buf" to a memory buffer.
682///
683/// @param buf
684/// @param din
685///
686/// @return FAIL for failure.
687static int diff_write_buffer(buf_T *buf, diffin_T *din)
688{
689 linenr_T lnum;
690 char_u *s;
691 long len = 0;
692 char_u *ptr;
693
694 // xdiff requires one big block of memory with all the text.
695 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
696 len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1;
697 }
698 ptr = try_malloc(len);
699 if (ptr == NULL) {
700 // Allocating memory failed. This can happen, because we try to read
701 // the whole buffer text into memory. Set the failed flag, the diff
702 // will be retried with external diff. The flag is never reset.
703 buf->b_diff_failed = true;
704 if (p_verbose > 0) {
705 verbose_enter();
706 smsg(_("Not enough memory to use internal diff for buffer \"%s\""),
707 buf->b_fname);
708 verbose_leave();
709 }
710 return FAIL;
711 }
712 din->din_mmfile.ptr = (char *)ptr;
713 din->din_mmfile.size = len;
714
715 len = 0;
716 for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
717 for (s = ml_get_buf(buf, lnum, false); *s != NUL; ) {
718 if (diff_flags & DIFF_ICASE) {
719 int c;
720
721 // xdiff doesn't support ignoring case, fold-case the text.
722 int orig_len;
723 char_u cbuf[MB_MAXBYTES + 1];
724
725 c = PTR2CHAR(s);
726 c = enc_utf8 ? utf_fold(c) : TOLOWER_LOC(c);
727 orig_len = MB_PTR2LEN(s);
728 if (utf_char2bytes(c, cbuf) != orig_len) {
729 // TODO(Bram): handle byte length difference
730 memmove(ptr + len, s, orig_len);
731 } else {
732 memmove(ptr + len, cbuf, orig_len);
733 }
734
735 s += orig_len;
736 len += orig_len;
737 } else {
738 ptr[len++] = *s++;
739 }
740 }
741 ptr[len++] = NL;
742 }
743 return OK;
744}
745
746/// Write buffer "buf" to file or memory buffer.
747///
748/// Always use 'fileformat' set to "unix".
749///
750/// @param buf
751/// @param din
752///
753/// @return FAIL for failure
754static int diff_write(buf_T *buf, diffin_T *din)
755{
756 if (din->din_fname == NULL) {
757 return diff_write_buffer(buf, din);
758 }
759
760 // Always use 'fileformat' set to "unix".
761 char_u *save_ff = buf->b_p_ff;
762 buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
763 int r = buf_write(buf, din->din_fname, NULL,
764 (linenr_T)1, buf->b_ml.ml_line_count,
765 NULL, false, false, false, true);
766 free_string_option(buf->b_p_ff);
767 buf->b_p_ff = save_ff;
768 return r;
769}
770
771///
772/// Update the diffs for all buffers involved.
773///
774/// @param dio
775/// @param idx_orig
776/// @param eap can be NULL
777static void diff_try_update(diffio_T *dio,
778 int idx_orig,
779 exarg_T *eap)
780{
781 buf_T *buf;
782 int idx_new;
783
784 if (dio->dio_internal) {
785 ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000);
786 } else {
787 // We need three temp file names.
788 dio->dio_orig.din_fname = vim_tempname();
789 dio->dio_new.din_fname = vim_tempname();
790 dio->dio_diff.dout_fname = vim_tempname();
791 if (dio->dio_orig.din_fname == NULL
792 || dio->dio_new.din_fname == NULL
793 || dio->dio_diff.dout_fname == NULL) {
794 goto theend;
795 }
796 }
797
798 // Check external diff is actually working.
799 if (!dio->dio_internal && check_external_diff(dio) == FAIL) {
800 goto theend;
801 }
802
803 // :diffupdate!
804 if (eap != NULL && eap->forceit) {
805 for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) {
806 buf = curtab->tp_diffbuf[idx_new];
807 if (buf_valid(buf)) {
808 buf_check_timestamp(buf, false);
809 }
810 }
811 }
812
813 // Write the first buffer to a tempfile or mmfile_t.
814 buf = curtab->tp_diffbuf[idx_orig];
815 if (diff_write(buf, &dio->dio_orig) == FAIL) {
816 goto theend;
817 }
818
819 // Make a difference between the first buffer and every other.
820 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) {
821 buf = curtab->tp_diffbuf[idx_new];
822 if (buf == NULL || buf->b_ml.ml_mfp == NULL) {
823 continue; // skip buffer that isn't loaded
824 }
825
826 // Write the other buffer and diff with the first one.
827 if (diff_write(buf, &dio->dio_new) == FAIL) {
828 continue;
829 }
830 if (diff_file(dio) == FAIL) {
831 continue;
832 }
833
834 // Read the diff output and add each entry to the diff list.
835 diff_read(idx_orig, idx_new, &dio->dio_diff);
836
837 clear_diffin(&dio->dio_new);
838 clear_diffout(&dio->dio_diff);
839 }
840 clear_diffin(&dio->dio_orig);
841
842theend:
843 xfree(dio->dio_orig.din_fname);
844 xfree(dio->dio_new.din_fname);
845 xfree(dio->dio_diff.dout_fname);
846}
847
848///
849/// Return true if the options are set to use the internal diff library.
850/// Note that if the internal diff failed for one of the buffers, the external
851/// diff will be used anyway.
852///
853int diff_internal(void)
854{
855 return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL;
856}
857
858///
859/// Return true if the internal diff failed for one of the diff buffers.
860///
861static int diff_internal_failed(void)
862{
863 int idx;
864
865 // Only need to do something when there is another buffer.
866 for (idx = 0; idx < DB_COUNT; idx++) {
867 if (curtab->tp_diffbuf[idx] != NULL
868 && curtab->tp_diffbuf[idx]->b_diff_failed) {
869 return true;
870 }
871 }
872 return false;
873}
874
875/// Completely update the diffs for the buffers involved.
876///
877/// When using the external "diff" command the buffers are written to a file,
878/// also for unmodified buffers (the file could have been produced by
879/// autocommands, e.g. the netrw plugin).
880///
881/// @param eap can be NULL
882void ex_diffupdate(exarg_T *eap)
883{
884 if (diff_busy) {
885 diff_need_update = true;
886 return;
887 }
888
889 int had_diffs = curtab->tp_first_diff != NULL;
890
891 // Delete all diffblocks.
892 diff_clear(curtab);
893 curtab->tp_diff_invalid = false;
894
895 // Use the first buffer as the original text.
896 int idx_orig;
897 for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) {
898 if (curtab->tp_diffbuf[idx_orig] != NULL) {
899 break;
900 }
901 }
902
903 if (idx_orig == DB_COUNT) {
904 goto theend;
905 }
906
907 // Only need to do something when there is another buffer.
908 int idx_new;
909 for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) {
910 if (curtab->tp_diffbuf[idx_new] != NULL) {
911 break;
912 }
913 }
914
915 if (idx_new == DB_COUNT) {
916 goto theend;
917 }
918
919 // Only use the internal method if it did not fail for one of the buffers.
920 diffio_T diffio;
921 memset(&diffio, 0, sizeof(diffio));
922 diffio.dio_internal = diff_internal() && !diff_internal_failed();
923
924 diff_try_update(&diffio, idx_orig, eap);
925 if (diffio.dio_internal && diff_internal_failed()) {
926 // Internal diff failed, use external diff instead.
927 memset(&diffio, 0, sizeof(diffio));
928 diff_try_update(&diffio, idx_orig, eap);
929 }
930
931 // force updating cursor position on screen
932 curwin->w_valid_cursor.lnum = 0;
933
934theend:
935 // A redraw is needed if there were diffs and they were cleared, or there
936 // are diffs now, which means they got updated.
937 if (had_diffs || curtab->tp_first_diff != NULL) {
938 diff_redraw(true);
939 apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
940 }
941}
942
943///
944/// Do a quick test if "diff" really works. Otherwise it looks like there
945/// are no differences. Can't use the return value, it's non-zero when
946/// there are differences.
947///
948static int check_external_diff(diffio_T *diffio)
949{
950 // May try twice, first with "-a" and then without.
951 int io_error = false;
952 TriState ok = kFalse;
953 for (;;) {
954 ok = kFalse;
955 FILE *fd = os_fopen((char *)diffio->dio_orig.din_fname, "w");
956
957 if (fd == NULL) {
958 io_error = true;
959 } else {
960 if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) {
961 io_error = true;
962 }
963 fclose(fd);
964 fd = os_fopen((char *)diffio->dio_new.din_fname, "w");
965
966 if (fd == NULL) {
967 io_error = true;
968 } else {
969 if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) {
970 io_error = true;
971 }
972 fclose(fd);
973 fd = NULL;
974 if (diff_file(diffio) == OK) {
975 fd = os_fopen((char *)diffio->dio_diff.dout_fname, "r");
976 }
977
978 if (fd == NULL) {
979 io_error = true;
980 } else {
981 char_u linebuf[LBUFLEN];
982
983 for (;;) {
984 // There must be a line that contains "1c1".
985 if (vim_fgets(linebuf, LBUFLEN, fd)) {
986 break;
987 }
988
989 if (STRNCMP(linebuf, "1c1", 3) == 0) {
990 ok = kTrue;
991 }
992 }
993 fclose(fd);
994 }
995 os_remove((char *)diffio->dio_diff.dout_fname);
996 os_remove((char *)diffio->dio_new.din_fname);
997 }
998 os_remove((char *)diffio->dio_orig.din_fname);
999 }
1000
1001 // When using 'diffexpr' break here.
1002 if (*p_dex != NUL) {
1003 break;
1004 }
1005
1006 // If we checked if "-a" works already, break here.
1007 if (diff_a_works != kNone) {
1008 break;
1009 }
1010 diff_a_works = ok;
1011
1012 // If "-a" works break here, otherwise retry without "-a".
1013 if (ok) {
1014 break;
1015 }
1016 }
1017
1018 if (!ok) {
1019 if (io_error) {
1020 EMSG(_("E810: Cannot read or write temp files"));
1021 }
1022 EMSG(_("E97: Cannot create diffs"));
1023 diff_a_works = kNone;
1024 return FAIL;
1025 }
1026 return OK;
1027}
1028
1029///
1030/// Invoke the xdiff function.
1031///
1032static int diff_file_internal(diffio_T *diffio)
1033{
1034 xpparam_t param;
1035 xdemitconf_t emit_cfg;
1036 xdemitcb_t emit_cb;
1037
1038 memset(&param, 0, sizeof(param));
1039 memset(&emit_cfg, 0, sizeof(emit_cfg));
1040 memset(&emit_cb, 0, sizeof(emit_cb));
1041
1042 param.flags = diff_algorithm;
1043
1044 if (diff_flags & DIFF_IWHITE) {
1045 param.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
1046 }
1047 if (diff_flags & DIFF_IWHITEALL) {
1048 param.flags |= XDF_IGNORE_WHITESPACE;
1049 }
1050 if (diff_flags & DIFF_IWHITEEOL) {
1051 param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
1052 }
1053 if (diff_flags & DIFF_IBLANK) {
1054 param.flags |= XDF_IGNORE_BLANK_LINES;
1055 }
1056
1057 emit_cfg.ctxlen = 0; // don't need any diff_context here
1058 emit_cb.priv = &diffio->dio_diff;
1059 emit_cb.outf = xdiff_out;
1060 if (xdl_diff(&diffio->dio_orig.din_mmfile,
1061 &diffio->dio_new.din_mmfile,
1062 &param, &emit_cfg, &emit_cb) < 0) {
1063 EMSG(_("E960: Problem creating the internal diff"));
1064 return FAIL;
1065 }
1066 return OK;
1067}
1068
1069/// Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff".
1070///
1071/// @param dio
1072///
1073/// @return OK or FAIL
1074static int diff_file(diffio_T *dio)
1075{
1076 char *tmp_orig = (char *)dio->dio_orig.din_fname;
1077 char *tmp_new = (char *)dio->dio_new.din_fname;
1078 char *tmp_diff = (char *)dio->dio_diff.dout_fname;
1079 if (*p_dex != NUL) {
1080 // Use 'diffexpr' to generate the diff file.
1081 eval_diff(tmp_orig, tmp_new, tmp_diff);
1082 return OK;
1083 }
1084 // Use xdiff for generating the diff.
1085 if (dio->dio_internal) {
1086 return diff_file_internal(dio);
1087 } else {
1088 const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff)
1089 + STRLEN(p_srr) + 27);
1090 char *const cmd = xmalloc(len);
1091
1092 // We don't want $DIFF_OPTIONS to get in the way.
1093 if (os_getenv("DIFF_OPTIONS")) {
1094 os_unsetenv("DIFF_OPTIONS");
1095 }
1096
1097 // Build the diff command and execute it. Always use -a, binary
1098 // differences are of no use. Ignore errors, diff returns
1099 // non-zero when differences have been found.
1100 vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s%s%s%s %s",
1101 diff_a_works == kFalse ? "" : "-a ",
1102 "",
1103 (diff_flags & DIFF_IWHITE) ? "-b " : "",
1104 (diff_flags & DIFF_IWHITEALL) ? "-w " : "",
1105 (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "",
1106 (diff_flags & DIFF_IBLANK) ? "-B " : "",
1107 (diff_flags & DIFF_ICASE) ? "-i " : "",
1108 tmp_orig, tmp_new);
1109 append_redir(cmd, len, (char *) p_srr, tmp_diff);
1110 block_autocmds(); // Avoid ShellCmdPost stuff
1111 (void)call_shell((char_u *) cmd,
1112 kShellOptFilter | kShellOptSilent | kShellOptDoOut,
1113 NULL);
1114 unblock_autocmds();
1115 xfree(cmd);
1116 return OK;
1117 }
1118}
1119
1120/// Create a new version of a file from the current buffer and a diff file.
1121///
1122/// The buffer is written to a file, also for unmodified buffers (the file
1123/// could have been produced by autocommands, e.g. the netrw plugin).
1124///
1125/// @param eap
1126void ex_diffpatch(exarg_T *eap)
1127{
1128 char_u *buf = NULL;
1129 win_T *old_curwin = curwin;
1130 char_u *newname = NULL; // name of patched file buffer
1131 char_u *esc_name = NULL;
1132
1133#ifdef UNIX
1134 char *fullname = NULL;
1135#endif
1136
1137 // We need two temp file names.
1138 // Name of original temp file.
1139 char_u *tmp_orig = vim_tempname();
1140 // Name of patched temp file.
1141 char_u *tmp_new = vim_tempname();
1142
1143 if ((tmp_orig == NULL) || (tmp_new == NULL)) {
1144 goto theend;
1145 }
1146
1147 // Write the current buffer to "tmp_orig".
1148 if (buf_write(curbuf, tmp_orig, NULL,
1149 (linenr_T)1, curbuf->b_ml.ml_line_count,
1150 NULL, false, false, false, true) == FAIL) {
1151 goto theend;
1152 }
1153
1154#ifdef UNIX
1155 // Get the absolute path of the patchfile, changing directory below.
1156 fullname = FullName_save((char *)eap->arg, false);
1157 esc_name = vim_strsave_shellescape(
1158 (fullname != NULL ? (char_u *)fullname : eap->arg), true, true);
1159#else
1160 esc_name = vim_strsave_shellescape(eap->arg, true, true);
1161#endif
1162 size_t buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16;
1163 buf = xmalloc(buflen);
1164
1165#ifdef UNIX
1166 char_u dirbuf[MAXPATHL];
1167 // Temporarily chdir to /tmp, to avoid patching files in the current
1168 // directory when the patch file contains more than one patch. When we
1169 // have our own temp dir use that instead, it will be cleaned up when we
1170 // exit (any .rej files created). Don't change directory if we can't
1171 // return to the current.
1172 if ((os_dirname(dirbuf, MAXPATHL) != OK)
1173 || (os_chdir((char *)dirbuf) != 0)) {
1174 dirbuf[0] = NUL;
1175 } else {
1176 char *tempdir = (char *)vim_gettempdir();
1177 if (tempdir == NULL) {
1178 tempdir = "/tmp";
1179 }
1180 os_chdir(tempdir);
1181 shorten_fnames(true);
1182 }
1183#endif
1184
1185 if (*p_pex != NUL) {
1186 // Use 'patchexpr' to generate the new file.
1187#ifdef UNIX
1188 eval_patch((char *)tmp_orig,
1189 (fullname != NULL ? fullname : (char *)eap->arg),
1190 (char *)tmp_new);
1191#else
1192 eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new);
1193#endif
1194 } else {
1195 // Build the patch command and execute it. Ignore errors.
1196 vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s",
1197 tmp_new, tmp_orig, esc_name);
1198 block_autocmds(); // Avoid ShellCmdPost stuff
1199 (void)call_shell(buf, kShellOptFilter, NULL);
1200 unblock_autocmds();
1201 }
1202
1203#ifdef UNIX
1204 if (dirbuf[0] != NUL) {
1205 if (os_chdir((char *)dirbuf) != 0) {
1206 EMSG(_(e_prev_dir));
1207 }
1208 shorten_fnames(true);
1209 }
1210#endif
1211
1212 // Delete any .orig or .rej file created.
1213 STRCPY(buf, tmp_new);
1214 STRCAT(buf, ".orig");
1215 os_remove((char *)buf);
1216 STRCPY(buf, tmp_new);
1217 STRCAT(buf, ".rej");
1218 os_remove((char *)buf);
1219
1220 // Only continue if the output file was created.
1221 FileInfo file_info;
1222 bool info_ok = os_fileinfo((char *)tmp_new, &file_info);
1223 uint64_t filesize = os_fileinfo_size(&file_info);
1224 if (!info_ok || filesize == 0) {
1225 EMSG(_("E816: Cannot read patch output"));
1226 } else {
1227 if (curbuf->b_fname != NULL) {
1228 newname = vim_strnsave(curbuf->b_fname,
1229 (int)(STRLEN(curbuf->b_fname) + 4));
1230 STRCAT(newname, ".new");
1231 }
1232
1233 // don't use a new tab page, each tab page has its own diffs
1234 cmdmod.tab = 0;
1235
1236 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) {
1237 // Pretend it was a ":split fname" command
1238 eap->cmdidx = CMD_split;
1239 eap->arg = tmp_new;
1240 do_exedit(eap, old_curwin);
1241
1242 // check that split worked and editing tmp_new
1243 if ((curwin != old_curwin) && win_valid(old_curwin)) {
1244 // Set 'diff', 'scrollbind' on and 'wrap' off.
1245 diff_win_options(curwin, true);
1246 diff_win_options(old_curwin, true);
1247
1248 if (newname != NULL) {
1249 // do a ":file filename.new" on the patched buffer
1250 eap->arg = newname;
1251 ex_file(eap);
1252
1253 // Do filetype detection with the new name.
1254 if (au_has_group((char_u *)"filetypedetect")) {
1255 do_cmdline_cmd(":doau filetypedetect BufRead");
1256 }
1257 }
1258 }
1259 }
1260 }
1261
1262theend:
1263 if (tmp_orig != NULL) {
1264 os_remove((char *)tmp_orig);
1265 }
1266 xfree(tmp_orig);
1267
1268 if (tmp_new != NULL) {
1269 os_remove((char *)tmp_new);
1270 }
1271 xfree(tmp_new);
1272 xfree(newname);
1273 xfree(buf);
1274#ifdef UNIX
1275 xfree(fullname);
1276#endif
1277 xfree(esc_name);
1278}
1279
1280/// Split the window and edit another file, setting options to show the diffs.
1281///
1282/// @param eap
1283void ex_diffsplit(exarg_T *eap)
1284{
1285 win_T *old_curwin = curwin;
1286 bufref_T old_curbuf;
1287 set_bufref(&old_curbuf, curbuf);
1288
1289 // Need to compute w_fraction when no redraw happened yet.
1290 validate_cursor();
1291 set_fraction(curwin);
1292
1293 // don't use a new tab page, each tab page has its own diffs
1294 cmdmod.tab = 0;
1295
1296 if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) {
1297 // Pretend it was a ":split fname" command
1298 eap->cmdidx = CMD_split;
1299 curwin->w_p_diff = true;
1300 do_exedit(eap, old_curwin);
1301
1302 // split must have worked
1303 if (curwin != old_curwin) {
1304 // Set 'diff', 'scrollbind' on and 'wrap' off.
1305 diff_win_options(curwin, true);
1306 if (win_valid(old_curwin)) {
1307 diff_win_options(old_curwin, true);
1308
1309 if (bufref_valid(&old_curbuf)) {
1310 // Move the cursor position to that of the old window.
1311 curwin->w_cursor.lnum = diff_get_corresponding_line(
1312 old_curbuf.br_buf, old_curwin->w_cursor.lnum);
1313 }
1314 }
1315 // Now that lines are folded scroll to show the cursor at the same
1316 // relative position.
1317 scroll_to_fraction(curwin, curwin->w_height);
1318 }
1319 }
1320}
1321
1322// Set options to show diffs for the current window.
1323void ex_diffthis(exarg_T *eap)
1324{
1325 // Set 'diff', 'scrollbind' on and 'wrap' off.
1326 diff_win_options(curwin, true);
1327}
1328
1329static void set_diff_option(win_T *wp, int value)
1330{
1331 win_T *old_curwin = curwin;
1332
1333 curwin = wp;
1334 curbuf = curwin->w_buffer;
1335 curbuf_lock++;
1336 set_option_value("diff", (long)value, NULL, OPT_LOCAL);
1337 curbuf_lock--;
1338 curwin = old_curwin;
1339 curbuf = curwin->w_buffer;
1340}
1341
1342
1343/// Set options in window "wp" for diff mode.
1344///
1345/// @param addbuf Add buffer to diff.
1346void diff_win_options(win_T *wp, int addbuf)
1347{
1348 win_T *old_curwin = curwin;
1349
1350 // close the manually opened folds
1351 curwin = wp;
1352 newFoldLevel();
1353 curwin = old_curwin;
1354
1355 // Use 'scrollbind' and 'cursorbind' when available
1356 if (!wp->w_p_diff) {
1357 wp->w_p_scb_save = wp->w_p_scb;
1358 }
1359 wp->w_p_scb = true;
1360
1361 if (!wp->w_p_diff) {
1362 wp->w_p_crb_save = wp->w_p_crb;
1363 }
1364 wp->w_p_crb = true;
1365
1366 if (!wp->w_p_diff) {
1367 wp->w_p_wrap_save = wp->w_p_wrap;
1368 }
1369 wp->w_p_wrap = false;
1370 curwin = wp; // -V519
1371 curbuf = curwin->w_buffer;
1372
1373 if (!wp->w_p_diff) {
1374 if (wp->w_p_diff_saved) {
1375 free_string_option(wp->w_p_fdm_save);
1376 }
1377 wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
1378 }
1379 set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff",
1380 OPT_LOCAL | OPT_FREE, 0);
1381 curwin = old_curwin;
1382 curbuf = curwin->w_buffer;
1383
1384 if (!wp->w_p_diff) {
1385 wp->w_p_fdc_save = wp->w_p_fdc;
1386 wp->w_p_fen_save = wp->w_p_fen;
1387 wp->w_p_fdl_save = wp->w_p_fdl;
1388 }
1389 wp->w_p_fdc = diff_foldcolumn;
1390 wp->w_p_fen = true;
1391 wp->w_p_fdl = 0;
1392 foldUpdateAll(wp);
1393
1394 // make sure topline is not halfway through a fold
1395 changed_window_setting_win(wp);
1396 if (vim_strchr(p_sbo, 'h') == NULL) {
1397 do_cmdline_cmd("set sbo+=hor");
1398 }
1399
1400 // Save the current values, to be restored in ex_diffoff().
1401 wp->w_p_diff_saved = true;
1402
1403 set_diff_option(wp, true);
1404
1405 if (addbuf) {
1406 diff_buf_add(wp->w_buffer);
1407 }
1408 redraw_win_later(wp, NOT_VALID);
1409}
1410
1411/// Set options not to show diffs. For the current window or all windows.
1412/// Only in the current tab page.
1413///
1414/// @param eap
1415void ex_diffoff(exarg_T *eap)
1416{
1417 int diffwin = false;
1418
1419 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
1420 if (eap->forceit ? wp->w_p_diff : (wp == curwin)) {
1421 // Set 'diff' off. If option values were saved in
1422 // diff_win_options(), restore the ones whose settings seem to have
1423 // been left over from diff mode.
1424 set_diff_option(wp, false);
1425
1426 if (wp->w_p_diff_saved) {
1427 if (wp->w_p_scb) {
1428 wp->w_p_scb = wp->w_p_scb_save;
1429 }
1430
1431 if (wp->w_p_crb) {
1432 wp->w_p_crb = wp->w_p_crb_save;
1433 }
1434
1435 if (!wp->w_p_wrap) {
1436 wp->w_p_wrap = wp->w_p_wrap_save;
1437 }
1438
1439 free_string_option(wp->w_p_fdm);
1440 wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save
1441 ? wp->w_p_fdm_save
1442 : (char_u *)"manual");
1443 if (wp->w_p_fdc == diff_foldcolumn) {
1444 wp->w_p_fdc = wp->w_p_fdc_save;
1445 }
1446 if (wp->w_p_fdl == 0) {
1447 wp->w_p_fdl = wp->w_p_fdl_save;
1448 }
1449 // Only restore 'foldenable' when 'foldmethod' is not
1450 // "manual", otherwise we continue to show the diff folds.
1451 if (wp->w_p_fen) {
1452 wp->w_p_fen = foldmethodIsManual(wp) ? false : wp->w_p_fen_save;
1453 }
1454
1455 foldUpdateAll(wp);
1456 }
1457 // remove filler lines
1458 wp->w_topfill = 0;
1459
1460 // make sure topline is not halfway a fold and cursor is
1461 // invalidated
1462 changed_window_setting_win(wp);
1463
1464 // Note: 'sbo' is not restored, it's a global option.
1465 diff_buf_adjust(wp);
1466 }
1467 diffwin |= wp->w_p_diff;
1468 }
1469
1470 // Also remove hidden buffers from the list.
1471 if (eap->forceit) {
1472 diff_buf_clear();
1473 }
1474
1475 // Remove "hor" from from 'scrollopt' if there are no diff windows left.
1476 if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) {
1477 do_cmdline_cmd("set sbo-=hor");
1478 }
1479}
1480
1481/// Read the diff output and add each entry to the diff list.
1482///
1483/// @param idx_orig idx of original file
1484/// @param idx_new idx of new file
1485/// @dout diff output
1486static void diff_read(int idx_orig, int idx_new, diffout_T *dout)
1487{
1488 FILE *fd = NULL;
1489 int line_idx = 0;
1490 diff_T *dprev = NULL;
1491 diff_T *dp = curtab->tp_first_diff;
1492 diff_T *dn, *dpl;
1493 char_u linebuf[LBUFLEN]; // only need to hold the diff line
1494 char_u *line;
1495 long off;
1496 int i;
1497 linenr_T lnum_orig, lnum_new;
1498 long count_orig, count_new;
1499 int notset = true; // block "*dp" not set yet
1500 enum {
1501 DIFF_ED,
1502 DIFF_UNIFIED,
1503 DIFF_NONE
1504 } diffstyle = DIFF_NONE;
1505
1506 if (dout->dout_fname == NULL) {
1507 diffstyle = DIFF_UNIFIED;
1508 } else {
1509 fd = os_fopen((char *)dout->dout_fname, "r");
1510 if (fd == NULL) {
1511 EMSG(_("E98: Cannot read diff output"));
1512 return;
1513 }
1514 }
1515
1516 for (;;) {
1517 if (fd == NULL) {
1518 if (line_idx >= dout->dout_ga.ga_len) {
1519 break; // did last line
1520 }
1521 line = ((char_u **)dout->dout_ga.ga_data)[line_idx++];
1522 } else {
1523 if (vim_fgets(linebuf, LBUFLEN, fd)) {
1524 break; // end of file
1525 }
1526 line = linebuf;
1527 }
1528
1529 if (diffstyle == DIFF_NONE) {
1530 // Determine diff style.
1531 // ed like diff looks like this:
1532 // {first}[,{last}]c{first}[,{last}]
1533 // {first}a{first}[,{last}]
1534 // {first}[,{last}]d{first}
1535 //
1536 // unified diff looks like this:
1537 // --- file1 2018-03-20 13:23:35.783153140 +0100
1538 // +++ file2 2018-03-20 13:23:41.183156066 +0100
1539 // @@ -1,3 +1,5 @@
1540 if (isdigit(*line)) {
1541 diffstyle = DIFF_ED;
1542 } else if ((STRNCMP(line, "@@ ", 3) == 0)) {
1543 diffstyle = DIFF_UNIFIED;
1544 } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
1545 && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
1546 && (STRNCMP(line, "+++ ", 4) == 0)
1547 && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
1548 && (STRNCMP(line, "@@ ", 3) == 0)) {
1549 diffstyle = DIFF_UNIFIED;
1550 } else {
1551 // Format not recognized yet, skip over this line. Cygwin diff
1552 // may put a warning at the start of the file.
1553 continue;
1554 }
1555 }
1556
1557 if (diffstyle == DIFF_ED) {
1558 if (!isdigit(*line)) {
1559 continue; // not the start of a diff block
1560 }
1561 if (parse_diff_ed(line, &lnum_orig, &count_orig,
1562 &lnum_new, &count_new) == FAIL) {
1563 continue;
1564 }
1565 } else {
1566 assert(diffstyle == DIFF_UNIFIED);
1567 if (STRNCMP(line, "@@ ", 3) != 0) {
1568 continue; // not the start of a diff block
1569 }
1570 if (parse_diff_unified(line, &lnum_orig, &count_orig,
1571 &lnum_new, &count_new) == FAIL) {
1572 continue;
1573 }
1574 }
1575
1576 // Go over blocks before the change, for which orig and new are equal.
1577 // Copy blocks from orig to new.
1578 while (dp != NULL
1579 && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig]) {
1580 if (notset) {
1581 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1582 }
1583 dprev = dp;
1584 dp = dp->df_next;
1585 notset = true;
1586 }
1587
1588 if ((dp != NULL)
1589 && (lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
1590 && (lnum_orig + count_orig >= dp->df_lnum[idx_orig])) {
1591 // New block overlaps with existing block(s).
1592 // First find last block that overlaps.
1593 for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next) {
1594 if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig]) {
1595 break;
1596 }
1597 }
1598
1599 // If the newly found block starts before the old one, set the
1600 // start back a number of lines.
1601 off = dp->df_lnum[idx_orig] - lnum_orig;
1602
1603 if (off > 0) {
1604 for (i = idx_orig; i < idx_new; ++i) {
1605 if (curtab->tp_diffbuf[i] != NULL) {
1606 dp->df_lnum[i] -= off;
1607 }
1608 }
1609 dp->df_lnum[idx_new] = lnum_new;
1610 dp->df_count[idx_new] = count_new;
1611 } else if (notset) {
1612 // new block inside existing one, adjust new block
1613 dp->df_lnum[idx_new] = lnum_new + off;
1614 dp->df_count[idx_new] = count_new - off;
1615 } else {
1616 // second overlap of new block with existing block
1617 dp->df_count[idx_new] += count_new - count_orig
1618 + dpl->df_lnum[idx_orig] +
1619 dpl->df_count[idx_orig]
1620 - (dp->df_lnum[idx_orig] +
1621 dp->df_count[idx_orig]);
1622 }
1623
1624 // Adjust the size of the block to include all the lines to the
1625 // end of the existing block or the new diff, whatever ends last.
1626 off = (lnum_orig + count_orig)
1627 - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
1628
1629 if (off < 0) {
1630 // new change ends in existing block, adjust the end if not
1631 // done already
1632 if (notset) {
1633 dp->df_count[idx_new] += -off;
1634 }
1635 off = 0;
1636 }
1637
1638 for (i = idx_orig; i < idx_new; ++i) {
1639 if (curtab->tp_diffbuf[i] != NULL) {
1640 dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
1641 - dp->df_lnum[i] + off;
1642 }
1643 }
1644
1645 // Delete the diff blocks that have been merged into one.
1646 dn = dp->df_next;
1647 dp->df_next = dpl->df_next;
1648
1649 while (dn != dp->df_next) {
1650 dpl = dn->df_next;
1651 xfree(dn);
1652 dn = dpl;
1653 }
1654 } else {
1655 // Allocate a new diffblock.
1656 dp = diff_alloc_new(curtab, dprev, dp);
1657
1658 dp->df_lnum[idx_orig] = lnum_orig;
1659 dp->df_count[idx_orig] = count_orig;
1660 dp->df_lnum[idx_new] = lnum_new;
1661 dp->df_count[idx_new] = count_new;
1662
1663 // Set values for other buffers, these must be equal to the
1664 // original buffer, otherwise there would have been a change
1665 // already.
1666 for (i = idx_orig + 1; i < idx_new; ++i) {
1667 if (curtab->tp_diffbuf[i] != NULL) {
1668 diff_copy_entry(dprev, dp, idx_orig, i);
1669 }
1670 }
1671 }
1672 notset = false; // "*dp" has been set
1673 }
1674
1675 // for remaining diff blocks orig and new are equal
1676 while (dp != NULL) {
1677 if (notset) {
1678 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1679 }
1680 dprev = dp;
1681 dp = dp->df_next;
1682 notset = true;
1683 }
1684
1685 if (fd != NULL) {
1686 fclose(fd);
1687 }
1688}
1689
1690/// Copy an entry at "dp" from "idx_orig" to "idx_new".
1691///
1692/// @param dprev
1693/// @param dp
1694/// @param idx_orig
1695/// @param idx_new
1696static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig,
1697 int idx_new)
1698{
1699 long off;
1700
1701 if (dprev == NULL) {
1702 off = 0;
1703 } else {
1704 off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig])
1705 - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]);
1706 }
1707 dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off;
1708 dp->df_count[idx_new] = dp->df_count[idx_orig];
1709}
1710
1711/// Clear the list of diffblocks for tab page "tp".
1712///
1713/// @param tp
1714void diff_clear(tabpage_T *tp)
1715{
1716 diff_T *p;
1717 diff_T *next_p;
1718 for (p = tp->tp_first_diff; p != NULL; p = next_p) {
1719 next_p = p->df_next;
1720 xfree(p);
1721 }
1722 tp->tp_first_diff = NULL;
1723}
1724
1725/// Check diff status for line "lnum" in buffer "buf":
1726///
1727/// Returns 0 for nothing special
1728/// Returns -1 for a line that should be highlighted as changed.
1729/// Returns -2 for a line that should be highlighted as added/deleted.
1730/// Returns > 0 for inserting that many filler lines above it (never happens
1731/// when 'diffopt' doesn't contain "filler").
1732/// This should only be used for windows where 'diff' is set.
1733///
1734/// @param wp
1735/// @param lnum
1736///
1737/// @return diff status.
1738int diff_check(win_T *wp, linenr_T lnum)
1739{
1740 int idx; // index in tp_diffbuf[] for this buffer
1741 diff_T *dp;
1742 int maxcount;
1743 int i;
1744 buf_T *buf = wp->w_buffer;
1745 int cmp;
1746
1747 if (curtab->tp_diff_invalid) {
1748 // update after a big change
1749 ex_diffupdate(NULL);
1750 }
1751
1752 // no diffs at all
1753 if ((curtab->tp_first_diff == NULL) || !wp->w_p_diff) {
1754 return 0;
1755 }
1756
1757 // safety check: "lnum" must be a buffer line
1758 if ((lnum < 1) || (lnum > buf->b_ml.ml_line_count + 1)) {
1759 return 0;
1760 }
1761
1762 idx = diff_buf_idx(buf);
1763
1764 if (idx == DB_COUNT) {
1765 // no diffs for buffer "buf"
1766 return 0;
1767 }
1768
1769 // A closed fold never has filler lines.
1770 if (hasFoldingWin(wp, lnum, NULL, NULL, true, NULL)) {
1771 return 0;
1772 }
1773
1774 // search for a change that includes "lnum" in the list of diffblocks.
1775 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
1776 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
1777 break;
1778 }
1779 }
1780
1781 if ((dp == NULL) || (lnum < dp->df_lnum[idx])) {
1782 return 0;
1783 }
1784
1785 if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) {
1786 int zero = false;
1787
1788 // Changed or inserted line. If the other buffers have a count of
1789 // zero, the lines were inserted. If the other buffers have the same
1790 // count, check if the lines are identical.
1791 cmp = false;
1792
1793 for (i = 0; i < DB_COUNT; ++i) {
1794 if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) {
1795 if (dp->df_count[i] == 0) {
1796 zero = true;
1797 } else {
1798 if (dp->df_count[i] != dp->df_count[idx]) {
1799 // nr of lines changed.
1800 return -1;
1801 }
1802 cmp = true;
1803 }
1804 }
1805 }
1806
1807 if (cmp) {
1808 // Compare all lines. If they are equal the lines were inserted
1809 // in some buffers, deleted in others, but not changed.
1810 for (i = 0; i < DB_COUNT; ++i) {
1811 if ((i != idx)
1812 && (curtab->tp_diffbuf[i] != NULL)
1813 && (dp->df_count[i] != 0)) {
1814 if (!diff_equal_entry(dp, idx, i)) {
1815 return -1;
1816 }
1817 }
1818 }
1819 }
1820
1821 // If there is no buffer with zero lines then there is no difference
1822 // any longer. Happens when making a change (or undo) that removes
1823 // the difference. Can't remove the entry here, we might be halfway
1824 // through updating the window. Just report the text as unchanged.
1825 // Other windows might still show the change though.
1826 if (zero == false) {
1827 return 0;
1828 }
1829 return -2;
1830 }
1831
1832 // If 'diffopt' doesn't contain "filler", return 0.
1833 if (!(diff_flags & DIFF_FILLER)) {
1834 return 0;
1835 }
1836
1837 // Insert filler lines above the line just below the change. Will return
1838 // 0 when this buf had the max count.
1839 maxcount = 0;
1840 for (i = 0; i < DB_COUNT; ++i) {
1841 if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) {
1842 maxcount = dp->df_count[i];
1843 }
1844 }
1845 return maxcount - dp->df_count[idx];
1846}
1847
1848/// Compare two entries in diff "dp" and return true if they are equal.
1849///
1850/// @param dp diff
1851/// @param idx1 first entry in diff "dp"
1852/// @param idx2 second entry in diff "dp"
1853///
1854/// @return true if two entires are equal.
1855static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
1856 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
1857{
1858 if (dp->df_count[idx1] != dp->df_count[idx2]) {
1859 return false;
1860 }
1861
1862 if (diff_check_sanity(curtab, dp) == FAIL) {
1863 return false;
1864 }
1865
1866 for (int i = 0; i < dp->df_count[idx1]; i++) {
1867 char_u *line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
1868 dp->df_lnum[idx1] + i, false));
1869
1870 int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
1871 dp->df_lnum[idx2] + i, false));
1872 xfree(line);
1873
1874 if (cmp != 0) {
1875 return false;
1876 }
1877 }
1878 return true;
1879}
1880
1881// Compare the characters at "p1" and "p2". If they are equal (possibly
1882// ignoring case) return true and set "len" to the number of bytes.
1883static bool diff_equal_char(const char_u *const p1, const char_u *const p2,
1884 int *const len)
1885{
1886 const int l = utfc_ptr2len(p1);
1887
1888 if (l != utfc_ptr2len(p2)) {
1889 return false;
1890 }
1891 if (l > 1) {
1892 if (STRNCMP(p1, p2, l) != 0
1893 && (!(diff_flags & DIFF_ICASE)
1894 || utf_fold(utf_ptr2char(p1)) != utf_fold(utf_ptr2char(p2)))) {
1895 return false;
1896 }
1897 *len = l;
1898 } else {
1899 if ((*p1 != *p2)
1900 && (!(diff_flags & DIFF_ICASE)
1901 || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2))) {
1902 return false;
1903 }
1904 *len = 1;
1905 }
1906 return true;
1907}
1908
1909/// Compare strings "s1" and "s2" according to 'diffopt'.
1910/// Return non-zero when they are different.
1911///
1912/// @param s1 The first string
1913/// @param s2 The second string
1914///
1915/// @return on-zero if the two strings are different.
1916static int diff_cmp(char_u *s1, char_u *s2)
1917{
1918 if ((diff_flags & DIFF_IBLANK)
1919 && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) {
1920 return 0;
1921 }
1922
1923 if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) {
1924 return STRCMP(s1, s2);
1925 }
1926
1927 if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) {
1928 return mb_stricmp((const char *)s1, (const char *)s2);
1929 }
1930
1931 char_u *p1 = s1;
1932 char_u *p2 = s2;
1933
1934 // Ignore white space changes and possibly ignore case.
1935 while (*p1 != NUL && *p2 != NUL) {
1936 if (((diff_flags & DIFF_IWHITE)
1937 && ascii_iswhite(*p1) && ascii_iswhite(*p2))
1938 || ((diff_flags & DIFF_IWHITEALL)
1939 && (ascii_iswhite(*p1) || ascii_iswhite(*p2)))) {
1940 p1 = skipwhite(p1);
1941 p2 = skipwhite(p2);
1942 } else {
1943 int l;
1944 if (!diff_equal_char(p1, p2, &l)) {
1945 break;
1946 }
1947 p1 += l;
1948 p2 += l;
1949 }
1950 }
1951
1952 // Ignore trailing white space.
1953 p1 = skipwhite(p1);
1954 p2 = skipwhite(p2);
1955
1956 if ((*p1 != NUL) || (*p2 != NUL)) {
1957 return 1;
1958 }
1959 return 0;
1960}
1961
1962/// Return the number of filler lines above "lnum".
1963///
1964/// @param wp
1965/// @param lnum
1966///
1967/// @return Number of filler lines above lnum
1968int diff_check_fill(win_T *wp, linenr_T lnum)
1969{
1970 // be quick when there are no filler lines
1971 if (!(diff_flags & DIFF_FILLER)) {
1972 return 0;
1973 }
1974 int n = diff_check(wp, lnum);
1975
1976 if (n <= 0) {
1977 return 0;
1978 }
1979 return n;
1980}
1981
1982/// Set the topline of "towin" to match the position in "fromwin", so that they
1983/// show the same diff'ed lines.
1984///
1985/// @param fromwin
1986/// @param towin
1987void diff_set_topline(win_T *fromwin, win_T *towin)
1988{
1989 buf_T *frombuf = fromwin->w_buffer;
1990 linenr_T lnum = fromwin->w_topline;
1991 diff_T *dp;
1992 int max_count;
1993 int i;
1994
1995 int fromidx = diff_buf_idx(frombuf);
1996 if (fromidx == DB_COUNT) {
1997 // safety check
1998 return;
1999 }
2000
2001 if (curtab->tp_diff_invalid) {
2002 // update after a big change
2003 ex_diffupdate(NULL);
2004 }
2005 towin->w_topfill = 0;
2006
2007 // search for a change that includes "lnum" in the list of diffblocks.
2008 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2009 if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
2010 break;
2011 }
2012 }
2013
2014 if (dp == NULL) {
2015 // After last change, compute topline relative to end of file; no
2016 // filler lines.
2017 towin->w_topline = towin->w_buffer->b_ml.ml_line_count
2018 - (frombuf->b_ml.ml_line_count - lnum);
2019 } else {
2020 // Find index for "towin".
2021 int toidx = diff_buf_idx(towin->w_buffer);
2022
2023 if (toidx == DB_COUNT) {
2024 // safety check
2025 return;
2026 }
2027 towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
2028
2029 if (lnum >= dp->df_lnum[fromidx]) {
2030 // Inside a change: compute filler lines. With three or more
2031 // buffers we need to know the largest count.
2032 max_count = 0;
2033
2034 for (i = 0; i < DB_COUNT; ++i) {
2035 if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) {
2036 max_count = dp->df_count[i];
2037 }
2038 }
2039
2040 if (dp->df_count[toidx] == dp->df_count[fromidx]) {
2041 // same number of lines: use same filler count
2042 towin->w_topfill = fromwin->w_topfill;
2043 } else if (dp->df_count[toidx] > dp->df_count[fromidx]) {
2044 if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
2045 // more lines in towin and fromwin doesn't show diff
2046 // lines, only filler lines
2047 if (max_count - fromwin->w_topfill >= dp->df_count[toidx]) {
2048 // towin also only shows filler lines
2049 towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
2050 towin->w_topfill = fromwin->w_topfill;
2051 } else {
2052 // towin still has some diff lines to show
2053 towin->w_topline = dp->df_lnum[toidx]
2054 + max_count - fromwin->w_topfill;
2055 }
2056 }
2057 } else if (towin->w_topline >= dp->df_lnum[toidx]
2058 + dp->df_count[toidx]) {
2059 // less lines in towin and no diff lines to show: compute
2060 // filler lines
2061 towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
2062
2063 if (diff_flags & DIFF_FILLER) {
2064 if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx]) {
2065 // fromwin is also out of diff lines
2066 towin->w_topfill = fromwin->w_topfill;
2067 } else {
2068 // fromwin has some diff lines
2069 towin->w_topfill = dp->df_lnum[fromidx] + max_count - lnum;
2070 }
2071 }
2072 }
2073 }
2074 }
2075
2076 // safety check (if diff info gets outdated strange things may happen)
2077 towin->w_botfill = false;
2078
2079 if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) {
2080 towin->w_topline = towin->w_buffer->b_ml.ml_line_count;
2081 towin->w_botfill = true;
2082 }
2083
2084 if (towin->w_topline < 1) {
2085 towin->w_topline = 1;
2086 towin->w_topfill = 0;
2087 }
2088
2089 // When w_topline changes need to recompute w_botline and cursor position
2090 invalidate_botline_win(towin);
2091 changed_line_abv_curs_win(towin);
2092
2093 check_topfill(towin, false);
2094 (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline,
2095 NULL, true, NULL);
2096}
2097
2098/// This is called when 'diffopt' is changed.
2099///
2100/// @return
2101int diffopt_changed(void)
2102{
2103 int diff_context_new = 6;
2104 int diff_flags_new = 0;
2105 int diff_foldcolumn_new = 2;
2106 long diff_algorithm_new = 0;
2107 long diff_indent_heuristic = 0;
2108
2109 char_u *p = p_dip;
2110 while (*p != NUL) {
2111 if (STRNCMP(p, "filler", 6) == 0) {
2112 p += 6;
2113 diff_flags_new |= DIFF_FILLER;
2114 } else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
2115 p += 8;
2116 diff_context_new = getdigits_int(&p, false, diff_context_new);
2117 } else if (STRNCMP(p, "iblank", 6) == 0) {
2118 p += 6;
2119 diff_flags_new |= DIFF_IBLANK;
2120 } else if (STRNCMP(p, "icase", 5) == 0) {
2121 p += 5;
2122 diff_flags_new |= DIFF_ICASE;
2123 } else if (STRNCMP(p, "iwhiteall", 9) == 0) {
2124 p += 9;
2125 diff_flags_new |= DIFF_IWHITEALL;
2126 } else if (STRNCMP(p, "iwhiteeol", 9) == 0) {
2127 p += 9;
2128 diff_flags_new |= DIFF_IWHITEEOL;
2129 } else if (STRNCMP(p, "iwhite", 6) == 0) {
2130 p += 6;
2131 diff_flags_new |= DIFF_IWHITE;
2132 } else if (STRNCMP(p, "horizontal", 10) == 0) {
2133 p += 10;
2134 diff_flags_new |= DIFF_HORIZONTAL;
2135 } else if (STRNCMP(p, "vertical", 8) == 0) {
2136 p += 8;
2137 diff_flags_new |= DIFF_VERTICAL;
2138 } else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
2139 p += 11;
2140 diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new);
2141 } else if (STRNCMP(p, "hiddenoff", 9) == 0) {
2142 p += 9;
2143 diff_flags_new |= DIFF_HIDDEN_OFF;
2144 } else if (STRNCMP(p, "indent-heuristic", 16) == 0) {
2145 p += 16;
2146 diff_indent_heuristic = XDF_INDENT_HEURISTIC;
2147 } else if (STRNCMP(p, "internal", 8) == 0) {
2148 p += 8;
2149 diff_flags_new |= DIFF_INTERNAL;
2150 } else if (STRNCMP(p, "algorithm:", 10) == 0) {
2151 p += 10;
2152 if (STRNCMP(p, "myers", 5) == 0) {
2153 p += 5;
2154 diff_algorithm_new = 0;
2155 } else if (STRNCMP(p, "minimal", 7) == 0) {
2156 p += 7;
2157 diff_algorithm_new = XDF_NEED_MINIMAL;
2158 } else if (STRNCMP(p, "patience", 8) == 0) {
2159 p += 8;
2160 diff_algorithm_new = XDF_PATIENCE_DIFF;
2161 } else if (STRNCMP(p, "histogram", 9) == 0) {
2162 p += 9;
2163 diff_algorithm_new = XDF_HISTOGRAM_DIFF;
2164 } else {
2165 return FAIL;
2166 }
2167 }
2168
2169 if ((*p != ',') && (*p != NUL)) {
2170 return FAIL;
2171 }
2172
2173 if (*p == ',') {
2174 ++p;
2175 }
2176 }
2177
2178 diff_algorithm_new |= diff_indent_heuristic;
2179
2180 // Can't have both "horizontal" and "vertical".
2181 if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) {
2182 return FAIL;
2183 }
2184
2185 // If flags were added or removed, or the algorithm was changed, need to
2186 // update the diff.
2187 if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) {
2188 FOR_ALL_TABS(tp) {
2189 tp->tp_diff_invalid = true;
2190 }
2191 }
2192
2193 diff_flags = diff_flags_new;
2194 diff_context = diff_context_new == 0 ? 1 : diff_context_new;
2195 diff_foldcolumn = diff_foldcolumn_new;
2196 diff_algorithm = diff_algorithm_new;
2197
2198 diff_redraw(true);
2199
2200 // recompute the scroll binding with the new option value, may
2201 // remove or add filler lines
2202 check_scrollbind((linenr_T)0, 0L);
2203 return OK;
2204}
2205
2206/// Check that "diffopt" contains "horizontal".
2207bool diffopt_horizontal(void)
2208 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
2209{
2210 return (diff_flags & DIFF_HORIZONTAL) != 0;
2211}
2212
2213// Return true if 'diffopt' contains "hiddenoff".
2214bool diffopt_hiddenoff(void)
2215{
2216 return (diff_flags & DIFF_HIDDEN_OFF) != 0;
2217}
2218
2219/// Find the difference within a changed line.
2220///
2221/// @param wp window whose current buffer to check
2222/// @param lnum line number to check within the buffer
2223/// @param startp first char of the change
2224/// @param endp last char of the change
2225///
2226/// @return true if the line was added, no other buffer has it.
2227bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
2228 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
2229{
2230 char_u *line_new;
2231 int si_org;
2232 int si_new;
2233 int ei_org;
2234 int ei_new;
2235 bool added = true;
2236 int l;
2237
2238 // Make a copy of the line, the next ml_get() will invalidate it.
2239 char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, false));
2240
2241 int idx = diff_buf_idx(wp->w_buffer);
2242 if (idx == DB_COUNT) {
2243 // cannot happen
2244 xfree(line_org);
2245 return false;
2246 }
2247
2248 // search for a change that includes "lnum" in the list of diffblocks.
2249 diff_T *dp;
2250 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2251 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
2252 break;
2253 }
2254 }
2255
2256 if ((dp == NULL) || (diff_check_sanity(curtab, dp) == FAIL)) {
2257 xfree(line_org);
2258 return false;
2259 }
2260
2261 int off = lnum - dp->df_lnum[idx];
2262 int i;
2263 for (i = 0; i < DB_COUNT; ++i) {
2264 if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) {
2265 // Skip lines that are not in the other change (filler lines).
2266 if (off >= dp->df_count[i]) {
2267 continue;
2268 }
2269 added = false;
2270 line_new = ml_get_buf(curtab->tp_diffbuf[i],
2271 dp->df_lnum[i] + off, false);
2272
2273 // Search for start of difference
2274 si_org = si_new = 0;
2275
2276 while (line_org[si_org] != NUL) {
2277 if (((diff_flags & DIFF_IWHITE)
2278 && ascii_iswhite(line_org[si_org])
2279 && ascii_iswhite(line_new[si_new]))
2280 || ((diff_flags & DIFF_IWHITEALL)
2281 && (ascii_iswhite(line_org[si_org])
2282 || ascii_iswhite(line_new[si_new])))) {
2283 si_org = (int)(skipwhite(line_org + si_org) - line_org);
2284 si_new = (int)(skipwhite(line_new + si_new) - line_new);
2285 } else {
2286 if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
2287 break;
2288 }
2289 si_org += l;
2290 si_new += l;
2291 }
2292 }
2293
2294 // Move back to first byte of character in both lines (may
2295 // have "nn^" in line_org and "n^ in line_new).
2296 si_org -= utf_head_off(line_org, line_org + si_org);
2297 si_new -= utf_head_off(line_new, line_new + si_new);
2298
2299 if (*startp > si_org) {
2300 *startp = si_org;
2301 }
2302
2303 // Search for end of difference, if any.
2304 if ((line_org[si_org] != NUL) || (line_new[si_new] != NUL)) {
2305 ei_org = (int)STRLEN(line_org);
2306 ei_new = (int)STRLEN(line_new);
2307
2308 while (ei_org >= *startp
2309 && ei_new >= si_new
2310 && ei_org >= 0
2311 && ei_new >= 0) {
2312 if (((diff_flags & DIFF_IWHITE)
2313 && ascii_iswhite(line_org[ei_org])
2314 && ascii_iswhite(line_new[ei_new]))
2315 || ((diff_flags & DIFF_IWHITEALL)
2316 && (ascii_iswhite(line_org[ei_org])
2317 || ascii_iswhite(line_new[ei_new])))) {
2318 while (ei_org >= *startp && ascii_iswhite(line_org[ei_org])) {
2319 ei_org--;
2320 }
2321
2322 while (ei_new >= si_new && ascii_iswhite(line_new[ei_new])) {
2323 ei_new--;
2324 }
2325 } else {
2326 const char_u *p1 = line_org + ei_org;
2327 const char_u *p2 = line_new + ei_new;
2328
2329 p1 -= utf_head_off(line_org, p1);
2330 p2 -= utf_head_off(line_new, p2);
2331
2332 if (!diff_equal_char(p1, p2, &l)) {
2333 break;
2334 }
2335 ei_org -= l;
2336 ei_new -= l;
2337 }
2338 }
2339
2340 if (*endp < ei_org) {
2341 *endp = ei_org;
2342 }
2343 }
2344 }
2345 }
2346
2347 xfree(line_org);
2348 return added;
2349}
2350
2351/// Check that line "lnum" is not close to a diff block, this line should
2352/// be in a fold.
2353///
2354/// @param wp window containing the buffer to check
2355/// @param lnum line number to check within the buffer
2356///
2357/// @return false if there are no diff blocks at all in this window.
2358bool diff_infold(win_T *wp, linenr_T lnum)
2359 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
2360{
2361 bool other = false;
2362 diff_T *dp;
2363
2364 // Return if 'diff' isn't set.
2365 if (!wp->w_p_diff) {
2366 return false;
2367 }
2368
2369 int idx = -1;
2370 int i;
2371 for (i = 0; i < DB_COUNT; ++i) {
2372 if (curtab->tp_diffbuf[i] == wp->w_buffer) {
2373 idx = i;
2374 } else if (curtab->tp_diffbuf[i] != NULL) {
2375 other = true;
2376 }
2377 }
2378
2379 // return here if there are no diffs in the window
2380 if ((idx == -1) || !other) {
2381 return false;
2382 }
2383
2384 if (curtab->tp_diff_invalid) {
2385 // update after a big change
2386 ex_diffupdate(NULL);
2387 }
2388
2389 // Return if there are no diff blocks. All lines will be folded.
2390 if (curtab->tp_first_diff == NULL) {
2391 return true;
2392 }
2393
2394 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2395 // If this change is below the line there can't be any further match.
2396 if (dp->df_lnum[idx] - diff_context > lnum) {
2397 break;
2398 }
2399
2400 // If this change ends before the line we have a match.
2401 if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum) {
2402 return false;
2403 }
2404 }
2405 return true;
2406}
2407
2408/// "dp" and "do" commands.
2409void nv_diffgetput(bool put, size_t count)
2410{
2411 exarg_T ea;
2412 char buf[30];
2413
2414 if (count == 0) {
2415 ea.arg = (char_u *)"";
2416 } else {
2417 vim_snprintf(buf, 30, "%zu", count);
2418 ea.arg = (char_u *)buf;
2419 }
2420
2421 if (put) {
2422 ea.cmdidx = CMD_diffput;
2423 } else {
2424 ea.cmdidx = CMD_diffget;
2425 }
2426
2427 ea.addr_count = 0;
2428 ea.line1 = curwin->w_cursor.lnum;
2429 ea.line2 = curwin->w_cursor.lnum;
2430 ex_diffgetput(&ea);
2431}
2432
2433/// ":diffget" and ":diffput"
2434///
2435/// @param eap
2436void ex_diffgetput(exarg_T *eap)
2437{
2438 linenr_T lnum;
2439 int count;
2440 linenr_T off = 0;
2441 diff_T *dp;
2442 diff_T *dprev;
2443 diff_T *dfree;
2444 int i;
2445 int added;
2446 char_u *p;
2447 aco_save_T aco;
2448 buf_T *buf;
2449 int start_skip, end_skip;
2450 int new_count;
2451 int buf_empty;
2452 int found_not_ma = false;
2453 int idx_other;
2454 int idx_from;
2455 int idx_to;
2456
2457 // Find the current buffer in the list of diff buffers.
2458 int idx_cur = diff_buf_idx(curbuf);
2459 if (idx_cur == DB_COUNT) {
2460 EMSG(_("E99: Current buffer is not in diff mode"));
2461 return;
2462 }
2463
2464 if (*eap->arg == NUL) {
2465 // No argument: Find the other buffer in the list of diff buffers.
2466 for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) {
2467 if ((curtab->tp_diffbuf[idx_other] != curbuf)
2468 && (curtab->tp_diffbuf[idx_other] != NULL)) {
2469 if ((eap->cmdidx != CMD_diffput)
2470 || MODIFIABLE(curtab->tp_diffbuf[idx_other])) {
2471 break;
2472 }
2473 found_not_ma = true;
2474 }
2475 }
2476
2477 if (idx_other == DB_COUNT) {
2478 if (found_not_ma) {
2479 EMSG(_("E793: No other buffer in diff mode is modifiable"));
2480 } else {
2481 EMSG(_("E100: No other buffer in diff mode"));
2482 }
2483 return;
2484 }
2485
2486 // Check that there isn't a third buffer in the list
2487 for (i = idx_other + 1; i < DB_COUNT; ++i) {
2488 if ((curtab->tp_diffbuf[i] != curbuf)
2489 && (curtab->tp_diffbuf[i] != NULL)
2490 && ((eap->cmdidx != CMD_diffput)
2491 || MODIFIABLE(curtab->tp_diffbuf[i]))) {
2492 EMSG(_("E101: More than two buffers in diff mode, don't know "
2493 "which one to use"));
2494 return;
2495 }
2496 }
2497 } else {
2498 // Buffer number or pattern given. Ignore trailing white space.
2499 p = eap->arg + STRLEN(eap->arg);
2500 while (p > eap->arg && ascii_iswhite(p[-1])) {
2501 p--;
2502 }
2503
2504 for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) {
2505 }
2506
2507 if (eap->arg + i == p) {
2508 // digits only
2509 i = atol((char *)eap->arg);
2510 } else {
2511 i = buflist_findpat(eap->arg, p, false, true, false);
2512
2513 if (i < 0) {
2514 // error message already given
2515 return;
2516 }
2517 }
2518 buf = buflist_findnr(i);
2519
2520 if (buf == NULL) {
2521 EMSG2(_("E102: Can't find buffer \"%s\""), eap->arg);
2522 return;
2523 }
2524
2525 if (buf == curbuf) {
2526 // nothing to do
2527 return;
2528 }
2529 idx_other = diff_buf_idx(buf);
2530
2531 if (idx_other == DB_COUNT) {
2532 EMSG2(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg);
2533 return;
2534 }
2535 }
2536
2537 diff_busy = true;
2538
2539 // When no range given include the line above or below the cursor.
2540 if (eap->addr_count == 0) {
2541 // Make it possible that ":diffget" on the last line gets line below
2542 // the cursor line when there is no difference above the cursor.
2543 if ((eap->cmdidx == CMD_diffget)
2544 && (eap->line1 == curbuf->b_ml.ml_line_count)
2545 && (diff_check(curwin, eap->line1) == 0)
2546 && ((eap->line1 == 1) || (diff_check(curwin, eap->line1 - 1) == 0))) {
2547 ++eap->line2;
2548 } else if (eap->line1 > 0) {
2549 --eap->line1;
2550 }
2551 }
2552
2553 if (eap->cmdidx == CMD_diffget) {
2554 idx_from = idx_other;
2555 idx_to = idx_cur;
2556 } else {
2557 idx_from = idx_cur;
2558 idx_to = idx_other;
2559
2560 // Need to make the other buffer the current buffer to be able to make
2561 // changes in it.
2562
2563 // set curwin/curbuf to buf and save a few things
2564 aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]);
2565 }
2566
2567 // May give the warning for a changed buffer here, which can trigger the
2568 // FileChangedRO autocommand, which may do nasty things and mess
2569 // everything up.
2570 if (!curbuf->b_changed) {
2571 change_warning(0);
2572 if (diff_buf_idx(curbuf) != idx_to) {
2573 EMSG(_("E787: Buffer changed unexpectedly"));
2574 goto theend;
2575 }
2576 }
2577
2578 dprev = NULL;
2579
2580 for (dp = curtab->tp_first_diff; dp != NULL;) {
2581 if (dp->df_lnum[idx_cur] > eap->line2 + off) {
2582 // past the range that was specified
2583 break;
2584 }
2585 dfree = NULL;
2586 lnum = dp->df_lnum[idx_to];
2587 count = dp->df_count[idx_to];
2588
2589 if ((dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off)
2590 && (u_save(lnum - 1, lnum + count) != FAIL)) {
2591 // Inside the specified range and saving for undo worked.
2592 start_skip = 0;
2593 end_skip = 0;
2594
2595 if (eap->addr_count > 0) {
2596 // A range was specified: check if lines need to be skipped.
2597 start_skip = eap->line1 + off - dp->df_lnum[idx_cur];
2598 if (start_skip > 0) {
2599 // range starts below start of current diff block
2600 if (start_skip > count) {
2601 lnum += count;
2602 count = 0;
2603 } else {
2604 count -= start_skip;
2605 lnum += start_skip;
2606 }
2607 } else {
2608 start_skip = 0;
2609 }
2610
2611 end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1
2612 - (eap->line2 + off);
2613
2614 if (end_skip > 0) {
2615 // range ends above end of current/from diff block
2616 if (idx_cur == idx_from) {
2617 // :diffput
2618 i = dp->df_count[idx_cur] - start_skip - end_skip;
2619
2620 if (count > i) {
2621 count = i;
2622 }
2623 } else {
2624 // :diffget
2625 count -= end_skip;
2626 end_skip = dp->df_count[idx_from] - start_skip - count;
2627
2628 if (end_skip < 0) {
2629 end_skip = 0;
2630 }
2631 }
2632 } else {
2633 end_skip = 0;
2634 }
2635 }
2636
2637 buf_empty = BUFEMPTY();
2638 added = 0;
2639
2640 for (i = 0; i < count; ++i) {
2641 // remember deleting the last line of the buffer
2642 buf_empty = curbuf->b_ml.ml_line_count == 1;
2643 ml_delete(lnum, false);
2644 added--;
2645 }
2646
2647 for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) {
2648 linenr_T nr = dp->df_lnum[idx_from] + start_skip + i;
2649 if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) {
2650 break;
2651 }
2652 p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
2653 ml_append(lnum + i - 1, p, 0, false);
2654 xfree(p);
2655 added++;
2656 if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) {
2657 // Added the first line into an empty buffer, need to
2658 // delete the dummy empty line.
2659 buf_empty = false;
2660 ml_delete((linenr_T)2, false);
2661 }
2662 }
2663 new_count = dp->df_count[idx_to] + added;
2664 dp->df_count[idx_to] = new_count;
2665
2666 if ((start_skip == 0) && (end_skip == 0)) {
2667 // Check if there are any other buffers and if the diff is
2668 // equal in them.
2669 for (i = 0; i < DB_COUNT; ++i) {
2670 if ((curtab->tp_diffbuf[i] != NULL)
2671 && (i != idx_from)
2672 && (i != idx_to)
2673 && !diff_equal_entry(dp, idx_from, i)) {
2674 break;
2675 }
2676 }
2677
2678 if (i == DB_COUNT) {
2679 // delete the diff entry, the buffers are now equal here
2680 dfree = dp;
2681 dp = dp->df_next;
2682
2683 if (dprev == NULL) {
2684 curtab->tp_first_diff = dp;
2685 } else {
2686 dprev->df_next = dp;
2687 }
2688 }
2689 }
2690
2691 // Adjust marks. This will change the following entries!
2692 if (added != 0) {
2693 mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added, false);
2694 if (curwin->w_cursor.lnum >= lnum) {
2695 // Adjust the cursor position if it's in/after the changed
2696 // lines.
2697 if (curwin->w_cursor.lnum >= lnum + count) {
2698 curwin->w_cursor.lnum += added;
2699 } else if (added < 0) {
2700 curwin->w_cursor.lnum = lnum;
2701 }
2702 }
2703 }
2704 changed_lines(lnum, 0, lnum + count, (long)added, true);
2705
2706 if (dfree != NULL) {
2707 // Diff is deleted, update folds in other windows.
2708 diff_fold_update(dfree, idx_to);
2709 xfree(dfree);
2710 } else {
2711 // mark_adjust() may have changed the count in a wrong way
2712 dp->df_count[idx_to] = new_count;
2713 }
2714
2715 // When changing the current buffer, keep track of line numbers
2716 if (idx_cur == idx_to) {
2717 off += added;
2718 }
2719 }
2720
2721 // If before the range or not deleted, go to next diff.
2722 if (dfree == NULL) {
2723 dprev = dp;
2724 dp = dp->df_next;
2725 }
2726 }
2727
2728 // restore curwin/curbuf and a few other things
2729 if (eap->cmdidx != CMD_diffget) {
2730 // Syncing undo only works for the current buffer, but we change
2731 // another buffer. Sync undo if the command was typed. This isn't
2732 // 100% right when ":diffput" is used in a function or mapping.
2733 if (KeyTyped) {
2734 u_sync(false);
2735 }
2736 aucmd_restbuf(&aco);
2737 }
2738
2739theend:
2740 diff_busy = false;
2741 if (diff_need_update) {
2742 ex_diffupdate(NULL);
2743 }
2744
2745 // Check that the cursor is on a valid character and update its
2746 // position. When there were filler lines the topline has become
2747 // invalid.
2748 check_cursor();
2749 changed_line_abv_curs();
2750
2751 if (diff_need_update) {
2752 // redraw already done by ex_diffupdate()
2753 diff_need_update = false;
2754 } else {
2755 // Also need to redraw the other buffers.
2756 diff_redraw(false);
2757 apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf);
2758 }
2759}
2760
2761/// Update folds for all diff buffers for entry "dp".
2762///
2763/// Skip buffer with index "skip_idx".
2764/// When there are no diffs, all folds are removed.
2765///
2766/// @param dp
2767/// @param skip_idx
2768static void diff_fold_update(diff_T *dp, int skip_idx)
2769{
2770 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
2771 for (int i = 0; i < DB_COUNT; ++i) {
2772 if ((curtab->tp_diffbuf[i] == wp->w_buffer) && (i != skip_idx)) {
2773 foldUpdate(wp, dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i]);
2774 }
2775 }
2776 }
2777}
2778
2779/// Checks that the buffer is in diff-mode.
2780///
2781/// @param buf buffer to check.
2782bool diff_mode_buf(buf_T *buf)
2783 FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1)
2784{
2785 FOR_ALL_TABS(tp) {
2786 if (diff_buf_idx_tp(buf, tp) != DB_COUNT) {
2787 return true;
2788 }
2789 }
2790 return false;
2791}
2792
2793/// Move "count" times in direction "dir" to the next diff block.
2794///
2795/// @param dir
2796/// @param count
2797///
2798/// @return FAIL if there isn't such a diff block.
2799int diff_move_to(int dir, long count)
2800{
2801 linenr_T lnum = curwin->w_cursor.lnum;
2802 int idx = diff_buf_idx(curbuf);
2803 if ((idx == DB_COUNT) || (curtab->tp_first_diff == NULL)) {
2804 return FAIL;
2805 }
2806
2807 if (curtab->tp_diff_invalid) {
2808 // update after a big change
2809 ex_diffupdate(NULL);
2810 }
2811
2812 if (curtab->tp_first_diff == NULL) {
2813 // no diffs today
2814 return FAIL;
2815 }
2816
2817 while (--count >= 0) {
2818 // Check if already before first diff.
2819 if ((dir == BACKWARD) && (lnum <= curtab->tp_first_diff->df_lnum[idx])) {
2820 break;
2821 }
2822
2823 diff_T *dp;
2824 for (dp = curtab->tp_first_diff;; dp = dp->df_next) {
2825 if (dp == NULL) {
2826 break;
2827 }
2828
2829 if (((dir == FORWARD) && (lnum < dp->df_lnum[idx]))
2830 || ((dir == BACKWARD)
2831 && ((dp->df_next == NULL)
2832 || (lnum <= dp->df_next->df_lnum[idx])))) {
2833 lnum = dp->df_lnum[idx];
2834 break;
2835 }
2836 }
2837 }
2838
2839 // don't end up past the end of the file
2840 if (lnum > curbuf->b_ml.ml_line_count) {
2841 lnum = curbuf->b_ml.ml_line_count;
2842 }
2843
2844 // When the cursor didn't move at all we fail.
2845 if (lnum == curwin->w_cursor.lnum) {
2846 return FAIL;
2847 }
2848
2849 setpcmark();
2850 curwin->w_cursor.lnum = lnum;
2851 curwin->w_cursor.col = 0;
2852
2853 return OK;
2854}
2855
2856/// Return the line number in the current window that is closest to "lnum1" in
2857/// "buf1" in diff mode.
2858static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1)
2859{
2860 int idx1;
2861 int idx2;
2862 diff_T *dp;
2863 int baseline = 0;
2864
2865 idx1 = diff_buf_idx(buf1);
2866 idx2 = diff_buf_idx(curbuf);
2867
2868 if ((idx1 == DB_COUNT)
2869 || (idx2 == DB_COUNT)
2870 || (curtab->tp_first_diff == NULL)) {
2871 return lnum1;
2872 }
2873
2874 if (curtab->tp_diff_invalid) {
2875 // update after a big change
2876 ex_diffupdate(NULL);
2877 }
2878
2879 if (curtab->tp_first_diff == NULL) {
2880 // no diffs today
2881 return lnum1;
2882 }
2883
2884 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2885 if (dp->df_lnum[idx1] > lnum1) {
2886 return lnum1 - baseline;
2887 }
2888 if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) {
2889 // Inside the diffblock
2890 baseline = lnum1 - dp->df_lnum[idx1];
2891
2892 if (baseline > dp->df_count[idx2]) {
2893 baseline = dp->df_count[idx2];
2894 }
2895
2896 return dp->df_lnum[idx2] + baseline;
2897 }
2898 if ((dp->df_lnum[idx1] == lnum1)
2899 && (dp->df_count[idx1] == 0)
2900 && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum)
2901 && ((dp->df_lnum[idx2] + dp->df_count[idx2])
2902 > curwin->w_cursor.lnum)) {
2903 // Special case: if the cursor is just after a zero-count
2904 // block (i.e. all filler) and the target cursor is already
2905 // inside the corresponding block, leave the target cursor
2906 // unmoved. This makes repeated CTRL-W W operations work
2907 // as expected.
2908 return curwin->w_cursor.lnum;
2909 }
2910 baseline = (dp->df_lnum[idx1] + dp->df_count[idx1])
2911 - (dp->df_lnum[idx2] + dp->df_count[idx2]);
2912 }
2913
2914 // If we get here then the cursor is after the last diff
2915 return lnum1 - baseline;
2916}
2917
2918/// Finds the corresponding line in a diff.
2919///
2920/// @param buf1
2921/// @param lnum1
2922///
2923/// @return The corresponding line.
2924linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1)
2925{
2926 linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1);
2927
2928 // don't end up past the end of the file
2929 if (lnum > curbuf->b_ml.ml_line_count) {
2930 return curbuf->b_ml.ml_line_count;
2931 }
2932 return lnum;
2933}
2934
2935/// For line "lnum" in the current window find the equivalent lnum in window
2936/// "wp", compensating for inserted/deleted lines.
2937linenr_T diff_lnum_win(linenr_T lnum, win_T *wp)
2938{
2939 diff_T *dp;
2940 int idx;
2941 int i;
2942 linenr_T n;
2943
2944 idx = diff_buf_idx(curbuf);
2945
2946 if (idx == DB_COUNT) {
2947 // safety check
2948 return (linenr_T)0;
2949 }
2950
2951 if (curtab->tp_diff_invalid) {
2952 // update after a big change
2953 ex_diffupdate(NULL);
2954 }
2955
2956 // search for a change that includes "lnum" in the list of diffblocks.
2957 for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) {
2958 if (lnum <= dp->df_lnum[idx] + dp->df_count[idx]) {
2959 break;
2960 }
2961 }
2962
2963 // When after the last change, compute relative to the last line number.
2964 if (dp == NULL) {
2965 return wp->w_buffer->b_ml.ml_line_count
2966 - (curbuf->b_ml.ml_line_count - lnum);
2967 }
2968
2969 // Find index for "wp".
2970 i = diff_buf_idx(wp->w_buffer);
2971
2972 if (i == DB_COUNT) {
2973 // safety check
2974 return (linenr_T)0;
2975 }
2976
2977 n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]);
2978 if (n > dp->df_lnum[i] + dp->df_count[i]) {
2979 n = dp->df_lnum[i] + dp->df_count[i];
2980 }
2981 return n;
2982}
2983
2984///
2985/// Handle an ED style diff line.
2986/// Return FAIL if the line does not contain diff info.
2987///
2988static int parse_diff_ed(char_u *line,
2989 linenr_T *lnum_orig,
2990 long *count_orig,
2991 linenr_T *lnum_new,
2992 long *count_new)
2993{
2994 char_u *p;
2995 long f1, l1, f2, l2;
2996 int difftype;
2997
2998 // The line must be one of three formats:
2999 // change: {first}[,{last}]c{first}[,{last}]
3000 // append: {first}a{first}[,{last}]
3001 // delete: {first}[,{last}]d{first}
3002 p = line;
3003 f1 = getdigits(&p, true, 0);
3004 if (*p == ',') {
3005 p++;
3006 l1 = getdigits(&p, true, 0);
3007 } else {
3008 l1 = f1;
3009 }
3010 if (*p != 'a' && *p != 'c' && *p != 'd') {
3011 return FAIL; // invalid diff format
3012 }
3013 difftype = *p++;
3014 f2 = getdigits(&p, true, 0);
3015 if (*p == ',') {
3016 p++;
3017 l2 = getdigits(&p, true, 0);
3018 } else {
3019 l2 = f2;
3020 }
3021 if (l1 < f1 || l2 < f2) {
3022 return FAIL;
3023 }
3024
3025 if (difftype == 'a') {
3026 *lnum_orig = f1 + 1;
3027 *count_orig = 0;
3028 } else {
3029 *lnum_orig = f1;
3030 *count_orig = l1 - f1 + 1;
3031 }
3032 if (difftype == 'd') {
3033 *lnum_new = f2 + 1;
3034 *count_new = 0;
3035 } else {
3036 *lnum_new = f2;
3037 *count_new = l2 - f2 + 1;
3038 }
3039 return OK;
3040}
3041
3042///
3043/// Parses unified diff with zero(!) context lines.
3044/// Return FAIL if there is no diff information in "line".
3045///
3046static int parse_diff_unified(char_u *line,
3047 linenr_T *lnum_orig,
3048 long *count_orig,
3049 linenr_T *lnum_new,
3050 long *count_new)
3051{
3052 char_u *p;
3053 long oldline, oldcount, newline, newcount;
3054
3055 // Parse unified diff hunk header:
3056 // @@ -oldline,oldcount +newline,newcount @@
3057 p = line;
3058 if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') {
3059 oldline = getdigits(&p, true, 0);
3060 if (*p == ',') {
3061 p++;
3062 oldcount = getdigits(&p, true, 0);
3063 } else {
3064 oldcount = 1;
3065 }
3066 if (*p++ == ' ' && *p++ == '+') {
3067 newline = getdigits(&p, true, 0);
3068 if (*p == ',') {
3069 p++;
3070 newcount = getdigits(&p, true, 0);
3071 } else {
3072 newcount = 1;
3073 }
3074 } else {
3075 return FAIL; // invalid diff format
3076 }
3077
3078 if (oldcount == 0) {
3079 oldline += 1;
3080 }
3081 if (newcount == 0) {
3082 newline += 1;
3083 }
3084 if (newline == 0) {
3085 newline = 1;
3086 }
3087
3088 *lnum_orig = oldline;
3089 *count_orig = oldcount;
3090 *lnum_new = newline;
3091 *count_new = newcount;
3092
3093 return OK;
3094 }
3095
3096 return FAIL;
3097}
3098
3099///
3100/// Callback function for the xdl_diff() function.
3101/// Stores the diff output in a grow array.
3102///
3103static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf)
3104{
3105 diffout_T *dout = (diffout_T *)priv;
3106 char_u *p;
3107
3108 // The header line always comes by itself, text lines in at least two
3109 // parts. We drop the text part.
3110 if (nbuf > 1) {
3111 return 0;
3112 }
3113
3114 // sanity check
3115 if (STRNCMP(mb[0].ptr, "@@ ", 3) != 0) {
3116 return 0;
3117 }
3118
3119 ga_grow(&dout->dout_ga, 1);
3120
3121 p = vim_strnsave((char_u *)mb[0].ptr, mb[0].size);
3122 ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p;
3123 return 0;
3124}
3125