xutils.c (9887B)
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 26 long xdl_bogosqrt(long n) { 27 long i; 28 29 /* 30 * Classical integer square root approximation using shifts. 31 */ 32 for (i = 1; n > 0; n >>= 2) 33 i <<= 1; 34 35 return i; 36 } 37 38 39 int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, 40 xdemitcb_t *ecb) { 41 int i = 2; 42 mmbuffer_t mb[3]; 43 44 mb[0].ptr = (char *) pre; 45 mb[0].size = psize; 46 mb[1].ptr = (char *) rec; 47 mb[1].size = size; 48 if (size > 0 && rec[size - 1] != '\n') { 49 mb[2].ptr = (char *) "\n\\ No newline at end of file\n"; 50 mb[2].size = (long)strlen(mb[2].ptr); 51 i++; 52 } 53 if (ecb->out_line(ecb->priv, mb, i) < 0) { 54 55 return -1; 56 } 57 58 return 0; 59 } 60 61 void *xdl_mmfile_first(mmfile_t *mmf, long *size) 62 { 63 *size = mmf->size; 64 return mmf->ptr; 65 } 66 67 68 long xdl_mmfile_size(mmfile_t *mmf) 69 { 70 return mmf->size; 71 } 72 73 74 int xdl_cha_init(chastore_t *cha, long isize, long icount) { 75 76 cha->head = cha->tail = NULL; 77 cha->isize = isize; 78 cha->nsize = icount * isize; 79 cha->ancur = cha->sncur = NULL; 80 cha->scurr = 0; 81 82 return 0; 83 } 84 85 86 void xdl_cha_free(chastore_t *cha) { 87 chanode_t *cur, *tmp; 88 89 for (cur = cha->head; (tmp = cur) != NULL;) { 90 cur = cur->next; 91 xdl_free(tmp); 92 } 93 } 94 95 96 void *xdl_cha_alloc(chastore_t *cha) { 97 chanode_t *ancur; 98 void *data; 99 100 if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) { 101 if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) { 102 103 return NULL; 104 } 105 ancur->icurr = 0; 106 ancur->next = NULL; 107 if (cha->tail) 108 cha->tail->next = ancur; 109 if (!cha->head) 110 cha->head = ancur; 111 cha->tail = ancur; 112 cha->ancur = ancur; 113 } 114 115 data = (char *) ancur + sizeof(chanode_t) + ancur->icurr; 116 ancur->icurr += cha->isize; 117 118 return data; 119 } 120 121 long xdl_guess_lines(mmfile_t *mf, long sample) { 122 long nl = 0, size, tsize = 0; 123 char const *data, *cur, *top; 124 125 if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) { 126 for (top = data + size; nl < sample && cur < top; ) { 127 nl++; 128 if (!(cur = memchr(cur, '\n', top - cur))) 129 cur = top; 130 else 131 cur++; 132 } 133 tsize += (long) (cur - data); 134 } 135 136 if (nl && tsize) 137 nl = xdl_mmfile_size(mf) / (tsize / nl); 138 139 return nl + 1; 140 } 141 142 int xdl_blankline(const char *line, long size, long flags) 143 { 144 long i; 145 146 if (!(flags & XDF_WHITESPACE_FLAGS)) 147 return (size <= 1); 148 149 for (i = 0; i < size && XDL_ISSPACE(line[i]); i++) 150 ; 151 152 return (i == size); 153 } 154 155 /* 156 * Have we eaten everything on the line, except for an optional 157 * CR at the very end? 158 */ 159 static int ends_with_optional_cr(const char *l, long s, long i) 160 { 161 int complete = s && l[s-1] == '\n'; 162 163 if (complete) 164 s--; 165 if (s == i) 166 return 1; 167 /* do not ignore CR at the end of an incomplete line */ 168 if (complete && s == i + 1 && l[i] == '\r') 169 return 1; 170 return 0; 171 } 172 173 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) 174 { 175 int i1, i2; 176 177 if (s1 == s2 && !memcmp(l1, l2, s1)) 178 return 1; 179 if (!(flags & XDF_WHITESPACE_FLAGS)) 180 return 0; 181 182 i1 = 0; 183 i2 = 0; 184 185 /* 186 * -w matches everything that matches with -b, and -b in turn 187 * matches everything that matches with --ignore-space-at-eol, 188 * which in turn matches everything that matches with --ignore-cr-at-eol. 189 * 190 * Each flavor of ignoring needs different logic to skip whitespaces 191 * while we have both sides to compare. 192 */ 193 if (flags & XDF_IGNORE_WHITESPACE) { 194 goto skip_ws; 195 while (i1 < s1 && i2 < s2) { 196 if (l1[i1++] != l2[i2++]) 197 return 0; 198 skip_ws: 199 while (i1 < s1 && XDL_ISSPACE(l1[i1])) 200 i1++; 201 while (i2 < s2 && XDL_ISSPACE(l2[i2])) 202 i2++; 203 } 204 } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { 205 while (i1 < s1 && i2 < s2) { 206 if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) { 207 /* Skip matching spaces and try again */ 208 while (i1 < s1 && XDL_ISSPACE(l1[i1])) 209 i1++; 210 while (i2 < s2 && XDL_ISSPACE(l2[i2])) 211 i2++; 212 continue; 213 } 214 if (l1[i1++] != l2[i2++]) 215 return 0; 216 } 217 } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) { 218 while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { 219 i1++; 220 i2++; 221 } 222 } else if (flags & XDF_IGNORE_CR_AT_EOL) { 223 /* Find the first difference and see how the line ends */ 224 while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { 225 i1++; 226 i2++; 227 } 228 return (ends_with_optional_cr(l1, s1, i1) && 229 ends_with_optional_cr(l2, s2, i2)); 230 } 231 232 /* 233 * After running out of one side, the remaining side must have 234 * nothing but whitespace for the lines to match. Note that 235 * ignore-whitespace-at-eol case may break out of the loop 236 * while there still are characters remaining on both lines. 237 */ 238 if (i1 < s1) { 239 while (i1 < s1 && XDL_ISSPACE(l1[i1])) 240 i1++; 241 if (s1 != i1) 242 return 0; 243 } 244 if (i2 < s2) { 245 while (i2 < s2 && XDL_ISSPACE(l2[i2])) 246 i2++; 247 return (s2 == i2); 248 } 249 return 1; 250 } 251 252 static unsigned long xdl_hash_record_with_whitespace(char const **data, 253 char const *top, long flags) { 254 unsigned long ha = 5381; 255 char const *ptr = *data; 256 int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL; 257 258 for (; ptr < top && *ptr != '\n'; ptr++) { 259 if (cr_at_eol_only) { 260 /* do not ignore CR at the end of an incomplete line */ 261 if (*ptr == '\r' && 262 (ptr + 1 < top && ptr[1] == '\n')) 263 continue; 264 } 265 else if (XDL_ISSPACE(*ptr)) { 266 const char *ptr2 = ptr; 267 int at_eol; 268 while (ptr + 1 < top && XDL_ISSPACE(ptr[1]) 269 && ptr[1] != '\n') 270 ptr++; 271 at_eol = (top <= ptr + 1 || ptr[1] == '\n'); 272 if (flags & XDF_IGNORE_WHITESPACE) 273 ; /* already handled */ 274 else if (flags & XDF_IGNORE_WHITESPACE_CHANGE 275 && !at_eol) { 276 ha += (ha << 5); 277 ha ^= (unsigned long) ' '; 278 } 279 else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL 280 && !at_eol) { 281 while (ptr2 != ptr + 1) { 282 ha += (ha << 5); 283 ha ^= (unsigned long) *ptr2; 284 ptr2++; 285 } 286 } 287 continue; 288 } 289 ha += (ha << 5); 290 ha ^= (unsigned long) *ptr; 291 } 292 *data = ptr < top ? ptr + 1: ptr; 293 294 return ha; 295 } 296 297 unsigned long xdl_hash_record(char const **data, char const *top, long flags) { 298 unsigned long ha = 5381; 299 char const *ptr = *data; 300 301 if (flags & XDF_WHITESPACE_FLAGS) 302 return xdl_hash_record_with_whitespace(data, top, flags); 303 304 for (; ptr < top && *ptr != '\n'; ptr++) { 305 ha += (ha << 5); 306 ha ^= (unsigned long) *ptr; 307 } 308 *data = ptr < top ? ptr + 1: ptr; 309 310 return ha; 311 } 312 313 unsigned int xdl_hashbits(unsigned int size) { 314 unsigned int val = 1, bits = 0; 315 316 for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++); 317 return bits ? bits: 1; 318 } 319 320 321 int xdl_num_out(char *out, long val) { 322 char *ptr, *str = out; 323 char buf[32]; 324 325 ptr = buf + sizeof(buf) - 1; 326 *ptr = '\0'; 327 if (val < 0) { 328 *--ptr = '-'; 329 val = -val; 330 } 331 for (; val && ptr > buf; val /= 10) 332 *--ptr = "0123456789"[val % 10]; 333 if (*ptr) 334 for (; *ptr; ptr++, str++) 335 *str = *ptr; 336 else 337 *str++ = '0'; 338 *str = '\0'; 339 340 return str - out; 341 } 342 343 static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2, 344 const char *func, long funclen, 345 xdemitcb_t *ecb) { 346 int nb = 0; 347 mmbuffer_t mb; 348 char buf[128]; 349 350 memcpy(buf, "@@ -", 4); 351 nb += 4; 352 353 nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1); 354 355 if (c1 != 1) { 356 memcpy(buf + nb, ",", 1); 357 nb += 1; 358 359 nb += xdl_num_out(buf + nb, c1); 360 } 361 362 memcpy(buf + nb, " +", 2); 363 nb += 2; 364 365 nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1); 366 367 if (c2 != 1) { 368 memcpy(buf + nb, ",", 1); 369 nb += 1; 370 371 nb += xdl_num_out(buf + nb, c2); 372 } 373 374 memcpy(buf + nb, " @@", 3); 375 nb += 3; 376 if (func && funclen) { 377 buf[nb++] = ' '; 378 if (funclen > (long)sizeof(buf) - nb - 1) 379 funclen = sizeof(buf) - nb - 1; 380 memcpy(buf + nb, func, funclen); 381 nb += funclen; 382 } 383 buf[nb++] = '\n'; 384 385 mb.ptr = buf; 386 mb.size = nb; 387 if (ecb->out_line(ecb->priv, &mb, 1) < 0) 388 return -1; 389 return 0; 390 } 391 392 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, 393 const char *func, long funclen, 394 xdemitcb_t *ecb) { 395 if (!ecb->out_hunk) 396 return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb); 397 if (ecb->out_hunk(ecb->priv, 398 c1 ? s1 : s1 - 1, c1, 399 c2 ? s2 : s2 - 1, c2, 400 func, funclen) < 0) 401 return -1; 402 return 0; 403 } 404 405 int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, 406 int line1, int count1, int line2, int count2) 407 { 408 /* 409 * This probably does not work outside Git, since 410 * we have a very simple mmfile structure. 411 * 412 * Note: ideally, we would reuse the prepared environment, but 413 * the libxdiff interface does not (yet) allow for diffing only 414 * ranges of lines instead of the whole files. 415 */ 416 mmfile_t subfile1, subfile2; 417 xdfenv_t env; 418 419 subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr; 420 subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr + 421 diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr; 422 subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr; 423 subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr + 424 diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr; 425 if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0) 426 return -1; 427 428 memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1); 429 memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2); 430 431 xdl_free_env(&env); 432 433 return 0; 434 }