- move code around to avoid the need for the prototypes.
authorBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Thu, 6 Apr 2006 08:15:24 +0000 (08:15 -0000)
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Thu, 6 Apr 2006 08:15:24 +0000 (08:15 -0000)
coreutils/diff.c

index b2945dd87b04fa96ee4c517d8fde031c29c8729a..17975ad2079bd90aced6f88df10b21e68fcd901a 100644 (file)
@@ -3,14 +3,14 @@
  * Mini diff implementation for busybox, adapted from OpenBSD diff.
  *
  * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com>
- * 
+ *
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
 /*
  * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
  *
- * Permission to 
+ * Permission to
  * use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
@@ -71,8 +71,8 @@
  * D_SKIPPED2 - skipped path2 as it is a special file
  */
 
-#define D_SAME 0
-#define D_DIFFER       1
+#define D_SAME         0
+#define D_DIFFER       (1<<0)
 #define D_BINARY       (1<<1)
 #define D_COMMON       (1<<2)
 #define D_ONLY         (1<<3)
@@ -84,7 +84,7 @@
 
 /* Command line options */
 static unsigned long cmd_flags;
-#define FLAG_a 1
+#define FLAG_a (1<<0)
 #define FLAG_b (1<<1)
 #define FLAG_d  (1<<2)
 #define FLAG_i (1<<3)
@@ -145,207 +145,14 @@ static struct context_vec *context_vec_start;
 static struct context_vec *context_vec_end;
 static struct context_vec *context_vec_ptr;
 
-static void output(char *, FILE *, char *, FILE *);
-static void check(char *, FILE *, char *, FILE *);
-static void uni_range(int, int);
-static void dump_unified_vec(FILE *, FILE *);
-static void prepare(int, FILE *, off_t);
-static void prune(void);
-static void equiv(struct line *, int, struct line *, int, int *);
-static void unravel(int);
-static void unsort(struct line *, int, int *);
-static void change(char *, FILE *, char *, FILE *, int, int, int, int);
-static void sort(struct line *, int);
-static void print_header(const char *, const char *);
-static int  asciifile(FILE *);
-static int  fetch(long *, int, int, FILE *, int, int);
-static int  newcand(int, int, int);
-static int  search(int *, int, int);
-static int  skipline(FILE *);
-static int  stone(int *, int, int *, int *);
-static int  readhash(FILE *);
-static int  files_differ(FILE *, FILE *, int);
-
-static int     diffreg(char *, char *, int);
-static void    print_only(const char *, size_t, const char *);
-static void    print_status(int, char *, char *, char *);
-
-#ifdef CONFIG_FEATURE_DIFF_DIR
-static int dir_strcmp(const void *p1, const void *p2) {
-       return strcmp(*(char * const *)p1, *(char * const *)p2);
-}
-
-/* This function adds a filename to dl, the directory listing. */
-
-static int add_to_dirlist (const char *filename, struct stat *statbuf, void *userdata) {
-       dl_count++;
-       dl = xrealloc(dl, dl_count * sizeof(char *));
-       dl[dl_count - 1] = bb_xstrdup(filename);
-       if (cmd_flags & FLAG_r) {
-               int *pp = (int *) userdata;
-               int path_len = *pp + 1;
-               dl[dl_count - 1] = &(dl[dl_count - 1])[path_len];
-       }
-       return TRUE;
-}
-
-/* This returns a sorted directory listing. */
-static char **get_dir(char *path) {
-
-       int i;
-
-       /* Reset dl_count - there's no need to free dl as bb_xrealloc does
-        * the job nicely. */   
-       dl_count = 0;
-
-       /* If -r has been set, then the recursive_action function will be
-        * used. Unfortunately, this outputs the root directory along with
-        * the recursed paths, so use void *userdata to specify the string
-        * length of the root directory. It can then be removed in
-        * add_to_dirlist. */
-
-       int path_len = strlen(path);
-       void *userdata = &path_len;
-       
-       /* Now fill dl with a listing. */
-       if (cmd_flags & FLAG_r)
-               recursive_action(path, TRUE, TRUE, FALSE, add_to_dirlist, NULL, userdata);
-       else {
-               DIR *dp;
-               struct dirent *ep;
-               if ((dp = opendir(path)) == NULL)
-                       bb_error_msg("Error reading directory");
-               while ((ep = readdir(dp))) {
-                       if ((!strcmp(ep->d_name, "..")) || (!strcmp(ep->d_name, ".")))
-                               continue;
-                       add_to_dirlist(ep->d_name, NULL, NULL);
-               }
-               closedir(dp);
-       }
-
-       /* Sort dl alphabetically. */
-       qsort(dl, dl_count, sizeof(char *), dir_strcmp);
-
-       /* Copy dl so that we can return it. */
-       char **retval = xmalloc(dl_count * sizeof(char *));
-       for (i = 0; i < dl_count; i++)
-               retval[i] = bb_xstrdup(dl[i]);
-
-       return retval;
-}
-
-static void do_diff (char *dir1, char *path1, char *dir2, char *path2) {
-       
-       int flags = D_HEADER;
-       int val;
-       
-       char *fullpath1 = bb_xasprintf("%s/%s", dir1, path1);
-       char *fullpath2 = bb_xasprintf("%s/%s", dir2, path2);
-
-       if (stat(fullpath1, &stb1) != 0) {
-               flags |= D_EMPTY1;
-               memset(&stb1, 0, sizeof(stb1));
-               fullpath1 = bb_xasprintf("%s/%s", dir1, path2);
-       }
-       if (stat(fullpath2, &stb2) != 0) {
-               flags |= D_EMPTY2;
-               memset(&stb2, 0, sizeof(stb2));
-               stb2.st_mode = stb1.st_mode;
-               fullpath2 = bb_xasprintf("%s/%s", dir2, path1);
-       }
-
-       if (stb1.st_mode == 0)
-               stb1.st_mode = stb2.st_mode;
-       
-       if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
-               printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2);
-               return;
-       }
-
-       if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
-               val = D_SKIPPED1;
-       else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
-               val = D_SKIPPED2;
-       else
-               val = diffreg(fullpath1, fullpath2, flags);
-       
-       print_status(val, fullpath1, fullpath2, NULL);
-}
-
-static void diffdir (char *p1, char *p2) {
-       
-       char **dirlist1, **dirlist2;
-       char *dp1, *dp2;
-       int dirlist1_count, dirlist2_count;
-       int pos;
-
-       /* Check for trailing slashes. */
-       
-       if (p1[strlen(p1) - 1] == '/')
-               p1[strlen(p1) - 1] = '\0';
-       if (p2[strlen(p2) - 1] == '/')
-               p2[strlen(p2) - 1] = '\0';
-       
-       /* Get directory listings for p1 and p2. */
-       
-       dirlist1 = get_dir(p1);
-       dirlist1_count = dl_count;
-       dirlist1[dirlist1_count] = NULL;
-       dirlist2 = get_dir(p2);
-       dirlist2_count = dl_count;
-       dirlist2[dirlist2_count] = NULL;
-       
-       /* If -S was set, find the starting point. */
-       if (start) {
-               while (*dirlist1 != NULL && strcmp(*dirlist1, start) < 0)
-                       dirlist1++;
-               while (*dirlist2 != NULL && strcmp(*dirlist2, start) < 0)
-                       dirlist2++;
-               if ((*dirlist1 == NULL) || (*dirlist2 == NULL))
-                       bb_error_msg("Invalid argument to -S");
-       }
-       
-       /* Now that both dirlist1 and dirlist2 contain sorted directory
-        * listings, we can start to go through dirlist1. If both listings
-        * contain the same file, then do a normal diff. Otherwise, behaviour
-        * is determined by whether the -N flag is set. */      
-       while (*dirlist1 != NULL || *dirlist2 != NULL) {
-               dp1 = *dirlist1;
-               dp2 = *dirlist2;
-               pos = dp1 == NULL ? 1 : dp2 == NULL ? -1 : strcmp(dp1, dp2);
-               if (pos == 0) {
-                       do_diff(p1, dp1, p2, dp2);
-                       dirlist1++;
-                       dirlist2++;
-               }
-               else if (pos < 0) {
-                       if (cmd_flags & FLAG_N)
-                               do_diff(p1, dp1, p2, NULL);
-                       else
-                               print_only(p1, strlen(p1) + 1, dp1);
-                       dirlist1++;
-               }
-               else {
-                       if (cmd_flags & FLAG_N)
-                               do_diff(p1, NULL, p2, dp2);
-                       else
-                               print_only(p2, strlen(p2) + 1, dp2);
-                       dirlist2++;
-               }
-       }
-}
-#endif
-
-static void
-print_only(const char *path, size_t dirlen, const char *entry)
+static void print_only(const char *path, size_t dirlen, const char *entry)
 {
         if (dirlen > 1)
                 dirlen--;
         printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
 }
 
