traceroute: fix help text to not show -6 when traceroute6 is off
[oweals/busybox.git] / editors / diff.c
index 08729177cf35e31baf5e7ac8d3368c6bd8f96b88..745ef0a334be90b25e12a68b0f56b1d783f50ce7 100644 (file)
@@ -14,6 +14,9 @@
 
 #include "libbb.h"
 
+#define dbg_error_msg(...) ((void)0)
+//#define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
+
 // #define FSIZE_MAX 32768
 
 /* NOINLINEs added to prevent gcc from merging too much into diffreg()
@@ -102,9 +105,9 @@ struct globals {
        bool anychange;
        smallint exit_status;
        int opt_U_context;
-       size_t max_context;     /* size of context_vec_start */
-       USE_FEATURE_DIFF_DIR(int dl_count;)
-       USE_FEATURE_DIFF_DIR(char **dl;)
+       int context_idx;
+       IF_FEATURE_DIFF_DIR(int dl_count;)
+       IF_FEATURE_DIFF_DIR(char **dl;)
        char *opt_S_start;
        const char *label1;
        const char *label2;
@@ -119,9 +122,7 @@ struct globals {
        long *ixold;            /* will be overlaid on klist */
        struct line *nfile[2];
        struct line *sfile[2];  /* shortened by pruning common prefix/suffix */
-       struct context_vec *context_vec_start;
-       struct context_vec *context_vec_end;
-       struct context_vec *context_vec_ptr;
+       struct context_vec *context_vector;
        char *tempname1, *tempname2;
        struct stat stb1, stb2;
 };
