#include #include "diff.h" /* Print a label for a context diff, with a file name and date or a label. */ const char bb_hexdigits_upcase[] = "0123456789ABCDEF"; char *bin2hex(char *p, const char *cp, int count) { while (count) { unsigned char c = *cp++; /* put lowercase hex digits */ *p++ = 0x20 | bb_hexdigits_upcase[c >> 4]; *p++ = 0x20 | bb_hexdigits_upcase[c & 0xf]; count--; } return p; } static void print_context_label(char const *mark, struct file_data *inf, char const *name, char const *label) { if (label) fprintf(outfile, "%s %s\n", mark, label); else { if (inf->desc >= 0) { char buf[(64 * 2) + 1] = {0}; bin2hex(buf, (char*) inf->hash, 64); fprintf(outfile, "%s %s %s\n", mark, name, buf); } else { fprintf(outfile, "%s %s false\n", mark, name); } } } /* Print a header for a context diff, with the file names and dates. */ void print_context_header(struct file_data inf[], char const *const *names) { print_context_label("---", &inf[0], names[0], file_label[0]); print_context_label("+++", &inf[1], names[1], file_label[1]); } /* Print an edit script in context format. */ void print_context_script(struct change *script) { struct change *e; for (e = script; e; e = e->link) e->ignore = false; print_script(script); } /* Print a pair of line numbers with a comma, translated for file |file|. If the second number is smaller, use the first in place of it. If the numbers are equal, print just one number. Args |a| and |b| are internal line numbers. We print the translated (real) line numbers. */ static void print_unidiff_number_range(struct file_data const *file, lin a, lin b) { printint trans_a, trans_b; translate_range(file, a, b, &trans_a, &trans_b); /* We can have |b < a| in the case of a range of no lines. In this case, we print the line number before the range, which is |b|. It would be more logical to print |a|, but `patch' expects |b| in order to detect diffs against empty files. */ if (trans_b <= trans_a) fprintf(outfile, trans_b < trans_a ? "%"pI"d,0" : "%"pI"d", trans_b); else fprintf(outfile, "%"pI"d,%"pI"d", trans_a, trans_b - trans_a + 1); } /* Print a portion of an edit script in unidiff format. |hunk| is the beginning of the portion to be printed. The end is marked by a |link| that has been nulled out. Prints out lines from both files, and precedes each line with the appropriate flag-character. */ void pr_unidiff_hunk(struct change *hunk) { lin first0, last0, first1, last1; lin i, j, k; struct change *next; FILE *out; /* Determine range of line numbers involved in each file. */ if (!analyze_hunk(hunk, &first0, &last0, &first1, &last1)) return; /* Include a context's width before and after. */ i = -files[0].prefix_lines; first0 = MAX (first0 - context, i); first1 = MAX (first1 - context, i); if (last0 < files[0].valid_lines - context) last0 += context; else last0 = files[0].valid_lines - 1; if (last1 < files[1].valid_lines - context) last1 += context; else last1 = files[1].valid_lines - 1; begin_output(); out = outfile; fputs("@@ -", out); print_unidiff_number_range(&files[0], first0, last0); fputs(" +", out); print_unidiff_number_range(&files[1], first1, last1); fputs(" @@", out); putc('\n', out); next = hunk; i = first0; j = first1; while (i <= last0 || j <= last1) { /* If the line isn't a difference, output the context from file 0. */ if (!next || i < next->line0) { char const *const *line = &files[0].linbuf[i++]; if (!(suppress_blank_empty && **line == '\n')) putc(' ', out); print_1_line(NULL, line); j++; } else { /* For each difference, first output the deleted part. */ k = next->deleted; while (k--) { char const *const *line = &files[0].linbuf[i++]; putc('-', out); print_1_line_nl(NULL, line, true); if (line[1][-1] == '\n') putc('\n', out); } /* Then output the inserted part. */ k = next->inserted; while (k--) { char const *const *line = &files[1].linbuf[j++]; putc('+', out); print_1_line_nl(NULL, line, true); if (line[1][-1] == '\n') putc('\n', out); } /* We're done with this hunk, so on to the next! */ next = next->link; } } } /* Scan a (forward-ordered) edit script for the first place that more than |2*context| unchanged lines appear, and return a pointer to the |struct change| for the last change before those lines. */ struct change * find_hunk(struct change *start) { struct change *prev; lin top0, top1; lin thresh; /* Threshold distance is |context| if the second change is ignorable, |2 * context + 1| otherwise. Integer overflow can't happen, due to |CONTEXT_LIM|. */ lin ignorable_threshold = context; lin non_ignorable_threshold = 2 * context + 1; do { /* Compute number of first line in each file beyond this changed. */ top0 = start->line0 + start->deleted; top1 = start->line1 + start->inserted; prev = start; start = start->link; thresh = (start && start->ignore ? ignorable_threshold : non_ignorable_threshold); /* It is not supposed to matter which file we check in the end-test. If it would matter, crash. */ if (start && start->line0 - top0 != start->line1 - top1) abort(); } while (start /* Keep going if less than |thresh| lines elapse before the affected line. */ && start->line0 - top0 < thresh); return prev; } /* Set the |ignore| flag properly in each change in script. It should be 1 if all the lines inserted or deleted in that change are ignorable lines. */ static void mark_ignorable(struct change *script) { while (script) { struct change *next = script->link; lin first0, last0, first1, last1; /* Turn this change into a hunk: detach it from the others. */ script->link = NULL; /* Determine whether this change is ignorable. */ script->ignore = !analyze_hunk(script, &first0, &last0, &first1, &last1); /* Reconnect the chain as before. */ script->link = next; /* Advance to the following change. */ script = next; } }