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 
38 extern char **environ;
39 
40 static int opt_verbose;
41 static char *opt_diff_tool;
42 
43 #define	BLOCK_SIZE	4096
44 #define	BLOCK_MASK	0x100
45 #define	BLOCK_ADD	0x200
46 
47 struct block {
48 	TAILQ_ENTRY(block) entry;
49 	uint32_t length;
50 	uint32_t flags;
51 	uint8_t *data;
52 	uint8_t *mask;
53 };
54 
55 typedef TAILQ_HEAD(, block) block_head_t;
56 
57 static void
58 sigpipe(int sig)
59 {
60 }
61 
62 static struct block *
63 alloc_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 
78 static int
79 write_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 
93 static uint16_t
94 peek_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 
108 static void
109 set_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 
124 static uint64_t
125 size_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 
135 static int
136 diff_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 
187 static int
188 diff_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 
252 static void
253 free_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 
263 static void
264 cmd_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;
305 error:
306 	iop[0] = iop[1] = NULL;
307 }
308 
309 static void
310 cmd_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 
346 static void
347 usage(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 
367 int
368 main(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