-static void
-print_status(int val, char *path1, char *path2, char *entry)
+static void print_status(int val, char *path1, char *path2, char *entry)
 {
         switch (val) {
         case D_ONLY:
@@ -391,175 +198,76 @@ print_status(int val, char *path1, char *path2, char *entry)
 }
 
 /*
- *      The following code uses an algorithm due to Harold Stone, 
- *      which finds a pair of longest identical subsequences in 
- *      the two files.
- *
- *      The major goal is to generate the match vector J.
- *      J[i] is the index of the line in file1 corresponding
- *      to line i file0. J[i] = 0 if there is no
- *      such line in file1.
- *
- *      Lines are hashed so as to work in core. All potential
- *      matches are located by sorting the lines of each file
- *      on the hash (called ``value''). In particular, this
- *      collects the equivalence classes in file1 together.
- *      Subroutine equiv replaces the value of each line in
- *      file0 by the index of the first element of its
- *      matching equivalence in (the reordered) file1.
- *      To save space equiv squeezes file1 into a single
- *      array member in which the equivalence classes
- *      are simply concatenated, except that their first
- *      members are flagged by changing sign.
- *
- *      Next the indices that point into member are unsorted into
- *      array class according to the original order of file0.
- *
- *      The cleverness lies in routine stone. This marches
- *      through the lines of file0, developing a vector klist
- *      of "k-candidates". At step i a k-candidate is a matched
- *      pair of lines x,y (x in file0 y in file1) such that
- *      there is a common subsequence of length k
- *      between the first i lines of file0 and the first y
- *      lines of file1, but there is no such subsequence for
- *      any smaller y. x is the earliest possible mate to y
- *      that occurs in such a subsequence.
- *
- *      Whenever any of the members of the equivalence class of
- *      lines in file1 matable to a line in file0 has serial number
- *      less than the y of some k-candidate, that k-candidate
- *      with the smallest such y is replaced. The new
- *      k-candidate is chained (via pred) to the current
- *      k-1 candidate so that the actual subsequence can
- *      be recovered. When a member has serial number greater
- *      that the y of all k-candidates, the klist is extended.
- *      At the end, the longest subsequence is pulled out
- *      and placed in the array J by unravel
- *
- *      With J in hand, the matches there recorded are
- *      checked against reality to assure that no spurious
- *      matches have crept in due to hashing. If they have,
- *      they are broken, and "jackpot" is recorded--a harmless
- *      matter except that a true match for a spuriously
- *      mated line may now be unnecessarily reported as a change.
- *
- *      Much of the complexity of the program comes simply
- *      from trying to minimize core utilization and
- *      maximize the range of doable problems by dynamically
- *      allocating what is needed and reusing what is not.
- *      The core requirements for problems larger than somewhat
- *      are (in words) 2*length(file0) + length(file1) +
- *      3*(number of k-candidates installed),  typically about
- *      6n words for files of length n.
+ * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
  */
-
-static int diffreg(char *ofile1, char *ofile2, int flags)
+static int readhash(FILE *f)
 {
-        char *file1 = ofile1;
-        char *file2 = ofile2;
-        FILE *f1 = NULL;
-        FILE *f2 = NULL;
-        int rval = D_SAME;
-        int i;
-
-        anychange = 0;
-        context_vec_ptr = context_vec_start - 1;
-                
-        if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
-                return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
-        if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
-                goto closem;
-
-        if (flags & D_EMPTY1)
-                f1 = bb_xfopen(_PATH_DEVNULL, "r");
-        else {
-                if (strcmp(file1, "-") == 0)
-                        f1 = stdin;
-                else
-                        f1 = bb_xfopen(file1, "r");
-        }
+        int i, t, space;
+        int sum;
 
-        if (flags & D_EMPTY2)
-                f2 = bb_xfopen(_PATH_DEVNULL, "r");
-        else {
-                if (strcmp(file2, "-") == 0)
-                        f2 = stdin;
+        sum = 1;
+        space = 0;
+        if (!(cmd_flags & FLAG_b) && !(cmd_flags & FLAG_w)) {
+                if (FLAG_i)
+                        for (i = 0; (t = getc(f)) != '\n'; i++) {
+                                if (t == EOF) {
+                                        if (i == 0)
+                                                return (0);
+                                        break;
+                                }
+                                sum = sum * 127 + t;
+                        }
                 else
-                        f2 = bb_xfopen(file2, "r");
-        }
-       
-       switch (files_differ(f1, f2, flags)) {
-        case 0:
-                goto closem;
-        case 1:
-                break;
-        default:
-                /* error */
-                status |= 2;
-                goto closem;
-        }
-
-        if (!asciifile(f1) || !asciifile(f2)) {
-                rval = D_BINARY;
-                status |= 1;
-                goto closem;
+                        for (i = 0; (t = getc(f)) != '\n'; i++) {
+                                if (t == EOF) {
+                                        if (i == 0)
+                                                return (0);
+                                        break;
+                                }
+                                sum = sum * 127 + t;
+                        }
+        } else {
+                for (i = 0;;) {
+                        switch (t = getc(f)) {
+                        case '\t':
+                        case '\r':
+                        case '\v':
+                        case '\f':
+                        case ' ':
+                                space++;
+                                continue;
+                        default:
+                                if (space && !(cmd_flags & FLAG_w)) {
+                                        i++;
+                                        space = 0;
+                                }
+                                sum = sum * 127 + t;
+                                i++;
+                                continue;
+                        case EOF:
+                                if (i == 0)
+                                        return (0);
+                                /* FALLTHROUGH */
+                        case '\n':
+                                break;
+                        }
+                        break;
+                }
         }
+        /*
+         * There is a remote possibility that we end up with a zero sum.
+         * Zero is used as an EOF marker, so return 1 instead.
+         */
+        return (sum == 0 ? 1 : sum);
+}
 
-        prepare(0, f1, stb1.st_size);
-        prepare(1, f2, stb2.st_size);
-        prune();
-        sort(sfile[0], slen[0]);
-        sort(sfile[1], slen[1]);
-
-        member = (int *)file[1];
-        equiv(sfile[0], slen[0], sfile[1], slen[1], member);
-        member = xrealloc(member, (slen[1] + 2) * sizeof(int));
-
-        class = (int *)file[0];
-        unsort(sfile[0], slen[0], class);
-        class = xrealloc(class, (slen[0] + 2) * sizeof(int));
-
-        klist = xmalloc((slen[0] + 2) * sizeof(int));
-        clen = 0;
-        clistlen = 100;
-        clist = xmalloc(clistlen * sizeof(struct cand));
-        i = stone(class, slen[0], member, klist);
-        free(member);
-        free(class);
-
-        J = xrealloc(J, (len[0] + 2) * sizeof(int));
-        unravel(klist[i]);
-        free(clist);
-        free(klist);
-
-        ixold = xrealloc(ixold, (len[0] + 2) * sizeof(long));
-        ixnew = xrealloc(ixnew, (len[1] + 2) * sizeof(long));
-        check(file1, f1, file2, f2);
-        output(file1, f1, file2, f2);
 
-closem:
-        if (anychange) {
-               status |= 1;
-                if (rval == D_SAME)
-                        rval = D_DIFFER;
-        }
-        if (f1 != NULL)
-                fclose(f1);
-        if (f2 != NULL)
-                fclose(f2);
-        if (file1 != ofile1)
-                free(file1);
-        if (file2 != ofile2)
-                free(file2);
-       return (rval);
-}
 
 /*
  * Check to see if the given files differ.
  * Returns 0 if they are the same, 1 if different, and -1 on error.
  */
-static int
-files_differ(FILE *f1, FILE *f2, int flags)
+static int files_differ(FILE *f1, FILE *f2, int flags)
 {
         char buf1[BUFSIZ], buf2[BUFSIZ];
         size_t i, j;
@@ -582,8 +290,7 @@ files_differ(FILE *f1, FILE *f2, int flags)
         }
 }
 
