From 10ac20335c43f6f865f98459bc02fbe7bb59b788 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Wed, 25 Nov 2015 13:26:42 +0000 Subject: [PATCH] Add simple indent wrapper tool for style(9) checking GIT/SVN patches. The indent_wrapper tool only accepts full context diffs and works by identifying the surrounding C-block touched by a diff and passing only that to indent for styling. In the end a diff is produced or an external tool like meld can be invoked, to show the styling differences. --- tools/tools/README | 1 + tools/tools/indent_wrapper/Makefile | 9 + tools/tools/indent_wrapper/indent_wrapper.c | 744 ++++++++++++++++++++ 3 files changed, 754 insertions(+) create mode 100644 tools/tools/indent_wrapper/Makefile create mode 100644 tools/tools/indent_wrapper/indent_wrapper.c diff --git a/tools/tools/README b/tools/tools/README index 44d13bd9fd0e..307cf16156c7 100644 --- a/tools/tools/README +++ b/tools/tools/README @@ -33,6 +33,7 @@ hcomp Compress header files by removing comments and whitespace. html-mv Rename HTML generated filenames to human readable filenames. ifinfo Uses the interface MIB to print out all the information an interface exports in an ugly form. +indent_wrapper Tool for style(9) checking SVN/GIT patches. iso Tool to compare the iso3166 and iso639 files in /usr/share/misc with the data from the master sites. iwi Tools specific to the Intel PRO/Wireless 2200BG/2225BG/2915ABG diff --git a/tools/tools/indent_wrapper/Makefile b/tools/tools/indent_wrapper/Makefile new file mode 100644 index 000000000000..87aa812b8049 --- /dev/null +++ b/tools/tools/indent_wrapper/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ +PREFIX?= /usr/local +LOCALBASE?= /usr/local +BINDIR= ${PREFIX}/sbin +PROG= indent_wrapper +MAN= +SRCS+= indent_wrapper.c + +.include diff --git a/tools/tools/indent_wrapper/indent_wrapper.c b/tools/tools/indent_wrapper/indent_wrapper.c new file mode 100644 index 000000000000..537efb02c076 --- /dev/null +++ b/tools/tools/indent_wrapper/indent_wrapper.c @@ -0,0 +1,744 @@ +/*- + * Copyright (c) 2015 Hans Petter Selasky. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char **environ; + +static int opt_verbose; +static char *opt_diff_tool; + +#define BLOCK_SIZE 4096 +#define BLOCK_MASK 0x100 +#define BLOCK_ADD 0x200 + +struct block { + TAILQ_ENTRY(block) entry; + uint32_t length; + uint32_t flags; + uint8_t *data; + uint8_t *mask; +}; + +typedef TAILQ_HEAD(, block) block_head_t; + +static void +sigpipe(int sig) +{ +} + +static struct block * +alloc_block(void) +{ + struct block *pb; + size_t size = sizeof(*pb) + (2 * BLOCK_SIZE); + + pb = malloc(size); + if (pb == NULL) + errx(EX_SOFTWARE, "Out of memory"); + memset(pb, 0, size); + pb->data = (void *)(pb + 1); + pb->mask = pb->data + BLOCK_SIZE; + pb->length = BLOCK_SIZE; + return (pb); +} + +static int +write_block(int fd, block_head_t *ph) +{ + struct block *ptr; + + if (fd < 0) + return (-1); + + TAILQ_FOREACH(ptr, ph, entry) { + if (write(fd, ptr->data, ptr->length) != ptr->length) + return (-1); + } + return (0); +} + +static uint16_t +peek_block(block_head_t *pbh, uint64_t off) +{ + struct block *ptr; + + TAILQ_FOREACH(ptr, pbh, entry) { + if (off < ptr->length) + break; + off -= ptr->length; + } + if (ptr == NULL) + return (0); + return (ptr->data[off] | (ptr->mask[off] << 8)); +} + +static void +set_block(block_head_t *pbh, uint64_t off, uint16_t ch) +{ + struct block *ptr; + + TAILQ_FOREACH(ptr, pbh, entry) { + if (off < ptr->length) + break; + off -= ptr->length; + } + if (ptr == NULL) + return; + ptr->data[off] = ch & 0xFF; + ptr->mask[off] = (ch >> 8) & 0xFF; +} + +static uint64_t +size_block(block_head_t *pbh) +{ + struct block *ptr; + uint64_t off = 0; + + TAILQ_FOREACH(ptr, pbh, entry) + off += ptr->length; + return (off); +} + +static int +diff_tool(block_head_t *pa, block_head_t *pb) +{ + char ca[] = {"/tmp/diff.orig.XXXXXX"}; + char cb[] = {"/tmp/diff.styled.XXXXXX"}; + char cc[256]; + uint64_t sa; + uint64_t sb; + uint64_t s; + uint64_t x; + int fa; + int fb; + + sa = size_block(pa); + sb = size_block(pb); + s = (sa > sb) ? sa : sb; + + for (x = 0; x != s; x++) { + char cha = peek_block(pa, x) & 0xFF; + char chb = peek_block(pb, x) & 0xFF; + + if (cha != chb) { + /* false positive */ + if (cha == '\n' && chb == 0 && x == sa - 1) + return (0); + break; + } + } + if (x == s) + return (0); /* identical */ + + fa = mkstemp(ca); + fb = mkstemp(cb); + + if (write_block(fa, pa) < 0 || write_block(fb, pb) < 0) { + close(fa); + close(fb); + unlink(ca); + unlink(cb); + err(EX_SOFTWARE, "Could not write data to temporary files"); + } + close(fa); + close(fb); + + snprintf(cc, sizeof(cc), "%s %s %s", opt_diff_tool, ca, cb); + system(cc); + + unlink(ca); + unlink(cb); + return (-1); +} + +static int +diff_block(block_head_t *pa, block_head_t *pb) +{ + uint64_t sa = size_block(pa); + uint64_t sb = size_block(pb); + uint64_t s; + uint64_t x; + uint64_t y; + uint64_t n; + + s = (sa > sb) ? sa : sb; + + for (y = x = 0; x != s; x++) { + char cha = peek_block(pa, x) & 0xFF; + char chb = peek_block(pb, x) & 0xFF; + + if (cha != chb) { + int nonspace; + + /* false positive */ + if (cha == '\n' && chb == 0 && x == sa - 1) + return (0); + + n = x - y; + printf("Style error:\n"); + nonspace = 0; + for (n = y; n < sa; n++) { + char ch = peek_block(pa, n) & 0xFF; + + if (nonspace && ch == '\n') + break; + printf("%c", ch); + if (!isspace(ch)) + nonspace = 1; + } + printf("\n"); + printf("Style corrected:\n"); + nonspace = 0; + for (n = y; n < sb; n++) { + char ch = peek_block(pb, n) & 0xFF; + + if (nonspace && ch == '\n') + break; + printf("%c", ch); + if (!isspace(ch)) + nonspace = 1; + } + printf("\n"); + for (n = y; n != x; n++) { + if ((peek_block(pa, n) & 0xFF) == '\t') + printf("\t"); + else + printf(" "); + } + printf("^ %sdifference%s\n", + (isspace(cha) || isspace(chb)) ? "whitespace " : "", + (x >= sa || x >= sb) ? " in the end of a block" : ""); + return (1); + } else if (cha == '\n') { + y = x + 1; + } + } + return (0); +} + +static void +free_block(block_head_t *pbh) +{ + struct block *ptr; + + while ((ptr = TAILQ_FIRST(pbh))) { + TAILQ_REMOVE(pbh, ptr, entry); + free(ptr); + } +} + +static void +cmd_popen(char *command, FILE **iop) +{ + char *argv[4]; + int pdes[4]; + int pid; + + if (pipe(pdes) < 0) + goto error; + + if (pipe(pdes + 2) < 0) { + close(pdes[0]); + close(pdes[1]); + goto error; + } + argv[0] = "sh"; + argv[1] = "-c"; + argv[2] = command; + argv[3] = NULL; + + switch ((pid = vfork())) { + case -1: /* Error. */ + close(pdes[0]); + close(pdes[1]); + close(pdes[2]); + close(pdes[3]); + goto error; + case 0: /* Child. */ + dup2(pdes[1], STDOUT_FILENO); + dup2(pdes[2], STDIN_FILENO); + close(pdes[0]); + close(pdes[3]); + execve("/bin/sh", argv, environ); + exit(127); + default: + break; + } + iop[0] = fdopen(pdes[3], "w"); + iop[1] = fdopen(pdes[0], "r"); + close(pdes[1]); + close(pdes[2]); + return; +error: + iop[0] = iop[1] = NULL; +} + +static void +cmd_block_process(block_head_t *pbh_in, block_head_t *pbh_out, char *cmd_str) +{ + FILE *pfd[2]; + struct block *ptr; + + TAILQ_INIT(pbh_out); + + cmd_popen(cmd_str, pfd); + + if (pfd[0] == NULL || pfd[1] == NULL) + errx(EX_SOFTWARE, "Cannot invoke command '%s'", cmd_str); + + if (pbh_in != NULL) { + TAILQ_FOREACH(ptr, pbh_in, entry) { + if (fwrite(ptr->data, 1, ptr->length, pfd[0]) != ptr->length) + err(EX_SOFTWARE, "Cannot write all data to command '%s'", cmd_str); + } + fflush(pfd[0]); + } + fclose(pfd[0]); + + while (1) { + int len; + + ptr = alloc_block(); + len = fread(ptr->data, 1, BLOCK_SIZE, pfd[1]); + if (len <= 0) { + free(ptr); + break; + } + ptr->length = len; + TAILQ_INSERT_TAIL(pbh_out, ptr, entry); + } + fclose(pfd[1]); +} + +static void +usage(void) +{ + fprintf(stderr, + "indent_wrapper [-v] [-d] [-D] [-g ]\n" + "\t" "[-s ] [ -t ] [ -c ]\n" + "\t" "-v Increase verbosity\n" + "\t" "-d Check output from git diff\n" + "\t" "-D Check output from svn diff\n" + "\t" "-g Check output from git hash\n" + "\t" "-s Check output from svn revision\n" + "\t" "-t Launch external diff tool\n" + "\n" + "Examples:\n" + "\t" "indent_wrapper -D\n" + "\t" "indent_wrapper -D -t meld\n"); + exit(EX_SOFTWARE); +} + +int +main(int argc, char **argv) +{ + block_head_t diff_head; + block_head_t diff_a_head; + block_head_t diff_b_head; + block_head_t indent_in_head; + block_head_t indent_out_head; + struct block *p1 = NULL; + struct block *p2 = NULL; + uint64_t size; + uint64_t x; + uint64_t y1 = 0; + uint64_t y2 = 0; + int recurse = 0; + int inside_string = 0; + int escape_char = 0; + int do_parse = 0; + char cmdbuf[256]; + uint16_t ch; + uint16_t chn; + int c; + int retval = 0; + + signal(SIGPIPE, &sigpipe); + + cmdbuf[0] = 0; + + while ((c = getopt(argc, argv, "dDvg:s:c:ht:")) != -1) { + switch (c) { + case 'v': + opt_verbose++; + break; + case 't': + opt_diff_tool = optarg; + break; + case 'g': + snprintf(cmdbuf, sizeof(cmdbuf), "git show -U1000000 %s", optarg); + break; + case 'd': + snprintf(cmdbuf, sizeof(cmdbuf), "git diff -U1000000"); + break; + case 'D': + snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000"); + break; + case 's': + snprintf(cmdbuf, sizeof(cmdbuf), "svn diff --diff-cmd=diff -x -U1000000 -r %s", optarg); + break; + case 'c': + snprintf(cmdbuf, sizeof(cmdbuf), "%s", optarg); + break; + default: + usage(); + } + } + if (cmdbuf[0] == 0) + usage(); + + cmd_block_process(NULL, &diff_head, cmdbuf); + + TAILQ_INIT(&diff_a_head); + TAILQ_INIT(&diff_b_head); + + size = size_block(&diff_head); + p1 = alloc_block(); + y1 = 0; + p2 = alloc_block(); + y2 = 0; + + for (x = 0; x < size;) { + ch = peek_block(&diff_head, x); + switch (ch & 0xFF) { + case '+': + if (ch == peek_block(&diff_head, x + 1) && + ch == peek_block(&diff_head, x + 2) && + ' ' == (peek_block(&diff_head, x + 3) & 0xFF)) + goto parse_filename; + if (do_parse == 0) + break; + for (x++; x != size; x++) { + ch = peek_block(&diff_head, x); + p1->mask[y1] = BLOCK_ADD >> 8; + p1->data[y1++] = ch; + if (y1 == BLOCK_SIZE) { + TAILQ_INSERT_TAIL(&diff_a_head, p1, entry); + p1 = alloc_block(); + y1 = 0; + } + if ((ch & 0xFF) == '\n') + break; + } + break; + case '-': + if (ch == peek_block(&diff_head, x + 1) && + ch == peek_block(&diff_head, x + 2) && + ' ' == (peek_block(&diff_head, x + 3) & 0xFF)) + goto parse_filename; + if (do_parse == 0) + break; + for (x++; x != size; x++) { + ch = peek_block(&diff_head, x); + p2->data[y2++] = ch; + if (y2 == BLOCK_SIZE) { + TAILQ_INSERT_TAIL(&diff_b_head, p2, entry); + p2 = alloc_block(); + y2 = 0; + } + if ((ch & 0xFF) == '\n') + break; + } + break; + case ' ': + if (do_parse == 0) + break; + for (x++; x != size; x++) { + ch = peek_block(&diff_head, x); + p1->data[y1++] = ch; + if (y1 == BLOCK_SIZE) { + TAILQ_INSERT_TAIL(&diff_a_head, p1, entry); + p1 = alloc_block(); + y1 = 0; + } + p2->data[y2++] = ch; + if (y2 == BLOCK_SIZE) { + TAILQ_INSERT_TAIL(&diff_b_head, p2, entry); + p2 = alloc_block(); + y2 = 0; + } + if ((ch & 0xFF) == '\n') + break; + } + break; + parse_filename: + for (x += 3; x != size; x++) { + ch = peek_block(&diff_head, x); + chn = peek_block(&diff_head, x + 1); + if ((ch & 0xFF) == '.') { + /* only accept .c and .h files */ + do_parse = ((chn & 0xFF) == 'c' || (chn & 0xFF) == 'h'); + } + if ((ch & 0xFF) == '\n') + break; + } + default: + break; + } + /* skip till end of line */ + for (; x < size; x++) { + ch = peek_block(&diff_head, x); + if ((ch & 0xFF) == '\n') { + x++; + break; + } + } + } + p1->length = y1; + p2->length = y2; + TAILQ_INSERT_TAIL(&diff_a_head, p1, entry); + TAILQ_INSERT_TAIL(&diff_b_head, p2, entry); + + /* first pass - verify input */ + size = size_block(&diff_a_head); + for (x = 0; x != size; x++) { + ch = peek_block(&diff_a_head, x) & 0xFF; + if (!(ch & 0x80) && ch != '\t' && ch != '\r' && ch != '\n' && + ch != ' ' && !isprint(ch)) + errx(EX_SOFTWARE, "Non printable characters are not allowed: '%c'", ch); + else if (ch & 0x80) { + set_block(&diff_a_head, x, ch | BLOCK_MASK); + } + } + + /* second pass - identify all comments */ + for (x = 0; x < size; x++) { + ch = peek_block(&diff_a_head, x); + chn = peek_block(&diff_a_head, x + 1); + if ((ch & 0xFF) == '/' && (chn & 0xFF) == '/') { + set_block(&diff_a_head, x, ch | BLOCK_MASK); + set_block(&diff_a_head, x + 1, chn | BLOCK_MASK); + for (x += 2; x < size; x++) { + ch = peek_block(&diff_a_head, x); + if ((ch & 0xFF) == '\n') + break; + set_block(&diff_a_head, x, ch | BLOCK_MASK); + } + } else if ((ch & 0xFF) == '/' && (chn & 0xFF) == '*') { + set_block(&diff_a_head, x, ch | BLOCK_MASK); + set_block(&diff_a_head, x + 1, chn | BLOCK_MASK); + for (x += 2; x < size; x++) { + ch = peek_block(&diff_a_head, x); + chn = peek_block(&diff_a_head, x + 1); + if ((ch & 0xFF) == '*' && (chn & 0xFF) == '/') { + set_block(&diff_a_head, x, ch | BLOCK_MASK); + set_block(&diff_a_head, x + 1, chn | BLOCK_MASK); + x++; + break; + } + set_block(&diff_a_head, x, ch | BLOCK_MASK); + } + } + } + + /* third pass - identify preprocessor tokens and strings */ + for (x = 0; x < size; x++) { + ch = peek_block(&diff_a_head, x); + if (ch & BLOCK_MASK) + continue; + if (inside_string == 0 && (ch & 0xFF) == '#') { + int skip_newline = 0; + + set_block(&diff_a_head, x, ch | BLOCK_MASK); + for (x++; x < size; x++) { + ch = peek_block(&diff_a_head, x); + if ((ch & 0xFF) == '\n') { + if (!skip_newline) + break; + skip_newline = 0; + } + if (ch & BLOCK_MASK) + continue; + if ((ch & 0xFF) == '\\') + skip_newline = 1; + set_block(&diff_a_head, x, ch | BLOCK_MASK); + } + } + if ((ch & 0xFF) == '"' || (ch & 0xFF) == '\'') { + if (inside_string == 0) { + inside_string = (ch & 0xFF); + } else { + if (escape_char == 0 && inside_string == (ch & 0xFF)) + inside_string = 0; + } + escape_char = 0; + set_block(&diff_a_head, x, ch | BLOCK_MASK); + } else if (inside_string != 0) { + if ((ch & 0xFF) == '\\') + escape_char = !escape_char; + else + escape_char = 0; + set_block(&diff_a_head, x, ch | BLOCK_MASK); + } + } + + /* fourth pass - identify function blocks */ + if (opt_verbose > 0) { + chn = peek_block(&diff_a_head, x); + printf("L%02d%c|", recurse, + (chn & BLOCK_ADD) ? '+' : ' '); + } + for (x = 0; x < size; x++) { + ch = peek_block(&diff_a_head, x); + if (opt_verbose > 0) { + printf("%c", ch & 0xFF); + if ((ch & 0xFF) == '\n') { + chn = peek_block(&diff_a_head, x + 1); + printf("L%02d%c|", recurse, + (chn & BLOCK_ADD) ? '+' : ' '); + } + } + if (ch & BLOCK_MASK) + continue; + switch (ch & 0xFF) { + case '{': + case '(': + recurse++; + break; + default: + break; + } + if (recurse != 0) + set_block(&diff_a_head, x, ch | BLOCK_MASK); + switch (ch & 0xFF) { + case '}': + case ')': + recurse--; + break; + default: + break; + } + } + if (opt_verbose > 0) + printf("\n"); + if (recurse != 0) + errx(EX_SOFTWARE, "Unbalanced parenthesis"); + if (inside_string != 0) + errx(EX_SOFTWARE, "String without end"); + + /* fifth pass - on the same line statements */ + for (x = 0; x < size; x++) { + ch = peek_block(&diff_a_head, x); + if (ch & BLOCK_MASK) + continue; + switch (ch & 0xFF) { + case '\n': + break; + default: + set_block(&diff_a_head, x, ch | BLOCK_MASK); + break; + } + } + + /* sixth pass - output relevant blocks to indent */ + for (y1 = x = 0; x < size; x++) { + ch = peek_block(&diff_a_head, x); + if (ch & BLOCK_ADD) { + TAILQ_INIT(&indent_in_head); + + p2 = alloc_block(); + y2 = 0; + for (; y1 < size; y1++) { + ch = peek_block(&diff_a_head, y1); + if (y1 > x && !(ch & (BLOCK_MASK | BLOCK_ADD))) + break; + p2->data[y2++] = ch & 0xFF; + if (y2 == BLOCK_SIZE) { + TAILQ_INSERT_TAIL(&indent_in_head, p2, entry); + p2 = alloc_block(); + y2 = 0; + } + } + if (p2->data[y2] != '\n') + p2->data[y2++] = '\n'; + p2->length = y2; + TAILQ_INSERT_TAIL(&indent_in_head, p2, entry); + + cmd_block_process(&indent_in_head, &indent_out_head, + "indent " + "-Tbool " + "-Tclass " + "-TFILE " + "-TLIST_ENTRY " + "-TLIST_HEAD " + "-TSLIST_ENTRY " + "-TSLIST_HEAD " + "-TSTAILQ_ENTRY " + "-TSTAILQ_HEAD " + "-TTAILQ_ENTRY " + "-TTAILQ_HEAD " + "-T__aligned " + "-T__packed " + "-T__unused " + "-T__used " + "-Tfd_set " + "-Toss_mixerinfo " + "-Tu_char " + "-Tu_int " + "-Tu_long " + "-Tu_short " + "-ta -st -bad -bap -nbbb -nbc -br -nbs " + "-c41 -cd41 -cdb -ce -ci4 -cli0 -d0 -di8 -ndj -ei -nfc1 " + "-nfcb -i8 -ip8 -l79 -lc77 -ldi0 -nlp -npcs -psl -sc " + "-nsob -nv " + " | " + "sed " + "-e 's/_HEAD [(]/_HEAD(/g' " + "-e 's/_ENTRY [(]/_ENTRY(/g' " + "-e 's/\t__aligned/ __aligned/g' " + "-e 's/\t__packed/ __packed/g' " + "-e 's/\t__unused/ __unused/g' " + "-e 's/\t__used/ __used/g' " + "-e 's/^#define /#define\t/g'"); + + if (opt_diff_tool != NULL) { + if (diff_tool(&indent_in_head, &indent_out_head)) + retval = 1; + } else { + if (diff_block(&indent_in_head, &indent_out_head)) + retval = 1; + } + free_block(&indent_in_head); + free_block(&indent_out_head); + x = y1; + } else if (!(ch & BLOCK_MASK)) { + y1 = x + 1; + } + } + return (retval); +}