1291300Shselasky/*-
2291300Shselasky * Copyright (c) 2015 Hans Petter Selasky. All rights reserved.
3291300Shselasky *
4291300Shselasky * Redistribution and use in source and binary forms, with or without
5291300Shselasky * modification, are permitted provided that the following conditions
6291300Shselasky * are met:
7291300Shselasky * 1. Redistributions of source code must retain the above copyright
8291300Shselasky *    notice, this list of conditions and the following disclaimer.
9291300Shselasky * 2. Redistributions in binary form must reproduce the above copyright
10291300Shselasky *    notice, this list of conditions and the following disclaimer in the
11291300Shselasky *    documentation and/or other materials provided with the distribution.
12291300Shselasky *
13291300Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14291300Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15291300Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16291300Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17291300Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18291300Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19291300Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20291300Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21291300Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22291300Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23291300Shselasky * SUCH DAMAGE.
24291300Shselasky *
25291300Shselasky * $FreeBSD$
26291300Shselasky */
27291300Shselasky
28291300Shselasky#include <stdio.h>
29291300Shselasky#include <stdint.h>
30291300Shselasky#include <sys/queue.h>
31291300Shselasky#include <sysexits.h>
32291300Shselasky#include <err.h>
33291300Shselasky#include <fcntl.h>
34291300Shselasky#include <stdlib.h>
35291300Shselasky#include <unistd.h>
36291300Shselasky#include <string.h>
37291300Shselasky#include <ctype.h>
38291300Shselasky#include <signal.h>
39291300Shselasky
40291300Shselaskyextern char **environ;
41291300Shselasky
42291300Shselaskystatic int opt_verbose;
43291300Shselaskystatic char *opt_diff_tool;
44291300Shselasky
45291300Shselasky#define	BLOCK_SIZE	4096
46291300Shselasky#define	BLOCK_MASK	0x100
47291300Shselasky#define	BLOCK_ADD	0x200
48291300Shselasky
49291300Shselaskystruct block {
50291300Shselasky	TAILQ_ENTRY(block) entry;
51291300Shselasky	uint32_t length;
52291300Shselasky	uint32_t flags;
53291300Shselasky	uint8_t *data;
54291300Shselasky	uint8_t *mask;
55291300Shselasky};
56291300Shselasky
57291300Shselaskytypedef TAILQ_HEAD(, block) block_head_t;
58291300Shselasky
59291300Shselaskystatic void
60291300Shselaskysigpipe(int sig)
61291300Shselasky{
62291300Shselasky}
63291300Shselasky
64291300Shselaskystatic struct block *
65291300Shselaskyalloc_block(void)
66291300Shselasky{
67291300Shselasky	struct block *pb;
68291300Shselasky	size_t size = sizeof(*pb) + (2 * BLOCK_SIZE);
69291300Shselasky
70291300Shselasky	pb = malloc(size);
71291300Shselasky	if (pb == NULL)
72291300Shselasky		errx(EX_SOFTWARE, "Out of memory");
73291300Shselasky	memset(pb, 0, size);
74291300Shselasky	pb->data = (void *)(pb + 1);
75291300Shselasky	pb->mask = pb->data + BLOCK_SIZE;
76291300Shselasky	pb->length = BLOCK_SIZE;
77291300Shselasky	return (pb);
78291300Shselasky}
79291300Shselasky
80291300Shselaskystatic int
81291300Shselaskywrite_block(int fd, block_head_t *ph)
82291300Shselasky{
83291300Shselasky	struct block *ptr;
84291300Shselasky
85291300Shselasky	if (fd < 0)
86291300Shselasky		return (-1);
87291300Shselasky
88291300Shselasky	TAILQ_FOREACH(ptr, ph, entry) {
89291300Shselasky		if (write(fd, ptr->data, ptr->length) != ptr->length)
90291300Shselasky			return (-1);
91291300Shselasky	}
92291300Shselasky	return (0);
93291300Shselasky}
94291300Shselasky
95291300Shselaskystatic uint16_t
96291300Shselaskypeek_block(block_head_t *pbh, uint64_t off)
97291300Shselasky{
98291300Shselasky	struct block *ptr;
99291300Shselasky
100291300Shselasky	TAILQ_FOREACH(ptr, pbh, entry) {
101291300Shselasky		if (off < ptr->length)
102291300Shselasky			break;
103291300Shselasky		off -= ptr->length;
104291300Shselasky	}
105291300Shselasky	if (ptr == NULL)
106291300Shselasky		return (0);
107291300Shselasky	return (ptr->data[off] | (ptr->mask[off] << 8));
108291300Shselasky}
109291300Shselasky
110291300Shselaskystatic void
111291300Shselaskyset_block(block_head_t *pbh, uint64_t off, uint16_t ch)
112291300Shselasky{
113291300Shselasky	struct block *ptr;
114291300Shselasky
115291300Shselasky	TAILQ_FOREACH(ptr, pbh, entry) {
116291300Shselasky		if (off < ptr->length)
117291300Shselasky			break;
118291300Shselasky		off -= ptr->length;
119291300Shselasky	}
120291300Shselasky	if (ptr == NULL)
121291300Shselasky		return;
122291300Shselasky	ptr->data[off] = ch & 0xFF;
123291300Shselasky	ptr->mask[off] = (ch >> 8) & 0xFF;
124291300Shselasky}
125291300Shselasky
126291300Shselaskystatic uint64_t
127291300Shselaskysize_block(block_head_t *pbh)
128291300Shselasky{
129291300Shselasky	struct block *ptr;
130291300Shselasky	uint64_t off = 0;
131291300Shselasky
132291300Shselasky	TAILQ_FOREACH(ptr, pbh, entry)
133291300Shselasky	    off += ptr->length;
134291300Shselasky	return (off);
135291300Shselasky}
136291300Shselasky
137291300Shselaskystatic int
138291300Shselaskydiff_tool(block_head_t *pa, block_head_t *pb)
139291300Shselasky{
140291300Shselasky	char ca[] = {"/tmp/diff.orig.XXXXXX"};
141291300Shselasky	char cb[] = {"/tmp/diff.styled.XXXXXX"};
142291300Shselasky	char cc[256];
143291300Shselasky	uint64_t sa;
144291300Shselasky	uint64_t sb;
145291300Shselasky	uint64_t s;
146291300Shselasky	uint64_t x;
147291300Shselasky	int fa;
148291300Shselasky	int fb;
149291300Shselasky
150291300Shselasky	sa = size_block(pa);
151291300Shselasky	sb = size_block(pb);
152291300Shselasky	s = (sa > sb) ? sa : sb;
153291300Shselasky
154291300Shselasky	for (x = 0; x != s; x++) {
155291300Shselasky		char cha = peek_block(pa, x) & 0xFF;
156291300Shselasky		char chb = peek_block(pb, x) & 0xFF;
157291300Shselasky
158291300Shselasky		if (cha != chb) {
159291300Shselasky			/* false positive */
160291300Shselasky			if (cha == '\n' && chb == 0 && x == sa - 1)
161291300Shselasky				return (0);
162291300Shselasky			break;
163291300Shselasky		}
164291300Shselasky	}
165291300Shselasky	if (x == s)
166291300Shselasky		return (0);		/* identical */
167291300Shselasky
168291300Shselasky	fa = mkstemp(ca);
169291300Shselasky	fb = mkstemp(cb);
170291300Shselasky
171291300Shselasky	if (write_block(fa, pa) < 0 || write_block(fb, pb) < 0) {
172291300Shselasky		close(fa);
173291300Shselasky		close(fb);
174291300Shselasky		unlink(ca);
175291300Shselasky		unlink(cb);
176291300Shselasky		err(EX_SOFTWARE, "Could not write data to temporary files");
177291300Shselasky	}
178291300Shselasky	close(fa);
179291300Shselasky	close(fb);
180291300Shselasky
181291300Shselasky	snprintf(cc, sizeof(cc), "%s %s %s", opt_diff_tool, ca, cb);
182291300Shselasky	system(cc);
183291300Shselasky
184291300Shselasky	unlink(ca);
185291300Shselasky	unlink(cb);
186291300Shselasky	return (-1);
187291300Shselasky}
188291300Shselasky
189291300Shselaskystatic int
190291300Shselaskydiff_block(block_head_t *pa, block_head_t *pb)
191291300Shselasky{
192291300Shselasky	uint64_t sa = size_block(pa);
193291300Shselasky	uint64_t sb = size_block(pb);
194291300Shselasky	uint64_t s;
195291300Shselasky	uint64_t x;
196291300Shselasky	uint64_t y;
197291300Shselasky	uint64_t n;
198291300Shselasky
199291300Shselasky	s = (sa > sb) ? sa : sb;
200291300Shselasky
201291300Shselasky	for (y = x = 0; x != s; x++) {
202291300Shselasky		char cha = peek_block(pa, x) & 0xFF;
203291300Shselasky		char chb = peek_block(pb, x) & 0xFF;
204291300Shselasky
205291300Shselasky		if (cha != chb) {
206291300Shselasky			int nonspace;
207291300Shselasky
208291300Shselasky			/* false positive */
209291300Shselasky			if (cha == '\n' && chb == 0 && x == sa - 1)
210291300Shselasky				return (0);
211291300Shselasky
212291300Shselasky			n = x - y;
213291300Shselasky			printf("Style error:\n");
214291300Shselasky			nonspace = 0;
215291300Shselasky			for (n = y; n < sa; n++) {
216291300Shselasky				char ch = peek_block(pa, n) & 0xFF;
217291300Shselasky
218291300Shselasky				if (nonspace && ch == '\n')
219291300Shselasky					break;
220291300Shselasky				printf("%c", ch);
221291300Shselasky				if (!isspace(ch))
222291300Shselasky					nonspace = 1;
223291300Shselasky			}
224291300Shselasky			printf("\n");
225291300Shselasky			printf("Style corrected:\n");
226291300Shselasky			nonspace = 0;
227291300Shselasky			for (n = y; n < sb; n++) {
228291300Shselasky				char ch = peek_block(pb, n) & 0xFF;
229291300Shselasky
230291300Shselasky				if (nonspace && ch == '\n')
231291300Shselasky					break;
232291300Shselasky				printf("%c", ch);
233291300Shselasky				if (!isspace(ch))
234291300Shselasky					nonspace = 1;
235291300Shselasky			}
236291300Shselasky			printf("\n");
237291300Shselasky			for (n = y; n != x; n++) {
238291300Shselasky				if ((peek_block(pa, n) & 0xFF) == '\t')
239291300Shselasky					printf("\t");
240291300Shselasky				else
241291300Shselasky					printf(" ");
242291300Shselasky			}
243291300Shselasky			printf("^ %sdifference%s\n",
244291300Shselasky			    (isspace(cha) || isspace(chb)) ? "whitespace " : "",
245291300Shselasky			    (x >= sa || x >= sb) ? " in the end of a block" : "");
246291300Shselasky			return (1);
247291300Shselasky		} else if (cha == '\n') {
248291300Shselasky			y = x + 1;
249291300Shselasky		}
250291300Shselasky	}
251291300Shselasky	return (0);
252291300Shselasky}
253291300Shselasky
254291300Shselaskystatic void
255291300Shselaskyfree_block(block_head_t *pbh)
256291300Shselasky{
257291300Shselasky	struct block *ptr;
258291300Shselasky
259291300Shselasky	while ((ptr = TAILQ_FIRST(pbh))) {
260291300Shselasky		TAILQ_REMOVE(pbh, ptr, entry);
261291300Shselasky		free(ptr);
262291300Shselasky	}
263291300Shselasky}
264291300Shselasky
265291300Shselaskystatic void
266291300Shselaskycmd_popen(char *command, FILE **iop)
267291300Shselasky{
268291300Shselasky	char *argv[4];
269291300Shselasky	int pdes[4];
270291300Shselasky	int pid;
271291300Shselasky
272291300Shselasky	if (pipe(pdes) < 0)
273291300Shselasky		goto error;
274291300Shselasky
275291300Shselasky	if (pipe(pdes + 2) < 0) {
276291300Shselasky		close(pdes[0]);
277291300Shselasky		close(pdes[1]);
278291300Shselasky		goto error;
279291300Shselasky	}
280291300Shselasky	argv[0] = "sh";
281291300Shselasky	argv[1] = "-c";
282291300Shselasky	argv[2] = command;
283291300Shselasky	argv[3] = NULL;
284291300Shselasky
285291300Shselasky	switch ((pid = vfork())) {
286291300Shselasky	case -1:			/* Error. */
287291300Shselasky		close(pdes[0]);
288291300Shselasky		close(pdes[1]);
289291300Shselasky		close(pdes[2]);
290291300Shselasky		close(pdes[3]);
291291300Shselasky		goto error;
292291300Shselasky	case 0:			/* Child. */
293291300Shselasky		dup2(pdes[1], STDOUT_FILENO);
294291300Shselasky		dup2(pdes[2], STDIN_FILENO);
295291300Shselasky		close(pdes[0]);
296291300Shselasky		close(pdes[3]);
297291300Shselasky		execve("/bin/sh", argv, environ);
298291300Shselasky		exit(127);
299291300Shselasky	default:
300291300Shselasky		break;
301291300Shselasky	}
302291300Shselasky	iop[0] = fdopen(pdes[3], "w");
303291300Shselasky	iop[1] = fdopen(pdes[0], "r");
304291300Shselasky	close(pdes[1]);
305291300Shselasky	close(pdes[2]);
306291300Shselasky	return;
307291300Shselaskyerror:
308291300Shselasky	iop[0] = iop[1] = NULL;
309291300Shselasky}
310291300Shselasky
311291300Shselaskystatic void
312291300Shselaskycmd_block_process(block_head_t *pbh_in, block_head_t *pbh_out, char *cmd_str)
313291300Shselasky{
314291300Shselasky	FILE *pfd[2];
315291300Shselasky	struct block *ptr;
316291300Shselasky
317291300Shselasky	TAILQ_INIT(pbh_out);
318291300Shselasky
319291300Shselasky	cmd_popen(cmd_str, pfd);
320291300Shselasky
321291300Shselasky	if (pfd[0] == NULL || pfd[1] == NULL)
322291300Shselasky		errx(EX_SOFTWARE, "Cannot invoke command '%s'", cmd_str);
323291300Shselasky
324291300Shselasky	if (pbh_in != NULL) {
325291300Shselasky		TAILQ_FOREACH(ptr, pbh_in, entry) {
326291300Shselasky			if (fwrite(ptr->data, 1, ptr->length, pfd[0]) != ptr->length)
327291300Shselasky				err(EX_SOFTWARE, "Cannot write all data to command '%s'", cmd_str);
328291300Shselasky		}
329291300Shselasky		fflush(pfd[0]);
330291300Shselasky	}
331291300Shselasky	fclose(pfd[0]);
332291300Shselasky
333291300Shselasky	while (1) {
334291300Shselasky		int len;
335291300Shselasky
336291300Shselasky		ptr = alloc_block();
337291300Shselasky		len = fread(ptr->data, 1, BLOCK_SIZE, pfd[1]);
338291300Shselasky		if (len <= 0) {
339291300Shselasky			free(ptr);
340291300Shselasky			break;
341291300Shselasky		}
342291300Shselasky		ptr->length = len;
343291300Shselasky		TAILQ_INSERT_TAIL(pbh_out, ptr, entry);
344291300Shselasky	}
345291300Shselasky	fclose(pfd[1]);
346291300Shselasky}
347291300Shselasky
348291300Shselaskystatic void
349291300Shselaskyusage(void)
350291300Shselasky{
351291300Shselasky	fprintf(stderr,
352291300Shselasky	    "indent_wrapper [-v] [-d] [-D] [-g <githash>]\n"
353291300Shselasky	    "\t" "[-s <svnrevision> ] [ -t <tool> ] [ -c <command> ]\n"
354291300Shselasky	    "\t" "-v        Increase verbosity\n"
355291300Shselasky	    "\t" "-d        Check output from git diff\n"
356291300Shselasky	    "\t" "-D        Check output from svn diff\n"
357291302Shselasky	    "\t" "-c <cmd>  Set custom command to produce diff\n"
358291300Shselasky	    "\t" "-g <hash> Check output from git hash\n"
359291300Shselasky	    "\t" "-s <rev>  Check output from svn revision\n"
360291300Shselasky	    "\t" "-t <tool> Launch external diff tool\n"
361291300Shselasky	    "\n"
362291300Shselasky	    "Examples:\n"
363291300Shselasky	    "\t" "indent_wrapper -D\n"
364291302Shselasky	    "\t" "indent_wrapper -D -t meld\n"
365291302Shselasky	    "\t" "indent_wrapper -D -t \"diff -u\"\n");
366291300Shselasky	exit(EX_SOFTWARE);
367291300Shselasky}
368291300Shselasky
369291300Shselaskyint
370291300Shselaskymain(int argc, char **argv)
371291300Shselasky{
372291300Shselasky	block_head_t diff_head;
373291300Shselasky	block_head_t diff_a_head;
374291300Shselasky	block_head_t diff_b_head;
375291300Shselasky	block_head_t indent_in_head;
376291300Shselasky	block_head_t indent_out_head;
377291300Shselasky	struct block *p1 = NULL;
378291300Shselasky	struct block *p2 = NULL;
379291300Shselasky	uint64_t size;
380291300Shselasky	uint64_t x;
381291300Shselasky	uint64_t y1 = 0;
382291300Shselasky	uint64_t y2 = 0;
383291300Shselasky	int recurse = 0;
384291300Shselasky	int inside_string = 0;
385291300Shselasky	int escape_char = 0;
386291300Shselasky	int do_parse = 0;
387291300Shselasky	char cmdbuf[256];
388291300Shselasky	uint16_t ch;
389291300Shselasky	uint16_t chn;
390291300Shselasky	int c;
391291300Shselasky	int retval = 0;
392291300Shselasky
393291300Shselasky	signal(SIGPIPE, &sigpipe);
394291300Shselasky
395291300Shselasky	cmdbuf[0] = 0;
396291300Shselasky
397291300Shselasky	while ((c = getopt(argc, argv, "dDvg:s:c:ht:")) != -1) {
398291300Shselasky		switch (c) {
399291300Shselasky		case 'v':
400291300Shselasky			opt_verbose++;
401291300Shselasky			break;
402291300Shselasky		case 't':
403291300Shselasky			opt_diff_tool = optarg;
404291300Shselasky			break;
405291300Shselasky		case 'g':
406291300Shselasky			snprintf(cmdbuf, sizeof(cmdbuf), "git show -U1000000 %s", optarg);
407291300Shselasky			break;
408291300Shselasky		case 'd':
409291300Shselasky			snprintf(cmdbuf, sizeof(cmdbuf), "git diff -U1000000");
410291300Shselasky			break;
411291300Shselasky		case 'D':
412291300Shselasky			snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000");
413291300Shselasky			break;
414291300Shselasky		case 's':
415291300Shselasky			snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000 -r %s", optarg);
416291300Shselasky			break;
417291300Shselasky		case 'c':
418291300Shselasky			snprintf(cmdbuf, sizeof(cmdbuf), "%s", optarg);
419291300Shselasky			break;
420291300Shselasky		default:
421291300Shselasky			usage();
422291300Shselasky		}
423291300Shselasky	}
424291300Shselasky	if (cmdbuf[0] == 0)
425291300Shselasky		usage();
426291300Shselasky
427291300Shselasky	cmd_block_process(NULL, &diff_head, cmdbuf);
428291300Shselasky
429291300Shselasky	TAILQ_INIT(&diff_a_head);
430291300Shselasky	TAILQ_INIT(&diff_b_head);
431291300Shselasky
432291300Shselasky	size = size_block(&diff_head);
433291300Shselasky	p1 = alloc_block();
434291300Shselasky	y1 = 0;
435291300Shselasky	p2 = alloc_block();
436291300Shselasky	y2 = 0;
437291300Shselasky
438291300Shselasky	for (x = 0; x < size;) {
439291300Shselasky		ch = peek_block(&diff_head, x);
440291300Shselasky		switch (ch & 0xFF) {
441291300Shselasky		case '+':
442291300Shselasky			if (ch == peek_block(&diff_head, x + 1) &&
443291300Shselasky			    ch == peek_block(&diff_head, x + 2) &&
444291300Shselasky			    ' ' == (peek_block(&diff_head, x + 3) & 0xFF))
445291300Shselasky				goto parse_filename;
446291300Shselasky			if (do_parse == 0)
447291300Shselasky				break;
448291300Shselasky			for (x++; x != size; x++) {
449291300Shselasky				ch = peek_block(&diff_head, x);
450291300Shselasky				p1->mask[y1] = BLOCK_ADD >> 8;
451291300Shselasky				p1->data[y1++] = ch;
452291300Shselasky				if (y1 == BLOCK_SIZE) {
453291300Shselasky					TAILQ_INSERT_TAIL(&diff_a_head, p1, entry);
454291300Shselasky					p1 = alloc_block();
455291300Shselasky					y1 = 0;
456291300Shselasky				}
457291300Shselasky				if ((ch & 0xFF) == '\n')
458291300Shselasky					break;
459291300Shselasky			}
460291300Shselasky			break;
461291300Shselasky		case '-':
462291300Shselasky			if (ch == peek_block(&diff_head, x + 1) &&
463291300Shselasky			    ch == peek_block(&diff_head, x + 2) &&
464291300Shselasky			    ' ' == (peek_block(&diff_head, x + 3) & 0xFF))
465291300Shselasky				goto parse_filename;
466291300Shselasky			if (do_parse == 0)
467291300Shselasky				break;
468291300Shselasky			for (x++; x != size; x++) {
469291300Shselasky				ch = peek_block(&diff_head, x);
470291300Shselasky				p2->data[y2++] = ch;
471291300Shselasky				if (y2 == BLOCK_SIZE) {
472291300Shselasky					TAILQ_INSERT_TAIL(&diff_b_head, p2, entry);
473291300Shselasky					p2 = alloc_block();
474291300Shselasky					y2 = 0;
475291300Shselasky				}
476291300Shselasky				if ((ch & 0xFF) == '\n')
477291300Shselasky					break;
478291300Shselasky			}
479291300Shselasky			break;
480291300Shselasky		case ' ':
481291300Shselasky			if (do_parse == 0)
482291300Shselasky				break;
483291300Shselasky			for (x++; x != size; x++) {
484291300Shselasky				ch = peek_block(&diff_head, x);
485291300Shselasky				p1->data[y1++] = ch;
486291300Shselasky				if (y1 == BLOCK_SIZE) {
487291300Shselasky					TAILQ_INSERT_TAIL(&diff_a_head, p1, entry);
488291300Shselasky					p1 = alloc_block();
489291300Shselasky					y1 = 0;
490291300Shselasky				}
491291300Shselasky				p2->data[y2++] = ch;
492291300Shselasky				if (y2 == BLOCK_SIZE) {
493291300Shselasky					TAILQ_INSERT_TAIL(&diff_b_head, p2, entry);
494291300Shselasky					p2 = alloc_block();
495291300Shselasky					y2 = 0;
496291300Shselasky				}
497291300Shselasky				if ((ch & 0xFF) == '\n')
498291300Shselasky					break;
499291300Shselasky			}
500291300Shselasky			break;
501291300Shselasky	parse_filename:
502291300Shselasky			for (x += 3; x != size; x++) {
503291300Shselasky				ch = peek_block(&diff_head, x);
504291300Shselasky				chn = peek_block(&diff_head, x + 1);
505291300Shselasky				if ((ch & 0xFF) == '.') {
506291300Shselasky					/* only accept .c and .h files */
507291300Shselasky					do_parse = ((chn & 0xFF) == 'c' || (chn & 0xFF) == 'h');
508291300Shselasky				}
509291300Shselasky				if ((ch & 0xFF) == '\n')
510291300Shselasky					break;
511291300Shselasky			}
512291300Shselasky		default:
513291300Shselasky			break;
514291300Shselasky		}
515291300Shselasky		/* skip till end of line */
516291300Shselasky		for (; x < size; x++) {
517291300Shselasky			ch = peek_block(&diff_head, x);
518291300Shselasky			if ((ch & 0xFF) == '\n') {
519291300Shselasky				x++;
520291300Shselasky				break;
521291300Shselasky			}
522291300Shselasky		}
523291300Shselasky	}
524291300Shselasky	p1->length = y1;
525291300Shselasky	p2->length = y2;
526291300Shselasky	TAILQ_INSERT_TAIL(&diff_a_head, p1, entry);
527291300Shselasky	TAILQ_INSERT_TAIL(&diff_b_head, p2, entry);
528291300Shselasky
529291300Shselasky	/* first pass - verify input */
530291300Shselasky	size = size_block(&diff_a_head);
531291300Shselasky	for (x = 0; x != size; x++) {
532291300Shselasky		ch = peek_block(&diff_a_head, x) & 0xFF;
533291300Shselasky		if (!(ch & 0x80) && ch != '\t' && ch != '\r' && ch != '\n' &&
534291300Shselasky		    ch != ' ' && !isprint(ch))
535291300Shselasky			errx(EX_SOFTWARE, "Non printable characters are not allowed: '%c'", ch);
536291300Shselasky		else if (ch & 0x80) {
537291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
538291300Shselasky		}
539291300Shselasky	}
540291300Shselasky
541291300Shselasky	/* second pass - identify all comments */
542291300Shselasky	for (x = 0; x < size; x++) {
543291300Shselasky		ch = peek_block(&diff_a_head, x);
544291300Shselasky		chn = peek_block(&diff_a_head, x + 1);
545291300Shselasky		if ((ch & 0xFF) == '/' && (chn & 0xFF) == '/') {
546291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
547291300Shselasky			set_block(&diff_a_head, x + 1, chn | BLOCK_MASK);
548291300Shselasky			for (x += 2; x < size; x++) {
549291300Shselasky				ch = peek_block(&diff_a_head, x);
550291300Shselasky				if ((ch & 0xFF) == '\n')
551291300Shselasky					break;
552291300Shselasky				set_block(&diff_a_head, x, ch | BLOCK_MASK);
553291300Shselasky			}
554291300Shselasky		} else if ((ch & 0xFF) == '/' && (chn & 0xFF) == '*') {
555291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
556291300Shselasky			set_block(&diff_a_head, x + 1, chn | BLOCK_MASK);
557291300Shselasky			for (x += 2; x < size; x++) {
558291300Shselasky				ch = peek_block(&diff_a_head, x);
559291300Shselasky				chn = peek_block(&diff_a_head, x + 1);
560291300Shselasky				if ((ch & 0xFF) == '*' && (chn & 0xFF) == '/') {
561291300Shselasky					set_block(&diff_a_head, x, ch | BLOCK_MASK);
562291300Shselasky					set_block(&diff_a_head, x + 1, chn | BLOCK_MASK);
563291300Shselasky					x++;
564291300Shselasky					break;
565291300Shselasky				}
566291300Shselasky				set_block(&diff_a_head, x, ch | BLOCK_MASK);
567291300Shselasky			}
568291300Shselasky		}
569291300Shselasky	}
570291300Shselasky
571291300Shselasky	/* third pass - identify preprocessor tokens and strings */
572291300Shselasky	for (x = 0; x < size; x++) {
573291300Shselasky		ch = peek_block(&diff_a_head, x);
574291300Shselasky		if (ch & BLOCK_MASK)
575291300Shselasky			continue;
576291300Shselasky		if (inside_string == 0 && (ch & 0xFF) == '#') {
577291300Shselasky			int skip_newline = 0;
578291300Shselasky
579291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
580291300Shselasky			for (x++; x < size; x++) {
581291300Shselasky				ch = peek_block(&diff_a_head, x);
582291300Shselasky				if ((ch & 0xFF) == '\n') {
583291300Shselasky					if (!skip_newline)
584291300Shselasky						break;
585291300Shselasky					skip_newline = 0;
586291300Shselasky				}
587291300Shselasky				if (ch & BLOCK_MASK)
588291300Shselasky					continue;
589291300Shselasky				if ((ch & 0xFF) == '\\')
590291300Shselasky					skip_newline = 1;
591291300Shselasky				set_block(&diff_a_head, x, ch | BLOCK_MASK);
592291300Shselasky			}
593291300Shselasky		}
594291300Shselasky		if ((ch & 0xFF) == '"' || (ch & 0xFF) == '\'') {
595291300Shselasky			if (inside_string == 0) {
596291300Shselasky				inside_string = (ch & 0xFF);
597291300Shselasky			} else {
598291300Shselasky				if (escape_char == 0 && inside_string == (ch & 0xFF))
599291300Shselasky					inside_string = 0;
600291300Shselasky			}
601291300Shselasky			escape_char = 0;
602291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
603291300Shselasky		} else if (inside_string != 0) {
604291300Shselasky			if ((ch & 0xFF) == '\\')
605291300Shselasky				escape_char = !escape_char;
606291300Shselasky			else
607291300Shselasky				escape_char = 0;
608291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
609291300Shselasky		}
610291300Shselasky	}
611291300Shselasky
612291300Shselasky	/* fourth pass - identify function blocks */
613291300Shselasky	if (opt_verbose > 0) {
614291300Shselasky		chn = peek_block(&diff_a_head, x);
615291300Shselasky		printf("L%02d%c|", recurse,
616291300Shselasky		    (chn & BLOCK_ADD) ? '+' : ' ');
617291300Shselasky	}
618291300Shselasky	for (x = 0; x < size; x++) {
619291300Shselasky		ch = peek_block(&diff_a_head, x);
620291300Shselasky		if (opt_verbose > 0) {
621291300Shselasky			printf("%c", ch & 0xFF);
622291300Shselasky			if ((ch & 0xFF) == '\n') {
623291300Shselasky				chn = peek_block(&diff_a_head, x + 1);
624291300Shselasky				printf("L%02d%c|", recurse,
625291300Shselasky				    (chn & BLOCK_ADD) ? '+' : ' ');
626291300Shselasky			}
627291300Shselasky		}
628291300Shselasky		if (ch & BLOCK_MASK)
629291300Shselasky			continue;
630291300Shselasky		switch (ch & 0xFF) {
631291300Shselasky		case '{':
632291300Shselasky		case '(':
633291300Shselasky			recurse++;
634291300Shselasky			break;
635291300Shselasky		default:
636291300Shselasky			break;
637291300Shselasky		}
638291300Shselasky		if (recurse != 0)
639291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
640291300Shselasky		switch (ch & 0xFF) {
641291300Shselasky		case '}':
642291300Shselasky		case ')':
643291300Shselasky			recurse--;
644291300Shselasky			break;
645291300Shselasky		default:
646291300Shselasky			break;
647291300Shselasky		}
648291300Shselasky	}
649291300Shselasky	if (opt_verbose > 0)
650291300Shselasky		printf("\n");
651291300Shselasky	if (recurse != 0)
652291300Shselasky		errx(EX_SOFTWARE, "Unbalanced parenthesis");
653291300Shselasky	if (inside_string != 0)
654291300Shselasky		errx(EX_SOFTWARE, "String without end");
655291300Shselasky
656291300Shselasky	/* fifth pass - on the same line statements */
657291300Shselasky	for (x = 0; x < size; x++) {
658291300Shselasky		ch = peek_block(&diff_a_head, x);
659291300Shselasky		if (ch & BLOCK_MASK)
660291300Shselasky			continue;
661291300Shselasky		switch (ch & 0xFF) {
662291300Shselasky		case '\n':
663291300Shselasky			break;
664291300Shselasky		default:
665291300Shselasky			set_block(&diff_a_head, x, ch | BLOCK_MASK);
666291300Shselasky			break;
667291300Shselasky		}
668291300Shselasky	}
669291300Shselasky
670291300Shselasky	/* sixth pass - output relevant blocks to indent */
671291300Shselasky	for (y1 = x = 0; x < size; x++) {
672291300Shselasky		ch = peek_block(&diff_a_head, x);
673291300Shselasky		if (ch & BLOCK_ADD) {
674291300Shselasky			TAILQ_INIT(&indent_in_head);
675291300Shselasky
676291300Shselasky			p2 = alloc_block();
677291300Shselasky			y2 = 0;
678291300Shselasky			for (; y1 < size; y1++) {
679291300Shselasky				ch = peek_block(&diff_a_head, y1);
680291300Shselasky				if (y1 > x && !(ch & (BLOCK_MASK | BLOCK_ADD)))
681291300Shselasky					break;
682291300Shselasky				p2->data[y2++] = ch & 0xFF;
683291300Shselasky				if (y2 == BLOCK_SIZE) {
684291300Shselasky					TAILQ_INSERT_TAIL(&indent_in_head, p2, entry);
685291300Shselasky					p2 = alloc_block();
686291300Shselasky					y2 = 0;
687291300Shselasky				}
688291300Shselasky			}
689291300Shselasky			if (p2->data[y2] != '\n')
690291300Shselasky				p2->data[y2++] = '\n';
691291300Shselasky			p2->length = y2;
692291300Shselasky			TAILQ_INSERT_TAIL(&indent_in_head, p2, entry);
693291300Shselasky
694291300Shselasky			cmd_block_process(&indent_in_head, &indent_out_head,
695291300Shselasky			    "indent "
696291300Shselasky			    "-Tbool "
697291300Shselasky			    "-Tclass "
698291300Shselasky			    "-TFILE "
699291300Shselasky			    "-TLIST_ENTRY "
700291300Shselasky			    "-TLIST_HEAD "
701291300Shselasky			    "-TSLIST_ENTRY "
702291300Shselasky			    "-TSLIST_HEAD "
703291300Shselasky			    "-TSTAILQ_ENTRY "
704291300Shselasky			    "-TSTAILQ_HEAD "
705291300Shselasky			    "-TTAILQ_ENTRY "
706291300Shselasky			    "-TTAILQ_HEAD "
707291300Shselasky			    "-T__aligned "
708291300Shselasky			    "-T__packed "
709291300Shselasky			    "-T__unused "
710291300Shselasky			    "-T__used "
711291300Shselasky			    "-Tfd_set "
712291300Shselasky			    "-Toss_mixerinfo "
713291300Shselasky			    "-Tu_char "
714291300Shselasky			    "-Tu_int "
715291300Shselasky			    "-Tu_long "
716291300Shselasky			    "-Tu_short "
717291300Shselasky			    "-ta -st -bad -bap -nbbb -nbc -br -nbs "
718291300Shselasky			    "-c41 -cd41 -cdb -ce -ci4 -cli0 -d0 -di8 -ndj -ei -nfc1 "
719291300Shselasky			    "-nfcb -i8 -ip8 -l79 -lc77 -ldi0 -nlp -npcs -psl -sc "
720291300Shselasky			    "-nsob -nv "
721291300Shselasky			    " | "
722291300Shselasky			    "sed "
723291300Shselasky			    "-e 's/_HEAD [(]/_HEAD(/g' "
724291300Shselasky			    "-e 's/_ENTRY [(]/_ENTRY(/g' "
725291300Shselasky			    "-e 's/\t__aligned/ __aligned/g' "
726291300Shselasky			    "-e 's/\t__packed/ __packed/g' "
727291300Shselasky			    "-e 's/\t__unused/ __unused/g' "
728291300Shselasky			    "-e 's/\t__used/ __used/g' "
729291300Shselasky			    "-e 's/^#define /#define\t/g'");
730291300Shselasky
731291300Shselasky			if (opt_diff_tool != NULL) {
732291300Shselasky				if (diff_tool(&indent_in_head, &indent_out_head))
733291300Shselasky					retval = 1;
734291300Shselasky			} else {
735291300Shselasky				if (diff_block(&indent_in_head, &indent_out_head))
736291300Shselasky					retval = 1;
737291300Shselasky			}
738291300Shselasky			free_block(&indent_in_head);
739291300Shselasky			free_block(&indent_out_head);
740291300Shselasky			x = y1;
741291300Shselasky		} else if (!(ch & BLOCK_MASK)) {
742291300Shselasky			y1 = x + 1;
743291300Shselasky		}
744291300Shselasky	}
745291300Shselasky	return (retval);
746291300Shselasky}
747