-static void
-prepare(int i, FILE *fd, off_t filesize)
+static void prepare(int i, FILE *fd, off_t filesize)
 {
         struct line *p;
         int j, h;
@@ -607,8 +314,7 @@ prepare(int i, FILE *fd, off_t filesize)
         file[i] = p;
 }
 
-static void
-prune(void)
+static void prune(void)
 {
         int i, j;
 
@@ -628,8 +334,7 @@ prune(void)
         }
 }
 
-static void
-equiv(struct line *a, int n, struct line *b, int m, int *c)
+static void equiv(struct line *a, int n, struct line *b, int m, int *c)
 {
         int i, j;
 
@@ -658,8 +363,8 @@ equiv(struct line *a, int n, struct line *b, int m, int *c)
 
 static int isqrt(int n) {
        int y, x = 1;
-        if (n == 0) return(0);
-       
+       if (n == 0) return(0);
+
        do {
                y = x;
                x = n / x;
@@ -670,8 +375,48 @@ static int isqrt(int n) {
        return (x);
 }
 
-static int
-stone(int *a, int n, int *b, int *c)
+
+static int newcand(int x, int y, int pred)
+{
+        struct cand *q;
+
+        if (clen == clistlen) {
+                clistlen = clistlen * 11 / 10;
+                clist = xrealloc(clist, clistlen * sizeof(struct cand));
+        }
+        q = clist + clen;
+        q->x = x;
+        q->y = y;
+        q->pred = pred;
+        return (clen++);
+}
+
+
+static int search(int *c, int k, int y)
+{
+        int i, j, l, t;
+
+        if (clist[c[k]].y < y)        /* quick look for typical case */
+                return (k + 1);
+        i = 0;
+        j = k + 1;
+        while (1) {
+                l = i + j;
+                if ((l >>= 1) <= i)
+                        break;
+                t = clist[c[l]].y;
+                if (t > y)
+                        j = l;
+                else if (t < y)
+                        i = l;
+                else
+                        return (l);
+        }
+        return (l + 1);
+}
+
+
+static int stone(int *a, int n, int *b, int *c)
 {
         int i, k, y, j, l;
         int oldc, tc, oldl;
@@ -715,67 +460,48 @@ stone(int *a, int n, int *b, int *c)
         return (k);
 }
 
-static int
-newcand(int x, int y, int pred)
+static void unravel(int p)
 {
         struct cand *q;
+        int i;
 
-        if (clen == clistlen) {
-                clistlen = clistlen * 11 / 10;
-                clist = xrealloc(clist, clistlen * sizeof(struct cand));
-        }
-        q = clist + clen;
-        q->x = x;
-        q->y = y;
-        q->pred = pred;
-        return (clen++);
+        for (i = 0; i <= len[0]; i++)
+                J[i] = i <= pref ? i :
+                    i > len[0] - suff ? i + len[1] - len[0] : 0;
+        for (q = clist + p; q->y != 0; q = clist + q->pred)
+                J[q->x + pref] = q->y + pref;
 }
 
-static int
-search(int *c, int k, int y)
+
+static void unsort(struct line *f, int l, int *b)
 {
-        int i, j, l, t;
+        int *a, i;
 
-        if (clist[c[k]].y < y)        /* quick look for typical case */
-                return (k + 1);
-        i = 0;
-        j = k + 1;
-        while (1) {
-                l = i + j;
-                if ((l >>= 1) <= i)
-                        break;
-                t = clist[c[l]].y;
-                if (t > y)
-                        j = l;
-                else if (t < y)
-                        i = l;
-                else
-                        return (l);
-        }
-        return (l + 1);
+        a = xmalloc((l + 1) * sizeof(int));
+        for (i = 1; i <= l; i++)
+                a[f[i].serial] = f[i].value;
+        for (i = 1; i <= l; i++)
+                b[i] = a[i];
+        free(a);
 }
 
-static void
-unravel(int p)
+static int skipline(FILE *f)
 {
-        struct cand *q;
-        int i;
+        int i, c;
 
-        for (i = 0; i <= len[0]; i++)
-                J[i] = i <= pref ? i :
-                    i > len[0] - suff ? i + len[1] - len[0] : 0;
-        for (q = clist + p; q->y != 0; q = clist + q->pred)
-                J[q->x + pref] = q->y + pref;
+        for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
+                continue;
+        return (i);
 }
 
+
 /*
  * Check does double duty:
  *  1.  ferret out any fortuitous correspondences due
  *      to confounding by hashing (which result in "jackpot")
  *  2.  collect random access indexes to the two files
  */
-static void
-check(char *file1, FILE *f1, char *file2, FILE *f2)
+static void check(char *file1, FILE *f1, char *file2, FILE *f2)
 {
         int i, j, jackpot, c, d;
         long ctold, ctnew;
@@ -868,8 +594,7 @@ check(char *file1, FILE *f1, char *file2, FILE *f2)
 }
 
 /* shellsort CACM #201 */
-static void
-sort(struct line *a, int n)
+static void sort(struct line *a, int n)
 {
         struct line *ai, *aim, w;
         int j, m = 0, k;
@@ -900,121 +625,20 @@ sort(struct line *a, int n)
         }
 }
 
-static void
-unsort(struct line *f, int l, int *b)
-{
-        int *a, i;
-
-        a = xmalloc((l + 1) * sizeof(int));
-        for (i = 1; i <= l; i++)
-                a[f[i].serial] = f[i].value;
-        for (i = 1; i <= l; i++)
-                b[i] = a[i];
-        free(a);
-}
 
-static int
-skipline(FILE *f)
+static void uni_range(int a, int b)
 {
-        int i, c;
-
-        for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
-                continue;
-        return (i);
+        if (a < b)
+                printf("%d,%d", a, b - a + 1);
+        else if (a == b)
+                printf("%d", b);
+        else
+                printf("%d,0", b);
 }
 
-static void
-output(char *file1, FILE *f1, char *file2, FILE *f2)
+static int fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
 {
-        int m, i0, i1, j0, j1;
-
-        rewind(f1);
-        rewind(f2);
-        m = len[0];
-        J[0] = 0;
-        J[m + 1] = len[1] + 1;
-        for (i0 = 1; i0 <= m; i0 = i1 + 1) {
-                        while (i0 <= m && J[i0] == J[i0 - 1] + 1)
-                                i0++;
-                        j0 = J[i0 - 1] + 1;
-                        i1 = i0 - 1;
-                        while (i1 < m && J[i1 + 1] == 0)
-                                i1++;
-                        j1 = J[i1 + 1] - 1;
-                        J[i1] = j1;
-                        change(file1, f1, file2, f2, i0, i1, j0, j1);
-        }
-        if (m == 0) {
-                change(file1, f1, file2, f2, 1, 0, 1, len[1]);
-       }
-       if (anychange != 0) {
-               dump_unified_vec(f1, f2);
-        }
-}
-
-static void uni_range(int a, int b)
-{
-        if (a < b)
-                printf("%d,%d", a, b - a + 1);
-        else if (a == b)
-                printf("%d", b);
-        else
-                printf("%d,0", b);
-}
-
-/*
- * Indicate that there is a difference between lines a and b of the from file
- * to get to lines c to d of the to file.  If a is greater then b then there
- * are no lines in the from file involved and this means that there were
- * lines appended (beginning at b).  If c is greater than d then there are
- * lines missing from the to file.
- */
-static void
-change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d)
-{
-       static size_t max_context = 64;
-
-        if (a > b && c > d)    return;
-       if (cmd_flags & FLAG_q) return;
-       
-        /*
-        * Allocate change records as needed.
-        */
-                if (context_vec_ptr == context_vec_end - 1) {
-                        ptrdiff_t offset = context_vec_ptr - context_vec_start;
-                        max_context <<= 1;
-                        context_vec_start = xrealloc(context_vec_start,
-                            max_context * sizeof(struct context_vec));
-                        context_vec_end = context_vec_start + max_context;
-                        context_vec_ptr = context_vec_start + offset;
-                }
-                if (anychange == 0) {
-                        /*
-                         * Print the context/unidiff header first time through.
-                         */
-                        print_header(file1, file2);
-                        anychange = 1;
-                } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
-                    c > context_vec_ptr->d + (2 * context) + 1) {
-                        /*
-                         * If this change is more than 'context' lines from the
-                         * previous change, dump the record and reset it.
-                         */
-                       dump_unified_vec(f1, f2);
-                }
-                context_vec_ptr++;
-                context_vec_ptr->a = a;
-                context_vec_ptr->b = b;
-                context_vec_ptr->c = c;
-                context_vec_ptr->d = d;
-                return;
-        
-}
-
-static int
-fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
-{
-        int i, j, c, lastc, col, nc;
+        int i, j, c, lastc, col, nc;
 
         if (a > b)
                 return (0);
@@ -1045,81 +669,15 @@ fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
         return (0);
 }
 
