diff --git a/ChangeLog b/ChangeLog index ce7bf0b658..b55967c223 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11015,4 +11015,6 @@ From Pierre-noel Bouteville (2015-10-07). * fs/tmpfs: Created a directory that will eventually hold a trivial temporary RAM file file system (2015-10-0i8). + * tools/: Add crappy style checking tool nxstyle.c. See thee tools/ + README file for more info (2015-10-08). diff --git a/fs/tmpfs/fs_tmpfs.c b/fs/tmpfs/fs_tmpfs.c index fd365f05c5..9a08621fe9 100644 --- a/fs/tmpfs/fs_tmpfs.c +++ b/fs/tmpfs/fs_tmpfs.c @@ -36,6 +36,7 @@ /**************************************************************************** * Included Files ****************************************************************************/ + #include #include diff --git a/tools/Makefile.host b/tools/Makefile.host index 1580ec8ed5..1f9f0027d0 100644 --- a/tools/Makefile.host +++ b/tools/Makefile.host @@ -1,7 +1,7 @@ ############################################################################ # Makefile.host # -# Copyright (C) 2007, 2008, 2011-2012 Gregory Nutt. All rights reserved. +# Copyright (C) 2007, 2008, 2011-2012, 2015 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -61,8 +61,9 @@ endif # Targets all: b16$(HOSTEXEEXT) bdf-converter$(HOSTEXEEXT) cmpconfig$(HOSTEXEEXT) \ - configure$(HOSTEXEEXT) mkconfig$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) mksymtab$(HOSTEXEEXT) \ - mksyscall$(HOSTEXEEXT) mkversion$(HOSTEXEEXT) + configure$(HOSTEXEEXT) mkconfig$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) \ + mksymtab$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkversion$(HOSTEXEEXT) \ + nxstyle$(HOSTEXEEXT) default: mkconfig$(HOSTEXEEXT) mksyscall$(HOSTEXEEXT) mkdeps$(HOSTEXEEXT) ifdef HOSTEXEEXT @@ -152,6 +153,15 @@ ifdef HOSTEXEEXT bdf-converter: bdf-converter$(HOSTEXEEXT) endif +# nxsytle - Check a file for compliance to NuttX coding style + +nxstyle$(HOSTEXEEXT): nxstyle.c + $(Q) $(HOSTCC) $(HOSTCFLAGS) -o nxstyle$(HOSTEXEEXT) nxstyle.c + +ifdef HOSTEXEEXT +nxstyle: nxstyle$(HOSTEXEEXT) +endif + # Create dependencies for a list of files mkdeps$(HOSTEXEEXT): mkdeps.c csvparser.c diff --git a/tools/README.txt b/tools/README.txt index 604a4c7055..5766a98458 100644 --- a/tools/README.txt +++ b/tools/README.txt @@ -210,6 +210,30 @@ mkctags.sh A script for creating ctags from Ken Pettit. See http://en.wikipedia.org/wiki/Ctags and http://ctags.sourceforge.net/ +nxstyle.c +--------- + + I am embarassed that this is here. This program is a complete hack + but, unfortunately, it has become so useful to me that I need to keep + it here. + + A little background: I have tinkered with pretty printers for some + time and have not been happy with the results. An alternative that + occurred to me would be just a standard checker that examines a C + file that gives warnings for violations of the coding standard. + + This turns out to be more difficult that you might think. A pretty + printer understands C syntax: They break the file up into its C + components then reassembles the output in the format. But parsing the + C loses the original file layout and so it not useful in this case. + + This program instead, uses a collection of heuristics (i.e., hacks and + bandaids) to examine the C file for obvious violations of the coding + standard. This program is completely ignorant of C syntax; it simply + performs crude pattern matching to check the file. + + Usage: nxstyle + pic32mx ------- diff --git a/tools/nxstyle.c b/tools/nxstyle.c new file mode 100644 index 0000000000..1eada32187 --- /dev/null +++ b/tools/nxstyle.c @@ -0,0 +1,987 @@ +/**************************************************************************** + * tools/nxstyle.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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 ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LINE_SIZE 512 + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char **argv, char **envp) +{ + FILE *instream; + char line[LINE_SIZE]; + bool btabs; + bool bfunctions; + bool bstatm; + bool bfor; + bool bswitch; + bool bstring; + bool bquote; + int lineno; + int indent; + int prevnest; + int ncomment; + int nnest; + int declnest; + int prevdeclnest; + int prevncomment; + int n; + int i; + + instream = fopen(argv[1], "r"); + if (!instream) + { + fprintf(stderr, "Failed to open %s\n", argv[1]); + return 1; + } + + btabs = false; + bfunctions = false; + bswitch = false; + bstring = false; + lineno = 0; + ncomment = 0; + nnest = 0; + declnest = 0; + prevdeclnest = 0; + prevncomment = 0; + + while (fgets(line, LINE_SIZE, instream)) + { + lineno++; + indent = 0; + prevnest = nnest; + prevdeclnest = declnest; + prevncomment = ncomment; + bstatm = false; + bfor = false; /* REVISIT: Implies for() is all on one line */ + + /* STEP 1: Find the indentation level and the start of real stuff on + * the line. + */ + + for (n = 0; line[n] != '\n' && isspace((int)line[n]); n++) + { + switch (line[n]) + { + case ' ': + { + indent++; + } + break; + + case '\t': + { + if (!btabs) + { + fprintf(stderr, "TABs found. First at line %d:%d\n", lineno, n); + btabs = true; + } + + indent = (indent + 4) & ~3; + } + break; + + default: + { + fprintf(stderr, "Unexpected white space character %02x found at line %d:%d\n", line[n], lineno, n); + } + break; + } + } + + /* STEP 2: Detect some certain start of line conditions */ + /* Skip over pre-processor lines */ + + if (line[indent] == '#') + { + continue; + } + + /* Check for the comment block indicating the beginning of functions. */ + + if (!bfunctions && ncomment > 0 && + (strcmp(line, " * Private Functions\n") == 0 || + strcmp(line, " * Public Functions\n") == 0)) + { + //fprintf(stderr, "Functions begin at line %d:%d\n", lineno, n); + bfunctions = true; + } + + /* Check for some kind of declaration. + * REVISIT: The following logic fails for any non-standard types. + * REVISIT: Terminator after keyword might not be a space. Might be + * a newline, for example. struct and unions are often unnamed, for + * example. + */ + + else if (strncmp(&line[indent], "auto ", 5) == 0 || + strncmp(&line[indent], "bool ", 5) == 0 || + strncmp(&line[indent], "char ", 5) == 0 || + strncmp(&line[indent], "CODE ", 5) == 0 || + strncmp(&line[indent], "const ", 6) == 0 || + strncmp(&line[indent], "double ", 7) == 0 || +// strncmp(&line[indent], "struct ", 7) == 0 || + strncmp(&line[indent], "struct", 6) == 0 || /* May be unnamed */ + strncmp(&line[indent], "enum ", 5) == 0 || + strncmp(&line[indent], "extern ", 7) == 0 || + strncmp(&line[indent], "EXTERN ", 7) == 0 || + strncmp(&line[indent], "FAR ", 4) == 0 || + strncmp(&line[indent], "float ", 6) == 0 || + strncmp(&line[indent], "int ", 4) == 0 || + strncmp(&line[indent], "int16_t ", 8) == 0 || + strncmp(&line[indent], "int32_t ", 8) == 0 || + strncmp(&line[indent], "long ", 5) == 0 || + strncmp(&line[indent], "off_t ", 6) == 0 || + strncmp(&line[indent], "register ", 9) == 0 || + strncmp(&line[indent], "short ", 6) == 0 || + strncmp(&line[indent], "signed ", 7) == 0 || + strncmp(&line[indent], "size_t ", 7) == 0 || + strncmp(&line[indent], "ssize_t ", 8) == 0 || + strncmp(&line[indent], "static ", 7) == 0 || + strncmp(&line[indent], "time_t ", 7) == 0 || + strncmp(&line[indent], "typedef ", 8) == 0 || + strncmp(&line[indent], "uint8_t ", 8) == 0 || + strncmp(&line[indent], "uint16_t ", 9) == 0 || + strncmp(&line[indent], "uint32_t ", 9) == 0 || +// strncmp(&line[indent], "union ", 6) == 0 || + strncmp(&line[indent], "union", 5) == 0 || /* May be unnamed */ + strncmp(&line[indent], "unsigned ", 9) == 0 || + strncmp(&line[indent], "void ", 5) == 0 || + strncmp(&line[indent], "volatile ", 9) == 0) + { + /* REVISIT: Also picks up function return types */ + /* REVISIT: Logic problem for nested data/function declarations */ + + if ((!bfunctions || nnest > 0) && declnest == 0) + { + declnest = 1; + } + } + + /* Check for a keyword indicating the beginning of a statement. + * REVISIT: This, obviously, will not detect statements that do not + * begin with a C keyword (such as assignement statements). + */ + + else if (strncmp(&line[indent], "break ", 6) == 0 || + strncmp(&line[indent], "case ", 5) == 0 || +// strncmp(&line[indent], "case ", 5) == 0 || /* Part of switch */ + strncmp(&line[indent], "continue ", 9) == 0 || +// strncmp(&line[indent], "default ", 8) == 0 || /* Part of switch */ + strncmp(&line[indent], "do ", 3) == 0 || + strncmp(&line[indent], "else ", 5) == 0 || + strncmp(&line[indent], "goto ", 5) == 0 || + strncmp(&line[indent], "if ", 3) == 0 || + strncmp(&line[indent], "return ", 7) == 0 || +// strncmp(&line[indent], "switch ", 7) == 0 || /* Doesn't follow pattern */ + strncmp(&line[indent], "while ", 6) == 0) + { + bstatm = true; + } + + /* Spacing works a little differently for and switch statements */ + + else if (strncmp(&line[indent], "for ", 4) == 0) + { + bfor = true; + bstatm = true; + } + else if (strncmp(&line[indent], "switch ", 7) == 0) + { + bswitch = true; + } + + /* Also check for C keywords with missing white space */ + + else if (strncmp(&line[indent], "do(", 3) == 0 || + strncmp(&line[indent], "if(", 3) == 0 || + strncmp(&line[indent], "while(", 6) == 0) + { + fprintf(stderr, "Missing whitespace after keyword at line %d:%d\n", lineno, n); + bstatm = true; + } + else if (strncmp(&line[indent], "for(", 4) == 0) + { + fprintf(stderr, "Missing whitespace after keyword at line %d:%d\n", lineno, n); + bfor = true; + bstatm = true; + } + else if (strncmp(&line[indent], "switch(", 7) == 0) + { + fprintf(stderr, "Missing whitespace after keyword at line %d:%d\n", lineno, n); + bswitch = true; + } + + /* STEP 3: Parse each character on the line */ + + bquote = false; + for (; line[n] != '\n' && line[n] != '\0'; n++) + { + if (line[n] == '/' && !bstring) + { + /* Check for start of a C comment */ + + if (line[n+1] == '*') + { + if (line[n+2] == '\n') + { + fprintf(stderr, "C comment on separate line at %d:%d\n", + lineno, n); + } + else if (line[n+2] != ' ' && line[n+2] != '*') + { + fprintf(stderr, + "Missing space after opening C comment at line %d:%d\n", + lineno, n); + } + + ncomment++; + n++; + continue; + } + + /* Check for end of a C comment */ + + else if (n > 0 && line[n-1] == '*') + { + if (n < 2) + { + fprintf(stderr, "Closing C comment not indented at line %d:%d\n", + lineno, n); + } + else if (line[n-2] != ' ' && line[n-2] != '*') + { + fprintf(stderr, + "Missing space before closing C comment at line %d:%d\n", + lineno, n); + } + +#if 0 + /* REVISIT: Generates false alarms when portions of an + * expression are commented out within the expression. + */ + + if (line[n+1] != '\n') + { + fprintf(stderr, + "Garbage on line after C comment at line %d:%d\n", + lineno, n); + } +#endif + + if (ncomment > 0) + { + ncomment--; + } + else + { + ncomment = 0; + fprintf(stderr, + "Closing without opening comment at line %d:%d\n", + lineno, n); + } + } + + /* Check for C++ style comments */ + + else if (line[n+1] == '/') + { + fprintf(stderr, "C++ style comment on at %d:%d\n", + lineno, n); + n++; + continue; + } + } + + /* Check for a string... ignore if we are in the middle of a + * comment. + */ + + if (ncomment == 0) + { + /* Backslash quoted charater */ + + if (line[n] == '\\') + { + bquote = true; + n++; + } + + /* Check for quoated characters: \" in string */ + + if (line[n] == '"' && !bquote) + { + bstring = !bstring; + } + + bquote = false; + } + + /* The reset of the line is only examined of we are in a comment + * or a string. + * + * REVISIT: Should still check for whitespace at the end of the + * line. + */ + + if (ncomment == 0 && !bstring) + { + switch (line[n]) + { + /* Handle logic nested with curly braces */ + + case '{': + { + if (n > indent) + { + if (declnest == 0) + { + fprintf(stderr, + "Left bracket not on separate line at %d:%d\n", + lineno, n); + } + } + else if (line[n+1] != '\n') + { + if (declnest == 0) + { + fprintf(stderr, + "Garbage follows left bracket at line %d:%d\n", + lineno, n); + } + } + + nnest++; + if (declnest > 0) + { + declnest++; + } + } + break; + + case '}': + { + if (nnest < 1) + { + fprintf(stderr, "Unmatched right brace at line %d:%d\n", lineno, n); + } + else + { + nnest--; + if (nnest < 1) + { + nnest = 0; + bswitch = false; + } + } + + if (declnest < 3) + { + declnest = 0; + } + else + { + declnest--; + } + + if (n > indent) + { + if (declnest == 0) + { + fprintf(stderr, + "Right bracket not on separate line at %d:%d\n", + lineno, n); + } + } + else if (line[n+1] != '\n' && + line[n+1] != ',' && + line[n+1] != ';') + { + /* One case where there may be garbage after the right + * bracket is, for example, when declaring a until or + * structure variable using an un-named union or + * structure. + */ + + if (prevdeclnest <= 0 || declnest > 0) + { + fprintf(stderr, + "Garbage follows right bracket at line %d:%d\n", + lineno, n); + } + } + } + break; + + /* Check for inappropriate space around parentheses */ + + case '(': + { + if (line[n+1] == ' ' /* && !bfor */) + { + fprintf(stderr, + "Space follows left parenthesis at line %d:%d\n", + lineno, n); + } + } + break; + + case ')': + { + /* Allow ')' as first thing on the line (n == indent) + * Allow "for (xx; xx; )" (bfor == true) + */ + + if (n > 0 && n != indent && line[n-1] == ' ' && !bfor) + { + fprintf(stderr, + "Space precedes right parenthesis at line %d:%d\n", + lineno, n); + } + } + break; + + /* Check for inappropriate space around square brackets */ + + case '[': + { + if (line[n+1] == ' ') + { + fprintf(stderr, + "Space follows left bracket at line %d:%d\n", + lineno, n); + } + } + break; + + case ']': + { + if (n > 0 && line[n-1] == ' ') + { + fprintf(stderr, + "Space precedes right bracket at line %d:%d\n", + lineno, n); + } + } + break; + + /* Semi-colon may terminate a declaration */ + + case ';': + { + if (!isspace((int)line[n+1])) + { + fprintf(stderr, "Missing whitespace after semicolon at line %d:%d\n", + lineno, n); + } + + /* Semicolon terminates a declaration/definition if there + * was no left curly brace (i.e., declnest is only 1). + */ + + if (declnest == 1) + { + declnest = 0; + } + } + break; + + /* Semi-colon may terminate a declaration */ + + case ',': + { + if (!isspace((int)line[n+1])) + { + fprintf(stderr, "Missing whitespace after comma at line %d:%d\n", + lineno, n); + } + } + break; + + case '\r': + { + fprintf(stderr, + "Carriage return detected at line %d:%d\n", + lineno, n); + } + break; + + /* Skip over character constants */ + + case '\'': + { + int endndx = n + 2; + + if (line[n+1] != '\n' && line[n+1] != '\0') + { + if (line[n+1] == '\\') + { + for (; + line[endndx] != '\n' && + line[endndx] != '\0' && + line[endndx] != '\''; + endndx++); + } + + n = endndx + 1; + } + } + break; + + /* Check for space at the end of the line */ + + case '\n': + { + if (n > 0 && isspace((int)line[n-1])) + { + fprintf(stderr, + "Dangling whitespace at the end of line %d:%d\n", + lineno, n); + } + } + break; + + /* Check for space around various operators */ + + case '-': + /* -> */ + + if (line[n+1] == '>') + { + n++; + break; + } + + case '+': + /* ++, -- */ + + if (line[n+1] == line[n]) + { + n++; + break; + } + + case '&': + /* && */ + + if (line[n] == '&' && line[n+1] == line[n]) + { + int curr; + int next; + + curr = n; + n++; + next = n + 1; + + if (line[curr-1] != ' ') + { + fprintf(stderr, + "Operator/assignment must be preceded with whitespace at line %d:%d\n", + lineno, curr); + } + + if (line[next] != ' ' && line[next] != '\n') + { + fprintf(stderr, + "Operator/assignment needs whitespace separation at line %d:%d\n", + lineno, curr); + } + + break; + } + + /* & OR &()*/ + + else if (isalpha((int)line[n+1]) || line[n+1] == '_' || line[n+1] == '(') + { + break; + } + + case '/': + { + if (line[n] == '/') + { + if (line[n-1] == '*') + { + n++; + break; + } + else if (line[n+1] == '/') + { + fprintf(stderr, "C++ style comment on at %d:%d\n", + lineno, n); + n++; + break; + } + } + } + + case '*': + { + /* *\/, ** */ + + if (line[n] == '*' && + (line[n+1] == '/' || + line[n+1] == '*')) + { + n++; + break; + } + + /* *, *() */ + + else if (isalpha((int)line[n+1]) || + line[n+1] == '_' || + line[n+1] == '(') + { + break; + } + + /* ( *) */ + + else if (line[n+1] == ')') + { + if (line[n-1] != ' ') + { + fprintf(stderr, + "Operator/assignment must be preceded with whitespace at line %d:%d\n", + lineno, n); + } + + break; + } + } + + case '%': + { + if (isalnum((int)line[n+1])) + { + break; + } + } + + case '<': + case '>': + case '|': + case '^': + case '=': + { + int curr; + int next; + + curr = n; + if (line[curr-1] != ' ') + { + fprintf(stderr, + "Operator/assignment must be preceded with whitespace at line %d:%d\n", + lineno, curr); + } + + next = n+1; + + /* <<, >>, <<=, >>= */ + + if (line[curr] == '>' || line[curr] == '<') + { + if (line[next] == line[curr]) + { + next++; + n++; + } + + if (line[next] == '=') + { + next++; + n++; + } + } + else if (line[next] == '=' || line[next] == line[n]) + { + next++; + n++; + } + + if (line[curr] != '-' && line[next] != ' ' && line[next] != '\n') + { + fprintf(stderr, + "Operator/assignment needs whitespace separation at line %d:%d\n", + lineno, curr); + } + } + break; + + case '~': + case '!': + { + int curr; + int next; + + curr = n; + next = n + 1; + if (line[next] == '=' || line[next] == line[n]) + { + next++; + n++; + + if (line[next] != ' ' && line[next] != '\n') + { + fprintf(stderr, + "Operator/assignment needs whitespace separation at line %d:%d\n", + lineno, curr); + } + } + + if (line[curr-1] != ' ' && line[curr-1] != '(') + { + fprintf(stderr, + "Operator/assignment must be preceded with whitespace at line %d:%d\n", + lineno, curr); + } + } + break; + + default: + break; + } + } + } + + /* STEP 4: Check alignment */ + + /* Within a comment block, we need only check on the alignment of the + * comment. + */ + + if ((ncomment > 0 || prevncomment > 0) && !bstring) + { + if (indent == 0 && line[0] != '/') + { + fprintf(stderr, "No indentation line %d:%d\n", + lineno, indent); + } + else if (indent == 1 && line[0] == ' ' && line[1] == '*') + { + /* Good indentation */ + } + else if (indent > 0 && line[indent] == '\n') + { + fprintf(stderr, "Whitespace on blank line at line %d:%d\n", + lineno, indent); + } + else if (indent > 0 && indent < 2) + { + fprintf(stderr, "Insufficient indentation line %d:%d\n", + lineno, indent); + } + else if (indent > 0 && !bswitch) + { + if (line[indent] == '/') + { + if ((indent & 3) != 2) + { + fprintf(stderr, + "Bad comment alignment at line %d:%d\n", + lineno, indent); + } + + /* REVISIT: This screws up in cases where there is C code, + * followed by a comment that continues on the next line. + */ + + else if (line[indent+1] != '*') + { + fprintf(stderr, + "Missing asterisk in comment at line %d:%d\n", + lineno, indent); + } + } + else if (line[indent] == '*') + { + /* REVISIT: Generates false alarms on comments at the end of + * the line if there is nothing preceding (such as the aligned + * comments with a structure field definition). So disabled for + * comments before beginning of function definitions. + */ + + if ((indent & 3) != 3 && bfunctions && declnest == 0) + { + fprintf(stderr, + "Bad comment block alignment at line %d:%d\n", + lineno, indent); + } + + if (line[indent+1] != ' ' && + line[indent+1] != '\n' && + line[indent+1] != '/') + { + fprintf(stderr, + "Invalid character after asterisk in comment block at line %d:%d\n", + lineno, indent); + } + } + + /* If this is not the line containing the comment start, then this + * line should begin with '*' + */ + + else if (prevncomment > 0) + { + fprintf(stderr, "Missing asterisk in comment block at line %d:%d\n", + lineno, indent); + } + } + } + + /* Check for various alignment outside of the comment block */ + + else if ((ncomment > 0 || prevncomment > 0) && !bstring) + { + if (indent == 0 && strchr("\n#{}", line[0]) == NULL) + { + /* Ignore if we are at global scope */ + + if (prevnest > 0) + { + bool blabel = false; + + if (isalpha((int)line[indent])) + { + for (i = indent + 1; isalnum((int)line[i]) || line[i] == '_'; i++); + blabel = (line[i] == ':'); + } + + if (!blabel) + { + fprintf(stderr, "No indentation line %d:%d\n", + lineno, indent); + } + } + } + else if (indent == 1 && line[0] == ' ' && line[1] == '*') + { + /* Good indentation */ + } + else if (indent > 0 && line[indent] == '\n') + { + fprintf(stderr, "Whitespace on blank line at line %d:%d\n", + lineno, indent); + } + else if (indent > 0 && indent < 2) + { + fprintf(stderr, "Insufficient indentation line %d:%d\n", + lineno, indent); + } + else if (line[indent] == '{') + { + /* REVISIT: False alarms in data initializers and switch statements */ + + if ((indent & 3) != 0 && !bswitch && declnest == 0) + { + fprintf(stderr, "Bad left brace alignment at line %d:%d\n", + lineno, indent); + } + } + else if (line[indent] == '}') + { + /* REVISIT: False alarms in data initializers and switch statements */ + + if ((indent & 3) != 0 && !bswitch && prevdeclnest == 0) + { + fprintf(stderr, "Bad right brace alignment at line %d:%d\n", + lineno, indent); + } + } + else if (indent > 0) + { + /* REVISIT: Generates false alarms when a statement continues on + * the next line. The bstatm check limits to lines beginnnig with + * C keywords. + * REVISIT: The bstatm check will not detect statements that + * do not begin with a C keyword (such as assignement statements). + * REVISIT: Generates false alarms on comments at the end of + * the line if there is nothing preceding (such as the aligned + * comments with a structure field definition). So disabled for + * comments before beginning of function definitions. + */ + + if ((bstatm || /* Begins with C keyword */ + (line[indent] == '/' && bfunctions)) && /* Comment in functions */ + !bswitch && /* Not in a switch */ + declnest == 0) /* Not a data definition */ + { + if ((indent & 3) != 2) + { + fprintf(stderr, "Bad alignment at line %d:%d\n", + lineno, indent); + } + } + + /* Crazy cases. There should be no small odd alignements + * outside of comment/string. Odd alignments are possible + * on continued lines, but not if they are small. + */ + + else if (indent == 1 || indent == 3) + { + fprintf(stderr, "Small odd alignment at line %d:%d\n", + lineno, indent); + } + } + } + } + + if (ncomment > 0 || bstring) + { + fprintf(stderr, "In a comment/string at end of file\n"); + } + + fclose(instream); + return 0; +}