commit 06b928e23f06a14d0c75544ee5814fde7d801fe0 from: Martin Pieuchot date: Tue Feb 11 14:07:01 2020 UTC Keep all output code in one place commit - b5a3e4d3220d54590de58ecf5a2199285e3f9263 commit + 06b928e23f06a14d0c75544ee5814fde7d801fe0 blob - 239f256775bd901b4c9fe8229d961b6e6708b250 blob + cb905cf4b378e9d869b063bcb70bbfd7ea993c4b --- Makefile +++ Makefile @@ -6,8 +6,6 @@ SRCS= \ diff_myers.c \ diff_patience.c \ diff_output.c \ - diff_output_plain.c \ - diff_output_unidiff.c \ ${END} .if defined(PROFILE) blob - 9e92df1e83b5f18049b7152922d5f0d9e0f98f71 blob + f367d0243bf9c2dfbf553ef3a1c63a1e3bb114a2 --- diff_output.c +++ diff_output.c @@ -16,10 +16,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* Common parts for printing diff output */ - #include "diff_main.h" +#include "debug.h" + +/* + * Common parts for printing diff output + */ + void diff_output_lines(FILE *dest, const char *prefix, struct diff_atom *start_atom, unsigned int count) { @@ -55,3 +59,239 @@ diff_output_info(FILE *dest, const struct diff_input_i info->right_path ? : "b"); return DIFF_RC_OK; } + +/* + * Output all lines of a diff_result. + */ +enum diff_rc +diff_output_plain(FILE *dest, const struct diff_input_info *info, + const struct diff_result *result) { + if (!result) + return DIFF_RC_EINVAL; + if (result->rc != DIFF_RC_OK) + return result->rc; + + diff_output_info(dest, info); + + int i; + for (i = 0; i < result->chunks.len; i++) { + struct diff_chunk *c = &result->chunks.head[i]; + if (c->left_count && c->right_count) + diff_output_lines(dest, c->solved ? " " : "?", c->left_start, c->left_count); + else if (c->left_count && !c->right_count) + diff_output_lines(dest, c->solved ? "-" : "?", c->left_start, c->left_count); + else if (c->right_count && !c->left_count) + diff_output_lines(dest, c->solved ? "+" : "?", c->right_start, c->right_count); + } + return DIFF_RC_OK; +} + +enum diff_rc +diff_plain(FILE *dest, const struct diff_config *diff_config, + const struct diff_input_info *info, + const char *left, int left_len, const char *right, int right_len) +{ + enum diff_rc rc; + left_len = left_len < 0 ? strlen(left) : left_len; + right_len = right_len < 0 ? strlen(right) : right_len; + struct diff_result *result = diff_main(diff_config, left, left_len, right, right_len); + rc = diff_output_plain(dest, info, result); + diff_result_free(result); + return rc; +} + +/* + * Produce a unidiff output from a diff_result. + */ +enum chunk_type { + CHUNK_EMPTY, + CHUNK_PLUS, + CHUNK_MINUS, + CHUNK_SAME, + CHUNK_WEIRD, +}; + +static inline enum chunk_type +chunk_type(const struct diff_chunk *chunk) +{ + if (!chunk->left_count && !chunk->right_count) + return CHUNK_EMPTY; + if (!chunk->solved) + return CHUNK_WEIRD; + if (!chunk->right_count) + return CHUNK_MINUS; + if (!chunk->left_count) + return CHUNK_PLUS; + if (chunk->left_count != chunk->right_count) + return CHUNK_WEIRD; + return CHUNK_SAME; +} + +struct chunk_context { + struct range chunk; + struct range left, right; +}; + +static bool +chunk_context_empty(const struct chunk_context *cc) +{ + return range_empty(&cc->chunk); +} + +static void +chunk_context_get(struct chunk_context *cc, const struct diff_result *r, int chunk_idx, + int context_lines) +{ + const struct diff_chunk *c = &r->chunks.head[chunk_idx]; + int left_start = diff_atom_root_idx(&r->left, c->left_start); + int right_start = diff_atom_root_idx(&r->right, c->right_start); + + *cc = (struct chunk_context){ + .chunk = { + .start = chunk_idx, + .end = chunk_idx + 1, + }, + .left = { + .start = MAX(0, left_start - context_lines), + .end = MIN(r->left.atoms.len, left_start + c->left_count + context_lines), + }, + .right = { + .start = MAX(0, right_start - context_lines), + .end = MIN(r->right.atoms.len, right_start + c->right_count + context_lines), + }, + }; +} + +static bool +chunk_contexts_touch(const struct chunk_context *cc, const struct chunk_context *other) +{ + return ranges_touch(&cc->chunk, &other->chunk) || + ranges_touch(&cc->left, &other->left) || + ranges_touch(&cc->right, &other->right); +} + +static void +chunk_contexts_merge(struct chunk_context *cc, const struct chunk_context *other) +{ + ranges_merge(&cc->chunk, &other->chunk); + ranges_merge(&cc->left, &other->left); + ranges_merge(&cc->right, &other->right); +} + +static void +diff_output_unidiff_chunk(FILE *dest, bool *info_printed, const struct diff_input_info *info, + const struct diff_result *result, const struct chunk_context *cc) { + if (range_empty(&cc->left) && range_empty(&cc->right)) + return; + + if (!(*info_printed)) { + diff_output_info(dest, info); + *info_printed = true; + } + + fprintf(dest, "@@ -%d,%d +%d,%d @@\n", + cc->left.start + 1, cc->left.end - cc->left.start, + cc->right.start + 1, cc->right.end - cc->right.start); + + /* Got the absolute line numbers where to start printing, and the index of the interesting (non-context) chunk. + * To print context lines above the interesting chunk, nipping on the previous chunk index may be necessary. + * It is guaranteed to be only context lines where left == right, so it suffices to look on the left. */ + const struct diff_chunk *first_chunk = &result->chunks.head[cc->chunk.start]; + int chunk_start_line = diff_atom_root_idx(&result->left, first_chunk->left_start); + if (cc->left.start < chunk_start_line) + diff_output_lines(dest, " ", &result->left.atoms.head[cc->left.start], + chunk_start_line - cc->left.start); + + /* Now write out all the joined chunks and contexts between them */ + int c_idx; + for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) { + const struct diff_chunk *c = &result->chunks.head[c_idx]; + + if (c->left_count && c->right_count) + diff_output_lines(dest, c->solved ? " " : "?", c->left_start, c->left_count); + else if (c->left_count && !c->right_count) + diff_output_lines(dest, c->solved ? "-" : "?", c->left_start, c->left_count); + else if (c->right_count && !c->left_count) + diff_output_lines(dest, c->solved ? "+" : "?", c->right_start, c->right_count); + } + + /* Trailing context? */ + const struct diff_chunk *last_chunk = &result->chunks.head[cc->chunk.end - 1]; + int chunk_end_line = diff_atom_root_idx(&result->left, last_chunk->left_start + last_chunk->left_count); + if (cc->left.end > chunk_end_line) + diff_output_lines(dest, " ", &result->left.atoms.head[chunk_end_line], + cc->left.end - chunk_end_line); +} + +enum diff_rc +diff_output_unidiff(FILE *dest, const struct diff_input_info *info, + const struct diff_result *result, unsigned int context_lines) { + if (!result) + return DIFF_RC_EINVAL; + if (result->rc != DIFF_RC_OK) + return result->rc; + + struct chunk_context cc = {}; + bool info_printed = false; + + int i; + for (i = 0; i < result->chunks.len; i++) { + struct diff_chunk *c = &result->chunks.head[i]; + enum chunk_type t = chunk_type(c); + + if (t == CHUNK_MINUS || t == CHUNK_PLUS) { + if (chunk_context_empty(&cc)) { + /* These are the first lines being printed. + * Note down the start point, any number of subsequent chunks may be joined up to this + * unidiff chunk by context lines or by being directly adjacent. */ + chunk_context_get(&cc, result, i, context_lines); + debug("new chunk to be printed: chunk %d-%d left %d-%d right %d-%d\n", + cc.chunk.start, cc.chunk.end, + cc.left.start, cc.left.end, cc.right.start, cc.right.end); + } else { + /* There already is a previous chunk noted down for being printed. + * Does it join up with this one? */ + struct chunk_context next; + chunk_context_get(&next, result, i, context_lines); + debug("new chunk to be printed: chunk %d-%d left %d-%d right %d-%d\n", + next.chunk.start, next.chunk.end, + next.left.start, next.left.end, next.right.start, next.right.end); + if (chunk_contexts_touch(&cc, &next)) { + /* This next context touches or overlaps the previous one, join. */ + chunk_contexts_merge(&cc, &next); + debug("new chunk to be printed touches previous chunk, now: left %d-%d right %d-%d\n", + cc.left.start, cc.left.end, cc.right.start, cc.right.end); + } else { + /* No touching, so the previous context is complete with a gap between it and + * this next one. Print the previous one and start fresh here. */ + debug("new chunk to be printed does not touch previous chunk; print left %d-%d right %d-%d\n", + cc.left.start, cc.left.end, cc.right.start, cc.right.end); + diff_output_unidiff_chunk(dest, &info_printed, info, result, &cc); + cc = next; + debug("new unprinted chunk is left %d-%d right %d-%d\n", + cc.left.start, cc.left.end, cc.right.start, cc.right.end); + } + } + } + + } + + if (!chunk_context_empty(&cc)) + diff_output_unidiff_chunk(dest, &info_printed, info, result, &cc); + return DIFF_RC_OK; +} + +enum diff_rc +diff_unidiff(FILE *dest, const struct diff_config *diff_config, + const struct diff_input_info *info, + const char *left, int left_len, const char *right, int right_len, + unsigned int context_lines) +{ + enum diff_rc rc; + left_len = left_len < 0 ? strlen(left) : left_len; + right_len = right_len < 0 ? strlen(right) : right_len; + struct diff_result *result = diff_main(diff_config, left, left_len, right, right_len); + rc = diff_output_unidiff(dest, info, result, context_lines); + diff_result_free(result); + return rc; +} blob - 34916d08db89aba7a95acf281e681c11b69bbd4d (mode 644) blob + /dev/null --- diff_output_plain.c +++ /dev/null @@ -1,58 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2020 Neels Hofmeyr - * - * 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. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* Output all lines of a diff_result. */ - -#include "diff_main.h" - -enum diff_rc -diff_output_plain(FILE *dest, const struct diff_input_info *info, - const struct diff_result *result) { - if (!result) - return DIFF_RC_EINVAL; - if (result->rc != DIFF_RC_OK) - return result->rc; - - diff_output_info(dest, info); - - int i; - for (i = 0; i < result->chunks.len; i++) { - struct diff_chunk *c = &result->chunks.head[i]; - if (c->left_count && c->right_count) - diff_output_lines(dest, c->solved ? " " : "?", c->left_start, c->left_count); - else if (c->left_count && !c->right_count) - diff_output_lines(dest, c->solved ? "-" : "?", c->left_start, c->left_count); - else if (c->right_count && !c->left_count) - diff_output_lines(dest, c->solved ? "+" : "?", c->right_start, c->right_count); - } - return DIFF_RC_OK; -} - -enum diff_rc -diff_plain(FILE *dest, const struct diff_config *diff_config, - const struct diff_input_info *info, - const char *left, int left_len, const char *right, int right_len) -{ - enum diff_rc rc; - left_len = left_len < 0 ? strlen(left) : left_len; - right_len = right_len < 0 ? strlen(right) : right_len; - struct diff_result *result = diff_main(diff_config, left, left_len, right, right_len); - rc = diff_output_plain(dest, info, result); - diff_result_free(result); - return rc; -} blob - 4596ed14265a10e54dedc012c92f82780602e409 (mode 644) blob + /dev/null --- diff_output_unidiff.c +++ /dev/null @@ -1,215 +0,0 @@ -/* $OpenBSD$ */ - -/* - * Copyright (c) 2020 Neels Hofmeyr - * - * 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. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* Produce a unidiff output from a diff_result. */ - -#include "diff_main.h" -#include "debug.h" - -enum chunk_type { - CHUNK_EMPTY, - CHUNK_PLUS, - CHUNK_MINUS, - CHUNK_SAME, - CHUNK_WEIRD, -}; - -static inline enum chunk_type -chunk_type(const struct diff_chunk *chunk) -{ - if (!chunk->left_count && !chunk->right_count) - return CHUNK_EMPTY; - if (!chunk->solved) - return CHUNK_WEIRD; - if (!chunk->right_count) - return CHUNK_MINUS; - if (!chunk->left_count) - return CHUNK_PLUS; - if (chunk->left_count != chunk->right_count) - return CHUNK_WEIRD; - return CHUNK_SAME; -} - -struct chunk_context { - struct range chunk; - struct range left, right; -}; - -static bool -chunk_context_empty(const struct chunk_context *cc) -{ - return range_empty(&cc->chunk); -} - -static void -chunk_context_get(struct chunk_context *cc, const struct diff_result *r, int chunk_idx, - int context_lines) -{ - const struct diff_chunk *c = &r->chunks.head[chunk_idx]; - int left_start = diff_atom_root_idx(&r->left, c->left_start); - int right_start = diff_atom_root_idx(&r->right, c->right_start); - - *cc = (struct chunk_context){ - .chunk = { - .start = chunk_idx, - .end = chunk_idx + 1, - }, - .left = { - .start = MAX(0, left_start - context_lines), - .end = MIN(r->left.atoms.len, left_start + c->left_count + context_lines), - }, - .right = { - .start = MAX(0, right_start - context_lines), - .end = MIN(r->right.atoms.len, right_start + c->right_count + context_lines), - }, - }; -} - -static bool -chunk_contexts_touch(const struct chunk_context *cc, const struct chunk_context *other) -{ - return ranges_touch(&cc->chunk, &other->chunk) || - ranges_touch(&cc->left, &other->left) || - ranges_touch(&cc->right, &other->right); -} - -static void -chunk_contexts_merge(struct chunk_context *cc, const struct chunk_context *other) -{ - ranges_merge(&cc->chunk, &other->chunk); - ranges_merge(&cc->left, &other->left); - ranges_merge(&cc->right, &other->right); -} - -static void -diff_output_unidiff_chunk(FILE *dest, bool *info_printed, const struct diff_input_info *info, - const struct diff_result *result, const struct chunk_context *cc) { - if (range_empty(&cc->left) && range_empty(&cc->right)) - return; - - if (!(*info_printed)) { - diff_output_info(dest, info); - *info_printed = true; - } - - fprintf(dest, "@@ -%d,%d +%d,%d @@\n", - cc->left.start + 1, cc->left.end - cc->left.start, - cc->right.start + 1, cc->right.end - cc->right.start); - - /* Got the absolute line numbers where to start printing, and the index of the interesting (non-context) chunk. - * To print context lines above the interesting chunk, nipping on the previous chunk index may be necessary. - * It is guaranteed to be only context lines where left == right, so it suffices to look on the left. */ - const struct diff_chunk *first_chunk = &result->chunks.head[cc->chunk.start]; - int chunk_start_line = diff_atom_root_idx(&result->left, first_chunk->left_start); - if (cc->left.start < chunk_start_line) - diff_output_lines(dest, " ", &result->left.atoms.head[cc->left.start], - chunk_start_line - cc->left.start); - - /* Now write out all the joined chunks and contexts between them */ - int c_idx; - for (c_idx = cc->chunk.start; c_idx < cc->chunk.end; c_idx++) { - const struct diff_chunk *c = &result->chunks.head[c_idx]; - - if (c->left_count && c->right_count) - diff_output_lines(dest, c->solved ? " " : "?", c->left_start, c->left_count); - else if (c->left_count && !c->right_count) - diff_output_lines(dest, c->solved ? "-" : "?", c->left_start, c->left_count); - else if (c->right_count && !c->left_count) - diff_output_lines(dest, c->solved ? "+" : "?", c->right_start, c->right_count); - } - - /* Trailing context? */ - const struct diff_chunk *last_chunk = &result->chunks.head[cc->chunk.end - 1]; - int chunk_end_line = diff_atom_root_idx(&result->left, last_chunk->left_start + last_chunk->left_count); - if (cc->left.end > chunk_end_line) - diff_output_lines(dest, " ", &result->left.atoms.head[chunk_end_line], - cc->left.end - chunk_end_line); -} - -enum diff_rc -diff_output_unidiff(FILE *dest, const struct diff_input_info *info, - const struct diff_result *result, unsigned int context_lines) { - if (!result) - return DIFF_RC_EINVAL; - if (result->rc != DIFF_RC_OK) - return result->rc; - - struct chunk_context cc = {}; - bool info_printed = false; - - int i; - for (i = 0; i < result->chunks.len; i++) { - struct diff_chunk *c = &result->chunks.head[i]; - enum chunk_type t = chunk_type(c); - - if (t == CHUNK_MINUS || t == CHUNK_PLUS) { - if (chunk_context_empty(&cc)) { - /* These are the first lines being printed. - * Note down the start point, any number of subsequent chunks may be joined up to this - * unidiff chunk by context lines or by being directly adjacent. */ - chunk_context_get(&cc, result, i, context_lines); - debug("new chunk to be printed: chunk %d-%d left %d-%d right %d-%d\n", - cc.chunk.start, cc.chunk.end, - cc.left.start, cc.left.end, cc.right.start, cc.right.end); - } else { - /* There already is a previous chunk noted down for being printed. - * Does it join up with this one? */ - struct chunk_context next; - chunk_context_get(&next, result, i, context_lines); - debug("new chunk to be printed: chunk %d-%d left %d-%d right %d-%d\n", - next.chunk.start, next.chunk.end, - next.left.start, next.left.end, next.right.start, next.right.end); - if (chunk_contexts_touch(&cc, &next)) { - /* This next context touches or overlaps the previous one, join. */ - chunk_contexts_merge(&cc, &next); - debug("new chunk to be printed touches previous chunk, now: left %d-%d right %d-%d\n", - cc.left.start, cc.left.end, cc.right.start, cc.right.end); - } else { - /* No touching, so the previous context is complete with a gap between it and - * this next one. Print the previous one and start fresh here. */ - debug("new chunk to be printed does not touch previous chunk; print left %d-%d right %d-%d\n", - cc.left.start, cc.left.end, cc.right.start, cc.right.end); - diff_output_unidiff_chunk(dest, &info_printed, info, result, &cc); - cc = next; - debug("new unprinted chunk is left %d-%d right %d-%d\n", - cc.left.start, cc.left.end, cc.right.start, cc.right.end); - } - } - } - - } - - if (!chunk_context_empty(&cc)) - diff_output_unidiff_chunk(dest, &info_printed, info, result, &cc); - return DIFF_RC_OK; -} - -enum diff_rc -diff_unidiff(FILE *dest, const struct diff_config *diff_config, - const struct diff_input_info *info, - const char *left, int left_len, const char *right, int right_len, - unsigned int context_lines) -{ - enum diff_rc rc; - left_len = left_len < 0 ? strlen(left) : left_len; - right_len = right_len < 0 ? strlen(right) : right_len; - struct diff_result *result = diff_main(diff_config, left, left_len, right, right_len); - rc = diff_output_unidiff(dest, info, result, context_lines); - diff_result_free(result); - return rc; -}