-/*
- * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
- */
-static int
-readhash(FILE *f)
-{
-        int i, t, space;
-        int sum;
-
-        sum = 1;
-        space = 0;
-        if (!(cmd_flags & FLAG_b) && !(cmd_flags & FLAG_w)) {
-                if (FLAG_i)
-                        for (i = 0; (t = getc(f)) != '\n'; i++) {
-                                if (t == EOF) {
-                                        if (i == 0)
-                                                return (0);
-                                        break;
-                                }
-                                sum = sum * 127 + t;
-                        }
-                else
-                        for (i = 0; (t = getc(f)) != '\n'; i++) {
-                                if (t == EOF) {
-                                        if (i == 0)
-                                                return (0);
-                                        break;
-                                }
-                                sum = sum * 127 + t;
-                        }
-        } else {
-                for (i = 0;;) {
-                        switch (t = getc(f)) {
-                        case '\t':
-                        case '\r':
-                        case '\v':
-                        case '\f':
-                        case ' ':
-                                space++;
-                                continue;
-                        default:
-                                if (space && !(cmd_flags & FLAG_w)) {
-                                        i++;
-                                        space = 0;
-                                }
-                                sum = sum * 127 + t;
-                                i++;
-                                continue;
-                        case EOF:
-                                if (i == 0)
-                                        return (0);
-                                /* FALLTHROUGH */
-                        case '\n':
-                                break;
-                        }
-                        break;
-                }
-        }
-        /*
-         * There is a remote possibility that we end up with a zero sum.
-         * Zero is used as an EOF marker, so return 1 instead.
-         */
-        return (sum == 0 ? 1 : sum);
-}
-
-static int
-asciifile(FILE *f)
+static int asciifile(FILE *f)
 {
 
-        if ((cmd_flags & FLAG_a) || f == NULL)
-                return (1);
+       if ((cmd_flags & FLAG_a) || f == NULL)
+               return (1);
 #ifdef CONFIG_FEATURE_DIFF_BINARY
         unsigned char buf[BUFSIZ];
         int i, cnt;
-       
+
        rewind(f);
         cnt = fread(buf, 1, sizeof(buf), f);
         for (i = 0; i < cnt; i++)
@@ -1130,8 +688,7 @@ asciifile(FILE *f)
 }
 
 /* dump accumulated "unified" diff changes */
