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