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