Git fork
at reftables-rust 324 lines 8.1 kB view raw
1/* 2 * LibXDiff by Davide Libenzi ( File Differential Library ) 3 * Copyright (C) 2003 Davide Libenzi 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, see 17 * <http://www.gnu.org/licenses/>. 18 * 19 * Davide Libenzi <davidel@xmailserver.org> 20 * 21 */ 22 23#include "xinclude.h" 24 25 26static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) 27{ 28 xrecord_t *rec = &xdf->recs[ri]; 29 30 if (xdl_emit_diffrec(rec->ptr, rec->size, pre, strlen(pre), ecb) < 0) 31 return -1; 32 33 return 0; 34} 35 36static long saturating_add(long a, long b) 37{ 38 return signed_add_overflows(a, b) ? LONG_MAX : a + b; 39} 40 41/* 42 * Starting at the passed change atom, find the latest change atom to be included 43 * inside the differential hunk according to the specified configuration. 44 * Also advance xscr if the first changes must be discarded. 45 */ 46xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) 47{ 48 xdchange_t *xch, *xchp, *lxch; 49 long max_common = saturating_add(saturating_add(xecfg->ctxlen, 50 xecfg->ctxlen), 51 xecfg->interhunkctxlen); 52 long max_ignorable = xecfg->ctxlen; 53 long ignored = 0; /* number of ignored blank lines */ 54 55 /* remove ignorable changes that are too far before other changes */ 56 for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) { 57 xch = xchp->next; 58 59 if (xch == NULL || 60 xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable) 61 *xscr = xch; 62 } 63 64 if (!*xscr) 65 return NULL; 66 67 lxch = *xscr; 68 69 for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) { 70 long distance = xch->i1 - (xchp->i1 + xchp->chg1); 71 if (distance > max_common) 72 break; 73 74 if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) { 75 lxch = xch; 76 ignored = 0; 77 } else if (distance < max_ignorable && xch->ignore) { 78 ignored += xch->chg2; 79 } else if (lxch != xchp && 80 xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) { 81 break; 82 } else if (!xch->ignore) { 83 lxch = xch; 84 ignored = 0; 85 } else { 86 ignored += xch->chg2; 87 } 88 } 89 90 return lxch; 91} 92 93 94static long def_ff(const char *rec, long len, char *buf, long sz) 95{ 96 if (len > 0 && 97 (isalpha((unsigned char)*rec) || /* identifier? */ 98 *rec == '_' || /* also identifier? */ 99 *rec == '$')) { /* identifiers from VMS and other esoterico */ 100 if (len > sz) 101 len = sz; 102 while (0 < len && isspace((unsigned char)rec[len - 1])) 103 len--; 104 memcpy(buf, rec, len); 105 return len; 106 } 107 return -1; 108} 109 110static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri, 111 char *buf, long sz) 112{ 113 xrecord_t *rec = &xdf->recs[ri]; 114 115 if (!xecfg->find_func) 116 return def_ff(rec->ptr, rec->size, buf, sz); 117 return xecfg->find_func(rec->ptr, rec->size, buf, sz, xecfg->find_func_priv); 118} 119 120static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri) 121{ 122 char dummy[1]; 123 return match_func_rec(xdf, xecfg, ri, dummy, sizeof(dummy)) >= 0; 124} 125 126struct func_line { 127 long len; 128 char buf[80]; 129}; 130 131static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg, 132 struct func_line *func_line, long start, long limit) 133{ 134 long l, size, step = (start > limit) ? -1 : 1; 135 char *buf, dummy[1]; 136 137 buf = func_line ? func_line->buf : dummy; 138 size = func_line ? sizeof(func_line->buf) : sizeof(dummy); 139 140 for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) { 141 long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size); 142 if (len >= 0) { 143 if (func_line) 144 func_line->len = len; 145 return l; 146 } 147 } 148 return -1; 149} 150 151static int is_empty_rec(xdfile_t *xdf, long ri) 152{ 153 xrecord_t *rec = &xdf->recs[ri]; 154 long i = 0; 155 156 for (; i < rec->size && XDL_ISSPACE(rec->ptr[i]); i++); 157 158 return i == rec->size; 159} 160 161int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, 162 xdemitconf_t const *xecfg) { 163 long s1, s2, e1, e2, lctx; 164 xdchange_t *xch, *xche; 165 long funclineprev = -1; 166 struct func_line func_line = { 0 }; 167 168 for (xch = xscr; xch; xch = xche->next) { 169 xdchange_t *xchp = xch; 170 xche = xdl_get_hunk(&xch, xecfg); 171 if (!xch) 172 break; 173 174pre_context_calculation: 175 s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); 176 s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); 177 178 if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { 179 long fs1, i1 = xch->i1; 180 181 /* Appended chunk? */ 182 if (i1 >= xe->xdf1.nrec) { 183 long i2 = xch->i2; 184 185 /* 186 * We don't need additional context if 187 * a whole function was added. 188 */ 189 while (i2 < xe->xdf2.nrec) { 190 if (is_func_rec(&xe->xdf2, xecfg, i2)) 191 goto post_context_calculation; 192 i2++; 193 } 194 195 /* 196 * Otherwise get more context from the 197 * pre-image. 198 */ 199 i1 = xe->xdf1.nrec - 1; 200 } 201 202 fs1 = get_func_line(xe, xecfg, NULL, i1, -1); 203 while (fs1 > 0 && !is_empty_rec(&xe->xdf1, fs1 - 1) && 204 !is_func_rec(&xe->xdf1, xecfg, fs1 - 1)) 205 fs1--; 206 if (fs1 < 0) 207 fs1 = 0; 208 if (fs1 < s1) { 209 s2 = XDL_MAX(s2 - (s1 - fs1), 0); 210 s1 = fs1; 211 212 /* 213 * Did we extend context upwards into an 214 * ignored change? 215 */ 216 while (xchp != xch && 217 xchp->i1 + xchp->chg1 <= s1 && 218 xchp->i2 + xchp->chg2 <= s2) 219 xchp = xchp->next; 220 221 /* If so, show it after all. */ 222 if (xchp != xch) { 223 xch = xchp; 224 goto pre_context_calculation; 225 } 226 } 227 } 228 229 post_context_calculation: 230 lctx = xecfg->ctxlen; 231 lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1)); 232 lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2)); 233 234 e1 = xche->i1 + xche->chg1 + lctx; 235 e2 = xche->i2 + xche->chg2 + lctx; 236 237 if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { 238 long fe1 = get_func_line(xe, xecfg, NULL, 239 xche->i1 + xche->chg1, 240 xe->xdf1.nrec); 241 while (fe1 > 0 && is_empty_rec(&xe->xdf1, fe1 - 1)) 242 fe1--; 243 if (fe1 < 0) 244 fe1 = xe->xdf1.nrec; 245 if (fe1 > e1) { 246 e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec); 247 e1 = fe1; 248 } 249 250 /* 251 * Overlap with next change? Then include it 252 * in the current hunk and start over to find 253 * its new end. 254 */ 255 if (xche->next) { 256 long l = XDL_MIN(xche->next->i1, 257 xe->xdf1.nrec - 1); 258 if (l - xecfg->ctxlen <= e1 || 259 get_func_line(xe, xecfg, NULL, l, e1) < 0) { 260 xche = xche->next; 261 goto post_context_calculation; 262 } 263 } 264 } 265 266 /* 267 * Emit current hunk header. 268 */ 269 270 if (xecfg->flags & XDL_EMIT_FUNCNAMES) { 271 get_func_line(xe, xecfg, &func_line, 272 s1 - 1, funclineprev); 273 funclineprev = s1 - 1; 274 } 275 if (!(xecfg->flags & XDL_EMIT_NO_HUNK_HDR) && 276 xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, 277 func_line.buf, func_line.len, ecb) < 0) 278 return -1; 279 280 /* 281 * Emit pre-context. 282 */ 283 for (; s2 < xch->i2; s2++) 284 if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) 285 return -1; 286 287 for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) { 288 /* 289 * Merge previous with current change atom. 290 */ 291 for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++) 292 if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) 293 return -1; 294 295 /* 296 * Removes lines from the first file. 297 */ 298 for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++) 299 if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0) 300 return -1; 301 302 /* 303 * Adds lines from the second file. 304 */ 305 for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++) 306 if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0) 307 return -1; 308 309 if (xch == xche) 310 break; 311 s1 = xch->i1 + xch->chg1; 312 s2 = xch->i2 + xch->chg2; 313 } 314 315 /* 316 * Emit post-context. 317 */ 318 for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++) 319 if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) 320 return -1; 321 } 322 323 return 0; 324}