@@ -129,7 +130,7 @@ struct globals {
 #define anychange          (G.anychange         )
 #define exit_status        (G.exit_status       )
 #define opt_U_context      (G.opt_U_context     )
-#define max_context        (G.max_context       )
+#define context_idx        (G.context_idx       )
 #define dl_count           (G.dl_count          )
 #define dl                 (G.dl                )
 #define opt_S_start        (G.opt_S_start       )
@@ -147,9 +148,7 @@ struct globals {
 #define ixold              (G.ixold             )
 #define nfile              (G.nfile             )
 #define sfile              (G.sfile             )
-#define context_vec_start  (G.context_vec_start )
-#define context_vec_end    (G.context_vec_end   )
-#define context_vec_ptr    (G.context_vec_ptr   )
+#define context_vector     (G.context_vector    )
 #define stb1               (G.stb1              )
 #define stb2               (G.stb2              )
 #define tempname1          (G.tempname1         )
@@ -157,18 +156,18 @@ struct globals {
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
        opt_U_context = 3; \
-       max_context = 64; \
+       context_vector = xrealloc_vector(context_vector, 6, 0); \
 } while (0)
 
 
-/*static void print_only(const char *path, size_t dirlen, const char *entry)*/
+#if ENABLE_FEATURE_DIFF_DIR
 static void print_only(const char *path, const char *entry)
 {
        printf("Only in %s: %s\n", path, entry);
 }
+#endif
 
 
-/*static void print_status(int val, char *path1, char *path2, char *entry)*/
 static void print_status(int val, char *_path1, char *_path2)
 {
        /*const char *const _entry = entry ? entry : "";*/
@@ -230,11 +229,10 @@ static ALWAYS_INLINE int fiddle_sum(int sum, int t)
 }
 static int readhash(FILE *fp)
 {
-       int i, t, space;
+       int i, t;
        int sum;
 
        sum = 1;
-       space = 0;
        i = 0;
        if (!(option_mask32 & (FLAG_b | FLAG_w))) {
                while ((t = getc(fp)) != '\n') {
@@ -247,8 +245,11 @@ static int readhash(FILE *fp)
                        i = 1;
                }
        } else {
+               int space = 0;
+
                while (1) {
-                       switch (t = getc(fp)) {
+                       t = getc(fp);
+                       switch (t) {
                        case '\t':
                        case '\r':
                        case '\v':
@@ -282,6 +283,9 @@ static int readhash(FILE *fp)
 }
 
 
+/* Our diff implementation is using seek.
+ * When we meet non-seekable file, we must make a temp copy.
+ */
 static char *make_temp(FILE *f, struct stat *sb)
 {
        char *name;
@@ -473,12 +477,13 @@ static int stone(int *a, int n, int *b, int *c)
 {
        int i, k, y, j, l;
        int oldc, tc, oldl;
-       unsigned int numtries;
+       unsigned numtries;
+       int isq = isqrt(n);
 #if ENABLE_FEATURE_DIFF_MINIMAL
-       const unsigned int bound =
-               (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n));
+       const unsigned bound =
+               (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isq);
 #else
-       const unsigned int bound = MAX(256, isqrt(n));
+       const unsigned bound = MAX(256, isq);
 #endif
 
        k = 0;
@@ -744,7 +749,7 @@ static int asciifile(FILE *f)
        rewind(f);
        cnt = fread(g_read_buf, 1, COMMON_BUFSIZE, f);
        for (i = 0; i < cnt; i++) {
-               if (!isprint(g_read_buf[i])
+               if (!isprint_asciionly(g_read_buf[i])
                 && !isspace(g_read_buf[i])
                ) {
                        return 0;
@@ -760,19 +765,21 @@ static int asciifile(FILE *f)
 /* dump accumulated "unified" diff changes */
 static void dump_unified_vec(FILE *f1, FILE *f2)
 {
-       struct context_vec *cvp = context_vec_start;
+       struct context_vec *cvp = context_vector;
        int lowa, upb, lowc, upd;
        int a, b, c, d;
        char ch;
 
-       if (context_vec_start > context_vec_ptr)
+       if (context_idx < 0)
                return;
 
+       dbg_error_msg("dumping %d context_vecs", context_idx+1);
+
        b = d = 0;                      /* gcc */
        lowa = MAX(1, cvp->a - opt_U_context);
-       upb = MIN(nlen[0], context_vec_ptr->b + opt_U_context);
+       upb = MIN(nlen[0], context_vector[context_idx].b + opt_U_context);
        lowc = MAX(1, cvp->c - opt_U_context);
-       upd = MIN(nlen[1], context_vec_ptr->d + opt_U_context);
+       upd = MIN(nlen[1], context_vector[context_idx].d + opt_U_context);
 
        printf("@@ -");
        uni_range(lowa, upb);
@@ -784,7 +791,7 @@ static void dump_unified_vec(FILE *f1, FILE *f2)
         * Output changes in "unified" diff format--the old and new lines
         * are printed together.
         */
-       for (; cvp <= context_vec_ptr; cvp++) {
+       for (; cvp <= &context_vector[context_idx]; cvp++) {
                a = cvp->a;
                b = cvp->b;
                c = cvp->c;
@@ -831,7 +838,7 @@ static void dump_unified_vec(FILE *f1, FILE *f2)
        }
        fetch(ixnew, d + 1, upd, f2, ' ');
 
-       context_vec_ptr = context_vec_start - 1;
+       context_idx = -1;
 }
 
 
@@ -855,33 +862,25 @@ static void print_header(const char *file1, const char *file2)
  * 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,
+static void change(const char *file1, FILE *f1, const char *file2, FILE *f2,
                        int a, int b, int c, int d)
 {
        if ((a > b && c > d) || (option_mask32 & FLAG_q)) {
+//compat BUG: "diff -ub F1 F2" will output nothing, but will exit 1
+//if F1 and F2 differ only in whitespace. "standard" diff exits 0.
+//This is the place where this erroneous exitcode is set:
+               dbg_error_msg("%d: abcd:%d,%d,%d,%d, anychange=1", __LINE__, a,b,c,d);
                anychange = 1;
                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);
-       } else if (a > context_vec_ptr->b + (2 * opt_U_context) + 1
-               && c > context_vec_ptr->d + (2 * opt_U_context) + 1
+       } else if (a > context_vector[context_idx].b + (2 * opt_U_context) + 1
+               && c > context_vector[context_idx].d + (2 * opt_U_context) + 1
        ) {
                /*
                 * If this change is more than 'context' lines from the
@@ -890,16 +889,18 @@ static void change(char *file1, FILE *f1, char *file2, FILE *f2,
 // dump_unified_vec() seeks!
                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;
+       context_idx++;
+       context_vector = xrealloc_vector(context_vector, 6, context_idx);
+       context_vector[context_idx].a = a;
+       context_vector[context_idx].b = b;
+       context_vector[context_idx].c = c;
+       context_vector[context_idx].d = d;
+       dbg_error_msg("new context_vec[%d]:%d,%d,%d,%d", context_idx, a,b,c,d);
        anychange = 1;
 }
 
 
-static void output(char *file1, FILE *f1, char *file2, FILE *f2)
+static void output(const char *file1, FILE *f1, const char *file2, FILE *f2)
 {
        /* Note that j0 and j1 can't be used as they are defined in math.h.
         * This also allows the rather amusing variable 'j00'... */
@@ -996,7 +997,7 @@ static void output(char *file1, FILE *f1, char *file2, FILE *f2)
  */
 /* NB: files can be not REGular. The only sure thing that they
  * are not both DIRectories. */
-static unsigned diffreg(char *file1, char *file2, int flags)
+static unsigned diffreg(const char *file1, const char *file2, int flags)
 {
        int *member;     /* will be overlaid on nfile[1] */
        int *class;      /* will be overlaid on nfile[0] */
@@ -1007,7 +1008,7 @@ static unsigned diffreg(char *file1, char *file2, int flags)
        int i;
 
        anychange = 0;
-       context_vec_ptr = context_vec_start - 1;
+       context_idx = -1;
        tempname1 = tempname2 = NULL;
 
        /* Is any of them a directory? Then it's simple */
@@ -1018,13 +1019,12 @@ static unsigned diffreg(char *file1, char *file2, int flags)
        rval = D_SAME;
 
        if (flags & D_EMPTY1)
-               f1 = xfopen(bb_dev_null, "r");
-       else
-               f1 = xfopen_stdin(file1);
+               /* can't be stdin, but xfopen_stdin() is smaller code */
+               file1 = bb_dev_null;
+       f1 = xfopen_stdin(file1);
        if (flags & D_EMPTY2)
-               f2 = xfopen(bb_dev_null, "r");
-       else
-               f2 = xfopen_stdin(file2);
+               file2 = bb_dev_null;
+       f2 = xfopen_stdin(file2);
 
        /* NB: if D_EMPTY1/2 is set, other file is always a regular file,
         * not pipe/fifo/chardev/etc - D_EMPTY is used by "diff -r" only,
@@ -1055,6 +1055,7 @@ static unsigned diffreg(char *file1, char *file2, int flags)
 
        member = (int *) nfile[1];
        equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+//TODO: xrealloc_vector?
        member = xrealloc(member, (slen[1] + 2) * sizeof(int));
 
        class = (int *) nfile[0];
@@ -1084,6 +1085,7 @@ static unsigned diffreg(char *file1, char *file2, int flags)
  closem:
        if (anychange) {
                exit_status |= 1;
+               dbg_error_msg("exit_status|=1 = %d", exit_status);
                if (rval == D_SAME)
                        rval = D_DIFFER;
        }
@@ -1159,13 +1161,12 @@ static void do_diff(char *dir1, char *path1, char *dir2, char *path2)
 
 #if ENABLE_FEATURE_DIFF_DIR
 /* This function adds a filename to dl, the directory listing. */
-static int add_to_dirlist(const char *filename,
-               struct stat ATTRIBUTE_UNUSED *sb,
+static int FAST_FUNC add_to_dirlist(const char *filename,
+               struct stat *sb UNUSED_PARAM,
                void *userdata,
-               int depth ATTRIBUTE_UNUSED)
+               int depth UNUSED_PARAM)
 {
-       /* +2: with space for eventual trailing NULL */
-       dl = xrealloc(dl, (dl_count+2) * sizeof(dl[0]));
+       dl = xrealloc_vector(dl, 5, dl_count);
        dl[dl_count] = xstrdup(filename + (int)(ptrdiff_t)userdata);
        dl_count++;
        return TRUE;
@@ -1178,15 +1179,15 @@ static char **get_recursive_dirlist(char *path)
        dl_count = 0;
        dl = xzalloc(sizeof(dl[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 - '(void*)(strlen(path)+)'.
-        * add_to_dirlist then removes root dir prefix. */
+       /* We need to trim root directory prefix.
+        * Using void *userdata to specify its length,
+        * add_to_dirlist will remove it. */
        if (option_mask32 & FLAG_r) {
                recursive_action(path, ACTION_RECURSE|ACTION_FOLLOWLINKS,
-                                       add_to_dirlist, NULL,
-                                       (void*)(strlen(path)+1), 0);
+                                       add_to_dirlist, /* file_action */
+                                       NULL, /* dir_action */
+                                       (void*)(ptrdiff_t)(strlen(path) + 1),
+                                       0);
        } else {
                DIR *dp;
                struct dirent *ep;
@@ -1267,7 +1268,7 @@ static void diffdir(char *p1, char *p2)
 
 
 int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int diff_main(int argc ATTRIBUTE_UNUSED, char **argv)
+int diff_main(int argc UNUSED_PARAM, char **argv)
 {
        int gotstdin = 0;
        char *f1, *f2;
@@ -1285,14 +1286,9 @@ int diff_main(int argc ATTRIBUTE_UNUSED, char **argv)
        while (L_arg) {
                if (label1 && label2)
                        bb_show_usage();
-               if (!label1)
-                       label1 = L_arg->data;
-               else { /* then label2 is NULL */
+               if (label1) /* then label2 is NULL */
                        label2 = label1;
-                       label1 = L_arg->data;
-               }
-               /* we leak L_arg here... */
-               L_arg = L_arg->link;
+               label1 = llist_pop(&L_arg);
        }
 
        /*
@@ -1301,6 +1297,8 @@ int diff_main(int argc ATTRIBUTE_UNUSED, char **argv)
         */
        f1 = argv[0];
        f2 = argv[1];
+       /* Compat: "diff file name_which_doesnt_exist" exits with 2 */
+       xfunc_error_retval = 2;
        if (LONE_DASH(f1)) {
                fstat(STDIN_FILENO, &stb1);
                gotstdin++;
@@ -1311,6 +1309,7 @@ int diff_main(int argc ATTRIBUTE_UNUSED, char **argv)
                gotstdin++;
        } else
                xstat(f2, &stb2);
+       xfunc_error_retval = 1;
 
        if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
                bb_error_msg_and_die("can't compare stdin to a directory");
@@ -1328,12 +1327,12 @@ int diff_main(int argc ATTRIBUTE_UNUSED, char **argv)
                /* NB: "diff dir      dir2/dir3/file" must become
                 *     "diff dir/file dir2/dir3/file" */
                char *slash = strrchr(f2, '/');
-               f1 = concat_path_file(f1, slash ? slash+1 : f2);
+               f1 = concat_path_file(f1, slash ? slash + 1 : f2);
                xstat(f1, &stb1);
        }
        if (S_ISDIR(stb2.st_mode)) {
                char *slash = strrchr(f1, '/');
-               f2 = concat_path_file(f2, slash ? slash+1 : f1);
+               f2 = concat_path_file(f2, slash ? slash + 1 : f1);
                xstat(f2, &stb2);
        }