Commit Diff


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;
 }