/* * iocccsize - IOCCC Source Secondary Size * * Public Domain 1992, 2013 by Anthony Howe. All rights released. * With IOCCC mods in 2013 by chongo (Landon Curt Noll) /\oo/\ * * This IOCCC size tool source file is version 2013-07-30-v17. * * SYNOPSIS * * ioccc [-ikrs] < input * * -i print official IOCCC size to standard out * NOTE: -i implies -r -s * -k keep comments * -r count C reserved words as 1 byte * -s silence; do not output source code, only final count * * DESCRIPION * * Taking input from standard input, apply the IOCCC Source Size Rule * for 2001. The program's offical length is written to standard error. * Also filter out C comment blocks (-k to keep) sending the modified * source to standard output. * * The 2001 Source Size Rule was: * * Your entry must be <= 4096 bytes in length. The number of octets * excluding whitespace (tab, space, newline, formfeed, return), and * excluding any ';', '{' or '}' followed by whitespace or end of file, * must be <= 2048. * * NOTE: The above descipion is NOT the official IOCCC size tool algorithm! */ /* * The official IOCCC rule 2 secondary limit on C code size * * The IOCCC size tool should be compiled as: * * cc -pedantic -Wall -std=c99 iocccsize.c -o iocccsize * * This tool computes a 2nd size C code. To check your program source * against the 2nd limit of rule 2, use the -i command line option. * * For example: * * ./iocccsize -i < prog.c * * The IOCCC size tool, when using the -i option, may be summarized as: * * The size tool counts C language keywords (primary, secondary, and * selected preprocessor keywords) as 1. The size tool counts all * other octets as 1 excluding ASCII whitespace, and excluding any * ';', '{' or '}' followed by ASCII whitespace, and excluding any * ';', '{' or '}' octet immediately before the end of file. * * ASCII whitespace includes ASCII tab, ASCII space, ASCII newline, * ASCII formfeed, and ASCII carriage return. * * In cases where the above summary and the algorithm implemented by * the IOCCC size tool source code conflict, the algorithm implemented * by the IOCCC size tool source code is preferred by the judges. * * See the current IOCCC rules and guidelines for more information. * In particular, see the current IOCCC size rule for information about * the maximum value that this tool should print for an entry to be valid. */ /* * HINT: The algorithm implemented by this code may or not be obfuscated. * The algorithm may not or may appear to be obfuscated. * * In particular: * * We did not invent the algorithm. * The algorithm consistently finds Obfuscation. * The algorithm killed Obfuscation. * The algorithm is banned in C. * The algorithm is from Bell Labs in Jersey. * The algorithm constantly finds Obfuscation. * This is not the algorithm. * This is close. */ #include #include #include #include #include #define FLAG_SILENCE 1 #define FLAG_KEEP 2 #define FLAG_RESERVED 4 #define FLAG_IOCCC_SIZE 8 char usage[] = "usage: ioccc [-ikrs] < prog.c\n" "\n" "-i\t\tprint just the official secondary IOCCC size to stdout\n" "\t\t NOTE: -i implies -r -s\n" "-k\t\tkeep block comments\n" "-r\t\tcount C reserved words as 1 byte\n" "-s\t\tsilence; do not output source code, only final count\n"; #define STRLEN(s) (sizeof (s)-1) typedef struct { size_t length; const char *word; } Word; /* * C reserved words, plus a few #preprocessor tokens, that count as 1 * * NOTE: For a good list of reserved words in C, see: * * http://www.bezem.de/pdf/ReservedWordsInC.pdf * * by Johan Bezem of JB Enterprises: * * See http://www.bezem.de/en/ */ static Word cwords[] = { /* Yes Virginia, we left #define off the list on purpose! */ { STRLEN("#elif"), "#elif" }, { STRLEN("#else"), "#else" }, { STRLEN("#endif"), "#endif" }, { STRLEN("#error"), "#error" }, { STRLEN("#ident"), "#ident" }, { STRLEN("#if"), "#if" }, { STRLEN("#ifdef"), "#ifdef" }, { STRLEN("#include"), "#include" }, { STRLEN("#line"), "#line" }, { STRLEN("#pragma"), "#pragma" }, { STRLEN("#sccs"), "#sccs" }, { STRLEN("#warning"), "#warning" }, /**/ { STRLEN("_Alignas"), "_Alignas" }, { STRLEN("_Alignof"), "_Alignof" }, { STRLEN("_Atomic"), "_Atomic" }, { STRLEN("_Bool"), "_Bool" }, { STRLEN("_Complex"), "_Complex" }, { STRLEN("_Generic"), "_Generic" }, { STRLEN("_Imaginary"), "_Imaginary" }, { STRLEN("_Noreturn"), "_Noreturn" }, { STRLEN("_Pragma"), "_Pragma" }, { STRLEN("_Static_assert"), "_Static_assert" }, { STRLEN("_Thread_local"), "_Thread_local" }, /**/ { STRLEN("alignas"), "alignas" }, { STRLEN("alignof"), "alignof" }, { STRLEN("and"), "and" }, { STRLEN("and_eq"), "and_eq" }, { STRLEN("auto"), "auto" }, { STRLEN("bitand"), "bitand" }, { STRLEN("bitor"), "bitor" }, { STRLEN("bool"), "bool" }, { STRLEN("break"), "break" }, { STRLEN("case"), "case" }, { STRLEN("char"), "char" }, { STRLEN("compl"), "compl" }, { STRLEN("const"), "const" }, { STRLEN("continue"), "continue" }, { STRLEN("default"), "default" }, { STRLEN("do"), "do" }, { STRLEN("double"), "double" }, { STRLEN("else"), "else" }, { STRLEN("enum"), "enum" }, { STRLEN("extern"), "extern" }, { STRLEN("false"), "false" }, { STRLEN("float"), "float" }, { STRLEN("for"), "for" }, { STRLEN("goto"), "goto" }, { STRLEN("I"), "I" }, { STRLEN("if"), "if" }, { STRLEN("inline"), "inline" }, { STRLEN("int"), "int" }, { STRLEN("long"), "long" }, { STRLEN("noreturn"), "noreturn" }, { STRLEN("not"), "not" }, { STRLEN("not_eq"), "not_eq" }, { STRLEN("or"), "or" }, { STRLEN("or_eq"), "or_eq" }, { STRLEN("register"), "register" }, { STRLEN("restrict"), "restrict" }, { STRLEN("return"), "return" }, { STRLEN("short"), "short" }, { STRLEN("signed"), "signed" }, { STRLEN("sizeof"), "sizeof" }, { STRLEN("static"), "static" }, { STRLEN("static_assert"), "static_assert" }, { STRLEN("struct"), "struct" }, { STRLEN("switch"), "switch" }, { STRLEN("thread_local"), "thread_local" }, { STRLEN("true"), "true" }, { STRLEN("typedef"), "typedef" }, { STRLEN("union"), "union" }, { STRLEN("unsigned"), "unsigned" }, { STRLEN("void"), "void" }, { STRLEN("volatile"), "volatile" }, { STRLEN("while"), "while" }, { STRLEN("xor"), "xor" }, { STRLEN("xor_eq"), "xor_eq" }, /**/ { 0, NULL } }; Word * find_member(Word *table, const char *string) { Word *w; for (w = table; w->length != 0; w++) { if (strncmp(string, w->word, w->length) == 0 && !isalnum(string[w->length]) && string[w->length] != '_') return w; } return NULL; } /* * Count octets and strip comments. The stripped C input is sent to * standard output. If -s is set, then suppress the source output. * The various counters are sent to standard error (see below), unless * -i is given in which case source output is suppressed and only the * IOCCC size rule count is sent to standard out. * * The counter output format is: * * lcount wcount bcount icount isaved rcount rsaved * * where * lcount line count (same as wc) * wcount word count (same as wc) * bcount byte count (same as wc) * icount IOCCC secondary size rule count * isaved bytes saved by secondary size rule (bcount - icount) * rcount number of C reserved words * rsaved number of octets saved with -r */ int count(int flags) { Word *w; int span; char *p, buf[256]; int lcount, wcount, bcount; int is_comment, is_word, dquote; int count, keywords, saved, kw_saved; /* Start of buffer sentinel. */ buf[0] = ' '; count = saved = 0; keywords = kw_saved = 0; lcount = wcount = bcount = 0; is_comment = is_word = dquote = 0; /* * "no matter how well you may think you understand this code, * you don't, so don't mess with it." :-) */ for ( ; fgets(buf+1, sizeof (buf)-1, stdin) != NULL;) { if (!(flags & FLAG_KEEP)) { /* Leading whitespace before comment block? */ span = strspn(buf+1, "\t "); if (buf[1+span] == '/' && buf[2+span] == '*') { /* Strip leading whitespace before comment block. */ is_comment = 1; } } for (p = buf+1; *p != '\0'; p++) { /* In C comment block? */ if (is_comment) { /* End of comment block? */ if (*p == '*' && p[1] == '/') { is_comment = 0; if (!(flags & FLAG_KEEP)) { /* Strip whitespace and newline * trailing closing comment. */ p += 1 + strspn(p+2, " \t\r\n"); } } if (!(flags & FLAG_KEEP)) { /* Strip octets in comment block. */ continue; } } /* Start of comment block? */ else if (!dquote && *p == '/' && p[1] == '*') { /* Begin comment block. */ is_comment = 1; if (!(flags & FLAG_KEEP)) { /* Strip comment block. */ p++; continue; } } /* Toggle start/end of double-quote string. */ else if (*p == '"' && p[-1] != '\\' && p[-1] != '\'') dquote = !dquote; /* C reserved word? */ else if (!dquote && !isalnum(p[-1]) && p[-1] != '_' && (w = find_member(cwords, p)) != NULL) { keywords++; if (flags & FLAG_RESERVED) { bcount += w->length; if (!is_word) { is_word = 1; wcount++; } if (!(flags & FLAG_SILENCE)) fputs(w->word, stdout); /* Count reserved word as one. */ kw_saved += w->length - 1; p += w->length - 1; count++; continue; } } /* Everything above here is stripped from the input. */ if (!(flags & FLAG_SILENCE)) fputc(*p, stdout); bcount++; if (*p == '\n') lcount++; /* Ignore all whitespace. */ if (isspace(*p)) { is_word = 0; saved++; continue; } else if (!is_word) { is_word = 1; wcount++; } /* Ignore curly braces and semicolons when followed * by any whitspace or EOF. */ if (strchr("{;}", *p) != NULL && (isspace(p[1]) || p[1] == '\0')) { saved++; continue; } /* Count this octet. */ count++; } } /* final count */ if (flags & FLAG_IOCCC_SIZE) { /* output the official IOCCC size tool size to standard out */ printf("%d\n", count); } else { fprintf(stderr, "%d %d %d %d %d %d %d\n", lcount, wcount, bcount, count, saved, keywords, kw_saved); } return count; } int main(int argc, char **argv) { int ch; int flags = 0; /* parse args */ while ((ch = getopt(argc, argv, "ikrs")) != -1) { switch (ch) { case 'i': /* "You are not expected to understand this" :-) */ flags |= (FLAG_RESERVED | FLAG_SILENCE | FLAG_IOCCC_SIZE); break; case 'k': flags |= FLAG_KEEP; break; case 'r': flags |= FLAG_RESERVED; break; case 's': flags |= FLAG_SILENCE; break; default: fprintf(stderr, "%s\n", usage); return 2; } } /* count as directed by flags */ (void) count(flags); /* All Done!!! All Done!!! -- Jessical Noll, age 2 */ return 0; }