2 * Copyright (c) 2015 Hans Petter Selasky. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/queue.h>
38 extern char **environ;
40 static int opt_verbose;
41 static char *opt_diff_tool;
43 #define BLOCK_SIZE 4096
44 #define BLOCK_MASK 0x100
45 #define BLOCK_ADD 0x200
48 TAILQ_ENTRY(block) entry;
55 typedef TAILQ_HEAD(, block) block_head_t;
66 size_t size = sizeof(*pb) + (2 * BLOCK_SIZE);
70 errx(EX_SOFTWARE, "Out of memory");
72 pb->data = (void *)(pb + 1);
73 pb->mask = pb->data + BLOCK_SIZE;
74 pb->length = BLOCK_SIZE;
79 write_block(int fd, block_head_t *ph)
86 TAILQ_FOREACH(ptr, ph, entry) {
87 if (write(fd, ptr->data, ptr->length) != ptr->length)
94 peek_block(block_head_t *pbh, uint64_t off)
98 TAILQ_FOREACH(ptr, pbh, entry) {
99 if (off < ptr->length)
105 return (ptr->data[off] | (ptr->mask[off] << 8));
109 set_block(block_head_t *pbh, uint64_t off, uint16_t ch)
113 TAILQ_FOREACH(ptr, pbh, entry) {
114 if (off < ptr->length)
120 ptr->data[off] = ch & 0xFF;
121 ptr->mask[off] = (ch >> 8) & 0xFF;
125 size_block(block_head_t *pbh)
130 TAILQ_FOREACH(ptr, pbh, entry)
136 diff_tool(block_head_t *pa, block_head_t *pb)
138 char ca[] = {"/tmp/diff.orig.XXXXXX"};
139 char cb[] = {"/tmp/diff.styled.XXXXXX"};
150 s = (sa > sb) ? sa : sb;
152 for (x = 0; x != s; x++) {
153 char cha = peek_block(pa, x) & 0xFF;
154 char chb = peek_block(pb, x) & 0xFF;
158 if (cha == '\n' && chb == 0 && x == sa - 1)
164 return (0); /* identical */
169 if (write_block(fa, pa) < 0 || write_block(fb, pb) < 0) {
174 err(EX_SOFTWARE, "Could not write data to temporary files");
179 snprintf(cc, sizeof(cc), "%s %s %s", opt_diff_tool, ca, cb);
188 diff_block(block_head_t *pa, block_head_t *pb)
190 uint64_t sa = size_block(pa);
191 uint64_t sb = size_block(pb);
197 s = (sa > sb) ? sa : sb;
199 for (y = x = 0; x != s; x++) {
200 char cha = peek_block(pa, x) & 0xFF;
201 char chb = peek_block(pb, x) & 0xFF;
207 if (cha == '\n' && chb == 0 && x == sa - 1)
211 printf("Style error:\n");
213 for (n = y; n < sa; n++) {
214 char ch = peek_block(pa, n) & 0xFF;
216 if (nonspace && ch == '\n')
223 printf("Style corrected:\n");
225 for (n = y; n < sb; n++) {
226 char ch = peek_block(pb, n) & 0xFF;
228 if (nonspace && ch == '\n')
235 for (n = y; n != x; n++) {
236 if ((peek_block(pa, n) & 0xFF) == '\t')
241 printf("^ %sdifference%s\n",
242 (isspace(cha) || isspace(chb)) ? "whitespace " : "",
243 (x >= sa || x >= sb) ? " in the end of a block" : "");
245 } else if (cha == '\n') {
253 free_block(block_head_t *pbh)
257 while ((ptr = TAILQ_FIRST(pbh))) {
258 TAILQ_REMOVE(pbh, ptr, entry);
264 cmd_popen(char *command, FILE **iop)
273 if (pipe(pdes + 2) < 0) {
283 switch ((pid = vfork())) {
284 case -1: /* Error. */
291 dup2(pdes[1], STDOUT_FILENO);
292 dup2(pdes[2], STDIN_FILENO);
295 execve("/bin/sh", argv, environ);
300 iop[0] = fdopen(pdes[3], "w");
301 iop[1] = fdopen(pdes[0], "r");
306 iop[0] = iop[1] = NULL;
310 cmd_block_process(block_head_t *pbh_in, block_head_t *pbh_out, char *cmd_str)
317 cmd_popen(cmd_str, pfd);
319 if (pfd[0] == NULL || pfd[1] == NULL)
320 errx(EX_SOFTWARE, "Cannot invoke command '%s'", cmd_str);
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);
335 len = fread(ptr->data, 1, BLOCK_SIZE, pfd[1]);
341 TAILQ_INSERT_TAIL(pbh_out, ptr, entry);
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"
361 "\t" "indent_wrapper -D\n"
362 "\t" "indent_wrapper -D -t meld\n"
363 "\t" "indent_wrapper -D -t \"diff -u\"\n");
368 main(int argc, char **argv)
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;
382 int inside_string = 0;
391 signal(SIGPIPE, &sigpipe);
395 while ((c = getopt(argc, argv, "dDvg:s:c:ht:")) != -1) {
401 opt_diff_tool = optarg;
404 snprintf(cmdbuf, sizeof(cmdbuf), "git show -U1000000 %s", optarg);
407 snprintf(cmdbuf, sizeof(cmdbuf), "git diff -U1000000");
410 snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000");
413 snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000 -r %s", optarg);
416 snprintf(cmdbuf, sizeof(cmdbuf), "%s", optarg);
425 cmd_block_process(NULL, &diff_head, cmdbuf);
427 TAILQ_INIT(&diff_a_head);
428 TAILQ_INIT(&diff_b_head);
430 size = size_block(&diff_head);
436 for (x = 0; x < size;) {
437 ch = peek_block(&diff_head, x);
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))
446 for (x++; x != size; x++) {
447 ch = peek_block(&diff_head, x);
448 p1->mask[y1] = BLOCK_ADD >> 8;
450 if (y1 == BLOCK_SIZE) {
451 TAILQ_INSERT_TAIL(&diff_a_head, p1, entry);
455 if ((ch & 0xFF) == '\n')
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))
466 for (x++; x != size; x++) {
467 ch = peek_block(&diff_head, x);
469 if (y2 == BLOCK_SIZE) {
470 TAILQ_INSERT_TAIL(&diff_b_head, p2, entry);
474 if ((ch & 0xFF) == '\n')
481 for (x++; x != size; x++) {
482 ch = peek_block(&diff_head, x);
484 if (y1 == BLOCK_SIZE) {
485 TAILQ_INSERT_TAIL(&diff_a_head, p1, entry);
490 if (y2 == BLOCK_SIZE) {
491 TAILQ_INSERT_TAIL(&diff_b_head, p2, entry);
495 if ((ch & 0xFF) == '\n')
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');
507 if ((ch & 0xFF) == '\n')
513 /* skip till end of line */
514 for (; x < size; x++) {
515 ch = peek_block(&diff_head, x);
516 if ((ch & 0xFF) == '\n') {
524 TAILQ_INSERT_TAIL(&diff_a_head, p1, entry);
525 TAILQ_INSERT_TAIL(&diff_b_head, p2, entry);
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);
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')
550 set_block(&diff_a_head, x, ch | BLOCK_MASK);
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);
564 set_block(&diff_a_head, x, ch | BLOCK_MASK);
569 /* third pass - identify preprocessor tokens and strings */
570 for (x = 0; x < size; x++) {
571 ch = peek_block(&diff_a_head, x);
574 if (inside_string == 0 && (ch & 0xFF) == '#') {
575 int skip_newline = 0;
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') {
587 if ((ch & 0xFF) == '\\')
589 set_block(&diff_a_head, x, ch | BLOCK_MASK);
592 if ((ch & 0xFF) == '"' || (ch & 0xFF) == '\'') {
593 if (inside_string == 0) {
594 inside_string = (ch & 0xFF);
596 if (escape_char == 0 && inside_string == (ch & 0xFF))
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;
606 set_block(&diff_a_head, x, ch | BLOCK_MASK);
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) ? '+' : ' ');
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) ? '+' : ' ');
637 set_block(&diff_a_head, x, ch | BLOCK_MASK);
650 errx(EX_SOFTWARE, "Unbalanced parenthesis");
651 if (inside_string != 0)
652 errx(EX_SOFTWARE, "String without end");
654 /* fifth pass - on the same line statements */
655 for (x = 0; x < size; x++) {
656 ch = peek_block(&diff_a_head, x);
663 set_block(&diff_a_head, x, ch | BLOCK_MASK);
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);
676 for (; y1 < size; y1++) {
677 ch = peek_block(&diff_a_head, y1);
678 if (y1 > x && !(ch & (BLOCK_MASK | BLOCK_ADD)))
680 p2->data[y2++] = ch & 0xFF;
681 if (y2 == BLOCK_SIZE) {
682 TAILQ_INSERT_TAIL(&indent_in_head, p2, entry);
687 if (p2->data[y2] != '\n')
688 p2->data[y2++] = '\n';
690 TAILQ_INSERT_TAIL(&indent_in_head, p2, entry);
692 cmd_block_process(&indent_in_head, &indent_out_head,
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 "
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'");
729 if (opt_diff_tool != NULL) {
730 if (diff_tool(&indent_in_head, &indent_out_head))
733 if (diff_block(&indent_in_head, &indent_out_head))
736 free_block(&indent_in_head);
737 free_block(&indent_out_head);
739 } else if (!(ch & BLOCK_MASK)) {