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 
40 extern char **environ;
41 
42 static int opt_verbose;
43 static char *opt_diff_tool;
44 
45 #define	BLOCK_SIZE	4096
46 #define	BLOCK_MASK	0x100
47 #define	BLOCK_ADD	0x200
48 
49 struct block {
50 	TAILQ_ENTRY(block) entry;
51 	uint32_t length;
52 	uint32_t flags;
53 	uint8_t *data;
54 	uint8_t *mask;
55 };
56 
57 typedef TAILQ_HEAD(, block) block_head_t;
58 
59 static void
60 sigpipe(int sig)
61 {
62 }
63 
64 static struct block *
65 alloc_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 
80 static int
81 write_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 
95 static uint16_t
96 peek_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 
110 static void
111 set_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 
126 static uint64_t
127 size_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 
137 static int
138 diff_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 
189 static int
190 diff_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 
254 static void
255 free_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 
265 static void
266 cmd_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;
307 error:
308 	iop[0] = iop[1] = NULL;
309 }
310 
311 static void
312 cmd_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 
348 static void
349 usage(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 
369 int
370 main(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