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