1/*- 2 * Copyright (c) 2015 Hans Petter Selasky. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28#include <stdio.h> 29#include <stdint.h> 30#include <sys/queue.h> 31#include <sysexits.h> 32#include <err.h> 33#include <fcntl.h> 34#include <stdlib.h> 35#include <unistd.h> 36#include <string.h> 37#include <ctype.h> 38#include <signal.h> 39 40extern char **environ; 41 42static int opt_verbose; 43static char *opt_diff_tool; 44 45#define BLOCK_SIZE 4096 46#define BLOCK_MASK 0x100 47#define BLOCK_ADD 0x200 48 49struct block { 50 TAILQ_ENTRY(block) entry; 51 uint32_t length; 52 uint32_t flags; 53 uint8_t *data; 54 uint8_t *mask; 55}; 56 57typedef TAILQ_HEAD(, block) block_head_t; 58 59static void 60sigpipe(int sig) 61{ 62} 63 64static struct block * 65alloc_block(void) 66{ 67 struct block *pb; 68 size_t size = sizeof(*pb) + (2 * BLOCK_SIZE); 69 70 pb = malloc(size); 71 if (pb == NULL) 72 errx(EX_SOFTWARE, "Out of memory"); 73 memset(pb, 0, size); 74 pb->data = (void *)(pb + 1); 75 pb->mask = pb->data + BLOCK_SIZE; 76 pb->length = BLOCK_SIZE; 77 return (pb); 78} 79 80static int 81write_block(int fd, block_head_t *ph) 82{ 83 struct block *ptr; 84 85 if (fd < 0) 86 return (-1); 87 88 TAILQ_FOREACH(ptr, ph, entry) { 89 if (write(fd, ptr->data, ptr->length) != ptr->length) 90 return (-1); 91 } 92 return (0); 93} 94 95static uint16_t 96peek_block(block_head_t *pbh, uint64_t off) 97{ 98 struct block *ptr; 99 100 TAILQ_FOREACH(ptr, pbh, entry) { 101 if (off < ptr->length) 102 break; 103 off -= ptr->length; 104 } 105 if (ptr == NULL) 106 return (0); 107 return (ptr->data[off] | (ptr->mask[off] << 8)); 108} 109 110static void 111set_block(block_head_t *pbh, uint64_t off, uint16_t ch) 112{ 113 struct block *ptr; 114 115 TAILQ_FOREACH(ptr, pbh, entry) { 116 if (off < ptr->length) 117 break; 118 off -= ptr->length; 119 } 120 if (ptr == NULL) 121 return; 122 ptr->data[off] = ch & 0xFF; 123 ptr->mask[off] = (ch >> 8) & 0xFF; 124} 125 126static uint64_t 127size_block(block_head_t *pbh) 128{ 129 struct block *ptr; 130 uint64_t off = 0; 131 132 TAILQ_FOREACH(ptr, pbh, entry) 133 off += ptr->length; 134 return (off); 135} 136 137static int 138diff_tool(block_head_t *pa, block_head_t *pb) 139{ 140 char ca[] = {"/tmp/diff.orig.XXXXXX"}; 141 char cb[] = {"/tmp/diff.styled.XXXXXX"}; 142 char cc[256]; 143 uint64_t sa; 144 uint64_t sb; 145 uint64_t s; 146 uint64_t x; 147 int fa; 148 int fb; 149 150 sa = size_block(pa); 151 sb = size_block(pb); 152 s = (sa > sb) ? sa : sb; 153 154 for (x = 0; x != s; x++) { 155 char cha = peek_block(pa, x) & 0xFF; 156 char chb = peek_block(pb, x) & 0xFF; 157 158 if (cha != chb) { 159 /* false positive */ 160 if (cha == '\n' && chb == 0 && x == sa - 1) 161 return (0); 162 break; 163 } 164 } 165 if (x == s) 166 return (0); /* identical */ 167 168 fa = mkstemp(ca); 169 fb = mkstemp(cb); 170 171 if (write_block(fa, pa) < 0 || write_block(fb, pb) < 0) { 172 close(fa); 173 close(fb); 174 unlink(ca); 175 unlink(cb); 176 err(EX_SOFTWARE, "Could not write data to temporary files"); 177 } 178 close(fa); 179 close(fb); 180 181 snprintf(cc, sizeof(cc), "%s %s %s", opt_diff_tool, ca, cb); 182 system(cc); 183 184 unlink(ca); 185 unlink(cb); 186 return (-1); 187} 188 189static int 190diff_block(block_head_t *pa, block_head_t *pb) 191{ 192 uint64_t sa = size_block(pa); 193 uint64_t sb = size_block(pb); 194 uint64_t s; 195 uint64_t x; 196 uint64_t y; 197 uint64_t n; 198 199 s = (sa > sb) ? sa : sb; 200 201 for (y = x = 0; x != s; x++) { 202 char cha = peek_block(pa, x) & 0xFF; 203 char chb = peek_block(pb, x) & 0xFF; 204 205 if (cha != chb) { 206 int nonspace; 207 208 /* false positive */ 209 if (cha == '\n' && chb == 0 && x == sa - 1) 210 return (0); 211 212 n = x - y; 213 printf("Style error:\n"); 214 nonspace = 0; 215 for (n = y; n < sa; n++) { 216 char ch = peek_block(pa, n) & 0xFF; 217 218 if (nonspace && ch == '\n') 219 break; 220 printf("%c", ch); 221 if (!isspace(ch)) 222 nonspace = 1; 223 } 224 printf("\n"); 225 printf("Style corrected:\n"); 226 nonspace = 0; 227 for (n = y; n < sb; n++) { 228 char ch = peek_block(pb, n) & 0xFF; 229 230 if (nonspace && ch == '\n') 231 break; 232 printf("%c", ch); 233 if (!isspace(ch)) 234 nonspace = 1; 235 } 236 printf("\n"); 237 for (n = y; n != x; n++) { 238 if ((peek_block(pa, n) & 0xFF) == '\t') 239 printf("\t"); 240 else 241 printf(" "); 242 } 243 printf("^ %sdifference%s\n", 244 (isspace(cha) || isspace(chb)) ? "whitespace " : "", 245 (x >= sa || x >= sb) ? " in the end of a block" : ""); 246 return (1); 247 } else if (cha == '\n') { 248 y = x + 1; 249 } 250 } 251 return (0); 252} 253 254static void 255free_block(block_head_t *pbh) 256{ 257 struct block *ptr; 258 259 while ((ptr = TAILQ_FIRST(pbh))) { 260 TAILQ_REMOVE(pbh, ptr, entry); 261 free(ptr); 262 } 263} 264 265static void 266cmd_popen(char *command, FILE **iop) 267{ 268 char *argv[4]; 269 int pdes[4]; 270 int pid; 271 272 if (pipe(pdes) < 0) 273 goto error; 274 275 if (pipe(pdes + 2) < 0) { 276 close(pdes[0]); 277 close(pdes[1]); 278 goto error; 279 } 280 argv[0] = "sh"; 281 argv[1] = "-c"; 282 argv[2] = command; 283 argv[3] = NULL; 284 285 switch ((pid = vfork())) { 286 case -1: /* Error. */ 287 close(pdes[0]); 288 close(pdes[1]); 289 close(pdes[2]); 290 close(pdes[3]); 291 goto error; 292 case 0: /* Child. */ 293 dup2(pdes[1], STDOUT_FILENO); 294 dup2(pdes[2], STDIN_FILENO); 295 close(pdes[0]); 296 close(pdes[3]); 297 execve("/bin/sh", argv, environ); 298 exit(127); 299 default: 300 break; 301 } 302 iop[0] = fdopen(pdes[3], "w"); 303 iop[1] = fdopen(pdes[0], "r"); 304 close(pdes[1]); 305 close(pdes[2]); 306 return; 307error: 308 iop[0] = iop[1] = NULL; 309} 310 311static void 312cmd_block_process(block_head_t *pbh_in, block_head_t *pbh_out, char *cmd_str) 313{ 314 FILE *pfd[2]; 315 struct block *ptr; 316 317 TAILQ_INIT(pbh_out); 318 319 cmd_popen(cmd_str, pfd); 320 321 if (pfd[0] == NULL || pfd[1] == NULL) 322 errx(EX_SOFTWARE, "Cannot invoke command '%s'", cmd_str); 323 324 if (pbh_in != NULL) { 325 TAILQ_FOREACH(ptr, pbh_in, entry) { 326 if (fwrite(ptr->data, 1, ptr->length, pfd[0]) != ptr->length) 327 err(EX_SOFTWARE, "Cannot write all data to command '%s'", cmd_str); 328 } 329 fflush(pfd[0]); 330 } 331 fclose(pfd[0]); 332 333 while (1) { 334 int len; 335 336 ptr = alloc_block(); 337 len = fread(ptr->data, 1, BLOCK_SIZE, pfd[1]); 338 if (len <= 0) { 339 free(ptr); 340 break; 341 } 342 ptr->length = len; 343 TAILQ_INSERT_TAIL(pbh_out, ptr, entry); 344 } 345 fclose(pfd[1]); 346} 347 348static void 349usage(void) 350{ 351 fprintf(stderr, 352 "indent_wrapper [-v] [-d] [-D] [-g <githash>]\n" 353 "\t" "[-s <svnrevision> ] [ -t <tool> ] [ -c <command> ]\n" 354 "\t" "-v Increase verbosity\n" 355 "\t" "-d Check output from git diff\n" 356 "\t" "-D Check output from svn diff\n" 357 "\t" "-c <cmd> Set custom command to produce diff\n" 358 "\t" "-g <hash> Check output from git hash\n" 359 "\t" "-s <rev> Check output from svn revision\n" 360 "\t" "-t <tool> Launch external diff tool\n" 361 "\n" 362 "Examples:\n" 363 "\t" "indent_wrapper -D\n" 364 "\t" "indent_wrapper -D -t meld\n" 365 "\t" "indent_wrapper -D -t \"diff -u\"\n"); 366 exit(EX_SOFTWARE); 367} 368 369int 370main(int argc, char **argv) 371{ 372 block_head_t diff_head; 373 block_head_t diff_a_head; 374 block_head_t diff_b_head; 375 block_head_t indent_in_head; 376 block_head_t indent_out_head; 377 struct block *p1 = NULL; 378 struct block *p2 = NULL; 379 uint64_t size; 380 uint64_t x; 381 uint64_t y1 = 0; 382 uint64_t y2 = 0; 383 int recurse = 0; 384 int inside_string = 0; 385 int escape_char = 0; 386 int do_parse = 0; 387 char cmdbuf[256]; 388 uint16_t ch; 389 uint16_t chn; 390 int c; 391 int retval = 0; 392 393 signal(SIGPIPE, &sigpipe); 394 395 cmdbuf[0] = 0; 396 397 while ((c = getopt(argc, argv, "dDvg:s:c:ht:")) != -1) { 398 switch (c) { 399 case 'v': 400 opt_verbose++; 401 break; 402 case 't': 403 opt_diff_tool = optarg; 404 break; 405 case 'g': 406 snprintf(cmdbuf, sizeof(cmdbuf), "git show -U1000000 %s", optarg); 407 break; 408 case 'd': 409 snprintf(cmdbuf, sizeof(cmdbuf), "git diff -U1000000"); 410 break; 411 case 'D': 412 snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000"); 413 break; 414 case 's': 415 snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000 -r %s", optarg); 416 break; 417 case 'c': 418 snprintf(cmdbuf, sizeof(cmdbuf), "%s", optarg); 419 break; 420 default: 421 usage(); 422 } 423 } 424 if (cmdbuf[0] == 0) 425 usage(); 426 427 cmd_block_process(NULL, &diff_head, cmdbuf); 428 429 TAILQ_INIT(&diff_a_head); 430 TAILQ_INIT(&diff_b_head); 431 432 size = size_block(&diff_head); 433 p1 = alloc_block(); 434 y1 = 0; 435 p2 = alloc_block(); 436 y2 = 0; 437 438 for (x = 0; x < size;) { 439 ch = peek_block(&diff_head, x); 440 switch (ch & 0xFF) { 441 case '+': 442 if (ch == peek_block(&diff_head, x + 1) && 443 ch == peek_block(&diff_head, x + 2) && 444 ' ' == (peek_block(&diff_head, x + 3) & 0xFF)) 445 goto parse_filename; 446 if (do_parse == 0) 447 break; 448 for (x++; x != size; x++) { 449 ch = peek_block(&diff_head, x); 450 p1->mask[y1] = BLOCK_ADD >> 8; 451 p1->data[y1++] = ch; 452 if (y1 == BLOCK_SIZE) { 453 TAILQ_INSERT_TAIL(&diff_a_head, p1, entry); 454 p1 = alloc_block(); 455 y1 = 0; 456 } 457 if ((ch & 0xFF) == '\n') 458 break; 459 } 460 break; 461 case '-': 462 if (ch == peek_block(&diff_head, x + 1) && 463 ch == peek_block(&diff_head, x + 2) && 464 ' ' == (peek_block(&diff_head, x + 3) & 0xFF)) 465 goto parse_filename; 466 if (do_parse == 0) 467 break; 468 for (x++; x != size; x++) { 469 ch = peek_block(&diff_head, x); 470 p2->data[y2++] = ch; 471 if (y2 == BLOCK_SIZE) { 472 TAILQ_INSERT_TAIL(&diff_b_head, p2, entry); 473 p2 = alloc_block(); 474 y2 = 0; 475 } 476 if ((ch & 0xFF) == '\n') 477 break; 478 } 479 break; 480 case ' ': 481 if (do_parse == 0) 482 break; 483 for (x++; x != size; x++) { 484 ch = peek_block(&diff_head, x); 485 p1->data[y1++] = ch; 486 if (y1 == BLOCK_SIZE) { 487 TAILQ_INSERT_TAIL(&diff_a_head, p1, entry); 488 p1 = alloc_block(); 489 y1 = 0; 490 } 491 p2->data[y2++] = ch; 492 if (y2 == BLOCK_SIZE) { 493 TAILQ_INSERT_TAIL(&diff_b_head, p2, entry); 494 p2 = alloc_block(); 495 y2 = 0; 496 } 497 if ((ch & 0xFF) == '\n') 498 break; 499 } 500 break; 501 parse_filename: 502 for (x += 3; x != size; x++) { 503 ch = peek_block(&diff_head, x); 504 chn = peek_block(&diff_head, x + 1); 505 if ((ch & 0xFF) == '.') { 506 /* only accept .c and .h files */ 507 do_parse = ((chn & 0xFF) == 'c' || (chn & 0xFF) == 'h'); 508 } 509 if ((ch & 0xFF) == '\n') 510 break; 511 } 512 default: 513 break; 514 } 515 /* skip till end of line */ 516 for (; x < size; x++) { 517 ch = peek_block(&diff_head, x); 518 if ((ch & 0xFF) == '\n') { 519 x++; 520 break; 521 } 522 } 523 } 524 p1->length = y1; 525 p2->length = y2; 526 TAILQ_INSERT_TAIL(&diff_a_head, p1, entry); 527 TAILQ_INSERT_TAIL(&diff_b_head, p2, entry); 528 529 /* first pass - verify input */ 530 size = size_block(&diff_a_head); 531 for (x = 0; x != size; x++) { 532 ch = peek_block(&diff_a_head, x) & 0xFF; 533 if (!(ch & 0x80) && ch != '\t' && ch != '\r' && ch != '\n' && 534 ch != ' ' && !isprint(ch)) 535 errx(EX_SOFTWARE, "Non printable characters are not allowed: '%c'", ch); 536 else if (ch & 0x80) { 537 set_block(&diff_a_head, x, ch | BLOCK_MASK); 538 } 539 } 540 541 /* second pass - identify all comments */ 542 for (x = 0; x < size; x++) { 543 ch = peek_block(&diff_a_head, x); 544 chn = peek_block(&diff_a_head, x + 1); 545 if ((ch & 0xFF) == '/' && (chn & 0xFF) == '/') { 546 set_block(&diff_a_head, x, ch | BLOCK_MASK); 547 set_block(&diff_a_head, x + 1, chn | BLOCK_MASK); 548 for (x += 2; x < size; x++) { 549 ch = peek_block(&diff_a_head, x); 550 if ((ch & 0xFF) == '\n') 551 break; 552 set_block(&diff_a_head, x, ch | BLOCK_MASK); 553 } 554 } else if ((ch & 0xFF) == '/' && (chn & 0xFF) == '*') { 555 set_block(&diff_a_head, x, ch | BLOCK_MASK); 556 set_block(&diff_a_head, x + 1, chn | BLOCK_MASK); 557 for (x += 2; x < size; x++) { 558 ch = peek_block(&diff_a_head, x); 559 chn = peek_block(&diff_a_head, x + 1); 560 if ((ch & 0xFF) == '*' && (chn & 0xFF) == '/') { 561 set_block(&diff_a_head, x, ch | BLOCK_MASK); 562 set_block(&diff_a_head, x + 1, chn | BLOCK_MASK); 563 x++; 564 break; 565 } 566 set_block(&diff_a_head, x, ch | BLOCK_MASK); 567 } 568 } 569 } 570 571 /* third pass - identify preprocessor tokens and strings */ 572 for (x = 0; x < size; x++) { 573 ch = peek_block(&diff_a_head, x); 574 if (ch & BLOCK_MASK) 575 continue; 576 if (inside_string == 0 && (ch & 0xFF) == '#') { 577 int skip_newline = 0; 578 579 set_block(&diff_a_head, x, ch | BLOCK_MASK); 580 for (x++; x < size; x++) { 581 ch = peek_block(&diff_a_head, x); 582 if ((ch & 0xFF) == '\n') { 583 if (!skip_newline) 584 break; 585 skip_newline = 0; 586 } 587 if (ch & BLOCK_MASK) 588 continue; 589 if ((ch & 0xFF) == '\\') 590 skip_newline = 1; 591 set_block(&diff_a_head, x, ch | BLOCK_MASK); 592 } 593 } 594 if ((ch & 0xFF) == '"' || (ch & 0xFF) == '\'') { 595 if (inside_string == 0) { 596 inside_string = (ch & 0xFF); 597 } else { 598 if (escape_char == 0 && inside_string == (ch & 0xFF)) 599 inside_string = 0; 600 } 601 escape_char = 0; 602 set_block(&diff_a_head, x, ch | BLOCK_MASK); 603 } else if (inside_string != 0) { 604 if ((ch & 0xFF) == '\\') 605 escape_char = !escape_char; 606 else 607 escape_char = 0; 608 set_block(&diff_a_head, x, ch | BLOCK_MASK); 609 } 610 } 611 612 /* fourth pass - identify function blocks */ 613 if (opt_verbose > 0) { 614 chn = peek_block(&diff_a_head, x); 615 printf("L%02d%c|", recurse, 616 (chn & BLOCK_ADD) ? '+' : ' '); 617 } 618 for (x = 0; x < size; x++) { 619 ch = peek_block(&diff_a_head, x); 620 if (opt_verbose > 0) { 621 printf("%c", ch & 0xFF); 622 if ((ch & 0xFF) == '\n') { 623 chn = peek_block(&diff_a_head, x + 1); 624 printf("L%02d%c|", recurse, 625 (chn & BLOCK_ADD) ? '+' : ' '); 626 } 627 } 628 if (ch & BLOCK_MASK) 629 continue; 630 switch (ch & 0xFF) { 631 case '{': 632 case '(': 633 recurse++; 634 break; 635 default: 636 break; 637 } 638 if (recurse != 0) 639 set_block(&diff_a_head, x, ch | BLOCK_MASK); 640 switch (ch & 0xFF) { 641 case '}': 642 case ')': 643 recurse--; 644 break; 645 default: 646 break; 647 } 648 } 649 if (opt_verbose > 0) 650 printf("\n"); 651 if (recurse != 0) 652 errx(EX_SOFTWARE, "Unbalanced parenthesis"); 653 if (inside_string != 0) 654 errx(EX_SOFTWARE, "String without end"); 655 656 /* fifth pass - on the same line statements */ 657 for (x = 0; x < size; x++) { 658 ch = peek_block(&diff_a_head, x); 659 if (ch & BLOCK_MASK) 660 continue; 661 switch (ch & 0xFF) { 662 case '\n': 663 break; 664 default: 665 set_block(&diff_a_head, x, ch | BLOCK_MASK); 666 break; 667 } 668 } 669 670 /* sixth pass - output relevant blocks to indent */ 671 for (y1 = x = 0; x < size; x++) { 672 ch = peek_block(&diff_a_head, x); 673 if (ch & BLOCK_ADD) { 674 TAILQ_INIT(&indent_in_head); 675 676 p2 = alloc_block(); 677 y2 = 0; 678 for (; y1 < size; y1++) { 679 ch = peek_block(&diff_a_head, y1); 680 if (y1 > x && !(ch & (BLOCK_MASK | BLOCK_ADD))) 681 break; 682 p2->data[y2++] = ch & 0xFF; 683 if (y2 == BLOCK_SIZE) { 684 TAILQ_INSERT_TAIL(&indent_in_head, p2, entry); 685 p2 = alloc_block(); 686 y2 = 0; 687 } 688 } 689 if (p2->data[y2] != '\n') 690 p2->data[y2++] = '\n'; 691 p2->length = y2; 692 TAILQ_INSERT_TAIL(&indent_in_head, p2, entry); 693 694 cmd_block_process(&indent_in_head, &indent_out_head, 695 "indent " 696 "-Tbool " 697 "-Tclass " 698 "-TFILE " 699 "-TLIST_ENTRY " 700 "-TLIST_HEAD " 701 "-TSLIST_ENTRY " 702 "-TSLIST_HEAD " 703 "-TSTAILQ_ENTRY " 704 "-TSTAILQ_HEAD " 705 "-TTAILQ_ENTRY " 706 "-TTAILQ_HEAD " 707 "-T__aligned " 708 "-T__packed " 709 "-T__unused " 710 "-T__used " 711 "-Tfd_set " 712 "-Toss_mixerinfo " 713 "-Tu_char " 714 "-Tu_int " 715 "-Tu_long " 716 "-Tu_short " 717 "-ta -st -bad -bap -nbbb -nbc -br -nbs " 718 "-c41 -cd41 -cdb -ce -ci4 -cli0 -d0 -di8 -ndj -ei -nfc1 " 719 "-nfcb -i8 -ip8 -l79 -lc77 -ldi0 -nlp -npcs -psl -sc " 720 "-nsob -nv " 721 " | " 722 "sed " 723 "-e 's/_HEAD [(]/_HEAD(/g' " 724 "-e 's/_ENTRY [(]/_ENTRY(/g' " 725 "-e 's/\t__aligned/ __aligned/g' " 726 "-e 's/\t__packed/ __packed/g' " 727 "-e 's/\t__unused/ __unused/g' " 728 "-e 's/\t__used/ __used/g' " 729 "-e 's/^#define /#define\t/g'"); 730 731 if (opt_diff_tool != NULL) { 732 if (diff_tool(&indent_in_head, &indent_out_head)) 733 retval = 1; 734 } else { 735 if (diff_block(&indent_in_head, &indent_out_head)) 736 retval = 1; 737 } 738 free_block(&indent_in_head); 739 free_block(&indent_out_head); 740 x = y1; 741 } else if (!(ch & BLOCK_MASK)) { 742 y1 = x + 1; 743 } 744 } 745 return (retval); 746} 747