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