Git 2.23
[gitweb.git] / xdiff / xutils.c
index bc12f298953a4e72b323f73607278028ec4a2805..cfa6e2220ffd0461ce3293911c86366f6ccb1b05 100644 (file)
@@ -13,8 +13,8 @@
  *  Lesser General Public License for more details.
  *
  *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
 #include "xinclude.h"
 
 
-
-#define XDL_GUESS_NLINES 256
-
-
-
-
 long xdl_bogosqrt(long n) {
        long i;
 
@@ -56,7 +50,7 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
                mb[2].size = strlen(mb[2].ptr);
                i++;
        }
-       if (ecb->outf(ecb->priv, mb, i) < 0) {
+       if (ecb->out_line(ecb->priv, mb, i) < 0) {
 
                return -1;
        }
@@ -71,12 +65,6 @@ void *xdl_mmfile_first(mmfile_t *mmf, long *size)
 }
 
 
-void *xdl_mmfile_next(mmfile_t *mmf, long *size)
-{
-       return NULL;
-}
-
-
 long xdl_mmfile_size(mmfile_t *mmf)
 {
        return mmf->size;
@@ -130,47 +118,12 @@ void *xdl_cha_alloc(chastore_t *cha) {
        return data;
 }
 
-
-void *xdl_cha_first(chastore_t *cha) {
-       chanode_t *sncur;
-
-       if (!(cha->sncur = sncur = cha->head))
-               return NULL;
-
-       cha->scurr = 0;
-
-       return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
-void *xdl_cha_next(chastore_t *cha) {
-       chanode_t *sncur;
-
-       if (!(sncur = cha->sncur))
-               return NULL;
-       cha->scurr += cha->isize;
-       if (cha->scurr == sncur->icurr) {
-               if (!(sncur = cha->sncur = sncur->next))
-                       return NULL;
-               cha->scurr = 0;
-       }
-
-       return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
-long xdl_guess_lines(mmfile_t *mf) {
+long xdl_guess_lines(mmfile_t *mf, long sample) {
        long nl = 0, size, tsize = 0;
        char const *data, *cur, *top;
 
        if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
-               for (top = data + size; nl < XDL_GUESS_NLINES;) {
-                       if (cur >= top) {
-                               tsize += (long) (cur - data);
-                               if (!(cur = data = xdl_mmfile_next(mf, &size)))
-                                       break;
-                               top = data + size;
-                       }
+               for (top = data + size; nl < sample && cur < top; ) {
                        nl++;
                        if (!(cur = memchr(cur, '\n', top - cur)))
                                cur = top;
@@ -186,19 +139,53 @@ long xdl_guess_lines(mmfile_t *mf) {
        return nl + 1;
 }
 
+int xdl_blankline(const char *line, long size, long flags)
+{
+       long i;
+
+       if (!(flags & XDF_WHITESPACE_FLAGS))
+               return (size <= 1);
+
+       for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+               ;
+
+       return (i == size);
+}
+
+/*
+ * Have we eaten everything on the line, except for an optional
+ * CR at the very end?
+ */
+static int ends_with_optional_cr(const char *l, long s, long i)
+{
+       int complete = s && l[s-1] == '\n';
+
+       if (complete)
+               s--;
+       if (s == i)
+               return 1;
+       /* do not ignore CR at the end of an incomplete line */
+       if (complete && s == i + 1 && l[i] == '\r')
+               return 1;
+       return 0;
+}
+
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
        int i1, i2;
 
+       if (s1 == s2 && !memcmp(l1, l2, s1))
+               return 1;
        if (!(flags & XDF_WHITESPACE_FLAGS))
-               return s1 == s2 && !memcmp(l1, l2, s1);
+               return 0;
 
        i1 = 0;
        i2 = 0;
 
        /*
         * -w matches everything that matches with -b, and -b in turn
-        * matches everything that matches with --ignore-space-at-eol.
+        * matches everything that matches with --ignore-space-at-eol,
+        * which in turn matches everything that matches with --ignore-cr-at-eol.
         *
         * Each flavor of ignoring needs different logic to skip whitespaces
         * while we have both sides to compare.
@@ -209,18 +196,18 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
                        if (l1[i1++] != l2[i2++])
                                return 0;
                skip_ws:
-                       while (i1 < s1 && isspace(l1[i1]))
+                       while (i1 < s1 && XDL_ISSPACE(l1[i1]))
                                i1++;
-                       while (i2 < s2 && isspace(l2[i2]))
+                       while (i2 < s2 && XDL_ISSPACE(l2[i2]))
                                i2++;
                }
        } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
                while (i1 < s1 && i2 < s2) {
-                       if (isspace(l1[i1]) && isspace(l2[i2])) {
+                       if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
                                /* Skip matching spaces and try again */
-                               while (i1 < s1 && isspace(l1[i1]))
+                               while (i1 < s1 && XDL_ISSPACE(l1[i1]))
                                        i1++;
-                               while (i2 < s2 && isspace(l2[i2]))
+                               while (i2 < s2 && XDL_ISSPACE(l2[i2]))
                                        i2++;
                                continue;
                        }
@@ -228,8 +215,18 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
                                return 0;
                }
        } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
-               while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
-                       ; /* keep going */
+               while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
+                       i1++;
+                       i2++;
+               }
+       } else if (flags & XDF_IGNORE_CR_AT_EOL) {
+               /* Find the first difference and see how the line ends */
+               while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
+                       i1++;
+                       i2++;
+               }
+               return (ends_with_optional_cr(l1, s1, i1) &&
+                       ends_with_optional_cr(l2, s2, i2));
        }
 
        /*
@@ -239,13 +236,13 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
         * while there still are characters remaining on both lines.
         */
        if (i1 < s1) {
-               while (i1 < s1 && isspace(l1[i1]))
+               while (i1 < s1 && XDL_ISSPACE(l1[i1]))
                        i1++;
                if (s1 != i1)
                        return 0;
        }
        if (i2 < s2) {
-               while (i2 < s2 && isspace(l2[i2]))
+               while (i2 < s2 && XDL_ISSPACE(l2[i2]))
                        i2++;
                return (s2 == i2);
        }
@@ -256,12 +253,19 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
                char const *top, long flags) {
        unsigned long ha = 5381;
        char const *ptr = *data;
+       int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL;
 
        for (; ptr < top && *ptr != '\n'; ptr++) {
-               if (isspace(*ptr)) {
+               if (cr_at_eol_only) {
+                       /* do not ignore CR at the end of an incomplete line */
+                       if (*ptr == '\r' &&
+                           (ptr + 1 < top && ptr[1] == '\n'))
+                               continue;
+               }
+               else if (XDL_ISSPACE(*ptr)) {
                        const char *ptr2 = ptr;
                        int at_eol;
-                       while (ptr + 1 < top && isspace(ptr[1])
+                       while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
                                        && ptr[1] != '\n')
                                ptr++;
                        at_eol = (top <= ptr + 1 || ptr[1] == '\n');
@@ -290,7 +294,6 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
        return ha;
 }
 
-
 unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        unsigned long ha = 5381;
        char const *ptr = *data;
@@ -307,7 +310,6 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        return ha;
 }
 
-
 unsigned int xdl_hashbits(unsigned int size) {
        unsigned int val = 1, bits = 0;
 
@@ -338,22 +340,9 @@ int xdl_num_out(char *out, long val) {
        return str - out;
 }
 
-
-long xdl_atol(char const *str, char const **next) {
-       long val, base;
-       char const *top;
-
-       for (top = str; XDL_ISDIGIT(*top); top++);
-       if (next)
-               *next = top;
-       for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
-               val += base * (long)(*top - '0');
-       return val;
-}
-
-
-int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
-                     const char *func, long funclen, xdemitcb_t *ecb) {
+static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2,
+                              const char *func, long funclen,
+                              xdemitcb_t *ecb) {
        int nb = 0;
        mmbuffer_t mb;
        char buf[128];
@@ -395,8 +384,51 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
 
        mb.ptr = buf;
        mb.size = nb;
-       if (ecb->outf(ecb->priv, &mb, 1) < 0)
+       if (ecb->out_line(ecb->priv, &mb, 1) < 0)
                return -1;
+       return 0;
+}
+
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+                     const char *func, long funclen,
+                     xdemitcb_t *ecb) {
+       if (!ecb->out_hunk)
+               return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb);
+       if (ecb->out_hunk(ecb->priv,
+                         c1 ? s1 : s1 - 1, c1,
+                         c2 ? s2 : s2 - 1, c2,
+                         func, funclen) < 0)
+               return -1;
+       return 0;
+}
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+               int line1, int count1, int line2, int count2)
+{
+       /*
+        * This probably does not work outside Git, since
+        * we have a very simple mmfile structure.
+        *
+        * Note: ideally, we would reuse the prepared environment, but
+        * the libxdiff interface does not (yet) allow for diffing only
+        * ranges of lines instead of the whole files.
+        */
+       mmfile_t subfile1, subfile2;
+       xdfenv_t env;
+
+       subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+       subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+               diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+       subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+       subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+               diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+       if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+               return -1;
+
+       memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+       memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+       xdl_free_env(&env);
 
        return 0;
 }