summaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>2020-03-09 11:03:21 +0100
committerDr. David von Oheimb <David.von.Oheimb@siemens.com>2020-03-09 11:03:21 +0100
commit99a16e0459e5089c2cfb92ee775f1221a51b8d05 (patch)
tree4037504638169aad9004ad8850515d15f6c250e5 /util
parentc518117b99bc4aad62990e8a31b7bc1dae06d16c (diff)
Renew and extend the tool for checking adherence to C coding style rules
aims at checking most of https://www.openssl.org/policies/codingstyle.html and various requirements not yet explicitly stated there - see also #10725 add util/check-format.pl and its self-tests in util/check-format-test-{positives,negatives}.c remove util/openssl-format-source Reviewed-by: Richard Levitte <levitte@openssl.org> Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com> (Merged from https://github.com/openssl/openssl/pull/10363)
Diffstat (limited to 'util')
-rw-r--r--util/check-format-test-negatives.c678
-rw-r--r--util/check-format-test-positives.c345
-rw-r--r--util/check-format.pl1118
-rwxr-xr-xutil/openssl-format-source175
4 files changed, 2141 insertions, 175 deletions
diff --git a/util/check-format-test-negatives.c b/util/check-format-test-negatives.c
new file mode 100644
index 0000000000..c9f781e06b
--- /dev/null
+++ b/util/check-format-test-negatives.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright Nokia 2007-2019
+ * Copyright Siemens AG 2015-2019
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * A collection of test cases where check-format.pl should not report issues.
+ * There are some known false positives, though, which are marked below.
+ */
+
+/*-
+ * allow double space in format-tagged multi-line comment
+ */
+int f(void) /*
+ * trailing multi-line comment
+ */
+{
+ if (ctx == NULL) { /* non-leading intra-line comment */
+ if (pem_name != NULL)
+ /* entire-line comment indent usually like for the following line */
+ return NULL; /* hanging indent also for this line after comment */
+ /* leading comment has same indentation as normal code */ stmt;
+ /* entire-line comment may have same indent as normal code */
+ }
+
+#if X
+ if (1) /* bad style: just part of control structure depends on #if */
+#else
+ if (2) /*@ resulting false positive */
+#endif
+ c; /*@ resulting false positive */
+
+ if (1)
+ if (2)
+ c;
+ else
+ e;
+ else
+ f;
+ do
+ do
+ 2;
+ while (1);
+ while (2);
+
+ if (1)
+ f(a, b);
+ do
+ 1; while (2); /*@ more than one stmt just to construct case */
+ if (1)
+ f(a, b);
+ else
+ do
+ 1;
+ while (2);
+ if (1)
+ f(a, b);
+ else do /*@ (non-brace) code before 'do' just to construct case */
+ 1;
+ while (2);
+ f1234(a,
+ b); do /*@ (non-brace) code before 'do' just to construct case */
+ 1;
+ while (2);
+ if (1)
+ f(a,
+ b); do /*@ (non-brace) code before 'do' just to construct case */
+ 1;
+ while (2);
+ if (1)
+ f(a, b);
+ else
+ do f(c, c); /*@ (non-brace) code after 'do' just to construct case */
+ while (2);
+
+ if (1)
+ f(a, b);
+ else
+ return;
+ if (1)
+ f(a,
+ b); else /*@ (non-brace) code before 'else' just to construct case */
+ do
+ 1;
+ while (2);
+
+ if (1)
+ { /*@ brace after 'if' not on same line just to construct case */
+ c;
+ d;
+ }
+ /* this comment is correctly indented if it refers to the following line */
+ d;
+
+ if (1) {
+ 2;
+ } else /*@ no brace after 'else' just to construct case */
+ 3;
+ do {
+ } while (x);
+ if (1) {
+ 2;
+ } else {
+ 3;
+ }
+ if (4)
+ 5;
+ else
+ 6;
+}
+typedef * d(int)
+ x;
+typedef (int)
+x;
+typedef (int)*()
+ x;
+typedef *int *
+x;
+typedef OSSL_CMP_MSG *(*cmp_srv_process_cb_t)
+ (OSSL_CMP_SRV_CTX *ctx, OSSL_CMP_MSG *msg)
+ xx;
+int f()
+{
+ c;
+ if (1) {
+ c;
+ }
+ c;
+ if (1)
+ if (2)
+ { /*@ brace after 'if' not on same line just to construct case */
+ c;
+ }
+ e;
+ const usign = {
+ 0xDF,
+ {
+ dd
+ },
+ dd
+ };
+ const unsign = {
+ 0xDF, {
+ dd
+ },
+ dd
+ };
+}
+const unsigned char trans_id[OSSL_CMP_TRANSACTIONID_LENGTH] = {
+ 0xDF,
+};
+const unsigned char trans_id[OSSL_CMP_TRANSACTIONID_LENGTH] =
+ {
+ 0xDF,
+ };
+typedef
+int
+a;
+
+typedef
+struct
+{
+ int a;
+} b;
+typedef enum {
+ w = 0
+} e_type;
+typedef struct {
+ enum {
+ w = 0
+ } e_type;
+ enum {
+ w = 0
+ } e_type;
+} e;
+struct s_type {
+ enum e_type {
+ w = 0
+ };
+};
+struct s_type
+{
+ enum e_type {
+ w = 0
+ };
+ enum e2_type {
+ w = 0
+ };
+};
+
+#define X 1 + 1
+#define Y /* .. */ 2 + 2
+#define Z 3 + 3
+
+static varref cmp_vars[] = { /* comment */
+ {&opt_config}, {&opt_section},
+
+ {&opt_server}, {&opt_proxy}, {&opt_path},
+};
+
+#define SWITCH(x) \
+ switch (x) { \
+ case 0: \
+ break; \
+ default: \
+ break; \
+ }
+
+#define DEFINE_SET_GET_BASE_TEST(PREFIX, SETN, GETN, DUP, FIELD, TYPE, ERR, \
+ DEFAULT, NEW, FREE) \
+ static int execute_CTX_##SETN##_##GETN##_##FIELD( \
+ TEST_FIXTURE *fixture) \
+ { \
+ CTX *ctx = fixture->ctx; \
+ int (*set_fn)(CTX *ctx, TYPE) = \
+ (int (*)(CTX *ctx, TYPE))PREFIX##_##SETN##_##FIELD; \
+ /* comment */ \
+ }
+
+/* 'struct' in function header */
+static int f(struct pem_pass_data *pass_data)
+{
+ if (pass_data == NULL)
+ return 0;
+}
+
+static void *fun(void)
+{
+ if (pem_name != NULL)
+ /* comment */
+ return NULL;
+
+ do {
+ size_t available_len, data_len;
+ const char *curr = txt, *next = txt;
+ char *tmp;
+ } while (1);
+
+ char *intraline_string_with_comment_delimiters_and_dbl_space = "1 /*1";
+ char *multiline_string_with_comment_delimiters_and_dbl_space = "1 /*1\
+2222222\'22222222222222222\"222222222" "33333 /*3333333333" "44 /*44444444444\
+55555555555555\
+6666";
+}
+
+ASN1_CHOICE(OSSL_CRMF_POPO) = {
+ ASN1_IMP(OSSL_CRMF_POPO, value.raVerified, ASN1_NULL, 0),
+ ASN1_EXP(OSSL_CRMF_POPO, value.keyAgreement, OSSL_CRMF_POPOPRIVKEY, 3)
+} ASN1_CHOICE_END(OSSL_CRMF_POPO)
+IMPLEMENT_ASN1_FUNCTIONS(OSSL_CRMF_POPO)
+
+ASN1_ADB(OSSL_CRMF_ATTRIBUTETYPEANDVALUE) = {
+ ADB_ENTRY(NID_id_regCtrl_regToken,
+ ASN1_SIMPLE(OSSL_CRMF_ATTRIBUTETYPEANDVALUE,
+ value.regToken, ASN1_UTF8STRING)),
+} ASN1_ADB_END(OSSL_CRMF_ATTRIBUTETYPEANDVALUE, 0, type, 0,
+ &attributetypeandvalue_default_tt, NULL);
+
+ASN1_ITEM_TEMPLATE(OSSL_CRMF_MSGS) =
+ ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0,
+ OSSL_CRMF_MSGS, OSSL_CRMF_MSG)
+ASN1_ITEM_TEMPLATE_END(OSSL_CRMF_MSGS)
+
+void f_looong_body_200()
+{ /* function body length up to 200 lines accepted */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+void f_looong_body_201()
+{ /* function body length > 200 lines, but LONG BODY marker present */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/util/check-format-test-positives.c b/util/check-format-test-positives.c
new file mode 100644
index 0000000000..d14ceb375b
--- /dev/null
+++ b/util/check-format-test-positives.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2007-2019 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright Nokia 2007-2019
+ * Copyright Siemens AG 2015-2019
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This demonstrates/tests cases where check-format.pl should report issues.
+ * Some of the reports are due to sanity checks for proper nesting of comment
+ * delimiters and parenthesis-like symbols, e.g., on unexpected/unclosed braces.
+ */
+
+/*
+ * The '@'s after '*' are used for self-tests: they mark lines containing
+ * a single flaw that should be reported. Normally it should be reported
+ * while handling the given line, but in case of delayed checks there is a
+ * following digit indicating the number of reports expected for this line.
+ */
+
+/* For each of the following set of lines the tool should complain once */
+/*@ tab character: */
+/*@ intra-line carriage return character: */
+/*@ non-printable ASCII character:  */
+/*@ non-ASCII character: รค */
+/*@ whitespace at EOL: */
+// /*@ end-of-line comment style not allowed (for C90 compatibility) */
+ /*@0 intra-line comment indent off by 1, reported unless sloppy-cmt */
+/*X */ /*@2 no space nor '*' after comment start, reported unless sloppy-spc */
+/* X*/ /*@ no space before comment end , reported unless sloppy-spc */
+/*@ comment starting delimiter: /* inside intra-line comment */
+ /*@0
+ *@ above multi-line comment start indent off by 1, reported unless sloppy-cmt; this comment line is too long
+ *@ multi-line comment indent further off by 1 relative to comment start
+ *@ multi-line comment ending with text on last line */
+/*@2 multi-line comment starting with text on first line
+ *@ comment starting delimiter: /* inside multi-line comment
+*@ multi-line comment indent off by -1
+ *X*@ no spc after leading '*' in multi-line comment, reported unless sloppy-spc
+ *@0 more than two spaces after . in comment, reported unless sloppy-spc
+*/ /*@2 multi-line comment end indent off by -1 (relative to comment start) */
+*/ /*@ unexpected comment ending delimiter outside comment */
+/*@ comment line is 4 columns tooooooooooooooooo wide, reported unless sloppy-len */
+/*@ comment line is 5 columns toooooooooooooooooooooooooooooooooooooooooooooo wide */
+#define X 1 /*@0 double space false negative due to coincidence */
+ #define Y 2 /*@ indent of preprocessor directive off by 1 (must be 0) */
+typedef struct { /*@0 double space in code, reported unless sloppy-spc */
+ enum { /*@1 double space in comment, reported unless sloppy-spc */
+ w = 0 /*@2 hanging expr indent off by 1, or 3 for lines after '{' */
+ && 1, /*@ hanging expr indent off by 3, or -1 for leading '&&' */
+ x = 1, /*@ hanging expr indent off by -1 */
+ y,z /*@ no space after ',', reported unless sloppy-spc */
+ } e_member ; /*@ space before ';', reported unless sloppy-spc */
+ int v[1; /*@ unclosed bracket in type declaration */
+ union { /*@ statement/type declaration indent off by -1 */
+ struct{} s; /*@ no space before '{', reported unless sloppy-spc */
+ }u_member; /*@ no space after '}', reported unless sloppy-spc */
+ } s_type; /*@ statement/type declaration indent off by 4 */
+int* somefunc(); /*@ no space before '*' in type decl, r unless sloppy-spc */
+void main(int n) { /*@ opening brace at end of function definition header */
+ for (;;n++) { /*@ no space after ';', reported unless sloppy-spc */
+ return; /*@0 (1-line) single statement in braces */
+ }} /*@2 code after '}' outside expr */
+} /*@ unexpected closing brace (too many '}') outside expr */
+) /*@ unexpected closing paren outside expr */
+#endif /*@ unexpected #endif */
+int f (int a, /*@ space after fn before '(', reported unless sloppy-spc */
+ int b, /*@ hanging expr indent off by -1 */
+ long l) /*@ one-letter name 'l' */
+{ int /*@ code after '{' opening a block */
+ xx = 1) + /*@ unexpected closing parenthesis */
+ 2] - /*@ unexpected closing bracket */
+ 3: * /*@ unexpected ':' (without preceding '?') within expr */
+ 4}; /*@ unexpected closing brace within expression */
+ char y[] = { /*@0 unclosed brace within initializer/enum expression */
+ 1* 1, /*@ no space etc. before '*', reported unless sloppy-spc */
+ 2, /*@ hanging expr indent (for lines after '{') off by 1 */
+ (xx /*@0 unclosed parenthesis in expression */
+ ? y /*@0 unclosed '? (conditional expression) */
+ [0; /*@4 unclosed bracket in expression */
+ s_type s; /*@ local variable declaration indent off by -1 */
+ somefunc(a, /*@ statement indent off by -1 */
+ "aligned" /*@ expr indent off by -2 accepted if sloppy-hang */ "right"
+ , b, /*@ expr indent off by -1 */
+ b, /*@ expr indent as on line above, accepted if sloppy-hang */
+ b, /*@ expr indent off -8 but @ extra indent accepted if sloppy-hang */
+ "again aligned" /*@ expr indent off by -9 (left of stmt indent, */ "right",
+ 123 == /*@ .. so reported also with sloppy-hang; this line is too long */ 456
+# define MAC(A) (A) /*@ nesting indent of preprocessor directive off by 1 */
+ ? 1 /*@ hanging expr indent off by 1 */
+ : 2); /*@ hanging expr indent off by 2, or 1 for leading ':' */
+ if(a /*@ no space after 'if', reported unless sloppy-spc */
+ /*@0 intra-line comment indent off by -1 (not: by 3 due to '&&') */
+ && ! 0 /*@2 space after '!', reported unless sloppy-spc */
+ || b == /*@ hanging expr indent off by 2, or -2 for leading '||' */
+ (xx+= 2) + /*@ no space before '+=', reported unless sloppy-spc */
+ (a^ 1) + /*@ no space before '^', reported unless sloppy-spc */
+ a %2 / /*@ no space after '%', reported unless sloppy-spc */
+ 1 +/* */ /*@ no space before comment, reported unless sloppy-spc */
+ /* */+ /*@ no space after comment, reported unless sloppy-spc */
+ s. e_member) /*@ space after '.', reported unless sloppy-spc */
+ xx = a + b /*@ extra single-statement indent off by 1 */
+ + 0; /*@ two times extra single-statement indent off by 3 */
+ if (a ++) /*@ space before postfix '++', reported unless sloppy-spc */
+ { /*@ {' not on same line as preceding 'if' */
+ c; /*@0 single stmt in braces, reported on 1-stmt */
+ } else /*@ no '{' on same line after '} else' */
+ { /*@ statement indent off by 2 */
+ d; /*@0 single stmt in braces, reported on 1-stmt */
+ } /*@ statement indent off by 6 */
+ if (1) f(a, /*@ (non-brace) code after end of 'if' condition */
+ b); else /*@ (non-brace) code before 'else' */
+ do f(c, c); /*@ (non-brace) code after 'do' */
+ while ( 2); /*@ space after '(', reported unless sloppy-spc */
+ b; c; /*@ more than one statement per line */
+ do{ /*@ no space before '{', reported unless sloppy-spc */
+ f (3, /*@ space after fn before '(', reported unless sloppy-spc */
+ 4); /*@0 false negative: should report single stmt in braces */
+ } /*@0 'while' not on same line as preceding '}' */
+ while (a+ 0); /*@2 no space before '+', reported unless sloppy-spc */
+ switch (b ) { /*@ space before ')', reported unless sloppy-spc */
+ case 1: /*@ 'case' special statement indent off by -1 */
+ case(2): /*@ no space after 'case', reported unless sloppy-spc */
+ default: ; /*@ code after 'default:' */
+} /*@ statement indent off by -4 */
+ label: /*@ label special statement indent off by 1 */
+ return( /*@ no space after 'return', reported unless sloppy-spc */
+ x); } /*@ code before block-level '}' */
+/* Here the tool should stop complaining apart from the below issues at EOF */
+
+void f_looong_body()
+{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+} /*@ function body length > 200 lines */
+
+#if 0 /*@0 unclosed #if */
+struct t { /*@0 unclosed brace at decl/block level */
+ enum { /*@0 unclosed brace at enum/expression level */
+ v = (1 /*@0 unclosed parenthesis */
+ etyp /*@0 empty line follows just before EOF: */
+
diff --git a/util/check-format.pl b/util/check-format.pl
new file mode 100644
index 0000000000..af77d20920
--- /dev/null
+++ b/util/check-format.pl
@@ -0,0 +1,1118 @@
+#!/usr/bin/perl
+#
+# Copyright 2020 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright Siemens AG 2019-2020
+#
+# Licensed under the Apache License 2.0 (the "License").
+# You may not use this file except in compliance with the License.
+# You can obtain a copy in the file LICENSE in the source distribution
+# or at https://www.openssl.org/source/license.html
+#
+# check-format.pl
+# - check formatting of C source according to OpenSSL coding style
+#
+# usage:
+# check-format.pl [-l|--sloppy-len] [-l|--sloppy-bodylen]
+# [-s|--sloppy-spc] [-c|--sloppy-cmt] [-m|--sloppy-macro]
+# [-h|--sloppy-hang] [-1|--1-stmt]
+# <files>
+#
+# checks adherence to the formatting rules of the OpenSSL coding guidelines
+# assuming that the input files contain syntactically correct C code.
+# This pragmatic tool is incomplete and yields some false positives.
+# Still it should be useful for detecting most typical glitches.
+#
+# options:
+# -l | --sloppy-len increase accepted max line length from 80 to 84
+# -l | --sloppy-bodylen do not report function body length > 200
+# -s | --sloppy-spc do not report whitespace nits
+# -c | --sloppy-cmt do not report indentation of comments
+# Otherwise for each multi-line comment the indentation of
+# its lines is checked for consistency. For each comment
+# that does not begin to the right of normal code its
+# indentation must be as for normal code, while in case it
+# also has no normal code to its right it is considered to
+# refer to the following line and may be indented equally.
+# -m | --sloppy-macro allow missing extra indentation of macro bodies
+# -h | --sloppy-hang when checking hanging indentation, do not report
+# * same indentation as on line before
+# * same indentation as non-hanging indent level
+# * indentation moved left (not beyond non-hanging indent)
+# just to fit contents within the line length limit
+# -1 | --1-stmt do more aggressive checks for { 1 stmt } - see below
+#
+# There are non-trivial false positives and negatives such as the following.
+#
+# * When a line contains several issues of the same kind only one is reported.
+#
+# * When a line contains more than one statement this is (correctly) reported
+# but in some situations the indentation checks for subsequent lines go wrong.
+#
+# * There is the special OpenSSL rule not to unnecessarily use braces around
+# single statements:
+# {
+# stmt;
+# }
+# except within if ... else constructs where some branch contains more than one
+# statement. Since the exception is hard to recognize when such branches occur
+# after the current position (such that false positives would be reported)
+# the tool by checks for this rule by defaul only for do/while/for bodies.
+# Yet with the --1-stmt option false positives are preferred over negatives.
+# False negatives occur if the braces are more than two non-empty lines apart.
+#
+# * Use of multiple consecutive spaces is regarded a coding style nit except
+# when done in order to align certain columns over multiple lines, e.g.:
+# # define AB 1
+# # define CDE 22
+# # define F 3333
+# This pattern is recognized - and consequently double space not reported -
+# for a given line if in the nonempty line before or after (if existing)
+# for each occurrence of " \S" (where \S means non-space) in the given line
+# there is " \S" in the other line in the respective column position.
+# This may lead to both false negatives (in case of coincidental " \S")
+# and false positives (in case of more complex multi-column alignment).
+#
+# * When just part of control structures depend on #if(n)(def), which can be
+# considered bad programming style, indentation false positives occur, e.g.:
+# #if X
+# if (1) /* bad style */
+# #else
+# if (2) /* bad style resulting in false positive */
+# #endif
+# c; /* resulting further false positive */
+
+use strict;
+# use List::Util qw[min max];
+use POSIX;
+
+use constant INDENT_LEVEL => 4;
+use constant MAX_LINE_LENGTH => 80;
+use constant MAX_BODY_LENGTH => 200;
+
+# global variables @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+# command-line options
+my $max_length = MAX_LINE_LENGTH;
+my $sloppy_bodylen = 0;
+my $sloppy_SPC = 0;
+my $sloppy_hang = 0;
+my $sloppy_cmt = 0;
+my $sloppy_macro = 0;
+my $extended_1_stmt = 0;
+
+while ($ARGV[0] =~ m/^-(\w|-[\w\-]+)$/) {
+ my $arg = $1; shift;
+ if ($arg =~ m/^(l|-sloppy-len)$/) {
+ $max_length += INDENT_LEVEL;
+ } elsif ($arg =~ m/^(b|-sloppy-bodylen)$/) {
+ $sloppy_bodylen = 1;
+ } elsif ($arg =~ m/^(s|-sloppy-spc)$/) {
+ $sloppy_SPC = 1;
+ } elsif ($arg =~ m/^(c|-sloppy-cmt)$/) {
+ $sloppy_cmt = 1;
+ } elsif ($arg =~ m/^(m|-sloppy-macro)$/) {
+ $sloppy_macro = 1;
+ } elsif ($arg =~ m/^(h|-sloppy-hang)$/) {
+ $sloppy_hang = 1;
+ } elsif ($arg =~ m/^(1|-1-stmt)$/) {
+ $extended_1_stmt = 1;
+ } else {
+ die("unknown option: -$arg");
+ }
+}
+
+# status variables
+my $self_test; # whether the current input file is regarded to contain (positive/negative) self-tests
+my $line; # current line number
+my $line_before; # number of previous not essentially empty line (containing at most whitespace and '\')
+my $line_before2; # number of not essentially empty line before previous not essentially empty line
+my $contents; # contents of current line
+my $contents_before; # contents of $line_before, if $line_before > 0
+my $contents_before_; # contents of $line_before after blinding comments etc., if $line_before > 0
+my $contents_before2; # contents of $line_before2, if $line_before2 > 0
+my $contents_before_2; # contents of $line_before2 after blinding comments etc., if $line_before2 > 0
+my $in_multiline_string; # line starts within multi-line string literal
+my $count; # -1 or number of leading whitespace characters (except newline) in current line,
+ # which should be $block_indent + $hanging_offset + $local_offset or $expr_indent
+my $count_before; # number of leading whitespace characters (except line ending chars) in $contents_before
+my $has_label; # current line contains label
+my $local_offset; # current extra indent due to label, switch case/default, or leading closing brace(s)
+my $line_body_start; # number of line where last function body started, or 0
+my $line_function_start; # number of line where last function definition started, used if $line_body_start != 0
+my $last_function_header; # header containing name of last function defined, used if $line_function_start != 0
+my $line_opening_brace; # number of previous line with opening brace after do/while/for, optionally for if/else
+
+my $keyword_opening_brace; # name of previous keyword, used if $line_opening_brace != 0
+my $ifdef__cplusplus; # line before contained '#ifdef __cplusplus' (used in header files)
+my $block_indent; # currently required normal indentation at block/statement level
+my $hanging_offset; # extra indent, which may be nested, for just one hanging statement or expr or typedef
+my @in_do_hanging_offsets; # stack of hanging offsets for nested 'do' ... 'while'
+my @in_if_hanging_offsets; # stack of hanging offsets for nested 'if' (but not its potential 'else' branch)
+my $if_maybe_terminated; # 'if' ends and $hanging_offset should be reset unless the next line starts with 'else'
+my @nested_block_indents; # stack of indentations at block/statement level, needed due to hanging statements
+my @nested_hanging_offsets;# stack of nested $hanging_offset values, in parallel to @nested_block_indents
+my @nested_in_typedecl; # stack of nested $in_typedecl values, partly in parallel to @nested_block_indents
+my @nested_indents; # stack of hanging indents due to parentheses, braces, brackets, or conditionals
+my @nested_symbols; # stack of hanging symbols '(', '{', '[', or '?', in parallel to @nested_indents
+my @nested_conds_indents; # stack of hanging indents due to conditionals ('?' ... ':')
+my $expr_indent; # resulting hanging indent within (multi-line) expressions including type exprs, else 0
+my $hanging_symbol; # character ('(', '{', '[', not: '?') responsible for $expr_indent, if $expr_indent != 0
+my $in_expr; # in expression after if/while/for/switch/return/enum/LHS of assignment
+my $in_paren_expr; # in parenthesized if/while/for condition and switch expression, if $expr_indent != 0
+my $in_typedecl; # nesting level of typedef/struct/union/enum
+my $in_directive; # number of lines so far within preprocessor directive, e.g., macro definition
+my $directive_nesting; # currently required indentation of preprocessor directive according to #if(n)(def)
+my $directive_offset; # indent offset within multi-line preprocessor directive, if $in_directive > 0
+my $in_macro_header; # number of open parentheses + 1 in (multi-line) header of #define, if $in_directive > 0
+my $in_comment; # number of lines so far within multi-line comment, or < 0 when end is on current line
+my $leading_comment; # multi-line comment has no code before its beginning delimiter
+my $formatted_comment; # multi-line comment beginning with "/*-", which indicates/allows special formatting
+my $comment_indent; # comment indent, if $in_comment != 0
+my $num_reports_line = 0; # number of issues found on current line
+my $num_reports = 0; # total number of issues found
+my $num_indent_reports = 0;# total number of indentation issues found
+my $num_nesting_issues = 0;# total number of directive nesting issues found
+my $num_syntax_issues = 0; # total number of syntax issues found during sanity checks
+my $num_SPC_reports = 0; # total number of whitespace issues found
+my $num_length_reports = 0;# total number of line length issues found
+
+sub reset_file_state {
+ $line = 0;
+ $line_before = 0;
+ $line_before2 = 0;
+ @nested_block_indents = ();
+ @nested_hanging_offsets = ();
+ @nested_in_typedecl = ();
+ @nested_symbols = ();
+ @nested_indents = ();
+ @nested_conds_indents = ();
+