--- diff.c | 172 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 92 insertions(+), 80 deletions(-) diff --git a/diff.c b/diff.c index 3c99ae8..558f834 100644 --- a/diff.c +++ b/diff.c _AT_@ -32,7 +32,6 @@ #define eprintf(...) enprintf(EXIT_FAILURE, __VA_ARGS__) #define eperror(...) (perror(__VA_ARGS__), exit(EXIT_FAILURE)) -#define CLASSIFY(f) (!(f) ? "directory" : (f)->is_empty ? "regular empty file" : "regular file") #define BOLD(...) use_colour ? "\033[1m" : "", __VA_ARGS__, use_colour ? "\033[m" : "" struct file_data { _AT_@ -99,7 +98,8 @@ load_lines(const char *pathname) eperror(pathname); } - fstat(fd, &attr); + if (fstat(fd, &attr)) + eperror(pathname); if (S_ISDIR(attr.st_mode)) return 0; _AT_@ -126,6 +126,8 @@ load_lines(const char *pathname) } bin = (strchr(p, '\0') != buffer + ptr); + /* This is a bit ugly, if not done this way, it would require unnecessarily many + * malloc:s to create rc and unnecessarily many free:s to destroy it. */ rc = erealloc(buffer, sizeof(*rc) + (n + 1) * sizeof(char *) + (ptr + 1 + sizeof(NO_LF_MARK))); buffer = ((char *)rc) + sizeof(*rc) + (n + 1) * sizeof(char *); memmove(buffer, rc, ptr); _AT_@ -156,6 +158,32 @@ load_lines(const char *pathname) } static char * +join_paths(const char *a, const char *b) +{ + char *rc = emalloc(strlen(a) + strlen(b) + sizeof("/")); + sprintf(rc, "%s/%s", a, b); + return rc; +} + +static const char * +classify(struct file_data *f) +{ + return !f ? "directory" : f->is_empty ? "regular empty file" : "regular file"; +} + +static int +is_incommensurable(mode_t mode) +{ + /* POSIX specifies that if a and b refer to the same special device, + * there should be no comparision. This seems unnecessary since it + * also specifies that special devices and FIFO:s shall not be compared. + * We extend this to not compare sockets either. POSIX says that it + * is implementation-specified for other types than special files, + * FIFO:s, regular files and directories. */ + return S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode); +} + +static char * rstrip(char *text, char *removed) { char *end = strchr(text, '\0'); _AT_@ -183,45 +211,41 @@ strcmp_rstrip_a(char *a, char *b) /* TODO use <20160128154757.GA20170_AT_debian> when `an` is too large. */ static char * -diff2_(char **a, char **b, size_t an, size_t bn, int (*cmp)(char *, char *)) +diff2_minimal(char **a, char **b, size_t an, size_t bn, int (*cmp)(char *, char *)) { -#define matrix (*matrix) -#define map (*map) - char map[an + 1][bn + 1] = emalloc(sizeof(char[an + 1][bn + 1])); - size_t matrix[2][bn + 1] = ecalloc(1, sizeof(size_t[2][bn + 1])); + char (*map)[an + 1][bn + 1] = emalloc(sizeof(char[an + 1][bn + 1])); + size_t (*matrix)[2][bn + 1] = ecalloc(1, sizeof(size_t[2][bn + 1])); char *rc; size_t ai, bi, ri = 0, mi = 0; - memset(map[0], 2, bn + 1); + memset((*map)[0], 2, bn + 1); a--, b--; for (ai = 1; ai <= an; ai++) { - size_t *last = matrix[mi]; - size_t *this = matrix[mi ^= 1]; - map[ai][0] = 1; + size_t *last = (*matrix)[mi]; + size_t *this = (*matrix)[mi ^= 1]; + (*map)[ai][0] = 1; for (bi = 1; bi <= bn; bi++) { if (!cmp(a[ai], b[bi])) { this[bi] = last[bi - 1] + 1; - map[ai][bi] = 0; + (*map)[ai][bi] = 0; } else { size_t u = last[bi]; size_t l = this[bi - 1]; this[bi] = l >= u ? l : u; - map[ai][bi] = 1 + (l >= u); + (*map)[ai][bi] = 1 + (l >= u); } } } -#undef matrix free(matrix); rc = emalloc(an + bn + 1); rc[ri++] = END_OF_PATH; for (ai = an, bi = bn; ai + bi; ri++) { - rc[ri] = map[ai][bi]; + rc[ri] = (*map)[ai][bi]; ai -= rc[ri] != 2; bi -= rc[ri] != 1; } -#undef map free(map); return rc + ri; _AT_@ -296,14 +320,14 @@ enhance_trace(char *path) } static struct trace * -diff2(char **a, char **b, size_t an, size_t bn, int do_rstrip) +diff2(char **a, char **b, size_t an, size_t bn) { size_t skip_start = 0, skip_end = 0; char *rc; int (*cmp)(char *, char *) = (int (*)(char *, char *))strcmp; int transpose = bn < an; - if (do_rstrip) { + if (bflag) { char **lines; char _c; for (lines = !transpose ? b : a; *lines; lines++) _AT_@ -325,7 +349,7 @@ diff2(char **a, char **b, size_t an, size_t bn, int do_rstrip) if (cmp(a[an - 1], b[bn - 1])) break; - rc = !transpose ? diff2_(a, b, an, bn, cmp) : diff2_(b, a, bn, an, cmp); + rc = !transpose ? diff2_minimal(a, b, an, bn, cmp) : diff2_minimal(b, a, bn, an, cmp); if (transpose) { char *path; char trace; _AT_@ -373,16 +397,15 @@ get_time_string(const struct stat *attr) } static int -get_diff_chunks(struct trace *path, size_t an, size_t bn, struct chunk **head, struct chunk **tail) +get_diff_chunks(struct trace *path, size_t an, size_t bn, struct chunk **headp, struct chunk **tailp) { -#define head (*head) -#define tail (*tail) struct trace trace; size_t ai, bi; int ret = 0, suppressed = 1, have_a = 0, have_b = 0; + struct chunk *head = *headp; head = ecalloc(an + bn + 1, sizeof(*head)); - tail = head++; + *tailp = head++; for (ai = bi = 0; (trace = *path++).f != END_OF_PATH;) { if (trace.d > n_context) { _AT_@ -414,9 +437,8 @@ get_diff_chunks(struct trace *path, size_t an, size_t bn, struct chunk **head, s head++; } + *headp = head; return ret; -#undef head -#undef tail } #define OUTPUT_BEGIN\ _AT_@ -435,7 +457,7 @@ get_diff_chunks(struct trace *path, size_t an, size_t bn, struct chunk **head, s printf("%s"B" %s\t%s%s\n", BOLD(new->path, get_time_string(&(new->attr)))) #define OUTPUT_QUEUE\ - path = diff2(a, b, old->line_count, new->line_count, bflag);\ + path = diff2(a, b, old->line_count, new->line_count);\ ret = get_diff_chunks(path, old->line_count, new->line_count, &head, &tail);\ (void) chunk_old;\ for (head = tail;;) {\ _AT_@ -449,7 +471,7 @@ get_diff_chunks(struct trace *path, size_t an, size_t bn, struct chunk **head, s break #define OUTPUT_STACK\ - path = diff2(a, b, old->line_count, new->line_count, bflag);\ + path = diff2(a, b, old->line_count, new->line_count);\ ret = get_diff_chunks(path, old->line_count, new->line_count, &head, &tail);\ (void) chunk_old;\ for (;;) {\ _AT_@ -480,7 +502,7 @@ output_unified(struct file_data *old, struct file_data *new) int ret = 0; int suppressed = 1; - path = diff2(old->lines, new->lines, old->line_count, new->line_count, bflag); + path = diff2(old->lines, new->lines, old->line_count, new->line_count); path_ = path; OUTPUT_HEAD("---", "+++"); _AT_@ -522,9 +544,6 @@ output_unified(struct file_data *old, struct file_data *new) static int output_copied(struct file_data *old, struct file_data *new) { - OUTPUT_BEGIN; - OUTPUT_HEAD("***", "---"); - OUTPUT_QUEUE; #define PRINT_PART(L, C, S, A)\ printf("%s"A" %zu", use_colour ? "\033[1;3"#C"m" : "", L##i + 1 - (!have_##L));\ if (chunk->L##_len > 1)\ _AT_@ -541,20 +560,23 @@ output_copied(struct file_data *old, struct file_data *new) L##i += chunk->f != (3 - C);\ } + OUTPUT_BEGIN; + OUTPUT_HEAD("***", "---"); + OUTPUT_QUEUE; + printf("%s\n", use_colour ? "\033[36m***************\033[m" : "***************"); chunk_old = chunk; PRINT_PART(a, 1, "-", "***"); chunk = chunk_old; PRINT_PART(b, 2, "+", "---"); -#undef PRINT_PART + OUTPUT_END; +#undef PRINT_PART } static int output_default(struct file_data *old, struct file_data *new) { - OUTPUT_BEGIN; - OUTPUT_QUEUE; #define PRINT_PART(L, C, S)\ for (; have_##L && chunk->f != END_OF_PATH && chunk->d <= n_context; chunk++) {\ if (chunk->f == 0)\ _AT_@ -567,6 +589,9 @@ output_default(struct file_data *old, struct file_data *new) L##i += chunk->f != (3 - C);\ } + OUTPUT_BEGIN; + OUTPUT_QUEUE; + printf("%s%zu", use_colour ? "\033[36m" : "", ai + 1 - (!have_a)); if (chunk->a_len > 1) printf(",%zu", ai + chunk->a_len); _AT_@ -582,8 +607,9 @@ output_default(struct file_data *old, struct file_data *new) printf("%s\n", use_colour ? "\033[36m---\033[m" : "---"); chunk = chunk_old; PRINT_PART(b, 2, ">"); -#undef PRINT_PART + OUTPUT_END; +#undef PRINT_PART } static int _AT_@ -646,19 +672,18 @@ output_ed_alternative(struct file_data *old, struct file_data *new) static int do_binaries_differ(struct file_data *old, struct file_data *new) { -#define TURN_INTO_BINARY(f)\ - if (!f->is_binary) {\ - char **lines = f->lines;\ - size_t len = 0, part_len;\ - for (; *lines; lines++) {\ - len += 1 + (part_len = strlen(*lines));\ - (*lines)[part_len] = '\n';\ - }\ - f->line_count = len - !f->lf_terminated;\ - } - - TURN_INTO_BINARY(old); - TURN_INTO_BINARY(new); + struct file_data *f = old; + do { + if (!f->is_binary) { + char **lines = f->lines; + size_t len = 0, part_len; + for (; *lines; lines++) { + len += 1 + (part_len = strlen(*lines)); + (*lines)[part_len] = '\n'; + } + f->line_count = len - !f->lf_terminated; + } + } while (f == old ? (f = new) : (void*)0); if (old->line_count != new->line_count) return 1; _AT_@ -674,16 +699,16 @@ compare_files(struct file_data *old, struct file_data *new) if (old->is_binary || new->is_binary) { if (do_binaries_differ(old, new)) { printf("Binary files %s and %s differ\n", old->path, new->path); - ret = 2; + return 2; } - return ret; + return 0; } if (!(eflag || fflag)) { if (!old->lf_terminated) - strcpy(strchr(old->lines[old->line_count - 1], '\0'), NO_LF_MARK); + strcat(old->lines[old->line_count - 1], NO_LF_MARK); if (!new->lf_terminated) - strcpy(strchr(new->lines[new->line_count - 1], '\0'), NO_LF_MARK); + strcat(new->lines[new->line_count - 1], NO_LF_MARK); } ret = (uflag ? output_unified : _AT_@ -706,10 +731,6 @@ compare_files(struct file_data *old, struct file_data *new) static int compare_directories(const char *old, const char *new, const char *diff_line) { -#define GET_FILENAME(buf, i)\ - (buf = emalloc(strlen(paths[i]) + strlen(file->d_name) + 2),\ - stpcpy(stpcpy(stpcpy(buf, paths[i]), "/"), file->d_name)) - int ret = 0, r, i = 0, j = 1; DIR *dir; const char *paths[2] = { old, new }; _AT_@ -728,7 +749,7 @@ again: while ((errno = 0, file = readdir(dir))) { if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue; - GET_FILENAME(b_path, j); + b_path = join_paths(paths[j], file->d_name); if (access(b_path, F_OK)) { printf("%sOnly i %s: %s%s\n", BOLD(paths[i], file->d_name)); ret = ret > 1 ? ret : 1; _AT_@ -736,7 +757,7 @@ again: } else if (i == 1) { goto next; } - GET_FILENAME(a_path, i); + a_path = join_paths(paths[i], file->d_name); if (stat(a_path, &a_attr)) eperror(a_path); _AT_@ -745,14 +766,7 @@ again: if (a_attr.st_dev == b_attr.st_dev && a_attr.st_ino == b_attr.st_ino) goto skip; - /* POSIX specifies that if a and b refer to the same special device, - * there should be no comparision. This seems unnecessary since it - * also specifies that special devices and FIFO:s shall not be compared. - * We extend this to not compare sockets either. POSIX says that it - * is implementation-specified for other types than special files, - * FIFO:s, regular files and directories. */ -#define IS_INCOMMENSURABLE(mode) (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) - if (IS_INCOMMENSURABLE(a_attr.st_mode) || IS_INCOMMENSURABLE(b_attr.st_mode)) + if (is_incommensurable(a_attr.st_mode) || is_incommensurable(b_attr.st_mode)) goto skip; a = load_lines(a_path); _AT_@ -760,19 +774,18 @@ again: if (!a ^ !b) { printf("%sFile %s is a %s while file %s is a %s%s\n", - BOLD(a_path, CLASSIFY(a), b_path, CLASSIFY(b))); - ret = ret > 1 ? ret : 1; + BOLD(a_path, classify(a), b_path, classify(b))); + r = 1; } else if (!a && !b && !rflag) { printf("%sCommon subdirectories: %s and %s%s\n", BOLD(a_path, b_path)); - ret = ret > 1 ? ret : 1; + r = 1; } else if (!a && !b) { r = compare_directories(a_path, b_path, diff_line); - ret = ret > r ? ret : r; } else { printf("%s%s %s %s%s\n", BOLD(diff_line, a_path, b_path)); r = compare_files(a, b); - ret = ret > r ? ret : r; } + ret = ret > r ? ret : r; free(a); free(b); _AT_@ -814,7 +827,7 @@ main(int argc, char *argv[]) len += strlen(argv[i]) + 1; p = diff_line = emalloc(len + 1); for (i = 0; i < argc - 2; i++) - p = stpcpy(stpcpy(p, argv[i]), " "); + p += sprintf(p, "%s ", argv[i]); p[-1] = 0; } _AT_@ -830,7 +843,9 @@ main(int argc, char *argv[]) default: usage(); } ARGEND; - /* Use of `atol` is intentional, '-U -1' and '-C -1' shall display the entire file. */ + /* Use of `atol` is intentional, '-U -1' and '-C -1' shall display the entire file. + * This is a not specified in POSIX, but appears in other implementations and is + * useful whilst removing complexity. */ if (argc != 2 || (bflag | rflag) > 1 || cflag + eflag + fflag + uflag > 1) usage(); _AT_@ -843,16 +858,14 @@ redo: if ((old_proper || new_proper) && (!old || !new)) { printf("%sFile %s is a %s while file %s is a %s%s\n", - BOLD(old_proper ? old_proper : argv[0], CLASSIFY(old), - new_proper ? new_proper : argv[1], CLASSIFY(new))); + BOLD(old_proper ? old_proper : argv[0], classify(old), + new_proper ? new_proper : argv[1], classify(new))); ret = 1; } else if (!old && new) { - old_proper = emalloc(strlen(argv[0]) + strlen(argv[1]) + 2); - stpcpy(stpcpy(stpcpy(old_proper, argv[0]), "/"), basename(argv[1])); + old_proper = join_paths(argv[0], basename(argv[1])); goto redo; } else if (old && !new) { - old_proper = emalloc(strlen(argv[0]) + strlen(argv[1]) + 2); - stpcpy(stpcpy(stpcpy(old_proper, argv[0]), "/"), basename(argv[1])); + new_proper = join_paths(argv[1], basename(argv[0])); goto redo; } else if (!old && !new) { ret = compare_directories(argv[0], argv[1], diff_line); _AT_@ -860,7 +873,6 @@ redo: ret = compare_files(old, new); } -done: if (fshut(stdout, "<stdout>")) ret = EXIT_FAILURE; -- 2.7.0Received on Sun Jan 31 2016 - 20:55:42 CET
This archive was generated by hypermail 2.3.0 : Sun Jan 31 2016 - 21:00:14 CET