Tizen 2.0 Release
[external/vim.git] / src / diff.c
1 /* vim:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved    by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9
10 /*
11  * diff.c: code for diff'ing two, three or four buffers.
12  */
13
14 #include "vim.h"
15
16 #if defined(FEAT_DIFF) || defined(PROTO)
17
18 static int      diff_busy = FALSE;      /* ex_diffgetput() is busy */
19
20 /* flags obtained from the 'diffopt' option */
21 #define DIFF_FILLER     1       /* display filler lines */
22 #define DIFF_ICASE      2       /* ignore case */
23 #define DIFF_IWHITE     4       /* ignore change in white space */
24 #define DIFF_HORIZONTAL 8       /* horizontal splits */
25 #define DIFF_VERTICAL   16      /* vertical splits */
26 static int      diff_flags = DIFF_FILLER;
27
28 #define LBUFLEN 50              /* length of line in diff file */
29
30 static int diff_a_works = MAYBE; /* TRUE when "diff -a" works, FALSE when it
31                                     doesn't work, MAYBE when not checked yet */
32 #if defined(MSWIN) || defined(MSDOS)
33 static int diff_bin_works = MAYBE; /* TRUE when "diff --binary" works, FALSE
34                                       when it doesn't work, MAYBE when not
35                                       checked yet */
36 #endif
37
38 static int diff_buf_idx __ARGS((buf_T *buf));
39 static int diff_buf_idx_tp __ARGS((buf_T *buf, tabpage_T *tp));
40 static void diff_mark_adjust_tp __ARGS((tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after));
41 static void diff_check_unchanged __ARGS((tabpage_T *tp, diff_T *dp));
42 static int diff_check_sanity __ARGS((tabpage_T *tp, diff_T *dp));
43 static void diff_redraw __ARGS((int dofold));
44 static int diff_write __ARGS((buf_T *buf, char_u *fname));
45 static void diff_file __ARGS((char_u *tmp_orig, char_u *tmp_new, char_u *tmp_diff));
46 static int diff_equal_entry __ARGS((diff_T *dp, int idx1, int idx2));
47 static int diff_cmp __ARGS((char_u *s1, char_u *s2));
48 #ifdef FEAT_FOLDING
49 static void diff_fold_update __ARGS((diff_T *dp, int skip_idx));
50 #endif
51 static void diff_read __ARGS((int idx_orig, int idx_new, char_u *fname));
52 static void diff_copy_entry __ARGS((diff_T *dprev, diff_T *dp, int idx_orig, int idx_new));
53 static diff_T *diff_alloc_new __ARGS((tabpage_T *tp, diff_T *dprev, diff_T *dp));
54
55 #ifndef USE_CR
56 # define tag_fgets vim_fgets
57 #endif
58
59 /*
60  * Called when deleting or unloading a buffer: No longer make a diff with it.
61  */
62     void
63 diff_buf_delete(buf)
64     buf_T       *buf;
65 {
66     int         i;
67     tabpage_T   *tp;
68
69     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
70     {
71         i = diff_buf_idx_tp(buf, tp);
72         if (i != DB_COUNT)
73         {
74             tp->tp_diffbuf[i] = NULL;
75             tp->tp_diff_invalid = TRUE;
76             if (tp == curtab)
77                 diff_redraw(TRUE);
78         }
79     }
80 }
81
82 /*
83  * Check if the current buffer should be added to or removed from the list of
84  * diff buffers.
85  */
86     void
87 diff_buf_adjust(win)
88     win_T       *win;
89 {
90     win_T       *wp;
91     int         i;
92
93     if (!win->w_p_diff)
94     {
95         /* When there is no window showing a diff for this buffer, remove
96          * it from the diffs. */
97         for (wp = firstwin; wp != NULL; wp = wp->w_next)
98             if (wp->w_buffer == win->w_buffer && wp->w_p_diff)
99                 break;
100         if (wp == NULL)
101         {
102             i = diff_buf_idx(win->w_buffer);
103             if (i != DB_COUNT)
104             {
105                 curtab->tp_diffbuf[i] = NULL;
106                 curtab->tp_diff_invalid = TRUE;
107                 diff_redraw(TRUE);
108             }
109         }
110     }
111     else
112         diff_buf_add(win->w_buffer);
113 }
114
115 /*
116  * Add a buffer to make diffs for.
117  * Call this when a new buffer is being edited in the current window where
118  * 'diff' is set.
119  * Marks the current buffer as being part of the diff and requiring updating.
120  * This must be done before any autocmd, because a command may use info
121  * about the screen contents.
122  */
123     void
124 diff_buf_add(buf)
125     buf_T       *buf;
126 {
127     int         i;
128
129     if (diff_buf_idx(buf) != DB_COUNT)
130         return;         /* It's already there. */
131
132     for (i = 0; i < DB_COUNT; ++i)
133         if (curtab->tp_diffbuf[i] == NULL)
134         {
135             curtab->tp_diffbuf[i] = buf;
136             curtab->tp_diff_invalid = TRUE;
137             diff_redraw(TRUE);
138             return;
139         }
140
141     EMSGN(_("E96: Can not diff more than %ld buffers"), DB_COUNT);
142 }
143
144 /*
145  * Find buffer "buf" in the list of diff buffers for the current tab page.
146  * Return its index or DB_COUNT if not found.
147  */
148     static int
149 diff_buf_idx(buf)
150     buf_T       *buf;
151 {
152     int         idx;
153
154     for (idx = 0; idx < DB_COUNT; ++idx)
155         if (curtab->tp_diffbuf[idx] == buf)
156             break;
157     return idx;
158 }
159
160 /*
161  * Find buffer "buf" in the list of diff buffers for tab page "tp".
162  * Return its index or DB_COUNT if not found.
163  */
164     static int
165 diff_buf_idx_tp(buf, tp)
166     buf_T       *buf;
167     tabpage_T   *tp;
168 {
169     int         idx;
170
171     for (idx = 0; idx < DB_COUNT; ++idx)
172         if (tp->tp_diffbuf[idx] == buf)
173             break;
174     return idx;
175 }
176
177 /*
178  * Mark the diff info involving buffer "buf" as invalid, it will be updated
179  * when info is requested.
180  */
181     void
182 diff_invalidate(buf)
183     buf_T       *buf;
184 {
185     tabpage_T   *tp;
186     int         i;
187
188     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
189     {
190         i = diff_buf_idx_tp(buf, tp);
191         if (i != DB_COUNT)
192         {
193             tp->tp_diff_invalid = TRUE;
194             if (tp == curtab)
195                 diff_redraw(TRUE);
196         }
197     }
198 }
199
200 /*
201  * Called by mark_adjust(): update line numbers in "curbuf".
202  */
203     void
204 diff_mark_adjust(line1, line2, amount, amount_after)
205     linenr_T    line1;
206     linenr_T    line2;
207     long        amount;
208     long        amount_after;
209 {
210     int         idx;
211     tabpage_T   *tp;
212
213     /* Handle all tab pages that use the current buffer in a diff. */
214     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
215     {
216         idx = diff_buf_idx_tp(curbuf, tp);
217         if (idx != DB_COUNT)
218             diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after);
219     }
220 }
221
222 /*
223  * Update line numbers in tab page "tp" for "curbuf" with index "idx".
224  * This attempts to update the changes as much as possible:
225  * When inserting/deleting lines outside of existing change blocks, create a
226  * new change block and update the line numbers in following blocks.
227  * When inserting/deleting lines in existing change blocks, update them.
228  */
229     static void
230 diff_mark_adjust_tp(tp, idx, line1, line2, amount, amount_after)
231     tabpage_T   *tp;
232     int         idx;
233     linenr_T    line1;
234     linenr_T    line2;
235     long        amount;
236     long        amount_after;
237 {
238     diff_T      *dp;
239     diff_T      *dprev;
240     diff_T      *dnext;
241     int         i;
242     int         inserted, deleted;
243     int         n, off;
244     linenr_T    last;
245     linenr_T    lnum_deleted = line1;   /* lnum of remaining deletion */
246     int         check_unchanged;
247
248     if (line2 == MAXLNUM)
249     {
250         /* mark_adjust(99, MAXLNUM, 9, 0): insert lines */
251         inserted = amount;
252         deleted = 0;
253     }
254     else if (amount_after > 0)
255     {
256         /* mark_adjust(99, 98, MAXLNUM, 9): a change that inserts lines*/
257         inserted = amount_after;
258         deleted = 0;
259     }
260     else
261     {
262         /* mark_adjust(98, 99, MAXLNUM, -2): delete lines */
263         inserted = 0;
264         deleted = -amount_after;
265     }
266
267     dprev = NULL;
268     dp = tp->tp_first_diff;
269     for (;;)
270     {
271         /* If the change is after the previous diff block and before the next
272          * diff block, thus not touching an existing change, create a new diff
273          * block.  Don't do this when ex_diffgetput() is busy. */
274         if ((dp == NULL || dp->df_lnum[idx] - 1 > line2
275                     || (line2 == MAXLNUM && dp->df_lnum[idx] > line1))
276                 && (dprev == NULL
277                     || dprev->df_lnum[idx] + dprev->df_count[idx] < line1)
278                 && !diff_busy)
279         {
280             dnext = diff_alloc_new(tp, dprev, dp);
281             if (dnext == NULL)
282                 return;
283
284             dnext->df_lnum[idx] = line1;
285             dnext->df_count[idx] = inserted;
286             for (i = 0; i < DB_COUNT; ++i)
287                 if (tp->tp_diffbuf[i] != NULL && i != idx)
288                 {
289                     if (dprev == NULL)
290                         dnext->df_lnum[i] = line1;
291                     else
292                         dnext->df_lnum[i] = line1
293                             + (dprev->df_lnum[i] + dprev->df_count[i])
294                             - (dprev->df_lnum[idx] + dprev->df_count[idx]);
295                     dnext->df_count[i] = deleted;
296                 }
297         }
298
299         /* if at end of the list, quit */
300         if (dp == NULL)
301             break;
302
303         /*
304          * Check for these situations:
305          *        1  2  3
306          *        1  2  3
307          * line1     2  3  4  5
308          *           2  3  4  5
309          *           2  3  4  5
310          * line2     2  3  4  5
311          *              3     5  6
312          *              3     5  6
313          */
314         /* compute last line of this change */
315         last = dp->df_lnum[idx] + dp->df_count[idx] - 1;
316
317         /* 1. change completely above line1: nothing to do */
318         if (last >= line1 - 1)
319         {
320             /* 6. change below line2: only adjust for amount_after; also when
321              * "deleted" became zero when deleted all lines between two diffs */
322             if (dp->df_lnum[idx] - (deleted + inserted != 0) > line2)
323             {
324                 if (amount_after == 0)
325                     break;      /* nothing left to change */
326                 dp->df_lnum[idx] += amount_after;
327             }
328             else
329             {
330                 check_unchanged = FALSE;
331
332                 /* 2. 3. 4. 5.: inserted/deleted lines touching this diff. */
333                 if (deleted > 0)
334                 {
335                     if (dp->df_lnum[idx] >= line1)
336                     {
337                         off = dp->df_lnum[idx] - lnum_deleted;
338                         if (last <= line2)
339                         {
340                             /* 4. delete all lines of diff */
341                             if (dp->df_next != NULL
342                                     && dp->df_next->df_lnum[idx] - 1 <= line2)
343                             {
344                                 /* delete continues in next diff, only do
345                                  * lines until that one */
346                                 n = dp->df_next->df_lnum[idx] - lnum_deleted;
347                                 deleted -= n;
348                                 n -= dp->df_count[idx];
349                                 lnum_deleted = dp->df_next->df_lnum[idx];
350                             }
351                             else
352                                 n = deleted - dp->df_count[idx];
353                             dp->df_count[idx] = 0;
354                         }
355                         else
356                         {
357                             /* 5. delete lines at or just before top of diff */
358                             n = off;
359                             dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1;
360                             check_unchanged = TRUE;
361                         }
362                         dp->df_lnum[idx] = line1;
363                     }
364                     else
365                     {
366                         off = 0;
367                         if (last < line2)
368                         {
369                             /* 2. delete at end of of diff */
370                             dp->df_count[idx] -= last - lnum_deleted + 1;
371                             if (dp->df_next != NULL
372                                     && dp->df_next->df_lnum[idx] - 1 <= line2)
373                             {
374                                 /* delete continues in next diff, only do
375                                  * lines until that one */
376                                 n = dp->df_next->df_lnum[idx] - 1 - last;
377                                 deleted -= dp->df_next->df_lnum[idx]
378                                                                - lnum_deleted;
379                                 lnum_deleted = dp->df_next->df_lnum[idx];
380                             }
381                             else
382                                 n = line2 - last;
383                             check_unchanged = TRUE;
384                         }
385                         else
386                         {
387                             /* 3. delete lines inside the diff */
388                             n = 0;
389                             dp->df_count[idx] -= deleted;
390                         }
391                     }
392
393                     for (i = 0; i < DB_COUNT; ++i)
394                         if (tp->tp_diffbuf[i] != NULL && i != idx)
395                         {
396                             dp->df_lnum[i] -= off;
397                             dp->df_count[i] += n;
398                         }
399                 }
400                 else
401                 {
402                     if (dp->df_lnum[idx] <= line1)
403                     {
404                         /* inserted lines somewhere in this diff */
405                         dp->df_count[idx] += inserted;
406                         check_unchanged = TRUE;
407                     }
408                     else
409                         /* inserted lines somewhere above this diff */
410                         dp->df_lnum[idx] += inserted;
411                 }
412
413                 if (check_unchanged)
414                     /* Check if inserted lines are equal, may reduce the
415                      * size of the diff.  TODO: also check for equal lines
416                      * in the middle and perhaps split the block. */
417                     diff_check_unchanged(tp, dp);
418             }
419         }
420
421         /* check if this block touches the previous one, may merge them. */
422         if (dprev != NULL && dprev->df_lnum[idx] + dprev->df_count[idx]
423                                                           == dp->df_lnum[idx])
424         {
425             for (i = 0; i < DB_COUNT; ++i)
426                 if (tp->tp_diffbuf[i] != NULL)
427                     dprev->df_count[i] += dp->df_count[i];
428             dprev->df_next = dp->df_next;
429             vim_free(dp);
430             dp = dprev->df_next;
431         }
432         else
433         {
434             /* Advance to next entry. */
435             dprev = dp;
436             dp = dp->df_next;
437         }
438     }
439
440     dprev = NULL;
441     dp = tp->tp_first_diff;
442     while (dp != NULL)
443     {
444         /* All counts are zero, remove this entry. */
445         for (i = 0; i < DB_COUNT; ++i)
446             if (tp->tp_diffbuf[i] != NULL && dp->df_count[i] != 0)
447                 break;
448         if (i == DB_COUNT)
449         {
450             dnext = dp->df_next;
451             vim_free(dp);
452             dp = dnext;
453             if (dprev == NULL)
454                 tp->tp_first_diff = dnext;
455             else
456                 dprev->df_next = dnext;
457         }
458         else
459         {
460             /* Advance to next entry. */
461             dprev = dp;
462             dp = dp->df_next;
463         }
464
465     }
466
467     if (tp == curtab)
468     {
469         diff_redraw(TRUE);
470
471         /* Need to recompute the scroll binding, may remove or add filler
472          * lines (e.g., when adding lines above w_topline). But it's slow when
473          * making many changes, postpone until redrawing. */
474         diff_need_scrollbind = TRUE;
475     }
476 }
477
478 /*
479  * Allocate a new diff block and link it between "dprev" and "dp".
480  */
481     static diff_T *
482 diff_alloc_new(tp, dprev, dp)
483     tabpage_T   *tp;
484     diff_T      *dprev;
485     diff_T      *dp;
486 {
487     diff_T      *dnew;
488
489     dnew = (diff_T *)alloc((unsigned)sizeof(diff_T));
490     if (dnew != NULL)
491     {
492         dnew->df_next = dp;
493         if (dprev == NULL)
494             tp->tp_first_diff = dnew;
495         else
496             dprev->df_next = dnew;
497     }
498     return dnew;
499 }
500
501 /*
502  * Check if the diff block "dp" can be made smaller for lines at the start and
503  * end that are equal.  Called after inserting lines.
504  * This may result in a change where all buffers have zero lines, the caller
505  * must take care of removing it.
506  */
507     static void
508 diff_check_unchanged(tp, dp)
509     tabpage_T   *tp;
510     diff_T      *dp;
511 {
512     int         i_org;
513     int         i_new;
514     int         off_org, off_new;
515     char_u      *line_org;
516     int         dir = FORWARD;
517
518     /* Find the first buffers, use it as the original, compare the other
519      * buffer lines against this one. */
520     for (i_org = 0; i_org < DB_COUNT; ++i_org)
521         if (tp->tp_diffbuf[i_org] != NULL)
522             break;
523     if (i_org == DB_COUNT)      /* safety check */
524         return;
525
526     if (diff_check_sanity(tp, dp) == FAIL)
527         return;
528
529     /* First check lines at the top, then at the bottom. */
530     off_org = 0;
531     off_new = 0;
532     for (;;)
533     {
534         /* Repeat until a line is found which is different or the number of
535          * lines has become zero. */
536         while (dp->df_count[i_org] > 0)
537         {
538             /* Copy the line, the next ml_get() will invalidate it.  */
539             if (dir == BACKWARD)
540                 off_org = dp->df_count[i_org] - 1;
541             line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org],
542                                         dp->df_lnum[i_org] + off_org, FALSE));
543             if (line_org == NULL)
544                 return;
545             for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new)
546             {
547                 if (tp->tp_diffbuf[i_new] == NULL)
548                     continue;
549                 if (dir == BACKWARD)
550                     off_new = dp->df_count[i_new] - 1;
551                 /* if other buffer doesn't have this line, it was inserted */
552                 if (off_new < 0 || off_new >= dp->df_count[i_new])
553                     break;
554                 if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new],
555                                    dp->df_lnum[i_new] + off_new, FALSE)) != 0)
556                     break;
557             }
558             vim_free(line_org);
559
560             /* Stop when a line isn't equal in all diff buffers. */
561             if (i_new != DB_COUNT)
562                 break;
563
564             /* Line matched in all buffers, remove it from the diff. */
565             for (i_new = i_org; i_new < DB_COUNT; ++i_new)
566                 if (tp->tp_diffbuf[i_new] != NULL)
567                 {
568                     if (dir == FORWARD)
569                         ++dp->df_lnum[i_new];
570                     --dp->df_count[i_new];
571                 }
572         }
573         if (dir == BACKWARD)
574             break;
575         dir = BACKWARD;
576     }
577 }
578
579 /*
580  * Check if a diff block doesn't contain invalid line numbers.
581  * This can happen when the diff program returns invalid results.
582  */
583     static int
584 diff_check_sanity(tp, dp)
585     tabpage_T   *tp;
586     diff_T      *dp;
587 {
588     int         i;
589
590     for (i = 0; i < DB_COUNT; ++i)
591         if (tp->tp_diffbuf[i] != NULL)
592             if (dp->df_lnum[i] + dp->df_count[i] - 1
593                                       > tp->tp_diffbuf[i]->b_ml.ml_line_count)
594                 return FAIL;
595     return OK;
596 }
597
598 /*
599  * Mark all diff buffers in the current tab page for redraw.
600  */
601     static void
602 diff_redraw(dofold)
603     int         dofold;     /* also recompute the folds */
604 {
605     win_T       *wp;
606     int         n;
607
608     for (wp = firstwin; wp != NULL; wp = wp->w_next)
609         if (wp->w_p_diff)
610         {
611             redraw_win_later(wp, SOME_VALID);
612 #ifdef FEAT_FOLDING
613             if (dofold && foldmethodIsDiff(wp))
614                 foldUpdateAll(wp);
615 #endif
616             /* A change may have made filler lines invalid, need to take care
617              * of that for other windows. */
618             if (wp != curwin && wp->w_topfill > 0)
619             {
620                 n = diff_check(wp, wp->w_topline);
621                 if (wp->w_topfill > n)
622                     wp->w_topfill = (n < 0 ? 0 : n);
623             }
624         }
625 }
626
627 /*
628  * Write buffer "buf" to file "name".
629  * Always use 'fileformat' set to "unix".
630  * Return FAIL for failure
631  */
632     static int
633 diff_write(buf, fname)
634     buf_T       *buf;
635     char_u      *fname;
636 {
637     int         r;
638     char_u      *save_ff;
639
640     save_ff = buf->b_p_ff;
641     buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
642     r = buf_write(buf, fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count,
643                                              NULL, FALSE, FALSE, FALSE, TRUE);
644     free_string_option(buf->b_p_ff);
645     buf->b_p_ff = save_ff;
646     return r;
647 }
648
649 /*
650  * Completely update the diffs for the buffers involved.
651  * This uses the ordinary "diff" command.
652  * The buffers are written to a file, also for unmodified buffers (the file
653  * could have been produced by autocommands, e.g. the netrw plugin).
654  */
655     void
656 ex_diffupdate(eap)
657     exarg_T     *eap UNUSED;        /* can be NULL */
658 {
659     buf_T       *buf;
660     int         idx_orig;
661     int         idx_new;
662     char_u      *tmp_orig;
663     char_u      *tmp_new;
664     char_u      *tmp_diff;
665     FILE        *fd;
666     int         ok;
667     int         io_error = FALSE;
668
669     /* Delete all diffblocks. */
670     diff_clear(curtab);
671     curtab->tp_diff_invalid = FALSE;
672
673     /* Use the first buffer as the original text. */
674     for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig)
675         if (curtab->tp_diffbuf[idx_orig] != NULL)
676             break;
677     if (idx_orig == DB_COUNT)
678         return;
679
680     /* Only need to do something when there is another buffer. */
681     for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new)
682         if (curtab->tp_diffbuf[idx_new] != NULL)
683             break;
684     if (idx_new == DB_COUNT)
685         return;
686
687     /* We need three temp file names. */
688     tmp_orig = vim_tempname('o');
689     tmp_new = vim_tempname('n');
690     tmp_diff = vim_tempname('d');
691     if (tmp_orig == NULL || tmp_new == NULL || tmp_diff == NULL)
692         goto theend;
693
694     /*
695      * Do a quick test if "diff" really works.  Otherwise it looks like there
696      * are no differences.  Can't use the return value, it's non-zero when
697      * there are differences.
698      * May try twice, first with "-a" and then without.
699      */
700     for (;;)
701     {
702         ok = FALSE;
703         fd = mch_fopen((char *)tmp_orig, "w");
704         if (fd == NULL)
705             io_error = TRUE;
706         else
707         {
708             if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1)
709                 io_error = TRUE;
710             fclose(fd);
711             fd = mch_fopen((char *)tmp_new, "w");
712             if (fd == NULL)
713                 io_error = TRUE;
714             else
715             {
716                 if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1)
717                     io_error = TRUE;
718                 fclose(fd);
719                 diff_file(tmp_orig, tmp_new, tmp_diff);
720                 fd = mch_fopen((char *)tmp_diff, "r");
721                 if (fd == NULL)
722                     io_error = TRUE;
723                 else
724                 {
725                     char_u      linebuf[LBUFLEN];
726
727                     for (;;)
728                     {
729                         /* There must be a line that contains "1c1". */
730                         if (tag_fgets(linebuf, LBUFLEN, fd))
731                             break;
732                         if (STRNCMP(linebuf, "1c1", 3) == 0)
733                             ok = TRUE;
734                     }
735                     fclose(fd);
736                 }
737                 mch_remove(tmp_diff);
738                 mch_remove(tmp_new);
739             }
740             mch_remove(tmp_orig);
741         }
742
743 #ifdef FEAT_EVAL
744         /* When using 'diffexpr' break here. */
745         if (*p_dex != NUL)
746             break;
747 #endif
748
749 #if defined(MSWIN) || defined(MSDOS)
750         /* If the "-a" argument works, also check if "--binary" works. */
751         if (ok && diff_a_works == MAYBE && diff_bin_works == MAYBE)
752         {
753             diff_a_works = TRUE;
754             diff_bin_works = TRUE;
755             continue;
756         }
757         if (!ok && diff_a_works == TRUE && diff_bin_works == TRUE)
758         {
759             /* Tried --binary, but it failed. "-a" works though. */
760             diff_bin_works = FALSE;
761             ok = TRUE;
762         }
763 #endif
764
765         /* If we checked if "-a" works already, break here. */
766         if (diff_a_works != MAYBE)
767             break;
768         diff_a_works = ok;
769
770         /* If "-a" works break here, otherwise retry without "-a". */
771         if (ok)
772             break;
773     }
774     if (!ok)
775     {
776         if (io_error)
777             EMSG(_("E810: Cannot read or write temp files"));
778         EMSG(_("E97: Cannot create diffs"));
779         diff_a_works = MAYBE;
780 #if defined(MSWIN) || defined(MSDOS)
781         diff_bin_works = MAYBE;
782 #endif
783         goto theend;
784     }
785
786     /* Write the first buffer to a tempfile. */
787     buf = curtab->tp_diffbuf[idx_orig];
788     if (diff_write(buf, tmp_orig) == FAIL)
789         goto theend;
790
791     /* Make a difference between the first buffer and every other. */
792     for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new)
793     {
794         buf = curtab->tp_diffbuf[idx_new];
795         if (buf == NULL)
796             continue;
797         if (diff_write(buf, tmp_new) == FAIL)
798             continue;
799         diff_file(tmp_orig, tmp_new, tmp_diff);
800
801         /* Read the diff output and add each entry to the diff list. */
802         diff_read(idx_orig, idx_new, tmp_diff);
803         mch_remove(tmp_diff);
804         mch_remove(tmp_new);
805     }
806     mch_remove(tmp_orig);
807
808     /* force updating cursor position on screen */
809     curwin->w_valid_cursor.lnum = 0;
810
811     diff_redraw(TRUE);
812
813 theend:
814     vim_free(tmp_orig);
815     vim_free(tmp_new);
816     vim_free(tmp_diff);
817 }
818
819 /*
820  * Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff".
821  */
822     static void
823 diff_file(tmp_orig, tmp_new, tmp_diff)
824     char_u      *tmp_orig;
825     char_u      *tmp_new;
826     char_u      *tmp_diff;
827 {
828     char_u      *cmd;
829     size_t      len;
830
831 #ifdef FEAT_EVAL
832     if (*p_dex != NUL)
833         /* Use 'diffexpr' to generate the diff file. */
834         eval_diff(tmp_orig, tmp_new, tmp_diff);
835     else
836 #endif
837     {
838         len = STRLEN(tmp_orig) + STRLEN(tmp_new)
839                                       + STRLEN(tmp_diff) + STRLEN(p_srr) + 27;
840         cmd = alloc((unsigned)len);
841         if (cmd != NULL)
842         {
843             /* We don't want $DIFF_OPTIONS to get in the way. */
844             if (getenv("DIFF_OPTIONS"))
845                 vim_setenv((char_u *)"DIFF_OPTIONS", (char_u *)"");
846
847             /* Build the diff command and execute it.  Always use -a, binary
848              * differences are of no use.  Ignore errors, diff returns
849              * non-zero when differences have been found. */
850             vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s %s",
851                     diff_a_works == FALSE ? "" : "-a ",
852 #if defined(MSWIN) || defined(MSDOS)
853                     diff_bin_works == TRUE ? "--binary " : "",
854 #else
855                     "",
856 #endif
857                     (diff_flags & DIFF_IWHITE) ? "-b " : "",
858                     (diff_flags & DIFF_ICASE) ? "-i " : "",
859                     tmp_orig, tmp_new);
860             append_redir(cmd, (int)len, p_srr, tmp_diff);
861 #ifdef FEAT_AUTOCMD
862             block_autocmds();   /* Avoid ShellCmdPost stuff */
863 #endif
864             (void)call_shell(cmd, SHELL_FILTER|SHELL_SILENT|SHELL_DOOUT);
865 #ifdef FEAT_AUTOCMD
866             unblock_autocmds();
867 #endif
868             vim_free(cmd);
869         }
870     }
871 }
872
873 /*
874  * Create a new version of a file from the current buffer and a diff file.
875  * The buffer is written to a file, also for unmodified buffers (the file
876  * could have been produced by autocommands, e.g. the netrw plugin).
877  */
878     void
879 ex_diffpatch(eap)
880     exarg_T     *eap;
881 {
882     char_u      *tmp_orig;      /* name of original temp file */
883     char_u      *tmp_new;       /* name of patched temp file */
884     char_u      *buf = NULL;
885     size_t      buflen;
886     win_T       *old_curwin = curwin;
887     char_u      *newname = NULL;        /* name of patched file buffer */
888 #ifdef UNIX
889     char_u      dirbuf[MAXPATHL];
890     char_u      *fullname = NULL;
891 #endif
892 #ifdef FEAT_BROWSE
893     char_u      *browseFile = NULL;
894     int         browse_flag = cmdmod.browse;
895 #endif
896     struct stat st;
897
898 #ifdef FEAT_BROWSE
899     if (cmdmod.browse)
900     {
901         browseFile = do_browse(0, (char_u *)_("Patch file"),
902                          eap->arg, NULL, NULL, BROWSE_FILTER_ALL_FILES, NULL);
903         if (browseFile == NULL)
904             return;             /* operation cancelled */
905         eap->arg = browseFile;
906         cmdmod.browse = FALSE;  /* don't let do_ecmd() browse again */
907     }
908 #endif
909
910     /* We need two temp file names. */
911     tmp_orig = vim_tempname('o');
912     tmp_new = vim_tempname('n');
913     if (tmp_orig == NULL || tmp_new == NULL)
914         goto theend;
915
916     /* Write the current buffer to "tmp_orig". */
917     if (buf_write(curbuf, tmp_orig, NULL,
918                 (linenr_T)1, curbuf->b_ml.ml_line_count,
919                                      NULL, FALSE, FALSE, FALSE, TRUE) == FAIL)
920         goto theend;
921
922 #ifdef UNIX
923     /* Get the absolute path of the patchfile, changing directory below. */
924     fullname = FullName_save(eap->arg, FALSE);
925 #endif
926     buflen = STRLEN(tmp_orig) + (
927 # ifdef UNIX
928                     fullname != NULL ? STRLEN(fullname) :
929 # endif
930                     STRLEN(eap->arg)) + STRLEN(tmp_new) + 16;
931     buf = alloc((unsigned)buflen);
932     if (buf == NULL)
933         goto theend;
934
935 #ifdef UNIX
936     /* Temporarily chdir to /tmp, to avoid patching files in the current
937      * directory when the patch file contains more than one patch.  When we
938      * have our own temp dir use that instead, it will be cleaned up when we
939      * exit (any .rej files created).  Don't change directory if we can't
940      * return to the current. */
941     if (mch_dirname(dirbuf, MAXPATHL) != OK || mch_chdir((char *)dirbuf) != 0)
942         dirbuf[0] = NUL;
943     else
944     {
945 # ifdef TEMPDIRNAMES
946         if (vim_tempdir != NULL)
947             ignored = mch_chdir((char *)vim_tempdir);
948         else
949 # endif
950             ignored = mch_chdir("/tmp");
951         shorten_fnames(TRUE);
952     }
953 #endif
954
955 #ifdef FEAT_EVAL
956     if (*p_pex != NUL)
957         /* Use 'patchexpr' to generate the new file. */
958         eval_patch(tmp_orig,
959 # ifdef UNIX
960                 fullname != NULL ? fullname :
961 # endif
962                 eap->arg, tmp_new);
963     else
964 #endif
965     {
966         /* Build the patch command and execute it.  Ignore errors.  Switch to
967          * cooked mode to allow the user to respond to prompts. */
968         vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"",
969                 tmp_new, tmp_orig,
970 # ifdef UNIX
971                 fullname != NULL ? fullname :
972 # endif
973                 eap->arg);
974 #ifdef FEAT_AUTOCMD
975         block_autocmds();       /* Avoid ShellCmdPost stuff */
976 #endif
977         (void)call_shell(buf, SHELL_FILTER | SHELL_COOKED);
978 #ifdef FEAT_AUTOCMD
979         unblock_autocmds();
980 #endif
981     }
982
983 #ifdef UNIX
984     if (dirbuf[0] != NUL)
985     {
986         if (mch_chdir((char *)dirbuf) != 0)
987             EMSG(_(e_prev_dir));
988         shorten_fnames(TRUE);
989     }
990 #endif
991
992     /* patch probably has written over the screen */
993     redraw_later(CLEAR);
994
995     /* Delete any .orig or .rej file created. */
996     STRCPY(buf, tmp_new);
997     STRCAT(buf, ".orig");
998     mch_remove(buf);
999     STRCPY(buf, tmp_new);
1000     STRCAT(buf, ".rej");
1001     mch_remove(buf);
1002
1003     /* Only continue if the output file was created. */
1004     if (mch_stat((char *)tmp_new, &st) < 0 || st.st_size == 0)
1005         EMSG(_("E816: Cannot read patch output"));
1006     else
1007     {
1008         if (curbuf->b_fname != NULL)
1009         {
1010             newname = vim_strnsave(curbuf->b_fname,
1011                                           (int)(STRLEN(curbuf->b_fname) + 4));
1012             if (newname != NULL)
1013                 STRCAT(newname, ".new");
1014         }
1015
1016 #ifdef FEAT_GUI
1017         need_mouse_correct = TRUE;
1018 #endif
1019         /* don't use a new tab page, each tab page has its own diffs */
1020         cmdmod.tab = 0;
1021
1022         if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL)
1023         {
1024             /* Pretend it was a ":split fname" command */
1025             eap->cmdidx = CMD_split;
1026             eap->arg = tmp_new;
1027             do_exedit(eap, old_curwin);
1028
1029             /* check that split worked and editing tmp_new */
1030             if (curwin != old_curwin && win_valid(old_curwin))
1031             {
1032                 /* Set 'diff', 'scrollbind' on and 'wrap' off. */
1033                 diff_win_options(curwin, TRUE);
1034                 diff_win_options(old_curwin, TRUE);
1035
1036                 if (newname != NULL)
1037                 {
1038                     /* do a ":file filename.new" on the patched buffer */
1039                     eap->arg = newname;
1040                     ex_file(eap);
1041
1042 #ifdef FEAT_AUTOCMD
1043                     /* Do filetype detection with the new name. */
1044                     if (au_has_group((char_u *)"filetypedetect"))
1045                         do_cmdline_cmd((char_u *)":doau filetypedetect BufRead");
1046 #endif
1047                 }
1048             }
1049         }
1050     }
1051
1052 theend:
1053     if (tmp_orig != NULL)
1054         mch_remove(tmp_orig);
1055     vim_free(tmp_orig);
1056     if (tmp_new != NULL)
1057         mch_remove(tmp_new);
1058     vim_free(tmp_new);
1059     vim_free(newname);
1060     vim_free(buf);
1061 #ifdef UNIX
1062     vim_free(fullname);
1063 #endif
1064 #ifdef FEAT_BROWSE
1065     vim_free(browseFile);
1066     cmdmod.browse = browse_flag;
1067 #endif
1068 }
1069
1070 /*
1071  * Split the window and edit another file, setting options to show the diffs.
1072  */
1073     void
1074 ex_diffsplit(eap)
1075     exarg_T     *eap;
1076 {
1077     win_T       *old_curwin = curwin;
1078
1079 #ifdef FEAT_GUI
1080     need_mouse_correct = TRUE;
1081 #endif
1082     /* don't use a new tab page, each tab page has its own diffs */
1083     cmdmod.tab = 0;
1084
1085     if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL)
1086     {
1087         /* Pretend it was a ":split fname" command */
1088         eap->cmdidx = CMD_split;
1089         curwin->w_p_diff = TRUE;
1090         do_exedit(eap, old_curwin);
1091
1092         if (curwin != old_curwin)               /* split must have worked */
1093         {
1094             /* Set 'diff', 'scrollbind' on and 'wrap' off. */
1095             diff_win_options(curwin, TRUE);
1096             diff_win_options(old_curwin, TRUE);
1097         }
1098     }
1099 }
1100
1101 /*
1102  * Set options to show difs for the current window.
1103  */
1104     void
1105 ex_diffthis(eap)
1106     exarg_T     *eap UNUSED;
1107 {
1108     /* Set 'diff', 'scrollbind' on and 'wrap' off. */
1109     diff_win_options(curwin, TRUE);
1110 }
1111
1112 /*
1113  * Set options in window "wp" for diff mode.
1114  */
1115     void
1116 diff_win_options(wp, addbuf)
1117     win_T       *wp;
1118     int         addbuf;         /* Add buffer to diff. */
1119 {
1120 # ifdef FEAT_FOLDING
1121     win_T *old_curwin = curwin;
1122
1123     /* close the manually opened folds */
1124     curwin = wp;
1125     newFoldLevel();
1126     curwin = old_curwin;
1127 # endif
1128
1129     wp->w_p_diff = TRUE;
1130     /* Use 'scrollbind' and 'cursorbind' when available */
1131 #ifdef FEAT_SCROLLBIND
1132     wp->w_p_scb = TRUE;
1133 #endif
1134 #ifdef FEAT_CURSORBIND
1135     wp->w_p_crb = TRUE;
1136 #endif
1137     wp->w_p_wrap = FALSE;
1138 # ifdef FEAT_FOLDING
1139     curwin = wp;
1140     curbuf = curwin->w_buffer;
1141     set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff",
1142                                                        OPT_LOCAL|OPT_FREE, 0);
1143     curwin = old_curwin;
1144     curbuf = curwin->w_buffer;
1145     wp->w_p_fdc = diff_foldcolumn;
1146     wp->w_p_fen = TRUE;
1147     wp->w_p_fdl = 0;
1148     foldUpdateAll(wp);
1149     /* make sure topline is not halfway a fold */
1150     changed_window_setting_win(wp);
1151 # endif
1152 #ifdef FEAT_SCROLLBIND
1153     if (vim_strchr(p_sbo, 'h') == NULL)
1154         do_cmdline_cmd((char_u *)"set sbo+=hor");
1155 #endif
1156
1157     if (addbuf)
1158         diff_buf_add(wp->w_buffer);
1159     redraw_win_later(wp, NOT_VALID);
1160 }
1161
1162 /*
1163  * Set options not to show diffs.  For the current window or all windows.
1164  * Only in the current tab page.
1165  */
1166     void
1167 ex_diffoff(eap)
1168     exarg_T     *eap;
1169 {
1170     win_T       *wp;
1171     win_T       *old_curwin = curwin;
1172 #ifdef FEAT_SCROLLBIND
1173     int         diffwin = FALSE;
1174 #endif
1175
1176     for (wp = firstwin; wp != NULL; wp = wp->w_next)
1177     {
1178         if (wp == curwin || (eap->forceit && wp->w_p_diff))
1179         {
1180             /* Set 'diff', 'scrollbind' off and 'wrap' on. */
1181             wp->w_p_diff = FALSE;
1182             RESET_BINDING(wp);
1183             wp->w_p_wrap = TRUE;
1184 #ifdef FEAT_FOLDING
1185             curwin = wp;
1186             curbuf = curwin->w_buffer;
1187             set_string_option_direct((char_u *)"fdm", -1,
1188                                    (char_u *)"manual", OPT_LOCAL|OPT_FREE, 0);
1189             curwin = old_curwin;
1190             curbuf = curwin->w_buffer;
1191             wp->w_p_fdc = 0;
1192             wp->w_p_fen = FALSE;
1193             wp->w_p_fdl = 0;
1194             foldUpdateAll(wp);
1195             /* make sure topline is not halfway a fold */
1196             changed_window_setting_win(wp);
1197 #endif
1198             diff_buf_adjust(wp);
1199         }
1200 #ifdef FEAT_SCROLLBIND
1201         diffwin |= wp->w_p_diff;
1202 #endif
1203     }
1204
1205 #ifdef FEAT_SCROLLBIND
1206     /* Remove "hor" from from 'scrollopt' if there are no diff windows left. */
1207     if (!diffwin && vim_strchr(p_sbo, 'h') != NULL)
1208         do_cmdline_cmd((char_u *)"set sbo-=hor");
1209 #endif
1210 }
1211
1212 /*
1213  * Read the diff output and add each entry to the diff list.
1214  */
1215     static void
1216 diff_read(idx_orig, idx_new, fname)
1217     int         idx_orig;       /* idx of original file */
1218     int         idx_new;        /* idx of new file */
1219     char_u      *fname;         /* name of diff output file */
1220 {
1221     FILE        *fd;
1222     diff_T      *dprev = NULL;
1223     diff_T      *dp = curtab->tp_first_diff;
1224     diff_T      *dn, *dpl;
1225     long        f1, l1, f2, l2;
1226     char_u      linebuf[LBUFLEN];   /* only need to hold the diff line */
1227     int         difftype;
1228     char_u      *p;
1229     long        off;
1230     int         i;
1231     linenr_T    lnum_orig, lnum_new;
1232     long        count_orig, count_new;
1233     int         notset = TRUE;      /* block "*dp" not set yet */
1234
1235     fd = mch_fopen((char *)fname, "r");
1236     if (fd == NULL)
1237     {
1238         EMSG(_("E98: Cannot read diff output"));
1239         return;
1240     }
1241
1242     for (;;)
1243     {
1244         if (tag_fgets(linebuf, LBUFLEN, fd))
1245             break;              /* end of file */
1246         if (!isdigit(*linebuf))
1247             continue;           /* not the start of a diff block */
1248
1249         /* This line must be one of three formats:
1250          * {first}[,{last}]c{first}[,{last}]
1251          * {first}a{first}[,{last}]
1252          * {first}[,{last}]d{first}
1253          */
1254         p = linebuf;
1255         f1 = getdigits(&p);
1256         if (*p == ',')
1257         {
1258             ++p;
1259             l1 = getdigits(&p);
1260         }
1261         else
1262             l1 = f1;
1263         if (*p != 'a' && *p != 'c' && *p != 'd')
1264             continue;           /* invalid diff format */
1265         difftype = *p++;
1266         f2 = getdigits(&p);
1267         if (*p == ',')
1268         {
1269             ++p;
1270             l2 = getdigits(&p);
1271         }
1272         else
1273             l2 = f2;
1274         if (l1 < f1 || l2 < f2)
1275             continue;           /* invalid line range */
1276
1277         if (difftype == 'a')
1278         {
1279             lnum_orig = f1 + 1;
1280             count_orig = 0;
1281         }
1282         else
1283         {
1284             lnum_orig = f1;
1285             count_orig = l1 - f1 + 1;
1286         }
1287         if (difftype == 'd')
1288         {
1289             lnum_new = f2 + 1;
1290             count_new = 0;
1291         }
1292         else
1293         {
1294             lnum_new = f2;
1295             count_new = l2 - f2 + 1;
1296         }
1297
1298         /* Go over blocks before the change, for which orig and new are equal.
1299          * Copy blocks from orig to new. */
1300         while (dp != NULL
1301                 && lnum_orig > dp->df_lnum[idx_orig] + dp->df_count[idx_orig])
1302         {
1303             if (notset)
1304                 diff_copy_entry(dprev, dp, idx_orig, idx_new);
1305             dprev = dp;
1306             dp = dp->df_next;
1307             notset = TRUE;
1308         }
1309
1310         if (dp != NULL
1311                 && lnum_orig <= dp->df_lnum[idx_orig] + dp->df_count[idx_orig]
1312                 && lnum_orig + count_orig >= dp->df_lnum[idx_orig])
1313         {
1314             /* New block overlaps with existing block(s).
1315              * First find last block that overlaps. */
1316             for (dpl = dp; dpl->df_next != NULL; dpl = dpl->df_next)
1317                 if (lnum_orig + count_orig < dpl->df_next->df_lnum[idx_orig])
1318                     break;
1319
1320             /* If the newly found block starts before the old one, set the
1321              * start back a number of lines. */
1322             off = dp->df_lnum[idx_orig] - lnum_orig;
1323             if (off > 0)
1324             {
1325                 for (i = idx_orig; i < idx_new; ++i)
1326                     if (curtab->tp_diffbuf[i] != NULL)
1327                         dp->df_lnum[i] -= off;
1328                 dp->df_lnum[idx_new] = lnum_new;
1329                 dp->df_count[idx_new] = count_new;
1330             }
1331             else if (notset)
1332             {
1333                 /* new block inside existing one, adjust new block */
1334                 dp->df_lnum[idx_new] = lnum_new + off;
1335                 dp->df_count[idx_new] = count_new - off;
1336             }
1337             else
1338                 /* second overlap of new block with existing block */
1339                 dp->df_count[idx_new] += count_new - count_orig
1340                     + dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]
1341                     - (dp->df_lnum[idx_orig] + dp->df_count[idx_orig]);
1342
1343             /* Adjust the size of the block to include all the lines to the
1344              * end of the existing block or the new diff, whatever ends last. */
1345             off = (lnum_orig + count_orig)
1346                          - (dpl->df_lnum[idx_orig] + dpl->df_count[idx_orig]);
1347             if (off < 0)
1348             {
1349                 /* new change ends in existing block, adjust the end if not
1350                  * done already */
1351                 if (notset)
1352                     dp->df_count[idx_new] += -off;
1353                 off = 0;
1354             }
1355             for (i = idx_orig; i < idx_new; ++i)
1356                 if (curtab->tp_diffbuf[i] != NULL)
1357                     dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i]
1358                                                        - dp->df_lnum[i] + off;
1359
1360             /* Delete the diff blocks that have been merged into one. */
1361             dn = dp->df_next;
1362             dp->df_next = dpl->df_next;
1363             while (dn != dp->df_next)
1364             {
1365                 dpl = dn->df_next;
1366                 vim_free(dn);
1367                 dn = dpl;
1368             }
1369         }
1370         else
1371         {
1372             /* Allocate a new diffblock. */
1373             dp = diff_alloc_new(curtab, dprev, dp);
1374             if (dp == NULL)
1375                 goto done;
1376
1377             dp->df_lnum[idx_orig] = lnum_orig;
1378             dp->df_count[idx_orig] = count_orig;
1379             dp->df_lnum[idx_new] = lnum_new;
1380             dp->df_count[idx_new] = count_new;
1381
1382             /* Set values for other buffers, these must be equal to the
1383              * original buffer, otherwise there would have been a change
1384              * already. */
1385             for (i = idx_orig + 1; i < idx_new; ++i)
1386                 if (curtab->tp_diffbuf[i] != NULL)
1387                     diff_copy_entry(dprev, dp, idx_orig, i);
1388         }
1389         notset = FALSE;         /* "*dp" has been set */
1390     }
1391
1392     /* for remaining diff blocks orig and new are equal */
1393     while (dp != NULL)
1394     {
1395         if (notset)
1396             diff_copy_entry(dprev, dp, idx_orig, idx_new);
1397         dprev = dp;
1398         dp = dp->df_next;
1399         notset = TRUE;
1400     }
1401
1402 done:
1403     fclose(fd);
1404 }
1405
1406 /*
1407  * Copy an entry at "dp" from "idx_orig" to "idx_new".
1408  */
1409     static void
1410 diff_copy_entry(dprev, dp, idx_orig, idx_new)
1411     diff_T      *dprev;
1412     diff_T      *dp;
1413     int         idx_orig;
1414     int         idx_new;
1415 {
1416     long        off;
1417
1418     if (dprev == NULL)
1419         off = 0;
1420     else
1421         off = (dprev->df_lnum[idx_orig] + dprev->df_count[idx_orig])
1422             - (dprev->df_lnum[idx_new] + dprev->df_count[idx_new]);
1423     dp->df_lnum[idx_new] = dp->df_lnum[idx_orig] - off;
1424     dp->df_count[idx_new] = dp->df_count[idx_orig];
1425 }
1426
1427 /*
1428  * Clear the list of diffblocks for tab page "tp".
1429  */
1430     void
1431 diff_clear(tp)
1432     tabpage_T   *tp;
1433 {
1434     diff_T      *p, *next_p;
1435
1436     for (p = tp->tp_first_diff; p != NULL; p = next_p)
1437     {
1438         next_p = p->df_next;
1439         vim_free(p);
1440     }
1441     tp->tp_first_diff = NULL;
1442 }
1443
1444 /*
1445  * Check diff status for line "lnum" in buffer "buf":
1446  * Returns 0 for nothing special
1447  * Returns -1 for a line that should be highlighted as changed.
1448  * Returns -2 for a line that should be highlighted as added/deleted.
1449  * Returns > 0 for inserting that many filler lines above it (never happens
1450  * when 'diffopt' doesn't contain "filler").
1451  * This should only be used for windows where 'diff' is set.
1452  */
1453     int
1454 diff_check(wp, lnum)
1455     win_T       *wp;
1456     linenr_T    lnum;
1457 {
1458     int         idx;            /* index in tp_diffbuf[] for this buffer */
1459     diff_T      *dp;
1460     int         maxcount;
1461     int         i;
1462     buf_T       *buf = wp->w_buffer;
1463     int         cmp;
1464
1465     if (curtab->tp_diff_invalid)
1466         ex_diffupdate(NULL);            /* update after a big change */
1467
1468     if (curtab->tp_first_diff == NULL || !wp->w_p_diff) /* no diffs at all */
1469         return 0;
1470
1471     /* safety check: "lnum" must be a buffer line */
1472     if (lnum < 1 || lnum > buf->b_ml.ml_line_count + 1)
1473         return 0;
1474
1475     idx = diff_buf_idx(buf);
1476     if (idx == DB_COUNT)
1477         return 0;               /* no diffs for buffer "buf" */
1478
1479 #ifdef FEAT_FOLDING
1480     /* A closed fold never has filler lines. */
1481     if (hasFoldingWin(wp, lnum, NULL, NULL, TRUE, NULL))
1482         return 0;
1483 #endif
1484
1485     /* search for a change that includes "lnum" in the list of diffblocks. */
1486     for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
1487         if (lnum <= dp->df_lnum[idx] + dp->df_count[idx])
1488             break;
1489     if (dp == NULL || lnum < dp->df_lnum[idx])
1490         return 0;
1491
1492     if (lnum < dp->df_lnum[idx] + dp->df_count[idx])
1493     {
1494         int     zero = FALSE;
1495
1496         /* Changed or inserted line.  If the other buffers have a count of
1497          * zero, the lines were inserted.  If the other buffers have the same
1498          * count, check if the lines are identical. */
1499         cmp = FALSE;
1500         for (i = 0; i < DB_COUNT; ++i)
1501             if (i != idx && curtab->tp_diffbuf[i] != NULL)
1502             {
1503                 if (dp->df_count[i] == 0)
1504                     zero = TRUE;
1505                 else
1506                 {
1507                     if (dp->df_count[i] != dp->df_count[idx])
1508                         return -1;          /* nr of lines changed. */
1509                     cmp = TRUE;
1510                 }
1511             }
1512         if (cmp)
1513         {
1514             /* Compare all lines.  If they are equal the lines were inserted
1515              * in some buffers, deleted in others, but not changed. */
1516             for (i = 0; i < DB_COUNT; ++i)
1517                 if (i != idx && curtab->tp_diffbuf[i] != NULL && dp->df_count[i] != 0)
1518                     if (!diff_equal_entry(dp, idx, i))
1519                         return -1;
1520         }
1521         /* If there is no buffer with zero lines then there is no difference
1522          * any longer.  Happens when making a change (or undo) that removes
1523          * the difference.  Can't remove the entry here, we might be halfway
1524          * updating the window.  Just report the text as unchanged.  Other
1525          * windows might still show the change though. */
1526         if (zero == FALSE)
1527             return 0;
1528         return -2;
1529     }
1530
1531     /* If 'diffopt' doesn't contain "filler", return 0. */
1532     if (!(diff_flags & DIFF_FILLER))
1533         return 0;
1534
1535     /* Insert filler lines above the line just below the change.  Will return
1536      * 0 when this buf had the max count. */
1537     maxcount = 0;
1538     for (i = 0; i < DB_COUNT; ++i)
1539         if (curtab->tp_diffbuf[i] != NULL && dp->df_count[i] > maxcount)
1540             maxcount = dp->df_count[i];
1541     return maxcount - dp->df_count[idx];
1542 }
1543
1544 /*
1545  * Compare two entries in diff "*dp" and return TRUE if they are equal.
1546  */
1547     static int
1548 diff_equal_entry(dp, idx1, idx2)
1549     diff_T      *dp;
1550     int         idx1;
1551     int         idx2;
1552 {
1553     int         i;
1554     char_u      *line;
1555     int         cmp;
1556
1557     if (dp->df_count[idx1] != dp->df_count[idx2])
1558         return FALSE;
1559     if (diff_check_sanity(curtab, dp) == FAIL)
1560         return FALSE;
1561     for (i = 0; i < dp->df_count[idx1]; ++i)
1562     {
1563         line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
1564                                                dp->df_lnum[idx1] + i, FALSE));
1565         if (line == NULL)
1566             return FALSE;
1567         cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
1568                                                dp->df_lnum[idx2] + i, FALSE));
1569         vim_free(line);
1570         if (cmp != 0)
1571             return FALSE;
1572     }
1573     return TRUE;
1574 }
1575
1576 /*
1577  * Compare strings "s1" and "s2" according to 'diffopt'.
1578  * Return non-zero when they are different.
1579  */
1580     static int
1581 diff_cmp(s1, s2)
1582     char_u      *s1;
1583     char_u      *s2;
1584 {
1585     char_u      *p1, *p2;
1586 #ifdef FEAT_MBYTE
1587     int         l;
1588 #endif
1589
1590     if ((diff_flags & (DIFF_ICASE | DIFF_IWHITE)) == 0)
1591         return STRCMP(s1, s2);
1592     if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE))
1593         return MB_STRICMP(s1, s2);
1594
1595     /* Ignore white space changes and possibly ignore case. */
1596     p1 = s1;
1597     p2 = s2;
1598     while (*p1 != NUL && *p2 != NUL)
1599     {
1600         if (vim_iswhite(*p1) && vim_iswhite(*p2))
1601         {
1602             p1 = skipwhite(p1);
1603             p2 = skipwhite(p2);
1604         }
1605         else
1606         {
1607 #ifdef FEAT_MBYTE
1608             l  = (*mb_ptr2len)(p1);
1609             if (l != (*mb_ptr2len)(p2))
1610                 break;
1611             if (l > 1)
1612             {
1613                 if (STRNCMP(p1, p2, l) != 0
1614                         && (!enc_utf8
1615                             || !(diff_flags & DIFF_ICASE)
1616                             || utf_fold(utf_ptr2char(p1))
1617                                                != utf_fold(utf_ptr2char(p2))))
1618                     break;
1619                 p1 += l;
1620                 p2 += l;
1621             }
1622             else
1623 #endif
1624             {
1625                 if (*p1 != *p2 && (!(diff_flags & DIFF_ICASE)
1626                                      || TOLOWER_LOC(*p1) != TOLOWER_LOC(*p2)))
1627                     break;
1628                 ++p1;
1629                 ++p2;
1630             }
1631         }
1632     }
1633
1634     /* Ignore trailing white space. */
1635     p1 = skipwhite(p1);
1636     p2 = skipwhite(p2);
1637     if (*p1 != NUL || *p2 != NUL)
1638         return 1;
1639     return 0;
1640 }
1641
1642 /*
1643  * Return the number of filler lines above "lnum".
1644  */
1645     int
1646 diff_check_fill(wp, lnum)
1647     win_T       *wp;
1648     linenr_T    lnum;
1649 {
1650     int         n;
1651
1652     /* be quick when there are no filler lines */
1653     if (!(diff_flags & DIFF_FILLER))
1654         return 0;
1655     n = diff_check(wp, lnum);
1656     if (n <= 0)
1657         return 0;
1658     return n;
1659 }
1660
1661 /*
1662  * Set the topline of "towin" to match the position in "fromwin", so that they
1663  * show the same diff'ed lines.
1664  */
1665     void
1666 diff_set_topline(fromwin, towin)
1667     win_T       *fromwin;
1668     win_T       *towin;
1669 {
1670     buf_T       *frombuf = fromwin->w_buffer;
1671     linenr_T    lnum = fromwin->w_topline;
1672     int         fromidx;
1673     int         toidx;
1674     diff_T      *dp;
1675     int         max_count;
1676     int         i;
1677
1678     fromidx = diff_buf_idx(frombuf);
1679     if (fromidx == DB_COUNT)
1680         return;         /* safety check */
1681
1682     if (curtab->tp_diff_invalid)
1683         ex_diffupdate(NULL);            /* update after a big change */
1684
1685     towin->w_topfill = 0;
1686
1687     /* search for a change that includes "lnum" in the list of diffblocks. */
1688     for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
1689         if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx])
1690             break;
1691     if (dp == NULL)
1692     {
1693         /* After last change, compute topline relative to end of file; no
1694          * filler lines. */
1695         towin->w_topline = towin->w_buffer->b_ml.ml_line_count
1696                                        - (frombuf->b_ml.ml_line_count - lnum);
1697     }
1698     else
1699     {
1700         /* Find index for "towin". */
1701         toidx = diff_buf_idx(towin->w_buffer);
1702         if (toidx == DB_COUNT)
1703             return;             /* safety check */
1704
1705         towin->w_topline = lnum + (dp->df_lnum[toidx] - dp->df_lnum[fromidx]);
1706         if (lnum >= dp->df_lnum[fromidx])
1707         {
1708             /* Inside a change: compute filler lines. With three or more
1709              * buffers we need to know the largest count. */
1710             max_count = 0;
1711             for (i = 0; i < DB_COUNT; ++i)
1712                 if (curtab->tp_diffbuf[i] != NULL
1713                                                && max_count < dp->df_count[i])
1714                     max_count = dp->df_count[i];
1715
1716             if (dp->df_count[toidx] == dp->df_count[fromidx])
1717             {
1718                 /* same number of lines: use same filler count */
1719                 towin->w_topfill = fromwin->w_topfill;
1720             }
1721             else if (dp->df_count[toidx] > dp->df_count[fromidx])
1722             {
1723                 if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx])
1724                 {
1725                     /* more lines in towin and fromwin doesn't show diff
1726                      * lines, only filler lines */
1727                     if (max_count - fromwin->w_topfill >= dp->df_count[toidx])
1728                     {
1729                         /* towin also only shows filler lines */
1730                         towin->w_topline = dp->df_lnum[toidx]
1731                                                        + dp->df_count[toidx];
1732                         towin->w_topfill = fromwin->w_topfill;
1733                     }
1734                     else
1735                         /* towin still has some diff lines to show */
1736                         towin->w_topline = dp->df_lnum[toidx]
1737                                              + max_count - fromwin->w_topfill;
1738                 }
1739             }
1740             else if (towin->w_topline >= dp->df_lnum[toidx]
1741                                                         + dp->df_count[toidx])
1742             {
1743                 /* less lines in towin and no diff lines to show: compute
1744                  * filler lines */
1745                 towin->w_topline = dp->df_lnum[toidx] + dp->df_count[toidx];
1746                 if (diff_flags & DIFF_FILLER)
1747                 {
1748                     if (lnum == dp->df_lnum[fromidx] + dp->df_count[fromidx])
1749                         /* fromwin is also out of diff lines */
1750                         towin->w_topfill = fromwin->w_topfill;
1751                     else
1752                         /* fromwin has some diff lines */
1753                         towin->w_topfill = dp->df_lnum[fromidx]
1754                                                            + max_count - lnum;
1755                 }
1756             }
1757         }
1758     }
1759
1760     /* safety check (if diff info gets outdated strange things may happen) */
1761     towin->w_botfill = FALSE;
1762     if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count)
1763     {
1764         towin->w_topline = towin->w_buffer->b_ml.ml_line_count;
1765         towin->w_botfill = TRUE;
1766     }
1767     if (towin->w_topline < 1)
1768     {
1769         towin->w_topline = 1;
1770         towin->w_topfill = 0;
1771     }
1772
1773     /* When w_topline changes need to recompute w_botline and cursor position */
1774     invalidate_botline_win(towin);
1775     changed_line_abv_curs_win(towin);
1776
1777     check_topfill(towin, FALSE);
1778 #ifdef FEAT_FOLDING
1779     (void)hasFoldingWin(towin, towin->w_topline, &towin->w_topline,
1780                                                             NULL, TRUE, NULL);
1781 #endif
1782 }
1783
1784 /*
1785  * This is called when 'diffopt' is changed.
1786  */
1787     int
1788 diffopt_changed()
1789 {
1790     char_u      *p;
1791     int         diff_context_new = 6;
1792     int         diff_flags_new = 0;
1793     int         diff_foldcolumn_new = 2;
1794     tabpage_T   *tp;
1795
1796     p = p_dip;
1797     while (*p != NUL)
1798     {
1799         if (STRNCMP(p, "filler", 6) == 0)
1800         {
1801             p += 6;
1802             diff_flags_new |= DIFF_FILLER;
1803         }
1804         else if (STRNCMP(p, "context:", 8) == 0 && VIM_ISDIGIT(p[8]))
1805         {
1806             p += 8;
1807             diff_context_new = getdigits(&p);
1808         }
1809         else if (STRNCMP(p, "icase", 5) == 0)
1810         {
1811             p += 5;
1812             diff_flags_new |= DIFF_ICASE;
1813         }
1814         else if (STRNCMP(p, "iwhite", 6) == 0)
1815         {
1816             p += 6;
1817             diff_flags_new |= DIFF_IWHITE;
1818         }
1819         else if (STRNCMP(p, "horizontal", 10) == 0)
1820         {
1821             p += 10;
1822             diff_flags_new |= DIFF_HORIZONTAL;
1823         }
1824         else if (STRNCMP(p, "vertical", 8) == 0)
1825         {
1826             p += 8;
1827             diff_flags_new |= DIFF_VERTICAL;
1828         }
1829         else if (STRNCMP(p, "foldcolumn:", 11) == 0 && VIM_ISDIGIT(p[11]))
1830         {
1831             p += 11;
1832             diff_foldcolumn_new = getdigits(&p);
1833         }
1834         if (*p != ',' && *p != NUL)
1835             return FAIL;
1836         if (*p == ',')
1837             ++p;
1838     }
1839
1840     /* Can't have both "horizontal" and "vertical". */
1841     if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL))
1842         return FAIL;
1843
1844     /* If "icase" or "iwhite" was added or removed, need to update the diff. */
1845     if (diff_flags != diff_flags_new)
1846         for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
1847             tp->tp_diff_invalid = TRUE;
1848
1849     diff_flags = diff_flags_new;
1850     diff_context = diff_context_new;
1851     diff_foldcolumn = diff_foldcolumn_new;
1852
1853     diff_redraw(TRUE);
1854
1855     /* recompute the scroll binding with the new option value, may
1856      * remove or add filler lines */
1857     check_scrollbind((linenr_T)0, 0L);
1858
1859     return OK;
1860 }
1861
1862 /*
1863  * Return TRUE if 'diffopt' contains "horizontal".
1864  */
1865     int
1866 diffopt_horizontal()
1867 {
1868     return (diff_flags & DIFF_HORIZONTAL) != 0;
1869 }
1870
1871 /*
1872  * Find the difference within a changed line.
1873  * Returns TRUE if the line was added, no other buffer has it.
1874  */
1875     int
1876 diff_find_change(wp, lnum, startp, endp)
1877     win_T       *wp;
1878     linenr_T    lnum;
1879     int         *startp;        /* first char of the change */
1880     int         *endp;          /* last char of the change */
1881 {
1882     char_u      *line_org;
1883     char_u      *line_new;
1884     int         i;
1885     int         si_org, si_new;
1886     int         ei_org, ei_new;
1887     diff_T      *dp;
1888     int         idx;
1889     int         off;
1890     int         added = TRUE;
1891
1892     /* Make a copy of the line, the next ml_get() will invalidate it. */
1893     line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE));
1894     if (line_org == NULL)
1895         return FALSE;
1896
1897     idx = diff_buf_idx(wp->w_buffer);
1898     if (idx == DB_COUNT)        /* cannot happen */
1899     {
1900         vim_free(line_org);
1901         return FALSE;
1902     }
1903
1904     /* search for a change that includes "lnum" in the list of diffblocks. */
1905     for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
1906         if (lnum <= dp->df_lnum[idx] + dp->df_count[idx])
1907             break;
1908     if (dp == NULL || diff_check_sanity(curtab, dp) == FAIL)
1909     {
1910         vim_free(line_org);
1911         return FALSE;
1912     }
1913
1914     off = lnum - dp->df_lnum[idx];
1915
1916     for (i = 0; i < DB_COUNT; ++i)
1917         if (curtab->tp_diffbuf[i] != NULL && i != idx)
1918         {
1919             /* Skip lines that are not in the other change (filler lines). */
1920             if (off >= dp->df_count[i])
1921                 continue;
1922             added = FALSE;
1923             line_new = ml_get_buf(curtab->tp_diffbuf[i],
1924                                                  dp->df_lnum[i] + off, FALSE);
1925
1926             /* Search for start of difference */
1927             si_org = si_new = 0;
1928             while (line_org[si_org] != NUL)
1929             {
1930                 if ((diff_flags & DIFF_IWHITE)
1931                         && vim_iswhite(line_org[si_org])
1932                         && vim_iswhite(line_new[si_new]))
1933                 {
1934                     si_org = (int)(skipwhite(line_org + si_org) - line_org);
1935                     si_new = (int)(skipwhite(line_new + si_new) - line_new);
1936                 }
1937                 else
1938                 {
1939                     if (line_org[si_org] != line_new[si_new])
1940                         break;
1941                     ++si_org;
1942                     ++si_new;
1943                 }
1944             }
1945 #ifdef FEAT_MBYTE
1946             if (has_mbyte)
1947             {
1948                 /* Move back to first byte of character in both lines (may
1949                  * have "nn^" in line_org and "n^ in line_new). */
1950                 si_org -= (*mb_head_off)(line_org, line_org + si_org);
1951                 si_new -= (*mb_head_off)(line_new, line_new + si_new);
1952             }
1953 #endif
1954             if (*startp > si_org)
1955                 *startp = si_org;
1956
1957             /* Search for end of difference, if any. */
1958             if (line_org[si_org] != NUL || line_new[si_new] != NUL)
1959             {
1960                 ei_org = (int)STRLEN(line_org);
1961                 ei_new = (int)STRLEN(line_new);
1962                 while (ei_org >= *startp && ei_new >= si_new
1963                                                 && ei_org >= 0 && ei_new >= 0)
1964                 {
1965                     if ((diff_flags & DIFF_IWHITE)
1966                             && vim_iswhite(line_org[ei_org])
1967                             && vim_iswhite(line_new[ei_new]))
1968                     {
1969                         while (ei_org >= *startp
1970                                              && vim_iswhite(line_org[ei_org]))
1971                             --ei_org;
1972                         while (ei_new >= si_new
1973                                              && vim_iswhite(line_new[ei_new]))
1974                             --ei_new;
1975                     }
1976                     else
1977                     {
1978                         if (line_org[ei_org] != line_new[ei_new])
1979                             break;
1980                         --ei_org;
1981                         --ei_new;
1982                     }
1983                 }
1984                 if (*endp < ei_org)
1985                     *endp = ei_org;
1986             }
1987         }
1988
1989     vim_free(line_org);
1990     return added;
1991 }
1992
1993 #if defined(FEAT_FOLDING) || defined(PROTO)
1994 /*
1995  * Return TRUE if line "lnum" is not close to a diff block, this line should
1996  * be in a fold.
1997  * Return FALSE if there are no diff blocks at all in this window.
1998  */
1999     int
2000 diff_infold(wp, lnum)
2001     win_T       *wp;
2002     linenr_T    lnum;
2003 {
2004     int         i;
2005     int         idx = -1;
2006     int         other = FALSE;
2007     diff_T      *dp;
2008
2009     /* Return if 'diff' isn't set. */
2010     if (!wp->w_p_diff)
2011         return FALSE;
2012
2013     for (i = 0; i < DB_COUNT; ++i)
2014     {
2015         if (curtab->tp_diffbuf[i] == wp->w_buffer)
2016             idx = i;
2017         else if (curtab->tp_diffbuf[i] != NULL)
2018             other = TRUE;
2019     }
2020
2021     /* return here if there are no diffs in the window */
2022     if (idx == -1 || !other)
2023         return FALSE;
2024
2025     if (curtab->tp_diff_invalid)
2026         ex_diffupdate(NULL);            /* update after a big change */
2027
2028     /* Return if there are no diff blocks.  All lines will be folded. */
2029     if (curtab->tp_first_diff == NULL)
2030         return TRUE;
2031
2032     for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
2033     {
2034         /* If this change is below the line there can't be any further match. */
2035         if (dp->df_lnum[idx] - diff_context > lnum)
2036             break;
2037         /* If this change ends before the line we have a match. */
2038         if (dp->df_lnum[idx] + dp->df_count[idx] + diff_context > lnum)
2039             return FALSE;
2040     }
2041     return TRUE;
2042 }
2043 #endif
2044
2045 /*
2046  * "dp" and "do" commands.
2047  */
2048     void
2049 nv_diffgetput(put)
2050     int         put;
2051 {
2052     exarg_T     ea;
2053
2054     ea.arg = (char_u *)"";
2055     if (put)
2056         ea.cmdidx = CMD_diffput;
2057     else
2058         ea.cmdidx = CMD_diffget;
2059     ea.addr_count = 0;
2060     ea.line1 = curwin->w_cursor.lnum;
2061     ea.line2 = curwin->w_cursor.lnum;
2062     ex_diffgetput(&ea);
2063 }
2064
2065 /*
2066  * ":diffget"
2067  * ":diffput"
2068  */
2069     void
2070 ex_diffgetput(eap)
2071     exarg_T     *eap;
2072 {
2073     linenr_T    lnum;
2074     int         count;
2075     linenr_T    off = 0;
2076     diff_T      *dp;
2077     diff_T      *dprev;
2078     diff_T      *dfree;
2079     int         idx_cur;
2080     int         idx_other;
2081     int         idx_from;
2082     int         idx_to;
2083     int         i;
2084     int         added;
2085     char_u      *p;
2086     aco_save_T  aco;
2087     buf_T       *buf;
2088     int         start_skip, end_skip;
2089     int         new_count;
2090     int         buf_empty;
2091     int         found_not_ma = FALSE;
2092
2093     /* Find the current buffer in the list of diff buffers. */
2094     idx_cur = diff_buf_idx(curbuf);
2095     if (idx_cur == DB_COUNT)
2096     {
2097         EMSG(_("E99: Current buffer is not in diff mode"));
2098         return;
2099     }
2100
2101     if (*eap->arg == NUL)
2102     {
2103         /* No argument: Find the other buffer in the list of diff buffers. */
2104         for (idx_other = 0; idx_other < DB_COUNT; ++idx_other)
2105             if (curtab->tp_diffbuf[idx_other] != curbuf
2106                     && curtab->tp_diffbuf[idx_other] != NULL)
2107             {
2108                 if (eap->cmdidx != CMD_diffput
2109                                      || curtab->tp_diffbuf[idx_other]->b_p_ma)
2110                     break;
2111                 found_not_ma = TRUE;
2112             }
2113         if (idx_other == DB_COUNT)
2114         {
2115             if (found_not_ma)
2116                 EMSG(_("E793: No other buffer in diff mode is modifiable"));
2117             else
2118                 EMSG(_("E100: No other buffer in diff mode"));
2119             return;
2120         }
2121
2122         /* Check that there isn't a third buffer in the list */
2123         for (i = idx_other + 1; i < DB_COUNT; ++i)
2124             if (curtab->tp_diffbuf[i] != curbuf
2125                     && curtab->tp_diffbuf[i] != NULL
2126                     && (eap->cmdidx != CMD_diffput || curtab->tp_diffbuf[i]->b_p_ma))
2127             {
2128                 EMSG(_("E101: More than two buffers in diff mode, don't know which one to use"));
2129                 return;
2130             }
2131     }
2132     else
2133     {
2134         /* Buffer number or pattern given.  Ignore trailing white space. */
2135         p = eap->arg + STRLEN(eap->arg);
2136         while (p > eap->arg && vim_iswhite(p[-1]))
2137             --p;
2138         for (i = 0; vim_isdigit(eap->arg[i]) && eap->arg + i < p; ++i)
2139             ;
2140         if (eap->arg + i == p)      /* digits only */
2141             i = atol((char *)eap->arg);
2142         else
2143         {
2144             i = buflist_findpat(eap->arg, p, FALSE, TRUE);
2145             if (i < 0)
2146                 return;         /* error message already given */
2147         }
2148         buf = buflist_findnr(i);
2149         if (buf == NULL)
2150         {
2151             EMSG2(_("E102: Can't find buffer \"%s\""), eap->arg);
2152             return;
2153         }
2154         if (buf == curbuf)
2155             return;             /* nothing to do */
2156         idx_other = diff_buf_idx(buf);
2157         if (idx_other == DB_COUNT)
2158         {
2159             EMSG2(_("E103: Buffer \"%s\" is not in diff mode"), eap->arg);
2160             return;
2161         }
2162     }
2163
2164     diff_busy = TRUE;
2165
2166     /* When no range given include the line above or below the cursor. */
2167     if (eap->addr_count == 0)
2168     {
2169         /* Make it possible that ":diffget" on the last line gets line below
2170          * the cursor line when there is no difference above the cursor. */
2171         if (eap->cmdidx == CMD_diffget
2172                 && eap->line1 == curbuf->b_ml.ml_line_count
2173                 && diff_check(curwin, eap->line1) == 0
2174                 && (eap->line1 == 1 || diff_check(curwin, eap->line1 - 1) == 0))
2175             ++eap->line2;
2176         else if (eap->line1 > 0)
2177             --eap->line1;
2178     }
2179
2180     if (eap->cmdidx == CMD_diffget)
2181     {
2182         idx_from = idx_other;
2183         idx_to = idx_cur;
2184     }
2185     else
2186     {
2187         idx_from = idx_cur;
2188         idx_to = idx_other;
2189         /* Need to make the other buffer the current buffer to be able to make
2190          * changes in it. */
2191         /* set curwin/curbuf to buf and save a few things */
2192         aucmd_prepbuf(&aco, curtab->tp_diffbuf[idx_other]);
2193     }
2194
2195     /* May give the warning for a changed buffer here, which can trigger the
2196      * FileChangedRO autocommand, which may do nasty things and mess
2197      * everything up. */
2198     if (!curbuf->b_changed)
2199     {
2200         change_warning(0);
2201         if (diff_buf_idx(curbuf) != idx_to)
2202         {
2203             EMSG(_("E787: Buffer changed unexpectedly"));
2204             return;
2205         }
2206     }
2207
2208     dprev = NULL;
2209     for (dp = curtab->tp_first_diff; dp != NULL; )
2210     {
2211         if (dp->df_lnum[idx_cur] > eap->line2 + off)
2212             break;      /* past the range that was specified */
2213
2214         dfree = NULL;
2215         lnum = dp->df_lnum[idx_to];
2216         count = dp->df_count[idx_to];
2217         if (dp->df_lnum[idx_cur] + dp->df_count[idx_cur] > eap->line1 + off
2218                 && u_save(lnum - 1, lnum + count) != FAIL)
2219         {
2220             /* Inside the specified range and saving for undo worked. */
2221             start_skip = 0;
2222             end_skip = 0;
2223             if (eap->addr_count > 0)
2224             {
2225                 /* A range was specified: check if lines need to be skipped. */
2226                 start_skip = eap->line1 + off - dp->df_lnum[idx_cur];
2227                 if (start_skip > 0)
2228                 {
2229                     /* range starts below start of current diff block */
2230                     if (start_skip > count)
2231                     {
2232                         lnum += count;
2233                         count = 0;
2234                     }
2235                     else
2236                     {
2237                         count -= start_skip;
2238                         lnum += start_skip;
2239                     }
2240                 }
2241                 else
2242                     start_skip = 0;
2243
2244                 end_skip = dp->df_lnum[idx_cur] + dp->df_count[idx_cur] - 1
2245                                                          - (eap->line2 + off);
2246                 if (end_skip > 0)
2247                 {
2248                     /* range ends above end of current/from diff block */
2249                     if (idx_cur == idx_from)    /* :diffput */
2250                     {
2251                         i = dp->df_count[idx_cur] - start_skip - end_skip;
2252                         if (count > i)
2253                             count = i;
2254                     }
2255                     else                        /* :diffget */
2256                     {
2257                         count -= end_skip;
2258                         end_skip = dp->df_count[idx_from] - start_skip - count;
2259                         if (end_skip < 0)
2260                             end_skip = 0;
2261                     }
2262                 }
2263                 else
2264                     end_skip = 0;
2265             }
2266
2267             buf_empty = FALSE;
2268             added = 0;
2269             for (i = 0; i < count; ++i)
2270             {
2271                 /* remember deleting the last line of the buffer */
2272                 buf_empty = curbuf->b_ml.ml_line_count == 1;
2273                 ml_delete(lnum, FALSE);
2274                 --added;
2275             }
2276             for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i)
2277             {
2278                 linenr_T nr;
2279
2280                 nr = dp->df_lnum[idx_from] + start_skip + i;
2281                 if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count)
2282                     break;
2283                 p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from],
2284                                                                   nr, FALSE));
2285                 if (p != NULL)
2286                 {
2287                     ml_append(lnum + i - 1, p, 0, FALSE);
2288                     vim_free(p);
2289                     ++added;
2290                     if (buf_empty && curbuf->b_ml.ml_line_count == 2)
2291                     {
2292                         /* Added the first line into an empty buffer, need to
2293                          * delete the dummy empty line. */
2294                         buf_empty = FALSE;
2295                         ml_delete((linenr_T)2, FALSE);
2296                     }
2297                 }
2298             }
2299             new_count = dp->df_count[idx_to] + added;
2300             dp->df_count[idx_to] = new_count;
2301
2302             if (start_skip == 0 && end_skip == 0)
2303             {
2304                 /* Check if there are any other buffers and if the diff is
2305                  * equal in them. */
2306                 for (i = 0; i < DB_COUNT; ++i)
2307                     if (curtab->tp_diffbuf[i] != NULL && i != idx_from
2308                                                                 && i != idx_to
2309                             && !diff_equal_entry(dp, idx_from, i))
2310                         break;
2311                 if (i == DB_COUNT)
2312                 {
2313                     /* delete the diff entry, the buffers are now equal here */
2314                     dfree = dp;
2315                     dp = dp->df_next;
2316                     if (dprev == NULL)
2317                         curtab->tp_first_diff = dp;
2318                     else
2319                         dprev->df_next = dp;
2320                 }
2321             }
2322
2323             /* Adjust marks.  This will change the following entries! */
2324             if (added != 0)
2325             {
2326                 mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, (long)added);
2327                 if (curwin->w_cursor.lnum >= lnum)
2328                 {
2329                     /* Adjust the cursor position if it's in/after the changed
2330                      * lines. */
2331                     if (curwin->w_cursor.lnum >= lnum + count)
2332                         curwin->w_cursor.lnum += added;
2333                     else if (added < 0)
2334                         curwin->w_cursor.lnum = lnum;
2335                 }
2336             }
2337             changed_lines(lnum, 0, lnum + count, (long)added);
2338
2339             if (dfree != NULL)
2340             {
2341                 /* Diff is deleted, update folds in other windows. */
2342 #ifdef FEAT_FOLDING
2343                 diff_fold_update(dfree, idx_to);
2344 #endif
2345                 vim_free(dfree);
2346             }
2347             else
2348                 /* mark_adjust() may have changed the count in a wrong way */
2349                 dp->df_count[idx_to] = new_count;
2350
2351             /* When changing the current buffer, keep track of line numbers */
2352             if (idx_cur == idx_to)
2353                 off += added;
2354         }
2355
2356         /* If before the range or not deleted, go to next diff. */
2357         if (dfree == NULL)
2358         {
2359             dprev = dp;
2360             dp = dp->df_next;
2361         }
2362     }
2363
2364     /* restore curwin/curbuf and a few other things */
2365     if (eap->cmdidx != CMD_diffget)
2366     {
2367         /* Syncing undo only works for the current buffer, but we change
2368          * another buffer.  Sync undo if the command was typed.  This isn't
2369          * 100% right when ":diffput" is used in a function or mapping. */
2370         if (KeyTyped)
2371             u_sync(FALSE);
2372         aucmd_restbuf(&aco);
2373     }
2374
2375     diff_busy = FALSE;
2376
2377     /* Check that the cursor is on a valid character and update it's position.
2378      * When there were filler lines the topline has become invalid. */
2379     check_cursor();
2380     changed_line_abv_curs();
2381
2382     /* Also need to redraw the other buffers. */
2383     diff_redraw(FALSE);
2384 }
2385
2386 #ifdef FEAT_FOLDING
2387 /*
2388  * Update folds for all diff buffers for entry "dp".
2389  * Skip buffer with index "skip_idx".
2390  * When there are no diffs, all folds are removed.
2391  */
2392     static void
2393 diff_fold_update(dp, skip_idx)
2394     diff_T      *dp;
2395     int         skip_idx;
2396 {
2397     int         i;
2398     win_T       *wp;
2399
2400     for (wp = firstwin; wp != NULL; wp = wp->w_next)
2401         for (i = 0; i < DB_COUNT; ++i)
2402             if (curtab->tp_diffbuf[i] == wp->w_buffer && i != skip_idx)
2403                 foldUpdate(wp, dp->df_lnum[i],
2404                                             dp->df_lnum[i] + dp->df_count[i]);
2405 }
2406 #endif
2407
2408 /*
2409  * Return TRUE if buffer "buf" is in diff-mode.
2410  */
2411     int
2412 diff_mode_buf(buf)
2413     buf_T       *buf;
2414 {
2415     tabpage_T   *tp;
2416
2417     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
2418         if (diff_buf_idx_tp(buf, tp) != DB_COUNT)
2419             return TRUE;
2420     return FALSE;
2421 }
2422
2423 /*
2424  * Move "count" times in direction "dir" to the next diff block.
2425  * Return FAIL if there isn't such a diff block.
2426  */
2427     int
2428 diff_move_to(dir, count)
2429     int         dir;
2430     long        count;
2431 {
2432     int         idx;
2433     linenr_T    lnum = curwin->w_cursor.lnum;
2434     diff_T      *dp;
2435
2436     idx = diff_buf_idx(curbuf);
2437     if (idx == DB_COUNT || curtab->tp_first_diff == NULL)
2438         return FAIL;
2439
2440     if (curtab->tp_diff_invalid)
2441         ex_diffupdate(NULL);            /* update after a big change */
2442
2443     if (curtab->tp_first_diff == NULL)          /* no diffs today */
2444         return FAIL;
2445
2446     while (--count >= 0)
2447     {
2448         /* Check if already before first diff. */
2449         if (dir == BACKWARD && lnum <= curtab->tp_first_diff->df_lnum[idx])
2450             break;
2451
2452         for (dp = curtab->tp_first_diff; ; dp = dp->df_next)
2453         {
2454             if (dp == NULL)
2455                 break;
2456             if ((dir == FORWARD && lnum < dp->df_lnum[idx])
2457                     || (dir == BACKWARD
2458                         && (dp->df_next == NULL
2459                             || lnum <= dp->df_next->df_lnum[idx])))
2460             {
2461                 lnum = dp->df_lnum[idx];
2462                 break;
2463             }
2464         }
2465     }
2466
2467     /* don't end up past the end of the file */
2468     if (lnum > curbuf->b_ml.ml_line_count)
2469         lnum = curbuf->b_ml.ml_line_count;
2470
2471     /* When the cursor didn't move at all we fail. */
2472     if (lnum == curwin->w_cursor.lnum)
2473         return FAIL;
2474
2475     setpcmark();
2476     curwin->w_cursor.lnum = lnum;
2477     curwin->w_cursor.col = 0;
2478
2479     return OK;
2480 }
2481
2482 #if defined(FEAT_CURSORBIND) || defined(PROTO)
2483     linenr_T
2484 diff_get_corresponding_line(buf1, lnum1, buf2, lnum3)
2485     buf_T       *buf1;
2486     linenr_T    lnum1;
2487     buf_T       *buf2;
2488     linenr_T    lnum3;
2489 {
2490     int         idx1;
2491     int         idx2;
2492     diff_T      *dp;
2493     int         baseline = 0;
2494     linenr_T    lnum2;
2495
2496     idx1 = diff_buf_idx(buf1);
2497     idx2 = diff_buf_idx(buf2);
2498     if (idx1 == DB_COUNT || idx2 == DB_COUNT || curtab->tp_first_diff == NULL)
2499         return lnum1;
2500
2501     if (curtab->tp_diff_invalid)
2502         ex_diffupdate(NULL);            /* update after a big change */
2503
2504     if (curtab->tp_first_diff == NULL)          /* no diffs today */
2505         return lnum1;
2506
2507     for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
2508     {
2509         if (dp->df_lnum[idx1] > lnum1)
2510         {
2511             lnum2 = lnum1 - baseline;
2512             /* don't end up past the end of the file */
2513             if (lnum2 > buf2->b_ml.ml_line_count)
2514                 lnum2 = buf2->b_ml.ml_line_count;
2515
2516             return lnum2;
2517         }
2518         else if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1)
2519         {
2520             /* Inside the diffblock */
2521             baseline = lnum1 - dp->df_lnum[idx1];
2522             if (baseline > dp->df_count[idx2])
2523                 baseline = dp->df_count[idx2];
2524
2525             return dp->df_lnum[idx2] + baseline;
2526         }
2527         else if (   (dp->df_lnum[idx1] == lnum1)
2528                  && (dp->df_count[idx1] == 0)
2529                  && (dp->df_lnum[idx2] <= lnum3)
2530                  && ((dp->df_lnum[idx2] + dp->df_count[idx2]) > lnum3))
2531             /*
2532              * Special case: if the cursor is just after a zero-count
2533              * block (i.e. all filler) and the target cursor is already
2534              * inside the corresponding block, leave the target cursor
2535              * unmoved. This makes repeated CTRL-W W operations work
2536              * as expected.
2537              */
2538             return lnum3;
2539         baseline = (dp->df_lnum[idx1] + dp->df_count[idx1])
2540                                    - (dp->df_lnum[idx2] + dp->df_count[idx2]);
2541     }
2542
2543     /* If we get here then the cursor is after the last diff */
2544     lnum2 = lnum1 - baseline;
2545     /* don't end up past the end of the file */
2546     if (lnum2 > buf2->b_ml.ml_line_count)
2547         lnum2 = buf2->b_ml.ml_line_count;
2548
2549     return lnum2;
2550 }
2551 #endif
2552
2553 #if defined(FEAT_FOLDING) || defined(PROTO)
2554 /*
2555  * For line "lnum" in the current window find the equivalent lnum in window
2556  * "wp", compensating for inserted/deleted lines.
2557  */
2558     linenr_T
2559 diff_lnum_win(lnum, wp)
2560     linenr_T    lnum;
2561     win_T       *wp;
2562 {
2563     diff_T      *dp;
2564     int         idx;
2565     int         i;
2566     linenr_T    n;
2567
2568     idx = diff_buf_idx(curbuf);
2569     if (idx == DB_COUNT)                /* safety check */
2570         return (linenr_T)0;
2571
2572     if (curtab->tp_diff_invalid)
2573         ex_diffupdate(NULL);            /* update after a big change */
2574
2575     /* search for a change that includes "lnum" in the list of diffblocks. */
2576     for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next)
2577         if (lnum <= dp->df_lnum[idx] + dp->df_count[idx])
2578             break;
2579
2580     /* When after the last change, compute relative to the last line number. */
2581     if (dp == NULL)
2582         return wp->w_buffer->b_ml.ml_line_count
2583                                         - (curbuf->b_ml.ml_line_count - lnum);
2584
2585     /* Find index for "wp". */
2586     i = diff_buf_idx(wp->w_buffer);
2587     if (i == DB_COUNT)                  /* safety check */
2588         return (linenr_T)0;
2589
2590     n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]);
2591     if (n > dp->df_lnum[i] + dp->df_count[i])
2592         n = dp->df_lnum[i] + dp->df_count[i];
2593     return n;
2594 }
2595 #endif
2596
2597 #endif  /* FEAT_DIFF */