commit 2c20a3ed9d3c979c38e927387d4ea488e4dd631b from: Stefan Sperling date: Tue Sep 22 21:56:41 2020 UTC optionally provide information about the generated diff to the API user commit - 24b5052ac04dc722cd86b4cd6f4a7142542edb74 commit + 2c20a3ed9d3c979c38e927387d4ea488e4dd631b blob - f562ecca72b0513ec935200ca938e058c6375248 blob + 9e288bba939bf3e9c8595855f3933fd37a309fbb --- diff/diff.c +++ diff/diff.c @@ -160,7 +160,7 @@ diffreg(char *file1, char *file2, bool do_patience, bo #if 0 rc = diff_output_plain(stdout, &info, result); #else - rc = diff_output_unidiff(stdout, &info, result, context_lines); + rc = diff_output_unidiff(NULL, stdout, &info, result, context_lines); #endif diff_result_free(result); blob - b5512c4033d6e6b00e24ff6df424e84189c5a61c blob + eb04709b9c9adb66907177f82e036589906fcfb0 --- include/diff/diff_output.h +++ include/diff/diff_output.h @@ -20,16 +20,29 @@ struct diff_input_info { const char *right_path; }; +struct diff_output_info { + /* + * Byte offset to each line in the generated output file. + * The total number of lines in the file is line_offsets.len - 1. + * The last offset in this array corresponds to end-of-file. + */ + ARRAYLIST(off_t) line_offsets; +}; + +void diff_output_info_free(struct diff_output_info *output_info); + struct diff_chunk_context { struct diff_range chunk; struct diff_range left, right; }; -int diff_output_plain(FILE *dest, const struct diff_input_info *info, - const struct diff_result *result); -int diff_output_unidiff(FILE *dest, const struct diff_input_info *info, - const struct diff_result *result, - unsigned int context_lines); +int diff_output_plain(struct diff_output_info **output_info, FILE *dest, + const struct diff_input_info *info, + const struct diff_result *result); +int diff_output_unidiff(struct diff_output_info **output_info, + FILE *dest, const struct diff_input_info *info, + const struct diff_result *result, + unsigned int context_lines); void diff_chunk_context_get(struct diff_chunk_context *cc, const struct diff_result *r, int chunk_idx, int context_lines); @@ -37,19 +50,18 @@ struct diff_output_unidiff_state; struct diff_output_unidiff_state *diff_output_unidiff_state_alloc(void); void diff_output_unidiff_state_reset(struct diff_output_unidiff_state *state); void diff_output_unidiff_state_free(struct diff_output_unidiff_state *state); -void diff_output_unidiff_chunk(FILE *dest, +int diff_output_unidiff_chunk(struct diff_output_info **output_info, FILE *dest, struct diff_output_unidiff_state *state, const struct diff_input_info *info, const struct diff_result *result, const struct diff_chunk_context *cc); -int diff_output_info(FILE *dest, const struct diff_input_info *info); -int diff_output_lines(FILE *dest, const char *prefix, - struct diff_atom *start_atom, unsigned int count); -void diff_output_chunk_left_version(FILE *dest, +int diff_output_chunk_left_version(struct diff_output_info **output_info, + FILE *dest, const struct diff_input_info *info, const struct diff_result *result, const struct diff_chunk_context *cc); -void diff_output_chunk_right_version(FILE *dest, +int diff_output_chunk_right_version(struct diff_output_info **output_info, + FILE *dest, const struct diff_input_info *info, const struct diff_result *result, const struct diff_chunk_context *cc); blob - eda757a32c6d6cb95825ee2b4489c06e6bcc620f blob + a4745ea9dd19e94f0bc9f34bbf25b3adf9395682 --- lib/diff_internal.h +++ lib/diff_internal.h @@ -209,3 +209,11 @@ struct diff_chunk *diff_state_add_chunk(struct diff_st unsigned int left_count, struct diff_atom *right_start, unsigned int right_count); + +struct diff_output_info; + +int diff_output_lines(struct diff_output_info *output_info, FILE *dest, + const char *prefix, struct diff_atom *start_atom, + unsigned int count); + +struct diff_output_info *diff_output_info_alloc(void); blob - 8ac957ca5b3d57def908e535c7400599c2fd52f8 blob + 823cce7950430e64d7aba91ed37f484972e5f568 --- lib/diff_output.c +++ lib/diff_output.c @@ -54,15 +54,27 @@ get_atom_byte(int *ch, struct diff_atom *atom, off_t o } int -diff_output_lines(FILE *dest, const char *prefix, struct diff_atom *start_atom, +diff_output_lines(struct diff_output_info *outinfo, FILE *dest, + const char *prefix, struct diff_atom *start_atom, unsigned int count) { struct diff_atom *atom; + off_t outoff = 0, *offp; int rc; + + if (outinfo && outinfo->line_offsets.len > 0) { + unsigned int idx = outinfo->line_offsets.len - 1; + outoff = outinfo->line_offsets.head[idx]; + } + foreach_diff_atom(atom, start_atom, count) { - fprintf(dest, "%s", prefix); + off_t outlen = 0; int i, ch; unsigned int len = atom->len; + rc = fprintf(dest, "%s", prefix); + if (rc < 0) + return errno; + outlen += rc; if (len) { rc = get_atom_byte(&ch, atom, len - 1); if (rc) @@ -83,55 +95,109 @@ diff_output_lines(FILE *dest, const char *prefix, stru if (rc) return rc; if ((ch < 0x20 || ch >= 0x7f) && ch != '\t') - fprintf(dest, "\\x%02x", (unsigned char)ch); + rc = fprintf(dest, "\\x%02x", (unsigned char)ch); else - fprintf(dest, "%c", ch); + rc = fprintf(dest, "%c", ch); + if (rc < 0) + return errno; + outlen += rc; } - fprintf(dest, "\n"); + rc = fprintf(dest, "\n"); + if (rc < 0) + return errno; + outlen += rc; + if (outinfo) { + ARRAYLIST_ADD(offp, outinfo->line_offsets); + outoff += outlen; + *offp = outoff; + } } - return 0; + return DIFF_RC_OK; } -void -diff_output_chunk_left_version(FILE *dest, +int +diff_output_chunk_left_version(struct diff_output_info **output_info, + FILE *dest, const struct diff_input_info *info, const struct diff_result *result, const struct diff_chunk_context *cc) { int c_idx; + struct diff_output_info *outinfo = NULL; if (diff_range_empty(&cc->left)) - return; + return DIFF_RC_OK; + if (output_info) { + *output_info = diff_output_info_alloc(); + if (*output_info == NULL) + return ENOMEM; + outinfo = *output_info; + } + /* Write out all chunks on the left side. */ 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) - diff_output_lines(dest, "", c->left_start, + diff_output_lines(outinfo, dest, "", c->left_start, c->left_count); } + + return DIFF_RC_OK; } -void -diff_output_chunk_right_version(FILE *dest, +int +diff_output_chunk_right_version(struct diff_output_info **output_info, + FILE *dest, const struct diff_input_info *info, const struct diff_result *result, const struct diff_chunk_context *cc) { int c_idx; + struct diff_output_info *outinfo = NULL; if (diff_range_empty(&cc->right)) - return; + return DIFF_RC_OK; + if (output_info) { + *output_info = diff_output_info_alloc(); + if (*output_info == NULL) + return ENOMEM; + outinfo = *output_info; + } + /* Write out all chunks on the right side. */ 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->right_count) - diff_output_lines(dest, "", c->right_start, + diff_output_lines(outinfo, dest, "", c->right_start, c->right_count); } + + return DIFF_RC_OK; } +struct diff_output_info * +diff_output_info_alloc(void) +{ + struct diff_output_info *output_info; + off_t *offp; + + output_info = malloc(sizeof(*output_info)); + if (output_info != NULL) { + ARRAYLIST_INIT(output_info->line_offsets, 128); + ARRAYLIST_ADD(offp, output_info->line_offsets); + *offp = 0; + } + return output_info; +} + +void +diff_output_info_free(struct diff_output_info *output_info) +{ + ARRAYLIST_FREE(output_info->line_offsets); + free(output_info); +} blob - b79b53f3bb025ac548d288c82aa34658347b35a0 blob + 86535a9ca5c0f5f6702c617b1158a45dae70cf89 --- lib/diff_output_plain.c +++ lib/diff_output_plain.c @@ -28,25 +28,38 @@ #include "diff_internal.h" int -diff_output_plain(FILE *dest, const struct diff_input_info *info, - const struct diff_result *result) +diff_output_plain(struct diff_output_info **output_info, FILE *dest, + const struct diff_input_info *info, + const struct diff_result *result) { + struct diff_output_info *outinfo = NULL; + int i; + if (!result) return EINVAL; if (result->rc != DIFF_RC_OK) return result->rc; + + if (output_info) { + *output_info = diff_output_info_alloc(); + if (*output_info == NULL) + return errno; + outinfo = *output_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 ? " " : "?", + diff_output_lines(outinfo, dest, + c->solved ? " " : "?", c->left_start, c->left_count); else if (c->left_count && !c->right_count) - diff_output_lines(dest, c->solved ? "-" : "?", + diff_output_lines(outinfo, dest, + c->solved ? "-" : "?", c->left_start, c->left_count); else if (c->right_count && !c->left_count) - diff_output_lines(dest, c->solved ? "+" : "?", + diff_output_lines(outinfo, dest, + c->solved ? "+" : "?", c->right_start, c->right_count); } return DIFF_RC_OK; blob - 1ba0e22228be6b72005e9491693117d038503646 blob + 0d570314af3d662046e4e1266c1bd783c95848a6 --- lib/diff_output_unidiff.c +++ lib/diff_output_unidiff.c @@ -110,26 +110,58 @@ diff_output_unidiff_state_free(struct diff_output_unid free(state); } -void -diff_output_unidiff_chunk(FILE *dest, struct diff_output_unidiff_state *state, - const struct diff_input_info *info, - const struct diff_result *result, - const struct diff_chunk_context *cc) +static int +output_unidiff_chunk(struct diff_output_info *outinfo, FILE *dest, + struct diff_output_unidiff_state *state, + const struct diff_input_info *info, + const struct diff_result *result, + const struct diff_chunk_context *cc) { + int rc; + off_t outoff = 0, outlen = 0, *offp; + if (diff_range_empty(&cc->left) && diff_range_empty(&cc->right)) - return; + return DIFF_RC_OK; + if (outinfo && outinfo->line_offsets.len > 0) { + unsigned int idx = outinfo->line_offsets.len - 1; + outoff = outinfo->line_offsets.head[idx]; + } + if (!(state->header_printed)) { - fprintf(dest, "--- %s\n+++ %s\n", - info->left_path ? : "a", - info->right_path ? : "b"); + rc = fprintf(dest, "--- %s\n", info->left_path ? : "a"); + if (rc < 0) + return errno; + if (outinfo) { + ARRAYLIST_ADD(offp, outinfo->line_offsets); + outoff += rc; + *offp = outoff; + + } + rc = fprintf(dest, "+++ %s\n", info->right_path ? : "b"); + if (rc < 0) + return errno; + if (outinfo) { + ARRAYLIST_ADD(offp, outinfo->line_offsets); + outoff += rc; + *offp = outoff; + + } state->header_printed = true; } - fprintf(dest, "@@ -%d,%d +%d,%d @@\n", + rc = 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); + if (rc < 0) + return errno; + if (outinfo) { + ARRAYLIST_ADD(offp, outinfo->line_offsets); + outoff += rc; + *offp = outoff; + } + /* 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 @@ -141,10 +173,13 @@ diff_output_unidiff_chunk(FILE *dest, struct diff_outp first_chunk = &result->chunks.head[cc->chunk.start]; chunk_start_line = diff_atom_root_idx(&result->left, first_chunk->left_start); - if (cc->left.start < chunk_start_line) - diff_output_lines(dest, " ", + if (cc->left.start < chunk_start_line) { + rc = diff_output_lines(outinfo, dest, " ", &result->left.atoms.head[cc->left.start], chunk_start_line - cc->left.start); + if (rc) + return rc; + } /* Now write out all the joined chunks and contexts between them */ int c_idx; @@ -152,17 +187,19 @@ diff_output_unidiff_chunk(FILE *dest, struct diff_outp const struct diff_chunk *c = &result->chunks.head[c_idx]; if (c->left_count && c->right_count) - diff_output_lines(dest, + rc = diff_output_lines(outinfo, dest, c->solved ? " " : "?", c->left_start, c->left_count); else if (c->left_count && !c->right_count) - diff_output_lines(dest, + rc = diff_output_lines(outinfo, dest, c->solved ? "-" : "?", c->left_start, c->left_count); else if (c->right_count && !c->left_count) - diff_output_lines(dest, + rc = diff_output_lines(outinfo, dest, c->solved ? "+" : "?", c->right_start, c->right_count); + if (rc) + return rc; } /* Trailing context? */ @@ -172,19 +209,46 @@ diff_output_unidiff_chunk(FILE *dest, struct diff_outp 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, " ", + if (cc->left.end > chunk_end_line) { + rc = diff_output_lines(outinfo, dest, " ", &result->left.atoms.head[chunk_end_line], cc->left.end - chunk_end_line); + if (rc) + return rc; + } + + return DIFF_RC_OK; } int -diff_output_unidiff(FILE *dest, const struct diff_input_info *info, +diff_output_unidiff_chunk(struct diff_output_info **output_info, FILE *dest, + struct diff_output_unidiff_state *state, + const struct diff_input_info *info, + const struct diff_result *result, + const struct diff_chunk_context *cc) +{ + struct diff_output_info *outinfo = NULL; + + if (output_info) { + *output_info = diff_output_info_alloc(); + if (*output_info == NULL) + return ENOMEM; + outinfo = *output_info; + } + + return output_unidiff_chunk(outinfo, dest, state, info, + result, cc); +} + +int +diff_output_unidiff(struct diff_output_info **output_info, + FILE *dest, const struct diff_input_info *info, const struct diff_result *result, unsigned int context_lines) { struct diff_output_unidiff_state *state; struct diff_chunk_context cc = {}; + struct diff_output_info *outinfo = NULL; int i; if (!result) @@ -192,10 +256,23 @@ diff_output_unidiff(FILE *dest, const struct diff_inpu if (result->rc != DIFF_RC_OK) return result->rc; + if (output_info) { + *output_info = diff_output_info_alloc(); + if (*output_info == NULL) + return ENOMEM; + outinfo = *output_info; + } + state = diff_output_unidiff_state_alloc(); - if (state == NULL) + if (state == NULL) { + if (output_info) { + diff_output_info_free(*output_info); + *output_info = NULL; + } return ENOMEM; + } + for (i = 0; i < result->chunks.len; i++) { struct diff_chunk *c = &result->chunks.head[i]; enum diff_chunk_type t = diff_chunk_type(c); @@ -244,14 +321,14 @@ diff_output_unidiff(FILE *dest, const struct diff_inpu 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, state, info, result, &cc); + output_unidiff_chunk(outinfo, dest, state, 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, state, info, result, &cc); + output_unidiff_chunk(outinfo, dest, state, info, result, &cc); diff_output_unidiff_state_free(state); return DIFF_RC_OK; }