1 /* Produce ed(1) script output from a diff_result. */ 2 /* 3 * Copyright (c) 2020 Neels Hofmeyr <neels@hofmeyr.de> 4 * Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <errno.h> 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <stdbool.h> 24 25 #include <arraylist.h> 26 #include <diff_main.h> 27 #include <diff_output.h> 28 29 #include "diff_internal.h" 30 31 static int 32 output_edscript_chunk(struct diff_output_info *outinfo, 33 FILE *dest, const struct diff_input_info *info, 34 const struct diff_result *result, 35 struct diff_chunk_context *cc) 36 { 37 off_t outoff = 0, *offp; 38 int left_start, left_len, right_start, right_len; 39 int rc; 40 41 left_len = cc->left.end - cc->left.start; 42 if (left_len < 0) 43 return EINVAL; 44 else if (result->left->atoms.len == 0) 45 left_start = 0; 46 else if (left_len == 0 && cc->left.start > 0) 47 left_start = cc->left.start; 48 else if (cc->left.end > 0) 49 left_start = cc->left.start + 1; 50 else 51 left_start = cc->left.start; 52 53 right_len = cc->right.end - cc->right.start; 54 if (right_len < 0) 55 return EINVAL; 56 else if (result->right->atoms.len == 0) 57 right_start = 0; 58 else if (right_len == 0 && cc->right.start > 0) 59 right_start = cc->right.start; 60 else if (cc->right.end > 0) 61 right_start = cc->right.start + 1; 62 else 63 right_start = cc->right.start; 64 65 if (left_len == 0) { 66 /* addition */ 67 if (right_len == 1) { 68 rc = fprintf(dest, "%da%d\n", left_start, right_start); 69 } else { 70 rc = fprintf(dest, "%da%d,%d\n", left_start, 71 right_start, cc->right.end); 72 } 73 } else if (right_len == 0) { 74 /* deletion */ 75 if (left_len == 1) { 76 rc = fprintf(dest, "%dd%d\n", left_start, 77 right_start); 78 } else { 79 rc = fprintf(dest, "%d,%dd%d\n", left_start, 80 cc->left.end, right_start); 81 } 82 } else { 83 /* change */ 84 if (left_len == 1 && right_len == 1) { 85 rc = fprintf(dest, "%dc%d\n", left_start, right_start); 86 } else if (left_len == 1) { 87 rc = fprintf(dest, "%dc%d,%d\n", left_start, 88 right_start, cc->right.end); 89 } else if (right_len == 1) { 90 rc = fprintf(dest, "%d,%dc%d\n", left_start, 91 cc->left.end, right_start); 92 } else { 93 rc = fprintf(dest, "%d,%dc%d,%d\n", left_start, 94 cc->left.end, right_start, cc->right.end); 95 } 96 } 97 if (rc < 0) 98 return errno; 99 if (outinfo) { 100 ARRAYLIST_ADD(offp, outinfo->line_offsets); 101 if (offp == NULL) 102 return ENOMEM; 103 outoff += rc; 104 *offp = outoff; 105 } 106 107 return DIFF_RC_OK; 108 } 109 110 int 111 diff_output_edscript(struct diff_output_info **output_info, 112 FILE *dest, const struct diff_input_info *info, 113 const struct diff_result *result) 114 { 115 struct diff_output_info *outinfo = NULL; 116 struct diff_chunk_context cc = {}; 117 int atomizer_flags = (result->left->atomizer_flags| 118 result->right->atomizer_flags); 119 int flags = (result->left->root->diff_flags | 120 result->right->root->diff_flags); 121 bool force_text = (flags & DIFF_FLAG_FORCE_TEXT_DATA); 122 bool have_binary = (atomizer_flags & DIFF_ATOMIZER_FOUND_BINARY_DATA); 123 int i, rc; 124 125 if (!result) 126 return EINVAL; 127 if (result->rc != DIFF_RC_OK) 128 return result->rc; 129 130 if (output_info) { 131 *output_info = diff_output_info_alloc(); 132 if (*output_info == NULL) 133 return ENOMEM; 134 outinfo = *output_info; 135 } 136 137 if (have_binary && !force_text) { 138 for (i = 0; i < result->chunks.len; i++) { 139 struct diff_chunk *c = &result->chunks.head[i]; 140 enum diff_chunk_type t = diff_chunk_type(c); 141 142 if (t != CHUNK_MINUS && t != CHUNK_PLUS) 143 continue; 144 145 fprintf(dest, "Binary files %s and %s differ\n", 146 diff_output_get_label_left(info), 147 diff_output_get_label_right(info)); 148 break; 149 } 150 151 return DIFF_RC_OK; 152 } 153 154 for (i = 0; i < result->chunks.len; i++) { 155 struct diff_chunk *chunk = &result->chunks.head[i]; 156 enum diff_chunk_type t = diff_chunk_type(chunk); 157 struct diff_chunk_context next; 158 159 if (t != CHUNK_MINUS && t != CHUNK_PLUS) 160 continue; 161 162 if (diff_chunk_context_empty(&cc)) { 163 /* Note down the start point, any number of subsequent 164 * chunks may be joined up to this chunk by being 165 * directly adjacent. */ 166 diff_chunk_context_get(&cc, result, i, 0); 167 continue; 168 } 169 170 /* There already is a previous chunk noted down for being 171 * printed. Does it join up with this one? */ 172 diff_chunk_context_get(&next, result, i, 0); 173 174 if (diff_chunk_contexts_touch(&cc, &next)) { 175 /* This next context touches or overlaps the previous 176 * one, join. */ 177 diff_chunk_contexts_merge(&cc, &next); 178 continue; 179 } 180 181 rc = output_edscript_chunk(outinfo, dest, info, result, &cc); 182 if (rc != DIFF_RC_OK) 183 return rc; 184 cc = next; 185 } 186 187 if (!diff_chunk_context_empty(&cc)) 188 return output_edscript_chunk(outinfo, dest, info, result, &cc); 189 return DIFF_RC_OK; 190 } 191