-static void
-dump_unified_vec(FILE *f1, FILE *f2)
+static void dump_unified_vec(FILE *f1, FILE *f2)
 {
        struct context_vec *cvp = context_vec_start;
         int lowa, upb, lowc, upd;
@@ -1197,8 +754,8 @@ dump_unified_vec(FILE *f1, FILE *f2)
         context_vec_ptr = context_vec_start - 1;
 }
 
-static void
-print_header(const char *file1, const char *file2)
+
+static void print_header(const char *file1, const char *file2)
 {
         if (label[0] != NULL)
                 printf("%s %s\n", "---",
@@ -1214,6 +771,419 @@ print_header(const char *file1, const char *file2)
                     file2, ctime(&stb2.st_mtime));
 }
 
+
+
+/*
+ * Indicate that there is a difference between lines a and b of the from file
+ * to get to lines c to d of the to file.  If a is greater then b then there
+ * are no lines in the from file involved and this means that there were
+ * lines appended (beginning at b).  If c is greater than d then there are
+ * lines missing from the to file.
+ */
+static void change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d)
+{
+       static size_t max_context = 64;
+
+        if (a > b && c > d)    return;
+       if (cmd_flags & FLAG_q) return;
+       
+        /*
+        * Allocate change records as needed.
+        */
+                if (context_vec_ptr == context_vec_end - 1) {
+                        ptrdiff_t offset = context_vec_ptr - context_vec_start;
+                        max_context <<= 1;
+                        context_vec_start = xrealloc(context_vec_start,
+                            max_context * sizeof(struct context_vec));
+                        context_vec_end = context_vec_start + max_context;
+                        context_vec_ptr = context_vec_start + offset;
+                }
+                if (anychange == 0) {
+                        /*
+                         * Print the context/unidiff header first time through.
+                         */
+                        print_header(file1, file2);
+                        anychange = 1;
+                } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
+                    c > context_vec_ptr->d + (2 * context) + 1) {
+                        /*
+                         * If this change is more than 'context' lines from the
+                         * previous change, dump the record and reset it.
+                         */
+                       dump_unified_vec(f1, f2);
+                }
+                context_vec_ptr++;
+                context_vec_ptr->a = a;
+                context_vec_ptr->b = b;
+                context_vec_ptr->c = c;
+                context_vec_ptr->d = d;
+                return;
+        
+}
+
+
+static void output(char *file1, FILE *f1, char *file2, FILE *f2)
+{
+        int m, i0, i1, j0, j1;
+
+        rewind(f1);
+        rewind(f2);
+        m = len[0];
+        J[0] = 0;
+        J[m + 1] = len[1] + 1;
+        for (i0 = 1; i0 <= m; i0 = i1 + 1) {
+                        while (i0 <= m && J[i0] == J[i0 - 1] + 1)
+                                i0++;
+                        j0 = J[i0 - 1] + 1;
+                        i1 = i0 - 1;
+                        while (i1 < m && J[i1 + 1] == 0)
+                                i1++;
+                        j1 = J[i1 + 1] - 1;
+                        J[i1] = j1;
+                        change(file1, f1, file2, f2, i0, i1, j0, j1);
+        }
+        if (m == 0) {
+                change(file1, f1, file2, f2, 1, 0, 1, len[1]);
+       }
+       if (anychange != 0) {
+               dump_unified_vec(f1, f2);
+        }
+}
+
+/*
+ *      The following code uses an algorithm due to Harold Stone, 
+ *      which finds a pair of longest identical subsequences in 
+ *      the two files.
+ *
+ *      The major goal is to generate the match vector J.
+ *      J[i] is the index of the line in file1 corresponding
+ *      to line i file0. J[i] = 0 if there is no
+ *      such line in file1.
+ *
+ *      Lines are hashed so as to work in core. All potential
+ *      matches are located by sorting the lines of each file
+ *      on the hash (called ``value''). In particular, this
+ *      collects the equivalence classes in file1 together.
+ *      Subroutine equiv replaces the value of each line in
+ *      file0 by the index of the first element of its
+ *      matching equivalence in (the reordered) file1.
+ *      To save space equiv squeezes file1 into a single
+ *      array member in which the equivalence classes
+ *      are simply concatenated, except that their first
+ *      members are flagged by changing sign.
+ *
+ *      Next the indices that point into member are unsorted into
+ *      array class according to the original order of file0.
+ *
+ *      The cleverness lies in routine stone. This marches
+ *      through the lines of file0, developing a vector klist
+ *      of "k-candidates". At step i a k-candidate is a matched
+ *      pair of lines x,y (x in file0 y in file1) such that
+ *      there is a common subsequence of length k
+ *      between the first i lines of file0 and the first y
+ *      lines of file1, but there is no such subsequence for
+ *      any smaller y. x is the earliest possible mate to y
+ *      that occurs in such a subsequence.
+ *
+ *      Whenever any of the members of the equivalence class of
+ *      lines in file1 matable to a line in file0 has serial number
+ *      less than the y of some k-candidate, that k-candidate
+ *      with the smallest such y is replaced. The new
+ *      k-candidate is chained (via pred) to the current
+ *      k-1 candidate so that the actual subsequence can
+ *      be recovered. When a member has serial number greater
+ *      that the y of all k-candidates, the klist is extended.
+ *      At the end, the longest subsequence is pulled out
+ *      and placed in the array J by unravel
+ *
+ *      With J in hand, the matches there recorded are
+ *      checked against reality to assure that no spurious
+ *      matches have crept in due to hashing. If they have,
+ *      they are broken, and "jackpot" is recorded--a harmless
+ *      matter except that a true match for a spuriously
+ *      mated line may now be unnecessarily reported as a change.
+ *
+ *      Much of the complexity of the program comes simply
+ *      from trying to minimize core utilization and
+ *      maximize the range of doable problems by dynamically
+ *      allocating what is needed and reusing what is not.
+ *      The core requirements for problems larger than somewhat
+ *      are (in words) 2*length(file0) + length(file1) +
+ *      3*(number of k-candidates installed),  typically about
+ *      6n words for files of length n.
+ */
+
+static int diffreg(char *ofile1, char *ofile2, int flags)
+{
+        char *file1 = ofile1;
+        char *file2 = ofile2;
+        FILE *f1 = NULL;
+        FILE *f2 = NULL;
+        int rval = D_SAME;
+        int i;
+
+        anychange = 0;
+        context_vec_ptr = context_vec_start - 1;
+                
+        if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
+                return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
+        if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
+                goto closem;
+
+        if (flags & D_EMPTY1)
+                f1 = bb_xfopen(_PATH_DEVNULL, "r");
+        else {
+                if (strcmp(file1, "-") == 0)
+                        f1 = stdin;
+                else
+                        f1 = bb_xfopen(file1, "r");
+        }
+
+        if (flags & D_EMPTY2)
+                f2 = bb_xfopen(_PATH_DEVNULL, "r");
+        else {
+                if (strcmp(file2, "-") == 0)
+                        f2 = stdin;
+                else
+                        f2 = bb_xfopen(file2, "r");
+        }
+       
+       switch (files_differ(f1, f2, flags)) {
+        case 0:
+                goto closem;
+        case 1:
+                break;
+        default:
+                /* error */
+                status |= 2;
+                goto closem;
+        }
+
+        if (!asciifile(f1) || !asciifile(f2)) {
+                rval = D_BINARY;
+                status |= 1;
+                goto closem;
+        }
+
+        prepare(0, f1, stb1.st_size);
+        prepare(1, f2, stb2.st_size);
+        prune();
+        sort(sfile[0], slen[0]);
+        sort(sfile[1], slen[1]);
+
+        member = (int *)file[1];
+        equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+        member = xrealloc(member, (slen[1] + 2) * sizeof(int));
+
+        class = (int *)file[0];
+        unsort(sfile[0], slen[0], class);
+        class = xrealloc(class, (slen[0] + 2) * sizeof(int));
+
+        klist = xmalloc((slen[0] + 2) * sizeof(int));
+        clen = 0;
+        clistlen = 100;
+        clist = xmalloc(clistlen * sizeof(struct cand));
+        i = stone(class, slen[0], member, klist);
+        free(member);
+        free(class);
+
+        J = xrealloc(J, (len[0] + 2) * sizeof(int));
+        unravel(klist[i]);
+        free(clist);
+        free(klist);
+
+        ixold = xrealloc(ixold, (len[0] + 2) * sizeof(long));
+        ixnew = xrealloc(ixnew, (len[1] + 2) * sizeof(long));
+        check(file1, f1, file2, f2);
+        output(file1, f1, file2, f2);
+
+closem:
+        if (anychange) {
+               status |= 1;
+                if (rval == D_SAME)
+                        rval = D_DIFFER;
+        }
+        if (f1 != NULL)
+                fclose(f1);
+        if (f2 != NULL)
+                fclose(f2);
+        if (file1 != ofile1)
+                free(file1);
+        if (file2 != ofile2)
+                free(file2);
+       return (rval);
+}
+
+#if ENABLE_FEATURE_DIFF_DIR
+static void do_diff (char *dir1, char *path1, char *dir2, char *path2) {
+       
+       int flags = D_HEADER;
+       int val;
+       
+       char *fullpath1 = bb_xasprintf("%s/%s", dir1, path1);
+       char *fullpath2 = bb_xasprintf("%s/%s", dir2, path2);
+
+       if (stat(fullpath1, &stb1) != 0) {
+               flags |= D_EMPTY1;
+               memset(&stb1, 0, sizeof(stb1));
+               fullpath1 = bb_xasprintf("%s/%s", dir1, path2);
+       }
+       if (stat(fullpath2, &stb2) != 0) {
+               flags |= D_EMPTY2;
+               memset(&stb2, 0, sizeof(stb2));
+               stb2.st_mode = stb1.st_mode;
+               fullpath2 = bb_xasprintf("%s/%s", dir2, path1);
+       }
+
+       if (stb1.st_mode == 0)
+               stb1.st_mode = stb2.st_mode;
+       
+       if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
+               printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2);
+               return;
+       }
+
+       if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
+               val = D_SKIPPED1;
+       else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
+               val = D_SKIPPED2;
+       else
+               val = diffreg(fullpath1, fullpath2, flags);
+       
+       print_status(val, fullpath1, fullpath2, NULL);
+}
+#endif
+
+#ifdef CONFIG_FEATURE_DIFF_DIR
+static int dir_strcmp(const void *p1, const void *p2) {
+       return strcmp(*(char * const *)p1, *(char * const *)p2);
+}
+
+/* This function adds a filename to dl, the directory listing. */
+
+static int add_to_dirlist (const char *filename, struct stat *statbuf, void *userdata) {
+       dl_count++;
+       dl = xrealloc(dl, dl_count * sizeof(char *));
+       dl[dl_count - 1] = bb_xstrdup(filename);
+       if (cmd_flags & FLAG_r) {
+               int *pp = (int *) userdata;
+               int path_len = *pp + 1;
+               dl[dl_count - 1] = &(dl[dl_count - 1])[path_len];
+       }
+       return TRUE;
+}
+
+/* This returns a sorted directory listing. */
+static char **get_dir(char *path) {
+
+       int i;
+
+       /* Reset dl_count - there's no need to free dl as bb_xrealloc does
+        * the job nicely. */   
+       dl_count = 0;
+
+       /* If -r has been set, then the recursive_action function will be
+        * used. Unfortunately, this outputs the root directory along with
+        * the recursed paths, so use void *userdata to specify the string
+        * length of the root directory. It can then be removed in
+        * add_to_dirlist. */
+
+       int path_len = strlen(path);
+       void *userdata = &path_len;
+       
+       /* Now fill dl with a listing. */
+       if (cmd_flags & FLAG_r)
+               recursive_action(path, TRUE, TRUE, FALSE, add_to_dirlist, NULL, userdata);
+       else {
+               DIR *dp;
+               struct dirent *ep;
+               if ((dp = opendir(path)) == NULL)
+                       bb_error_msg("Error reading directory");
+               while ((ep = readdir(dp))) {
+                       if ((!strcmp(ep->d_name, "..")) || (!strcmp(ep->d_name, ".")))
+                               continue;
+                       add_to_dirlist(ep->d_name, NULL, NULL);
+               }
+               closedir(dp);
+       }
+
+       /* Sort dl alphabetically. */
+       qsort(dl, dl_count, sizeof(char *), dir_strcmp);
+
+       /* Copy dl so that we can return it. */
+       char **retval = xmalloc(dl_count * sizeof(char *));
+       for (i = 0; i < dl_count; i++)
+               retval[i] = bb_xstrdup(dl[i]);
+
+       return retval;
+}
+
+static void diffdir (char *p1, char *p2) {
+       
+       char **dirlist1, **dirlist2;
+       char *dp1, *dp2;
+       int dirlist1_count, dirlist2_count;
+       int pos;
+
+       /* Check for trailing slashes. */
+       
+       if (p1[strlen(p1) - 1] == '/')
+               p1[strlen(p1) - 1] = '\0';
+       if (p2[strlen(p2) - 1] == '/')
+               p2[strlen(p2) - 1] = '\0';
+       
+       /* Get directory listings for p1 and p2. */
+       
+       dirlist1 = get_dir(p1);
+       dirlist1_count = dl_count;
+       dirlist1[dirlist1_count] = NULL;
+       dirlist2 = get_dir(p2);
+       dirlist2_count = dl_count;
+       dirlist2[dirlist2_count] = NULL;
+       
+       /* If -S was set, find the starting point. */
+       if (start) {
+               while (*dirlist1 != NULL && strcmp(*dirlist1, start) < 0)
+                       dirlist1++;
+               while (*dirlist2 != NULL && strcmp(*dirlist2, start) < 0)
+                       dirlist2++;
+               if ((*dirlist1 == NULL) || (*dirlist2 == NULL))
+                       bb_error_msg("Invalid argument to -S");
+       }
+       
+       /* Now that both dirlist1 and dirlist2 contain sorted directory
+        * listings, we can start to go through dirlist1. If both listings
+        * contain the same file, then do a normal diff. Otherwise, behaviour
+        * is determined by whether the -N flag is set. */      
+       while (*dirlist1 != NULL || *dirlist2 != NULL) {
+               dp1 = *dirlist1;
+               dp2 = *dirlist2;
+               pos = dp1 == NULL ? 1 : dp2 == NULL ? -1 : strcmp(dp1, dp2);
+               if (pos == 0) {
+                       do_diff(p1, dp1, p2, dp2);
+                       dirlist1++;
+                       dirlist2++;
+               }
+               else if (pos < 0) {
+                       if (cmd_flags & FLAG_N)
+                               do_diff(p1, dp1, p2, NULL);
+                       else
+                               print_only(p1, strlen(p1) + 1, dp1);
+                       dirlist1++;
+               }
+               else {
+                       if (cmd_flags & FLAG_N)
+                               do_diff(p1, NULL, p2, dp2);
+                       else
+                               print_only(p2, strlen(p2) + 1, dp2);
+                       dirlist2++;
+               }
+       }
+}
+#endif
+
+
+
 extern int diff_main(int argc, char **argv) {
         char *ep;
         int gotstdin = 0;