#include "diff.h" #include #include #include /* Use when a system call returns non-zero status. |name| should normally be the file name. */ void perror_with_name(char const *name) { error(0, errno, "%s", name); } /* Use when a system call returns non-zero status and that is fatal. */ void pfatal_with_name(char const *name) { int e = errno; fprintf(stderr, "%s", name); exit(EXIT_TROUBLE); } /* Print an error message containing |msgid|, then exit. */ void fatal(char const *msgid) { fprintf(stderr, "%s", msgid); exit(EXIT_TROUBLE); } /* Like |printf|, except if -l in effect then save the message and print later. This is used for things like "Only in ...". */ void message(char const *format_msgid, char const *arg1, char const *arg2) { message5(format_msgid, arg1, arg2, 0, 0); } void message5(char const *format_msgid, char const *arg1, char const *arg2, char const *arg3, char const *arg4) { fprintf(stderr, format_msgid, arg1, arg2, arg3, arg4); } static char const *current_name0; static char const *current_name1; static bool currently_recursive; /* Call before outputting the results of comparing files |name0| and |name1| to set up |outfile|, the stdio stream for the output to go to. Usually, |outfile| is just stdout. But when -l was specified we fork off a |pr| and make |outfile| a pipe to it. 'pr' then outputs to our stdout. */ void setup_output(char const *name0, char const *name1, bool recursive) { current_name0 = name0; current_name1 = name1; currently_recursive = recursive; outfile = 0; } static char c_escape_char(char c) { switch (c) { case '\a': return 'a'; case '\b': return 'b'; case '\t': return 't'; case '\n': return 'n'; case '\v': return 'v'; case '\f': return 'f'; case '\r': return 'r'; case '"': return '"'; case '\\': return '\\'; default: return c < 32; } } static char * c_escape(char const *str) { char const *s; size_t plus = 0; bool must_quote = false; for (s = str; *s; s++) { char c = *s; if (c == ' ') { must_quote = true; continue; } switch (c_escape_char(*s)) { case 1: plus += 3; /* fall through */ case 0: break; default: plus++; break; } } if (must_quote || plus) { size_t s_len = s - str; char *buffer = xmalloc(s_len + plus + 3); char *b = buffer; *b++ = '"'; for (s = str; *s; s++) { char c = *s; char escape = c_escape_char(c); switch (escape) { case 0: *b++ = c; break; case 1: *b++ = '\\'; *b++ = ((c >> 6) & 03) + '0'; *b++ = ((c >> 3) & 07) + '0'; *b++ = ((c >> 0) & 07) + '0'; break; default: *b++ = '\\'; *b++ = escape; break; } } *b++ = '"'; *b = 0; return buffer; } return (char *) str; } void begin_output(void) { char *names[2]; char *name; if (outfile != 0) return; names[0] = c_escape(current_name0); names[1] = c_escape(current_name1); /* Construct the header of this piece of diff. */ if(asprintf(&name, "diff%s %s %s", switch_string, names[0], names[1]) == -1) xalloc_die(); outfile = stdout; /* If handling multiple files (because scanning a directory), print which files the following output is about. */ if (currently_recursive) printf("%s\n", name); free(name); print_context_header(files, (char const *const *) names); if (names[0] != current_name0) free(names[0]); if (names[1] != current_name1) free(names[1]); } void finish_output(void) { if (outfile != 0 && outfile != stdout) { if (ferror(outfile)) fatal("write failed"); if (fclose(outfile) != 0) pfatal_with_name("write failed"); } outfile = 0; } /* Compare two lines (typically one from each input file) according to the command line options. For efficiency, this is invoked only when the lines do not match exactly but an option like -i might cause us to ignore the difference. Return nonzero if the lines differ. */ bool lines_differ(char const *s1, char const *s2) { register char const *t1 = s1; register char const *t2 = s2; size_t column = 0; while (1) { register unsigned char c1 = *t1++; register unsigned char c2 = *t2++; /* Test for exact char equality first, since it's a common case. */ if (c1 != c2) { break; } if (c1 == '\n') return false; column++; } return true; } /* Find the consecutive changes at the start of the script START. Return the last link before the first gap. */ struct change * find_change(struct change *start) { return start; } /* Divide |script| into pieces by calling |hunkfun| and print each piece with |printfun|. Both functions take one arg, an edit script. |hunkfun| is called with the tail of the script and returns the last link that belongs together with the start of the tail. |printfun| takes a subscript which belongs together (with a null link at the end) and prints it. */ void print_script(struct change *script) { struct change *next = script; while (next) { struct change *this, *end; /* Find a set of changes that belong together. */ this = next; end = find_hunk(next); /* Disconnect them from the rest of the changes, making them a hunk, and remember the rest for next iteration. */ next = end->link; end->link = 0; #ifdef DEBUG debug_script (this); #endif /* Print this hunk. */ pr_unidiff_hunk(this); /* Reconnect the script so it will all be freed properly. */ end->link = next; } } /* Print the text of a single line |line|, flagging it with the characters in |line_flag| (which say whether the line is inserted, deleted, changed, etc.). |line_flag| must not end in a blank, unless it is a single blank. */ void print_1_line(char const *line_flag, char const *const *line) { print_1_line_nl(line_flag, line, false); } /* Print the text of a single line |line|, flagging it with the characters in |line_flag| (which say whether the line is inserted, deleted, changed, etc.). |line_flag| must not end in a blank, unless it is a single blank. If |skip_nl| is set, then the final |'\n'| is not printed. */ void print_1_line_nl(char const *line_flag, char const *const *line, bool skip_nl) { char const *base = line[0], *limit = line[1]; /* Help the compiler. */ FILE *out = outfile; /* Help the compiler some more. */ char const *flag_format = 0; /* If -T was specified, use a Tab between the line-flag and the text. Otherwise use a Space (as Unix diff does). Print neither space nor tab if line-flags are empty. But omit trailing blanks if requested. */ if (line_flag && *line_flag) { char const *flag_format_1 = flag_format = "%s "; char const *line_flag_1 = line_flag; if (suppress_blank_empty && **line == '\n') { flag_format_1 = "%s"; /* This hack to omit trailing blanks takes advantage of the fact that the only way that |line_flag| can end in a blank is when |line_flag| consists of a single blank. */ line_flag_1 += *line_flag_1 == ' '; } fprintf(out, flag_format_1, line_flag_1); } output_1_line(base, limit - (skip_nl && limit[-1] == '\n')); if ((!line_flag || line_flag[0]) && limit[-1] != '\n') { fprintf(out, "\n\\ %s\n", "No newline at end of file"); } } /* Output a line from |base| up to |limit|. */ void output_1_line(char const *base, char const *limit) { const size_t MAX_CHUNK = 1024; size_t left = limit - base; while (left) { size_t to_write = MIN (left, MAX_CHUNK); size_t written = fwrite(base, sizeof(char), to_write, outfile); if (written < to_write) return; base += written; left -= written; } } /* Translate an internal line number (an index into diff's table of lines) into an actual line number in the input file. The internal line number is |i|. |file| points to the data on the file. Internal line numbers count from 0 starting after the prefix. Actual line numbers count from 1 within the entire file. */ lin translate_line_number(struct file_data const *file, lin i) { return i + file->prefix_lines + 1; } /* Translate a line number range. This is always done for printing, so for convenience translate to printint rather than lin, so that the caller can use |printf| with |"%"pI"d"| without casting. */ void translate_range(struct file_data const *file, lin a, lin b, printint *aptr, printint *bptr) { *aptr = translate_line_number(file, a - 1) + 1; *bptr = translate_line_number(file, b + 1) - 1; } /* Look at a hunk of edit script and report the range of lines in each file that it applies to. |hunk| is the start of the hunk, which is a chain of |struct change|. The first and last line numbers of file 0 are stored in |*first0| and |*last0|, and likewise for file 1 in |*first1| and |*last1|. Note that these are internal line numbers that count from 0. If no lines from file 0 are deleted, then |first0| is |last0+1|. Return |UNCHANGED| if only ignorable lines are inserted or deleted, |OLD| if lines of file 0 are deleted, |NEW| if lines of file 1 are inserted, and |CHANGED| if both kinds of changes are found. */ enum changes analyze_hunk(struct change *hunk, lin *first0, lin *last0, lin *first1, lin *last1) { struct change *next; lin l0, l1; lin show_from, show_to; /* If 0, ignore zero-length lines; if |SIZE_MAX|, do not ignore lines just because of their length. */ char const *const *linbuf0 = files[0].linbuf; /* Help the compiler. */ char const *const *linbuf1 = files[1].linbuf; show_from = show_to = 0; *first0 = hunk->line0; *first1 = hunk->line1; next = hunk; do { l0 = next->line0 + next->deleted - 1; l1 = next->line1 + next->inserted - 1; show_from += next->deleted; show_to += next->inserted; } while ((next = next->link) != 0); *last0 = l0; *last1 = l1; return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED); } /* Yield a new block of |size| bytes, initialized to zero. */ void * zalloc(size_t size) { void *p = xmalloc(size); memset (p, 0, size); return p; } void debug_script(struct change *sp) { fflush(stdout); for (; sp; sp = sp->link) { printint line0 = sp->line0; printint line1 = sp->line1; printint deleted = sp->deleted; printint inserted = sp->inserted; fprintf(stderr, "%3"pI"d %3"pI"d delete %"pI"d insert %"pI"d\n", line0, line1, deleted, inserted); } fflush(stderr); }