Files
ioccc/2013/iocccsize.c
Yusuke Endoh e8d7ee3b6a Add 2013/rules.txt, iocccsize.c, and iocccsize.mk
These files are extracted from 2013/2013.tar.bz2.
2021-01-05 19:10:19 +09:00

412 lines
11 KiB
C

/*
* 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 <ctype.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <getopt.h>
#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;
}