commit b7ba71f0ee2562b0d5877b72d4de351a8e78a119 from: Stefan Sperling date: Wed Oct 07 22:23:45 2020 UTC add a diff output mode which produces simple ed(1) scripts commit - 0c9a7e9d9f2c0efc643a722ca46b8144fadf531f commit + b7ba71f0ee2562b0d5877b72d4de351a8e78a119 blob - 918cc0e92e3b84fb7583803f03d886355ceb6c5a blob + 82af7ac31f556849d0d60cf77d5570d2d911e6b4 --- diff/Makefile +++ diff/Makefile @@ -12,6 +12,7 @@ SRCS= \ diff_output.c \ diff_output_plain.c \ diff_output_unidiff.c \ + diff_output_edscript.c \ ${END} MAN = ${PROG}.1 blob - 1bf72dd40864c6752c57ff4ca4ff782b973d8768 blob + 33779b232e5c0a49cb587d8763bf9deb96411d0d --- diff/diff.c +++ diff/diff.c @@ -34,7 +34,7 @@ #include __dead void usage(void); -int diffreg(char *, char *, bool, bool, int); +int diffreg(char *, char *, bool, bool, int, bool); FILE * openfile(const char *, char **, struct stat *); __dead void @@ -56,6 +56,7 @@ main(int argc, char *argv[]) { int ch, rc; bool do_patience = false, ignore_whitespace = false; + bool edscript = false; int context_lines = 3; while ((ch = getopt(argc, argv, "pwU:e")) != -1) { @@ -69,6 +70,9 @@ main(int argc, char *argv[]) case 'U': context_lines = atoi(optarg); break; + case 'e': + edscript = true; + break; default: usage(); } @@ -81,7 +85,7 @@ main(int argc, char *argv[]) usage(); rc = diffreg(argv[0], argv[1], do_patience, ignore_whitespace, - context_lines); + context_lines, edscript); if (rc != DIFF_RC_OK) { fprintf(stderr, "diff: %s\n", strerror(rc)); return 1; @@ -134,7 +138,7 @@ const struct diff_config diff_config_patience = { int diffreg(char *file1, char *file2, bool do_patience, bool ignore_whitespace, - int context_lines) + int context_lines, bool edscript) { char *str1, *str2; FILE *f1, *f2; @@ -161,7 +165,12 @@ diffreg(char *file1, char *file2, bool do_patience, bo #if 0 rc = diff_output_plain(stdout, &info, result); #else - rc = diff_output_unidiff(NULL, stdout, &info, result, context_lines); + if (edscript) + rc = diff_output_edscript(NULL, stdout, &info, result); + else { + rc = diff_output_unidiff(NULL, stdout, &info, result, + context_lines); + } #endif diff_result_free(result); blob - 20767052a7fd8ba51f93706796155ffdbfb04a82 blob + eb5ea9058a20bee4cbe0ab59e51b057ac8067878 --- include/diff_output.h +++ include/diff_output.h @@ -43,6 +43,9 @@ int diff_output_unidiff(struct diff_output_info **outp FILE *dest, const struct diff_input_info *info, const struct diff_result *result, unsigned int context_lines); +int diff_output_edscript(struct diff_output_info **output_info, + FILE *dest, const struct diff_input_info *info, + const struct diff_result *result); int diff_chunk_get_left_start(const struct diff_chunk *c, const struct diff_result *r, int context_lines); blob - 8bc527e0f06aeeca1e930f349552b5503cdae9c8 blob + 0549afc893dd068df20d061b9a9cc67d1251a9da --- lib/GNUmakefile +++ lib/GNUmakefile @@ -6,6 +6,7 @@ SRCS = \ diff_output.c \ diff_output_plain.c \ diff_output_unidiff.c \ + diff_output_edscript.c \ $(END) # Compat sources blob - /dev/null blob + 4ee960cab7a4020d7f2a8ed9b40e0a1bc9ae768a (mode 644) --- /dev/null +++ lib/diff_output_edscript.c @@ -0,0 +1,125 @@ +/* Produce ed(1) script output from a diff_result. */ +/* + * Copyright (c) 2020 Neels Hofmeyr + * Copyright (c) 2020 Stefan Sperling + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "diff_internal.h" + +static int +output_edscript_chunk(struct diff_output_info *outinfo, + FILE *dest, const struct diff_input_info *info, + const struct diff_result *result, + struct diff_chunk_context *cc, enum diff_chunk_type chunk_type) +{ + off_t outoff = 0, *offp; + int left_start, left_len, right_start, right_len; + int rc; + + left_len = cc->left.end - cc->left.start; + if (left_len < 0) + return EINVAL; + else if (left_len == 0 && cc->left.start > 0) + left_start = cc->left.start; + else + left_start = cc->left.start + 1; + + right_len = cc->right.end - cc->right.start; + if (right_len < 0) + return EINVAL; + else if (right_len == 0 && cc->right.start > 0) + right_start = cc->right.start; + else + right_start = cc->right.start + 1; + + if (chunk_type == CHUNK_MINUS) { + if (left_len == 1) { + rc = fprintf(dest, "%dd%d", left_start, right_start); + } else { + rc = fprintf(dest, "%d,%dd%d\n", left_start, + cc->left.end, right_start); + } + } else if (chunk_type == CHUNK_PLUS) { + if (right_len == 1) { + rc = fprintf(dest, "%da%d\n", left_start, right_start); + } else { + rc = fprintf(dest, "%da%d,%d\n", left_start, + right_start, cc->right.end); + } + } else + return EINVAL; + + if (rc < 0) + return errno; + if (outinfo) { + ARRAYLIST_ADD(offp, outinfo->line_offsets); + if (offp == NULL) + return ENOMEM; + outoff += rc; + *offp = outoff; + } + + return DIFF_RC_OK; +} + +int +diff_output_edscript(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, rc; + + 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 ENOMEM; + outinfo = *output_info; + } + + for (i = 0; i < result->chunks.len; i++) { + struct diff_chunk *chunk = &result->chunks.head[i]; + enum diff_chunk_type t = diff_chunk_type(chunk); + struct diff_chunk_context cc = {}; + + if (t != CHUNK_MINUS && t != CHUNK_PLUS) + continue; + + diff_chunk_context_get(&cc, result, i, 0); + + if (diff_range_empty(&cc.chunk)) + continue; + + rc = output_edscript_chunk(outinfo, dest, info, result, &cc, t); + if (rc != DIFF_RC_OK) + return rc; + } + + return DIFF_RC_OK; +}