Add preview of 1984-2019
The 2020 winners are hinted it, but the source code is not there,
nor are the hints. Someone could look into the HTML and
see what was commented out, but that is not a problem because
WHO won is known and the name of their AWARD is known already.
Moreover a number of the winners have talked about what their
entirs do, so the commented out spoiler text is not a bit problem.
One will find HTML commented out with:
<!-- XXX remove when 2020 is ready
2020 links here
XXX remove when 2020 is ready -->
The "XXX remove when 2020 is ready" string is found at the
start and end comment blocks. Again, soneone could read
what was commented out, but that is OK since most of that
information is known or can be known. The following files
have such HTML comment blocks in them.
./winners.html
./years-spoiler.html
./years.html
In addition, ./all/summary.txt was modified to not
include the 2020 entries for the momemt.
The 2020 winner code AND write-ups are NOT part of this set.
p.s. The all/all.tar.bz2 does not contain the 2020 source code (yet?).
This commit is contained in:
1 parent
28efc67f5d
commit
c0663537cb
1683 files changed
+220064
No files matched your search
@@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env make
|
||||
#
|
||||
# 2018 makefile
|
||||
#
|
||||
# This work by Landon Curt Noll, Simon Cooper, and Leonid A. Broukhis
|
||||
# is licensed under:
|
||||
#
|
||||
# Creative Commons Attribution-ShareAlike 3.0 Unported License.
|
||||
#
|
||||
# See: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
|
||||
################
|
||||
# tool locations
|
||||
################
|
||||
#
|
||||
SHELL= /bin/bash
|
||||
CP= cp
|
||||
CPP= cpp
|
||||
GUNZIP= gunzip
|
||||
BUNZIP2= bunzip2
|
||||
GZIP_PROG= gzip
|
||||
BZIP2= bzip2
|
||||
CURL= curl
|
||||
DD= dd
|
||||
LD= ld
|
||||
MAKE= make
|
||||
RM= rm
|
||||
SED= sed
|
||||
TAR= tar
|
||||
TRUE= true
|
||||
MKDIR= mkdir
|
||||
MV= mv
|
||||
TR= tr
|
||||
GREP= grep
|
||||
SORT= sort
|
||||
UNIQ= uniq
|
||||
|
||||
# Set X11_LIBDIR to the directory where the X11 library resides
|
||||
#
|
||||
#X11_LIBDIR= /usr/X11R6/lib
|
||||
#X11_LIBDIR= /usr/X11/lib
|
||||
X11_LIBDIR= /opt/X11/lib
|
||||
|
||||
# Set X11_INCLUDEDIR to the directory where the X11 include files reside
|
||||
#
|
||||
#X11_INCDIR= /usr/X11R6/include
|
||||
#X11_INCDIR= /usr/X11/include
|
||||
X11_INCDIR= /opt/X11/include
|
||||
|
||||
# Compiler warnings
|
||||
#
|
||||
#CWARN=
|
||||
#CWARN= -Wall
|
||||
#CWARN= -Wall -Wextra
|
||||
CWARN= -Wall -Wextra ${CSILENCE}
|
||||
#CWARN= -Wall -Wextra -pedantic ${CSILENCE}
|
||||
#CWARN= -Wall -Wextra -Weverything
|
||||
#CWARN= -Wall -Wextra -Weverything -pedantic
|
||||
#CWARN= -Wall -Wextra -Weverything -pedantic ${CSILENCE}
|
||||
|
||||
# Silence warnings that ${CWARN} would normally complain about
|
||||
#
|
||||
CSILENCE= -Wno-empty-body -Wno-parentheses -Wno-pointer-sign -Wno-char-subscripts -Wno-trigraphs -Wno-sign-compare
|
||||
#CSILENCE= -Wno-implicit-int
|
||||
|
||||
# compiler standard
|
||||
#
|
||||
#CSTD=
|
||||
#CSTD= -ansi
|
||||
CSTD= -std=c11
|
||||
|
||||
# compiler bit architecture
|
||||
#
|
||||
# Some entries require 32-bitness:
|
||||
# ARCH= -m32
|
||||
#
|
||||
# Some entries require 64-bitness:
|
||||
# ARCH= -m64
|
||||
#
|
||||
# By default we assume nothing:
|
||||
#
|
||||
ARCH=
|
||||
|
||||
# defines that are needed to compile
|
||||
#
|
||||
CDEFINE= -trigraphs -DW=\"keywords\" -DU=$(USAGE)
|
||||
#CDEFINE= -DIOCCC=25
|
||||
|
||||
# include files that are needed to compile
|
||||
#
|
||||
CINCLUDE=
|
||||
#CINCLUDE= -include stdlib.h
|
||||
#CINCLUDE= -include stdio.h
|
||||
#CINCLUDE= -include stdlib.h -include stdio.h
|
||||
#CINCLUDE= -I ${X11_INCDIR}
|
||||
|
||||
# optimization
|
||||
#
|
||||
# Most compiles will safely use -O2. Some can use only -O1 or -O.
|
||||
# A few compilers have broken optimizers or this entry make break
|
||||
# under those buggy optimizers and thus you may not want anything.
|
||||
# Reasonable compilers will allow for -O3.
|
||||
#
|
||||
#OPT=
|
||||
#OPT= -O
|
||||
#OPT= -O1
|
||||
#OPT= -O2
|
||||
OPT= -O3
|
||||
|
||||
# default flags for ANSI C compilation
|
||||
#
|
||||
CFLAGS= ${CSTD} ${CWARN} ${ARCH} ${CDEFINE} ${CINCLUDE} ${OPT}
|
||||
|
||||
# Libraries needed to build
|
||||
#
|
||||
LIBS=
|
||||
#LIBS= -L ${X11_LIBDIR}
|
||||
|
||||
# ANSI compiler
|
||||
#
|
||||
# Set CC to the name of your ANSI compiler.
|
||||
#
|
||||
# Some entries seem to need gcc. If you have gcc, set
|
||||
# both CC and MAY_NEED_GCC to gcc.
|
||||
#
|
||||
# If you do not have gcc, set CC to the name of your ANSI compiler, and
|
||||
# set MAY_NEED_GCC to either ${CC} (and hope for the best) or to just :
|
||||
# to disable such programs.
|
||||
#
|
||||
CC= cc
|
||||
#CC=clang
|
||||
MAY_NEED_GCC= gcc
|
||||
|
||||
|
||||
##############################
|
||||
# Special flags for this entry
|
||||
##############################
|
||||
#
|
||||
OBJ= prog.o
|
||||
DATA= keywords
|
||||
EXTRA_DATA= ansi c++11 c++14 c++98 c11 c99 discrepancies \
|
||||
test01.c test02.c test03.c test04.c test05.c test06.c test07.c \
|
||||
test08.c test09.c test10.c test11.c test12.c test13.c test14.c \
|
||||
iocccbug01.c iocccbug02.c iocccbug03.c iocccbug04.c iocccbug05.c \
|
||||
iocccbug06.c results results.k src.doc.rot13 patch tac.man README \
|
||||
ioc_without_quote
|
||||
UTILITIES= unob.sh tokenfix spotcheck spotdiff freqcount
|
||||
TARGET= prog
|
||||
#
|
||||
ALT_OBJ=
|
||||
ALT_TARGET=
|
||||
##
|
||||
USAGE="\"prog [-tcksri] < file.c\ntokenize and count stdin\n\t-t\ttokens only\n\t-c\tcompatible counts\n\t-k\tkeep comments\n\t-s\tsuppress output\n\t-r\treserved words count as 1\n\t-i\tioccc count only\n\""
|
||||
|
||||
#################
|
||||
# build the entry
|
||||
#################
|
||||
#
|
||||
all: ${TARGET} ${DATA} ${UTILITIES}
|
||||
@${TRUE}
|
||||
|
||||
prog: prog.c keywords
|
||||
${CC} ${CFLAGS} $< -o $@ ${LIBS}
|
||||
|
||||
tac: tac.c
|
||||
${CC} ${CFLAGS} $< -o $@ ${LIBS}
|
||||
|
||||
# alternative executable
|
||||
#
|
||||
alt: ${ALT_TARGET}
|
||||
@${TRUE}
|
||||
|
||||
# data files
|
||||
#
|
||||
data: ${DATA}
|
||||
@${TRUE}
|
||||
|
||||
# keywords: c11
|
||||
keywords: ioccc.kw.freq mkkeywords
|
||||
./mkkeywords $< > keywords
|
||||
|
||||
fixed_iocccsize: fixed_iocccsize.c
|
||||
${CC} ${CFLAGS} $< -o $@ ${LIBS}
|
||||
|
||||
test: prog tac runtest fixed_iocccsize ${EXTRA_DATA}
|
||||
-./runtest | diff - results
|
||||
-opt=kcrs ./runtest | diff - results.k
|
||||
-opt=rs prog=tac ./runtest | diff - results
|
||||
-opt=rsk prog=tac ./runtest | diff - results.k
|
||||
./fixed_iocccsize -rs < prog.c
|
||||
./tac -rs prog.c
|
||||
|
||||
###############
|
||||
# utility rules
|
||||
###############
|
||||
#
|
||||
everything: all alt
|
||||
|
||||
clean:
|
||||
${RM} -f ${OBJ} ${ALT_OBJ}
|
||||
|
||||
clobber: clean
|
||||
${RM} -f ${TARGET} ${ALT_TARGET}
|
||||
${RM} -f fixed_iocccsize tac keywords iocccsize
|
||||
@-if [ -e sandwich ]; then \
|
||||
${RM} -f sandwich; \
|
||||
echo 'ate sandwich'; \
|
||||
fi
|
||||
|
||||
nuke: clobber
|
||||
@${TRUE}
|
||||
|
||||
dist_clean: nuke
|
||||
@${TRUE}
|
||||
|
||||
install:
|
||||
@echo "Surely you are performing, Dr. May!"
|
||||
@${TRUE}
|
||||
|
||||
# backwards compatibility
|
||||
#
|
||||
build: all
|
||||
@${TRUE}
|
||||
|
||||
|
||||
##################
|
||||
# 133t hacker rulz
|
||||
##################
|
||||
#
|
||||
love:
|
||||
@echo 'not war?'
|
||||
@${TRUE}
|
||||
|
||||
haste:
|
||||
$(MAKE) waste
|
||||
@${TRUE}
|
||||
|
||||
waste:
|
||||
@echo 'haste'
|
||||
@${TRUE}
|
||||
|
||||
make:
|
||||
@echo 'Attend a maker faire'
|
||||
@${TRUE}
|
||||
|
||||
easter_egg:
|
||||
@echo you expected to often mis-understand this $${RANDOM} magic
|
||||
@echo chongo '<was here>' "/\\oo/\\"
|
||||
@echo Eggy
|
||||
|
||||
fabricate fashion form frame manufacture produce: make
|
||||
@${TRUE}
|
||||
|
||||
sandwich:
|
||||
@if [ `id -u` -eq 0 ]; then \
|
||||
echo 'Okay.'; \
|
||||
echo $${RANDOM}`date +%s`$${RANDOM} > $@; \
|
||||
else \
|
||||
echo 'What? Make it yourself.'; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# Understand the history of the Homebrew Computer Club
|
||||
# as well as the West Coast Computer Faire and
|
||||
# you might be confused different. :-)
|
||||
#
|
||||
supernova: nuke
|
||||
@-if [ -r .code_anal ]; then \
|
||||
${RM} -f .code_anal_v6; \
|
||||
else \
|
||||
echo "planet deniers, like some members of the IAU, are so cute when they try to defend their logic"; \
|
||||
fi
|
||||
@echo A $@ helps ${MAKE} the elements that help form planets
|
||||
@${TRUE}
|
||||
|
||||
deep_magic:
|
||||
@-if [ -r .code_anal ]; then \
|
||||
ccode_analysis --deep_magic 3a3fabb90892eeb2f610ae0f74a35a970060665a063ab702bd686c17c4ae07f6 --FNV1a_hash_256_bit "prog"; \
|
||||
else \
|
||||
echo "Wrong! Do it again!"; \
|
||||
sleep 2; \
|
||||
echo "Wrong! Do it again!"; \
|
||||
sleep 2; \
|
||||
echo "Wrong! Do it again!"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
magic: deep_magic
|
||||
@-if [ -r .code_anal ]; then \
|
||||
ccode_analysis --mode 216091 --level 216193 --FNV1a_hash_256_bit "prog"; \
|
||||
else \
|
||||
echo "If you don't eat yer meat, you can't have any pudding!"; \
|
||||
echo "How can you have any pudding if you don't eat yer meat?!"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
# The IOCCC resident astronomer states
|
||||
#
|
||||
charon: supernova
|
||||
@echo $@ is a dwarf planet
|
||||
@echo dwarf is a type of planet
|
||||
@echo $@ is a planet
|
||||
|
||||
pluto: supernova
|
||||
${MAKE} charon
|
||||
@echo $@ is a dwarf planet
|
||||
@echo dwarf is a type of planet
|
||||
@echo $@ is a planet
|
||||
@echo get used to having lots of planets because a $< can ${MAKE} a lot of them
|
||||
@@ -0,0 +1,71 @@
|
||||
tac comes with a small suite of support programs to test and validate against.
|
||||
Untar tests and variations in the same dir as the sources,
|
||||
or change runtest and unob.sh to look elsewhere for the keyword and test files.
|
||||
|
||||
keywords is a critical file, created from ioccc.kw.freq by mkkeywords.
|
||||
Only ioccc.kw.freq produces results compatible with iocccsize.c;
|
||||
however, any of the variations can be used instead:
|
||||
|
||||
mkkeywords c11 > keywords
|
||||
|
||||
unob.sh uses the same input file as mkkeywords to control spaces after keywords.
|
||||
The script allows an environment variable, KW, to override this.
|
||||
Use it simply:
|
||||
|
||||
unob.sh prog.c | less
|
||||
unob.sh -p prog.c | less
|
||||
KW=c11 unob.sh prog.c | less
|
||||
|
||||
To handle obscure command line defines, use the environment variable DEF:
|
||||
|
||||
DEF="-D'g(o)'='goto o;'" KW=c11 unob.sh -p prog.c | less
|
||||
|
||||
It does an amazingly good job for such a simple script.
|
||||
|
||||
Manifest
|
||||
|
||||
prog.c the reason for this README
|
||||
keywords the file #included in prog.c, output of mkkeywords
|
||||
mkkeywords reads one of the files from variations, produces string for prog.c
|
||||
ioccc.kw.freq keywords used by iocccsize, sorted by frequency in 201x ioccc winners
|
||||
unob.sh a more robust unobfuscation script; requires variations/c11 in cur dir
|
||||
tokenfix patches up prog -t output for ::, >>>, ...
|
||||
src.doc.rot13 the answers, or at least significant help
|
||||
tac.c more clearly written code, offered as a replacement of iocccsize.c
|
||||
patch fixes single quotes in iocccsize.c, adds #ifndef, removes "I"
|
||||
fixed_iocccsize.c iocccsize with patch applied, and nop -c argument added
|
||||
|
||||
tests/*
|
||||
runtest script to run the tests; ./runtest | diff results -
|
||||
results results without -k
|
||||
results.k results with -k; opt=kcrs ./runtest | diff results.k -
|
||||
test*.c various test cases looking for compatability
|
||||
iocccbug*.c various test cases demonstrating problems with iocccsize
|
||||
spotcheck script to test a lot of files vs iocccsize
|
||||
spotdiff summarize the results of spotcheck, showing only differences
|
||||
discrepancies text file of the differences found using all the ioccc winning entries
|
||||
|
||||
to test prog:
|
||||
|
||||
runtest | diff - results
|
||||
opt=kcrs runtest | diff - results.k
|
||||
find ~/src/obc -type f -a -name "*.c" | xargs spotcheck prog
|
||||
[ | spotdiff | diff -bw - discrepancies ]
|
||||
|
||||
to test tac:
|
||||
|
||||
prog=tac runtest | diff - results
|
||||
prog=tac opt=krs runtest | diff - results.k
|
||||
find ~/src/obc -type f -a -name "*.c" | xargs spotcheck tac
|
||||
|
||||
variations/* as described in the remarks, and in each file below
|
||||
ansi
|
||||
c++11
|
||||
c++14
|
||||
c++98
|
||||
c11
|
||||
c99
|
||||
java8
|
||||
kandr
|
||||
kandr2
|
||||
v7unix
|
||||
@@ -0,0 +1,46 @@
|
||||
$ ANS X3.159-1989, SS 3.1.1, pg 20, SS 3.8, pg 86
|
||||
#define
|
||||
#elif
|
||||
#else
|
||||
#endif
|
||||
#error
|
||||
#if
|
||||
#ifdef
|
||||
#ifndef
|
||||
#include
|
||||
#line
|
||||
#pragma
|
||||
#undef
|
||||
auto
|
||||
break
|
||||
case
|
||||
char
|
||||
const
|
||||
continue
|
||||
default
|
||||
defined
|
||||
do
|
||||
double
|
||||
else
|
||||
enum
|
||||
extern
|
||||
float
|
||||
for
|
||||
goto
|
||||
if
|
||||
int
|
||||
long
|
||||
register
|
||||
return
|
||||
short
|
||||
signed
|
||||
sizeof
|
||||
static
|
||||
struct
|
||||
switch
|
||||
typedef
|
||||
union
|
||||
unsigned
|
||||
void
|
||||
volatile
|
||||
while
|
||||
@@ -0,0 +1,99 @@
|
||||
$ N3337 2012-01-16 $
|
||||
alignas
|
||||
alignof
|
||||
and
|
||||
and_eq
|
||||
asm
|
||||
auto
|
||||
bitand
|
||||
bitor
|
||||
bool
|
||||
break
|
||||
case
|
||||
catch
|
||||
char
|
||||
char16_t
|
||||
char32_t
|
||||
class
|
||||
compl
|
||||
const
|
||||
constexpr
|
||||
const_cast
|
||||
continue
|
||||
decltype
|
||||
default
|
||||
delete
|
||||
do
|
||||
double
|
||||
dynamic_cast
|
||||
else
|
||||
enum
|
||||
explicit
|
||||
export
|
||||
extern
|
||||
false
|
||||
float
|
||||
for
|
||||
friend
|
||||
goto
|
||||
if
|
||||
inline
|
||||
int
|
||||
long
|
||||
mutable
|
||||
namespace
|
||||
new
|
||||
noexcept
|
||||
not
|
||||
not_eq
|
||||
nullptr
|
||||
operator
|
||||
or
|
||||
or_eq
|
||||
private
|
||||
protected
|
||||
public
|
||||
register
|
||||
reinterpret_cast
|
||||
return
|
||||
short
|
||||
signed
|
||||
sizeof
|
||||
static
|
||||
static_assert
|
||||
static_cast
|
||||
struct
|
||||
switch
|
||||
template
|
||||
this
|
||||
thread_local
|
||||
throw
|
||||
true
|
||||
try
|
||||
typedef
|
||||
typeid
|
||||
typename
|
||||
union
|
||||
unsigned
|
||||
using
|
||||
virtual
|
||||
void
|
||||
volatile
|
||||
wchar_t
|
||||
while
|
||||
xor
|
||||
xor_eq
|
||||
#if
|
||||
#elif
|
||||
#else
|
||||
#endif
|
||||
defined
|
||||
#ifdef
|
||||
#ifndef
|
||||
#define
|
||||
#undef
|
||||
#include
|
||||
#line
|
||||
#error
|
||||
#pragma
|
||||
_Pragma
|
||||
@@ -0,0 +1,99 @@
|
||||
$ N4296 2014-11-09 Working Draft, Standard for Programming Language C++ $
|
||||
alignas
|
||||
alignof
|
||||
and
|
||||
and_eq
|
||||
asm
|
||||
auto
|
||||
bitand
|
||||
bitor
|
||||
bool
|
||||
break
|
||||
case
|
||||
catch
|
||||
char
|
||||
char16_t
|
||||
char32_t
|
||||
class
|
||||
compl
|
||||
const
|
||||
constexpr
|
||||
const_cast
|
||||
continue
|
||||
decltype
|
||||
default
|
||||
delete
|
||||
do
|
||||
double
|
||||
dynamic_cast
|
||||
else
|
||||
enum
|
||||
explicit
|
||||
export
|
||||
extern
|
||||
false
|
||||
float
|
||||
for
|
||||
friend
|
||||
goto
|
||||
if
|
||||
inline
|
||||
int
|
||||
long
|
||||
mutable
|
||||
namespace
|
||||
new
|
||||
noexcept
|
||||
not
|
||||
not_eq
|
||||
nullptr
|
||||
operator
|
||||
or
|
||||
or_eq
|
||||
private
|
||||
protected
|
||||
public
|
||||
register
|
||||
reinterpret_cast
|
||||
return
|
||||
short
|
||||
signed
|
||||
sizeof
|
||||
static
|
||||
static_assert
|
||||
static_cast
|
||||
struct
|
||||
switch
|
||||
template
|
||||
this
|
||||
thread_local
|
||||
throw
|
||||
true
|
||||
try
|
||||
typedef
|
||||
typeid
|
||||
typename
|
||||
union
|
||||
unsigned
|
||||
using
|
||||
virtual
|
||||
void
|
||||
volatile
|
||||
wchar_t
|
||||
while
|
||||
xor
|
||||
xor_eq
|
||||
#if
|
||||
#elif
|
||||
#else
|
||||
#endif
|
||||
defined
|
||||
#ifdef
|
||||
#ifndef
|
||||
#define
|
||||
#undef
|
||||
#include
|
||||
#line
|
||||
#error
|
||||
#pragma
|
||||
_Pragma
|
||||
@@ -0,0 +1,88 @@
|
||||
$ ISO/IEC 14882:1998(E) 2.11 $
|
||||
and
|
||||
and_eq
|
||||
asm
|
||||
auto
|
||||
bitand
|
||||
bitor
|
||||
bool
|
||||
break
|
||||
case
|
||||
catch
|
||||
char
|
||||
class
|
||||
compl
|
||||
const
|
||||
const_cast
|
||||
continue
|
||||
default
|
||||
delete
|
||||
do
|
||||
double
|
||||
dynamic_cast
|
||||
else
|
||||
enum
|
||||
explicit
|
||||
export
|
||||
extern
|
||||
false
|
||||
float
|
||||
for
|
||||
friend
|
||||
goto
|
||||
if
|
||||
inline
|
||||
int
|
||||
long
|
||||
mutable
|
||||
namespace
|
||||
new
|
||||
not
|
||||
not_eq
|
||||
operator
|
||||
or
|
||||
or_eq
|
||||
private
|
||||
protected
|
||||
public
|
||||
register
|
||||
reinterpret_cast
|
||||
return
|
||||
short
|
||||
signed
|
||||
sizeof
|
||||
static
|
||||
static_cast
|
||||
struct
|
||||
switch
|
||||
template
|
||||
this
|
||||
throw
|
||||
true
|
||||
try
|
||||
typedef
|
||||
typeid
|
||||
typename
|
||||
union
|
||||
unsigned
|
||||
using
|
||||
virtual
|
||||
void
|
||||
volatile
|
||||
wchar_t
|
||||
while
|
||||
xor
|
||||
xor_eq
|
||||
#if
|
||||
#elif
|
||||
#else
|
||||
#endif
|
||||
defined
|
||||
#ifdef
|
||||
#ifndef
|
||||
#define
|
||||
#undef
|
||||
#include
|
||||
#line
|
||||
#error
|
||||
#pragma
|
||||
@@ -0,0 +1,58 @@
|
||||
$ C11: ISO/IEC 9899:201x, April 12, 2012 Committee Draft, N1570 $
|
||||
#define
|
||||
#elif
|
||||
#else
|
||||
#endif
|
||||
#error
|
||||
#if
|
||||
#ifdef
|
||||
#ifndef
|
||||
#include
|
||||
#line
|
||||
#pragma
|
||||
#undef
|
||||
_Alignas
|
||||
_Alignof
|
||||
_Atomic
|
||||
_Bool
|
||||
_Complex
|
||||
_Generic
|
||||
_Imaginary
|
||||
_Noreturn
|
||||
_Static_assert
|
||||
_Thread_local
|
||||
auto
|
||||
break
|
||||
case
|
||||
char
|
||||
const
|
||||
continue
|
||||
default
|
||||
defined
|
||||
do
|
||||
double
|
||||
else
|
||||
enum
|
||||
extern
|
||||
float
|
||||
for
|
||||
goto
|
||||
if
|
||||
inline
|
||||
int
|
||||
long
|
||||
register
|
||||
restrict
|
||||
return
|
||||
short
|
||||
signed
|
||||
sizeof
|
||||
static
|
||||
struct
|
||||
switch
|
||||
typedef
|
||||
union
|
||||
unsigned
|
||||
void
|
||||
volatile
|
||||
while
|
||||
@@ -0,0 +1,50 @@
|
||||
$ C99: ISO/IEC 9899:1999(E), pg 50 $
|
||||
#define
|
||||
#elif
|
||||
#else
|
||||
#endif
|
||||
#error
|
||||
#if
|
||||
#ifdef
|
||||
#ifndef
|
||||
#include
|
||||
#line
|
||||
#pragma
|
||||
#undef
|
||||
_Bool
|
||||
_Complex
|
||||
_Imaginary
|
||||
auto
|
||||
break
|
||||
case
|
||||
char
|
||||
const
|
||||
continue
|
||||
default
|
||||
do
|
||||
double
|
||||
else
|
||||
enum
|
||||
extern
|
||||
float
|
||||
for
|
||||
goto
|
||||
if
|
||||
inline
|
||||
int
|
||||
long
|
||||
register
|
||||
restrict
|
||||
return
|
||||
short
|
||||
signed
|
||||
sizeof
|
||||
static
|
||||
struct
|
||||
switch
|
||||
typedef
|
||||
union
|
||||
unsigned
|
||||
void
|
||||
volatile
|
||||
while
|
||||
@@ -0,0 +1,62 @@
|
||||
The values below are the field-by-field difference iocccsize - ioc.
|
||||
(>0 means iocccsize counted more than ioc, <0 ioc counted more)
|
||||
cl: lines, cw: words, cc: chars, ci: rule2, ts: saved, kw: keywords, ks: kw_saved
|
||||
|
||||
cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1986/pawka.c
|
||||
cl -45 cw -55 cc -617 ci -463 ts -85 kw -26 ks -69 src/obc/1990/theorem.c
|
||||
ioc is correct; cl is correct even in compat (91) due to comments eating newlines
|
||||
iocccsize fails at "z;/*/", eating everything until the following "/**/main("
|
||||
looks to be due to mis-identification of a comment closing block as opening block
|
||||
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1991/westley.c
|
||||
cl 0 cw -3 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1992/adrian.c
|
||||
cl 0 cw -3 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1992/adrian.orig.c
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1994/shapiro.c
|
||||
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1996/gandalf.c
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/1996/schweikh1.c
|
||||
cl 0 cw -2 cc 0 ci -2 ts 0 kw 1 ks 2 src/obc/1998/df.c
|
||||
iocccsize counts as keyword __##int##__, and saving two characters
|
||||
but int is not a plausible keyword
|
||||
cl 0 cw -1 cc 3 ci 2 ts 1 kw 0 ks 0 src/obc/2001/herrmann1.c
|
||||
the text:
|
||||
d M Y(O)/* state to */
|
||||
d
|
||||
counts as cw 3 in iocccsize, but this is clearly 4
|
||||
and the cc,ci,ts is due entirely to:
|
||||
/*/*/*/*/*/ /*/*/
|
||||
E E /*/*/*/*/*/,/) F
|
||||
which reduces to:
|
||||
* E E * ,/) F
|
||||
but iocccsize counts this as * E E *,/) F, whereas ANSI C says comments are
|
||||
replaced with a single space equivalent (non-portable token-paste with comments)
|
||||
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2004/arachnid.c
|
||||
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2004/arachnid.orig.c
|
||||
cl 0 cw 0 cc 0 ci -4 ts 0 kw 2 ks 4 src/obc/2004/hoyle.c
|
||||
cl 0 cw 0 cc 0 ci -4 ts 0 kw 2 ks 4 src/obc/2004/hoyle.orig.c
|
||||
ioccccsize counts as two keywords the token-pasted int##if (e.g int1)
|
||||
thus the kw 2, and another bug in iocccsize counts #if as a keyword to 1
|
||||
thus saving 2 bytes on int, and 2 more on #if, when neither is a plausible keyword
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2004/omoikane.c
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2005/chia/chia.c
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2005/mikeash/mikeash.c
|
||||
cl 0 cw 0 cc 0 ci 1 ts 1 kw 0 ks 0 src/obc/2005/toledo/toledo.c
|
||||
cl 0 cw 0 cc 0 ci 1 ts 1 kw 0 ks 0 src/obc/2005/toledo/toledo2.c
|
||||
cl 0 cw 0 cc 0 ci 1 ts 1 kw 0 ks 0 src/obc/2005/toledo/toledo3.c
|
||||
these three discrepancies are due to backslash-newline of longjm\p --
|
||||
at exactly the 521 byte line length boundary (which is arbitrary)
|
||||
ioc is correct, because it has no line length limit
|
||||
cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2011/akari/akari.c
|
||||
cl 0 cw -7 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2011/hamaji/hamaji.c
|
||||
cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/endoh1/endoh1.c
|
||||
cl 0 cw -3 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/endoh1/endoh1_color.c
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/endoh1/endoh1_deobfuscate.c
|
||||
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2012/omoikane/nyaruko.c
|
||||
cl 0 cw 0 cc 0 ci -3 ts 0 kw 1 ks 3 src/obc/2013/cable2/cable2.c
|
||||
this is due to token-pasting of _##char; iocccsize counts this as a keyword
|
||||
but it is a pre-processor token-pasted string of the arg to Y() and char
|
||||
rendering this not a keyword, even in disguise
|
||||
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2013/endoh4/endoh4.c
|
||||
cl 0 cw -2 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2013/misaka/misaka.c
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/endoh2/prog.c
|
||||
cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/maffiodo1/prog.c
|
||||
cl 0 cw -4 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/maffiodo1/prog.orig.c
|
||||
cl 0 cw -1 cc 0 ci 0 ts 0 kw 0 ks 0 src/obc/2014/sinon/prog.c
|
||||
@@ -0,0 +1,637 @@
|
||||
/*
|
||||
* iocccsize - IOCCC Source Size Tool
|
||||
*
|
||||
* This IOCCC size tool source file is version 2017-12-25-v25.
|
||||
*
|
||||
* "You are not expected to understand this" :-)
|
||||
*
|
||||
* Public Domain 1992, 2015 by Anthony Howe. All rights released.
|
||||
* With IOCCC minor mods in 2013-2017 by chongo (Landon Curt Noll) ^oo^
|
||||
*
|
||||
* SYNOPSIS
|
||||
*
|
||||
* ioccc [-kirs] < input
|
||||
*
|
||||
* -k keep comment blocks
|
||||
* -i IOCCC size rule (overall size, modified)
|
||||
* -r count C reserved words as 1 byte
|
||||
* -s suppress source output, write only the IOCCC 2nd size count
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* Taking input from standard input, apply the IOCCC Source Size Rule
|
||||
* for 2001. The program's official 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, form-feed, return), and
|
||||
* excluding any ';', '{' or '}' followed by whitespace or end of file,
|
||||
* must be <= 2048.
|
||||
*
|
||||
* 2013 Source Size Rule is (-r):
|
||||
*
|
||||
* Your entry must be <= 4096 bytes in length. The number of octets
|
||||
* excluding whitespace (tab, space, newline, form-feed, return), and
|
||||
* excluding any ';', '{' or '}' followed by whitespace or end of file,
|
||||
* and where C reserved words, including a subset of preprocessor words,
|
||||
* count as 1 byte, must be <= 2053 (first prime after 2048).
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 form-feed, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* IOCCC Judge's remarks:
|
||||
*
|
||||
* This code contains undocumented features. On the other hand, this code
|
||||
* is RTFS (for certain values of RTFS). One might say that this code
|
||||
* perfectly documents itself. :-)
|
||||
*
|
||||
* Many thanks to Anthony Howe for taking the time to put his OCD
|
||||
* (Obfuscated Coding Determination) into this code!
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 8
|
||||
#define FLAG_SILLY_C 16
|
||||
#define FLAG_SILLY_H 32
|
||||
|
||||
#define BUFFER_SIZE 521
|
||||
#define MAX_SIZE 4096 /* IOCCC Rule 2 */
|
||||
#define MAX_COUNT 2053 /* IOCCC Rule 2 */
|
||||
|
||||
static char usage[] =
|
||||
"usage: ioccc [-kirsch] < prog.c\n"
|
||||
"\n"
|
||||
"-k\t\tkeep block comments\n"
|
||||
"-i\t\tprint official secondary IOCCC size to stdout\n"
|
||||
"\t\tmax.size warnings to stderr, NOTE: implies -r -s\n"
|
||||
"-r\t\tcount C reserved words as 1 byte\n"
|
||||
"-s\t\tsuppress source output, write only the official size\n"
|
||||
"-c\t\tan undocumented feature\n"
|
||||
"-h\t\tyet another undocumented feature\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/
|
||||
*/
|
||||
/*
|
||||
* The following editorial plea expresses a view shared by more than zero
|
||||
* IOCCC judges. It may not represent the opinion of all those involved
|
||||
* with this code nor the International Obfuscated C Code Contest as a whole:
|
||||
*
|
||||
* The long list of reserved words below should be a source
|
||||
* of embarrassment to some of those involved in standardizing C.
|
||||
* The growing list of reserved words, along with an expanding set of
|
||||
* linguistic inventions has the appearance of feature
|
||||
* creep that, if left unchecked, risks turning a beautifully elegant
|
||||
* language into a steaming pile of biological excretion.
|
||||
*
|
||||
* The history of the IOCCC has taught us that even minor changes
|
||||
* to the language are not always well understood by compiler writers,
|
||||
* let alone the standards body who publishes them. We have enormous
|
||||
* sympathy for C compiler writers who must keep up with the creeping
|
||||
* featurism. We are aware of some C standards members who share
|
||||
* these concerns. Alas, they seem to be a minority.
|
||||
*
|
||||
* The C standards body as a whole, before they emit yet more mountains of new
|
||||
* standardese, might wish consider the option of moth-balling their committee.
|
||||
* Or if they must produce a new standard, consider naming whatever
|
||||
* follows c11 as CNC (C's Not C). :-)
|
||||
*/
|
||||
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("#ifndef"), "#ifndef" }, /* XXX bugfix */
|
||||
{ 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" },
|
||||
/*
|
||||
* this comment is not expected to understand itself
|
||||
*/
|
||||
{ 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" }, */ /* XXX seriously? */
|
||||
{ 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 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Keep track of discarded bytes by read_line() so count() can report
|
||||
* correct wc(1) equivalent counts.
|
||||
*/
|
||||
static int xlcount = 0;
|
||||
static int xwcount = 0;
|
||||
static int xbcount = 0;
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* ISO C11 section 5.2.1.1 Trigraph Sequences, leading sequence "??"
|
||||
*/
|
||||
static char trigraph[] = "=(/)'<!>-";
|
||||
static char asciimap[] = "#[\\]^{|}~";
|
||||
|
||||
static size_t
|
||||
read_line(char *buf, size_t size)
|
||||
{
|
||||
int ch;
|
||||
size_t length;
|
||||
|
||||
if (buf == NULL || size == 0)
|
||||
return 0;
|
||||
|
||||
for (size--, length = 0; length < size; ) {
|
||||
if ((ch = fgetc(stdin)) == EOF)
|
||||
break;
|
||||
/* Map NUL bytes to space, though ideally I should chuck'em. */
|
||||
if (ch == '\0')
|
||||
ch = ' ';
|
||||
/* Discard bare CR and those part of CRLF. */
|
||||
if (ch == '\r') {
|
||||
xbcount++;
|
||||
continue;
|
||||
}
|
||||
/* Trigraph mapping? */
|
||||
if (2 <= length && buf[length-2] == '?' && buf[length-1] == '?') {
|
||||
char *tri;
|
||||
if ((tri = strchr(trigraph, ch)) != NULL) {
|
||||
/* Mapped trigraphs count as 1 byte. */
|
||||
ch = asciimap[tri - trigraph];
|
||||
xbcount += 2;
|
||||
length -= 2;
|
||||
}
|
||||
}
|
||||
if (ch == '\n' && 1 <= length && buf[length-1] == '\\') {
|
||||
/* ISO C11 section 5.1.1.2 Translation Phases
|
||||
* point 2 discards backslash newlines.
|
||||
*/
|
||||
xbcount += 2;
|
||||
xwcount++;
|
||||
xlcount++;
|
||||
length--;
|
||||
continue;
|
||||
}
|
||||
buf[length++] = (char) ch;
|
||||
if (ch == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buf[length] = '\0';
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* No longer reported:
|
||||
* isaved bytes saved by secondary size rule (bcount - icount)
|
||||
* rcount number of C reserved words
|
||||
* rsaved number of octets saved with -r
|
||||
*/
|
||||
static int
|
||||
count(int flags)
|
||||
{
|
||||
Word *w;
|
||||
unsigned long span;
|
||||
char *p, buf[BUFFER_SIZE];
|
||||
int lcount, wcount, bcount;
|
||||
int is_comment, is_word, dquote, escape;
|
||||
int count, keywords, saved, kw_saved;
|
||||
int squote;
|
||||
|
||||
/* Start of buffer sentinel. */
|
||||
buf[0] = ' ';
|
||||
buf[BUFFER_SIZE - 1] = 0; /* paranoia */
|
||||
|
||||
count = saved = 0;
|
||||
keywords = kw_saved = 0;
|
||||
lcount = wcount = bcount = 0;
|
||||
is_comment = is_word = dquote = escape = 0;
|
||||
squote = 0;
|
||||
|
||||
/*
|
||||
* "no matter how well you may think you understand this code,
|
||||
* you don't, so don't mess with it." :-)
|
||||
*/
|
||||
while (0 < read_line(buf+1, sizeof (buf)-1)) {
|
||||
if (!(flags & FLAG_KEEP)) {
|
||||
/* Leading whitespace before comment block? */
|
||||
span = strspn(buf+1, "\t ");
|
||||
|
||||
/* Split / * across reads? */
|
||||
if (buf[1 + span] == '/' && buf[2 + span] == '\0') {
|
||||
(void) ungetc('/', stdin);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* no comment is a comment
|
||||
*/
|
||||
if (buf[1 + span] == '/' && buf[2 + span] == '/') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[1 + span] == '/' && buf[2 + span] == '*') {
|
||||
/* Strip leading whitespace before comment block. */
|
||||
is_comment = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = buf+1; *p != '\0'; p++) {
|
||||
/* Within quoted string? */
|
||||
if (dquote) {
|
||||
/* Escape _this_ character. */
|
||||
if (escape) {
|
||||
escape = 0;
|
||||
}
|
||||
|
||||
/* Escape next character. */
|
||||
else if (*p == '\\') {
|
||||
escape = 1;
|
||||
}
|
||||
|
||||
/* Close quoted string? */
|
||||
else if (*p == '"') {
|
||||
dquote = 0;
|
||||
}
|
||||
}
|
||||
|
||||
else if (squote) {
|
||||
if (escape)
|
||||
escape = 0;
|
||||
else if (*p == '\\')
|
||||
escape = 1;
|
||||
else if (*p == '\'')
|
||||
squote = 0;
|
||||
}
|
||||
|
||||
/* Not quote string. */
|
||||
else {
|
||||
/* In C comment block? */
|
||||
if (is_comment) {
|
||||
/* Split * / across reads? */
|
||||
if (*p == '*' && p[1] == '\0') {
|
||||
ungetc('*', stdin);
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Split / / or / * across reads? */
|
||||
else if (*p == '/' && p[1] == '\0') {
|
||||
ungetc('/', stdin);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Start of comment line? */
|
||||
else if (*p == '/' && p[1] == '/') {
|
||||
if (!(flags & FLAG_KEEP)) {
|
||||
/* Strip comment to end of buffer. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start of comment block? */
|
||||
else if (*p == '/' && p[1] == '*') {
|
||||
/* Begin comment block. */
|
||||
is_comment = 1;
|
||||
|
||||
if (!(flags & FLAG_KEEP)) {
|
||||
/* Strip comment block. */
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* C reserved word? */
|
||||
else if (!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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open single quote string? */
|
||||
else if (*p == '\'') {
|
||||
squote = 1;
|
||||
}
|
||||
|
||||
/* Open quoted string? */
|
||||
else if (*p == '"') {
|
||||
dquote = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & FLAG_SILENCE)) {
|
||||
fputc(*p, stdout);
|
||||
}
|
||||
|
||||
bcount++;
|
||||
if (*p == '\n') {
|
||||
lcount++;
|
||||
}
|
||||
|
||||
/* Ignore all whitespace.
|
||||
*
|
||||
* Well not ALL in the Universe, just all here.
|
||||
*/
|
||||
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 whitespace or EOF.
|
||||
*/
|
||||
if (strchr("{;}", *p) != NULL && (isspace(p[1]) || p[1] == '\0')) {
|
||||
saved++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Count this octet. */
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & FLAG_IOCCC) {
|
||||
/* Output the official IOCCC size tool size to standard out */
|
||||
printf("%d\n", count);
|
||||
if (MAX_SIZE < bcount) {
|
||||
fprintf(stderr, "WARNING: program size (%d) exceeds IOCCC Rule 2a max. size (%d)\n", bcount, MAX_SIZE);
|
||||
}
|
||||
if (MAX_COUNT < count) {
|
||||
fprintf(stderr, "WARNING: iocccsize count (%d) exceeds IOCCC Rule 2b max. count (%d)\n", count, MAX_COUNT);
|
||||
}
|
||||
} else {
|
||||
/* The Ugly Truth */
|
||||
fprintf(
|
||||
stderr, "%d %d %d %d %d %d %d\n",
|
||||
lcount + xlcount, wcount + xwcount, bcount + xbcount, count,
|
||||
saved, keywords, kw_saved
|
||||
);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch;
|
||||
int flags = 0;
|
||||
|
||||
while ((ch = getopt(argc, argv, "kirsch")) != -1) {
|
||||
switch (ch) {
|
||||
case 'k':
|
||||
flags |= FLAG_KEEP;
|
||||
break;
|
||||
case 'i':
|
||||
flags |= FLAG_IOCCC | FLAG_RESERVED | FLAG_SILENCE;
|
||||
break;
|
||||
case 'r':
|
||||
flags |= FLAG_RESERVED;
|
||||
break;
|
||||
case 's':
|
||||
flags |= FLAG_SILENCE;
|
||||
break;
|
||||
case 'c':
|
||||
flags |= FLAG_SILLY_C;
|
||||
break;
|
||||
case 'h':
|
||||
flags |= FLAG_SILLY_H;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s\n", usage);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
// make test hack: while for do until goto if else switch case default continue return break
|
||||
if (flags == (FLAG_KEEP | FLAG_IOCCC | FLAG_RESERVED | FLAG_SILENCE | FLAG_SILLY_C | FLAG_SILLY_H)) {
|
||||
fprintf(stderr, "The source toasts you a glass to your health!\n"
|
||||
"BTW: judges have toasted a glass of this at the South Pole.\n"
|
||||
"Now try using useful flags.\n\n"
|
||||
"%s\n", usage);
|
||||
return 3;
|
||||
} else if (flags & FLAG_SILLY_C) {
|
||||
fprintf(stderr, "Do you not see this is C?\n\n"
|
||||
"%s\n", usage);
|
||||
return 4;
|
||||
} else if (flags & FLAG_SILLY_H) {
|
||||
fprintf(stderr, "%s\n", usage);
|
||||
return 5;
|
||||
}
|
||||
|
||||
/*
|
||||
* count as directed - 1 Muha .. 2 Muhaha .. 3 Muhahaha ...
|
||||
*/
|
||||
(void) count(flags);
|
||||
|
||||
/*
|
||||
* All Done!!! All Done!!! -- Jessica Noll, age 2
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
function iskeyword {
|
||||
awk 'BEGIN{f="c11";while(getline<f)k[$1]=0}
|
||||
{if($1 in k)k[$1]++}END{for(i in k)print k[i],i}'
|
||||
}
|
||||
cat $* | ./prog -t | iskeyword | sort -k1nr
|
||||
@@ -0,0 +1,436 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta name="GENERATOR" content="mkd2html 2.1.5a DL=DISCOUNT">
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
|
||||
<link rel="stylesheet"
|
||||
type="text/css"
|
||||
href="../hint.css">
|
||||
<title>Best abuse of the rules</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Best abuse of the rules</h1>
|
||||
|
||||
<p>Dave Burton<br>
|
||||
<a href="http://ioccc.snox.net">http://ioccc.snox.net</a></p>
|
||||
|
||||
<h2>Judges' comments:</h2>
|
||||
|
||||
<h3>To use:</h3>
|
||||
|
||||
<pre><code>make
|
||||
|
||||
./prog [-tcksri] < file.c
|
||||
</code></pre>
|
||||
|
||||
<h3>Try:</h3>
|
||||
|
||||
<pre><code>./prog -i < prog.c
|
||||
./prog -s < prog.c
|
||||
./prog -sk < prog.c
|
||||
</code></pre>
|
||||
|
||||
<p>If you get stuck, try:</p>
|
||||
|
||||
<pre><code>make test
|
||||
</code></pre>
|
||||
|
||||
<p>If you get really stuck, try:</p>
|
||||
|
||||
<pre><code>man ./tac.man
|
||||
</code></pre>
|
||||
|
||||
<h3>Selected Judges Remarks:</h3>
|
||||
|
||||
<p>They say size isn’t everything, and in the case of IOCCC <a href="http://www.ioccc.org/2018/iocccsize.c">iocccsize.c</a>
|
||||
that is saying something! What is this program weighing and how much does it weigh?</p>
|
||||
|
||||
<h2>Author’s comments:</h2>
|
||||
|
||||
<h2>tac - tokenize and count C and derivative languages</h2>
|
||||
|
||||
<blockquote><pre style="font-style:italic">
|
||||
tac computes C program size
|
||||
(obfuscated / otherwise)
|
||||
by splitting code as tokens small,
|
||||
ignoring space, then counting all.
|
||||
|
||||
It tries quite hard to act the same
|
||||
as counting tool of I-OCCC fame.
|
||||
Some bugs were fixed, it's faster too,
|
||||
and does more things, just add some glue!
|
||||
|
||||
It does far more than print the size:
|
||||
the token part's a better prize!
|
||||
With each upon on a single line,
|
||||
how many things can you design?
|
||||
|
||||
Included is a keyword sorter,
|
||||
de-obfuscator, freq reporter.
|
||||
With code produced as little parts,
|
||||
just add your own creative arts.
|
||||
|
||||
But first you have to figure out
|
||||
the braces part, without a doubt!
|
||||
And then the code - it isn't easy:
|
||||
for as else? That's rather cheesy!
|
||||
|
||||
The rest of it is standard fare:
|
||||
expressions strange, the globals bare,
|
||||
confusing symbols one and oh,
|
||||
and precedence you have to know.
|
||||
|
||||
The keyword list was made external,
|
||||
because of standards change eternal.
|
||||
Thus other languages can now,
|
||||
be counted just like C, somehow!
|
||||
|
||||
I hope you find this code obscure
|
||||
enough to win, and thus procure
|
||||
a public place for all to see
|
||||
how badly I can butcher C!
|
||||
</pre></blockquote>
|
||||
|
||||
|
||||
<h2>Said another way….</h2>
|
||||
|
||||
<p><code>tac</code> is the inverse of <code>cat</code>(1): it un-concatenates its input into C tokens,
|
||||
writing the token stream to stdout. There are options to suppress the token ids,
|
||||
summarize the token counts, and several options to allow it to be compatible
|
||||
with <code>iocccsize</code>.</p>
|
||||
|
||||
<p>The token-based output is quite useful in analyzing source code (see below),
|
||||
and the counter is more accurate than <code>iocccsize</code> (see below); but this can be
|
||||
fixed by using the -c compatibility flag, restoring the following problems as
|
||||
in the official tool:</p>
|
||||
|
||||
<ul>
|
||||
<li>whitespace <strong>within</strong> a string is <strong>not</strong> counted;
|
||||
this seems wrong, as these chars <strong>are</strong> significant</li>
|
||||
<li>“<code>{;}</code>” is not counted within a double-quoted string</li>
|
||||
<li>whitespace preceding a comment is char-counted, unless the comment starts a line</li>
|
||||
<li>-k processes the reserved words within <code>//</code> comments(!), but not <code>/*</code>, changing the counts</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>The following bugs are always corrected:</p>
|
||||
|
||||
<ul>
|
||||
<li>treats <code>/*/</code> as a complete comment (<code>/*/int if for/*/</code> has three keywords)</li>
|
||||
<li>-k enables <code>//*</code> or within a line comment <code>/*</code> to initiate a comment block until following <code>*/</code></li>
|
||||
<li>-k processes double quote characters in <code>//</code> comments -
|
||||
thus a solitary quote consumes everything until the next quote</li>
|
||||
<li>single quotes are not handled correctly (<code>'"'</code> starts a dquote in <code>iocccsize</code>)</li>
|
||||
<li><code>#ifndef</code> is omitted as a keyword (but <code>#ifdef</code> is allowed?)</li>
|
||||
<li>I is not a reserved word.</li>
|
||||
<li><code>tac</code> correctly penetrates <a href="http://ioccc.org/2014/birken/hint.html" title="Best use of port 1701">23rd century cloaking technology</a>. ;-)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>There is one (known) remaining problem with detection:</p>
|
||||
|
||||
<ul>
|
||||
<li><p>comments between preprocessor hash and keyword are not replaced with whitespace;
|
||||
the following counts #include as two tokens, <code>#</code> and <code>include</code> (not as a reserved word):</p>
|
||||
|
||||
<pre><code> #/*this will not be counted*/include/*correctly*/<stdio.h>
|
||||
</code></pre></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>Fixing this properly hampers backwards compatibility in counting. This requires
|
||||
eliding comments during readsource (ISO 5.1.1.2, phase 3), and yet still
|
||||
counting words correctly absent -k.</p>
|
||||
|
||||
<h2>Backwards Compatibility</h2>
|
||||
|
||||
<p><code>tac</code> was run over all 366 previous IOCCC winning entries:</p>
|
||||
|
||||
<pre><code>find ~/src/obc -type f -name \*.c | wc
|
||||
</code></pre>
|
||||
|
||||
<p>The discrepancies found are documented and explained in the file “discrepancies”.</p>
|
||||
|
||||
<pre><code>find ~/src/obc -type f -a -name "*.c" | xargs ./spotcheck ./prog | ./spotdiff |
|
||||
grep -v keep | diff -bw - discrep* | grep "[<>] cl "
|
||||
</code></pre>
|
||||
|
||||
<p>In summary, there are only 6 unique entries out of 366 that have any variation
|
||||
in Rule 2 counts; in all cases, the differences are due to bugs within
|
||||
<code>iocccsize</code>, described in detail in the file <code>discrepancies</code>. There are only 40
|
||||
files (40/366, ~11%, including originals and variations) that differ at all, 31
|
||||
only in word count; most word count differences appear related to comment block
|
||||
detection, weighted more towards recent entries (2011 and onward = 20/40 ~50%).</p>
|
||||
|
||||
<p>This (minor) variation in word count values has not been addressed since
|
||||
it is not a qualifying metric, nor is it very useful in a programming context:
|
||||
whitespace surrounds English words, not necessarily C tokens, as in <code>a+=1;</code>.</p>
|
||||
|
||||
<p>Accuracy (versus compatibility) is demonstrated through a series of small test cases
|
||||
designed to allow exhaustive, hand-counted values for comparison with the tool values.
|
||||
These test cases were vital in debugging and regression testing, and provided a way to
|
||||
determine which tool was correct when there were differences.</p>
|
||||
|
||||
<blockquote><p>A version of this tool in more clearly written C (tac.c)
|
||||
is presented for the Judge’s consideration as a more accurate replacement for <code>iocccsize</code>.
|
||||
This obfuscated entry is derived from (and compatible with) that code,
|
||||
but due to obfuscation, has had some significant, deep changes for the contest.
|
||||
Nevertheless, the more clearly written code remains a spoiler for this entry.</p></blockquote>
|
||||
|
||||
<p>NB: <code>iocccsize</code> gets a different answer from <code>tac</code> on its own (iocccsize.c) source code;
|
||||
<code>tac</code> gets the correct answer. This is due to the aforementioned bugs within <code>iocccsize</code>,
|
||||
proved by fixing iocccsize.c with the included patch, so <code>iocccsize</code> reports
|
||||
the correct answer for itself.</p>
|
||||
|
||||
<h2>But wait… There’s More!</h2>
|
||||
|
||||
<p>There is no limit on line length, file length, comment length, or identifier length.</p>
|
||||
|
||||
<p><code>tac</code> features an accurate tokenizer; which presents each C token on a line by itself.
|
||||
This feature enables interesting analyses of C (and C++) code, hard to get any other way.</p>
|
||||
|
||||
<p>For instance, here is a simple token counter, useful in finding repeated long tokens, or operator
|
||||
frequency, or counting the references to identifiers, constants, or breadth of use of the
|
||||
language:</p>
|
||||
|
||||
<pre><code>!/bin/sh
|
||||
cat $* | ./prog -t | sort | uniq -c | sort -k1nr
|
||||
</code></pre>
|
||||
|
||||
<p>And here is a C keyword frequency counter:</p>
|
||||
|
||||
<pre><code>#!/bin/sh
|
||||
function iskeyword {
|
||||
awk 'BEGIN{f="c11";while(getline<f)k[$1]=0}
|
||||
{if($1 in k)k[$1]++}END{for(i in k)print k[i],i}'
|
||||
}
|
||||
cat $* | ./prog -t | iskeyword | sort -k1nr
|
||||
</code></pre>
|
||||
|
||||
<p>This script was used to “optimize” the reserved word order so the most frequent IOCCC
|
||||
winning entry keywords are checked first.</p>
|
||||
|
||||
<blockquote><p>An interesting aside: as might be expected, obfuscation has changed the frequency of
|
||||
keyword distribution over time. The top five for the decades:</p>
|
||||
|
||||
<pre><code> 1980s 1990s 2000s 2010s
|
||||
----- ----- ----- -----
|
||||
94 if 532 char 375 if 254 int
|
||||
69 for 524 if 372 char 217 for
|
||||
59 while 417 int 332 int 198 if
|
||||
48 char 223 for 184 for 121 return
|
||||
43 int 223 void 165 return 104 char
|
||||
</code></pre></blockquote>
|
||||
|
||||
<p>The keyword list is external to the program, and is easily changed, sorted, checked, verified.
|
||||
This allows such additional programs in concert with the tool’s primary operation.</p>
|
||||
|
||||
<p>A poor-man’s de-obfuscator can be based upon the output of <code>tac -t</code> and a handful of
|
||||
simple rules (and another use of the external keyword file).
|
||||
A more refined version of this is included in the file <code>unob.sh</code>,
|
||||
but the simple code below is a serviceable obfuscated C de-obfuscator in a <em>scripting language</em>.
|
||||
It really is this easy with <code>tac</code>:</p>
|
||||
|
||||
<pre><code>#!/bin/sh
|
||||
script='
|
||||
BEGIN {
|
||||
last=nl="\n";
|
||||
f="c11"; while(getline <f > 0) kw[$0]++; close(f);
|
||||
}
|
||||
|
||||
function iskw(a) { return a in kw }
|
||||
function indent(a) { return sprintf("%*s", n*3, " ") }
|
||||
function newline() { if (!infor && last != nl) printf last=nl; }
|
||||
function show(a) {
|
||||
if (last==nl) printf "%s", indent()
|
||||
printf "%s%s", space(), a
|
||||
last=a
|
||||
}
|
||||
function space() {
|
||||
return iskw(last) ||
|
||||
(last ~ /[A-Za-z0-9_+-\/%^[&\]\)=:<>;]$/ && $0 !~ /[:;()\[\],]/) ? " " : ""
|
||||
}
|
||||
|
||||
/^\(/ { ++paren }
|
||||
/^\)/ { --paren }
|
||||
|
||||
/^for/ { newline(); infor=1 }
|
||||
infor && /^;/ { ++infor; show($0 " "); next }
|
||||
infor==1 && /^:/ { ++infor }
|
||||
infor>1 && paren==0 { infor=0 }
|
||||
|
||||
/^\?/ { tern++; n++; newline(); show($0 " "); next }
|
||||
tern && /^:/ { newline(); show($0); --tern; --n; next }
|
||||
|
||||
/^#/ { newline(); show($0); newline(); next }
|
||||
/^;/ { show($0); newline(); next }
|
||||
/^{/ { show($0); ++n; newline(); next }
|
||||
/^}/ { n--; newline(); show($0); newline(); next }
|
||||
|
||||
{ show($0) }
|
||||
'
|
||||
cat $* | sed 's/#include/##include/' | cpp -E -trigraphs |
|
||||
sed 's/^# .*$//' | sed 's/^#//' |
|
||||
./prog -t | awk "$script"
|
||||
</code></pre>
|
||||
|
||||
<h2>On the question of obfuscation</h2>
|
||||
|
||||
<ul>
|
||||
<li><p>I assume you noticed the <a href="https://www.youtube.com/watch?v=fUspLVStPbk&start=53" title="Meaning of Life">braces</a>?</p>
|
||||
|
||||
<p><em>Every brace is sacred,<br>
|
||||
Every brace is great.<br>
|
||||
If a brace is wasted,<br>
|
||||
Clang gets quite irate</em>.</p></li>
|
||||
<li><p>Keywords follow the 2010s top five, without the <code>if</code>, for interesting flow control.</p></li>
|
||||
<li>Sometimes a <code>for</code> can be an <code>else</code> - <a href="https://www.youtube.com/watch?v=OHVjs4aobqs" title="Inconceivable">inconceivable</a>!</li>
|
||||
<li>I see the <code>getopt</code>. But “<a href="https://www.youtube.com/watch?v=Ug75diEyiA0" title="Where's the beef?">where’s the beef?</a>”</li>
|
||||
<li><p>No character constants, several int constants, and one string
|
||||
(hint: which does not encode letters) – how does this code parse code?</p>
|
||||
|
||||
<blockquote><p>Interesting aside: how many is “several int constants”?</p>
|
||||
|
||||
<pre><code> ./prog < prog.c 2>/dev/null | awk '$1~/260/{a[$2]++}END{for(i in a)print i,a[i]}'
|
||||
... | wc
|
||||
</code></pre>
|
||||
|
||||
<p>Which numbers are most often used?</p>
|
||||
|
||||
<pre><code> ... | sort -k2nr | sed 5q
|
||||
</code></pre></blockquote></li>
|
||||
<li><p>The code describes its function by careful arrangement of variables up front…</p></li>
|
||||
<li>…coupled with a description of the typical IOCCC contestant, or at least the author</li>
|
||||
<li>Why shouldn’t trigraph parsing be written in trigraph?</li>
|
||||
<li>Where iocccsize.c mocks, this code flaunts:
|
||||
“<em>no matter how well you may think you understand this code, you don’t, so don’t mess with it. :-)</em>”</li>
|
||||
<li><code>O,0,l,1</code> are used to confusing effect, local names obscure global names.</li>
|
||||
<li>Globals are used to pass information between routines: don’t reorder “unrelated” statements….</li>
|
||||
<li>How does the compatibility mode account for the fundamentally different manner of computation?</li>
|
||||
<li>The algorithm is quite efficient; reserved word detection and trigraph parsing particularly so.</li>
|
||||
<li>This may be the first obfuscated submission that provides the means for its own de-obfuscation.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>Rule 2</h2>
|
||||
|
||||
<p>The keyword list is externalized as an include file. This presents a useful
|
||||
feature for the source code: not only can the reserved word script above be
|
||||
produced, but the tool can be rebuilt with any set of reserved words, just by
|
||||
using a different list. For instance: removing all the secondary keywords;
|
||||
removing all the C11 keywords; trying just K&R C. Since C++ and Java share the
|
||||
same operators as C, just change the keyword list and <code>tac</code> will correctly
|
||||
tokenize both of these; the tokens >>> and :: can be handled with a short
|
||||
post-filter (tokenfix, included).</p>
|
||||
|
||||
<p>I believe this entry may also satisfy the request for gratuitous use of all the C11 keywords?
|
||||
Whether this context satisfies “intended C language context” is a matter of interpretation. ;-)
|
||||
And <code>tac</code> is ready for the IOCCC++ contest. And the IOJCC trashcan.</p>
|
||||
|
||||
<p>The following reserved word files are included:</p>
|
||||
|
||||
<pre><code>kandr from my venerable 1978 18th printing "The C Programming Language"
|
||||
v7unix 7th edition Unix source code, extracted from c00.c
|
||||
kandr2 from my 1988 1st printing "The C Programming Language", 2e
|
||||
ansi ANS X3.159-1989
|
||||
c99 ISO/IEC 9899:1999(E)
|
||||
c11 ISO/IEC 9899:201x(E) N1570
|
||||
c++98 http://en.cppreference.com/w/cpp/keyword
|
||||
c++11 ISO/IEC 14882:2011(E) N3337 2012-01-16
|
||||
c++14 ISO/IEC 14882:2014(E) N4296 2014-11-09
|
||||
java8 http://docs.oracle.com/javase/specs/index.html
|
||||
ioccc.kw.freq c11 + additional words, sorted on frequency of occurrence in ioccc winners
|
||||
</code></pre>
|
||||
|
||||
<p>NB: The keyword file used in this code is derived from the list in iocccsize.c,
|
||||
which is neither complete (<code>#define</code>, <code>#ifndef</code>, <code>#undef</code> are missing
|
||||
– yes, Virginia knows about <code>#define</code> omitted on purpose),
|
||||
nor correct (many more are added: <code>I</code>, <code>true</code>, <code>bool</code>, <code>compl</code>, …):</p>
|
||||
|
||||
<pre><code>(sed -n '1p' c11; sed -n '2,$p' ioccc.kw.freq | sort) | comm -3 - c11
|
||||
</code></pre>
|
||||
|
||||
<p>That is an obfuscated way to say “diff”. But it also more clearly shows the, um, diffs.</p>
|
||||
|
||||
<p>The usage string is similarly externalized, providing a better usability string than
|
||||
the limits would otherwise allow. If this is considered bending the rules too far,
|
||||
just -DU=O to get a passible, if cryptic, usage message using no additional octets
|
||||
than without the define.
|
||||
This also allows the builder to configure the amount of help a utility should provide
|
||||
(<a href="https://www.youtube.com/watch?v=VU0GYSA1POs" title="Ants">you want a manpage? Because that’s how you get a manpage.</a>)
|
||||
– a rather significant point of contention made moot.</p>
|
||||
|
||||
<p>There is definitely abuse of the Rule2 counter, exploiting a “feature” of <code>iocccsize</code>:
|
||||
<code>iocccsize</code> does not Rule2 count {, } and ; when followed by whitespace – even within a string!
|
||||
Thus: Freecode, a simple, uncounted, highly obfuscated encoding of a central part
|
||||
of this program. (The reserved word list is not Freecoded, both because it is useful
|
||||
apart from the program, and because Freecode is not entirely free.*)</p>
|
||||
|
||||
<ul>
|
||||
<li>Some restrictions apply. Not approved for all uses. Some assembly required.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>Compilation</h2>
|
||||
|
||||
<p>The code is ANSI compliant, but it does have a string longer than 509 characters.
|
||||
It has been tested on OSX and Linux, Clang and GCC.</p>
|
||||
|
||||
<p>The code uses trigraphs, ironically: it parses trigraphs with trigraph code.
|
||||
Because that’s funny. And it remains obscure after, so trigraphs are not used for obfuscation.
|
||||
Thus -trigraphs -Wno-trigraphs (which is also humorous).</p>
|
||||
|
||||
<p>It’s obfuscated C. Thus -Wno-parentheses -Wno-empty-body -Wno-char-subscripts.</p>
|
||||
|
||||
<p>The code uses unsigned chars, but C strings are signed. Thus -Wno-pointer-sign.
|
||||
NB: This is <em>not</em> a portability concern, the one string holds only printable ASCII characters.</p>
|
||||
|
||||
<p>Thus:</p>
|
||||
|
||||
<pre><code>clang -ansi -Wall -trigraphs -Wno-trigraphs -Wno-parentheses -Wno-empty-body -Wno-char-subscripts -Wno-pointer-sign -DU=O -DW=\"keywords\" -o prog prog.c
|
||||
</code></pre>
|
||||
|
||||
<h2>Coda</h2>
|
||||
|
||||
<p>Cody Ferguson was relentless in his pursuit of bugs.
|
||||
Thanks to his reports, the version of <code>unob.sh</code> is stronger,
|
||||
<code>tac</code> groks digraphs, <code>tokenfix</code> corrects for missing digraphs in <code>prog.c</code>,
|
||||
and the program <code>manpage</code> was added to the corpus.</p>
|
||||
|
||||
<p><code>manpage</code> is a useful program that turns ASCII versions of man pages (e.g. <code>tac.man</code>) into real
|
||||
man pages. It works for C and C-like languages, command lines,
|
||||
and was designed to handle man page sections 1-8.
|
||||
It works well on most inputs, but tends to need some raw nroff for more fancy constructions,
|
||||
such as combined arguments
|
||||
(e.g. the -r and -s arguments to tac are not recognised when written as -rs in prose).</p>
|
||||
|
||||
<p>Try:</p>
|
||||
|
||||
<pre><code>manpage tac
|
||||
|
||||
manpage -h
|
||||
manpage -h | manpage
|
||||
manpage -h | manpage -R | less
|
||||
manpage -h | manpage -P | lpr
|
||||
</code></pre>
|
||||
|
||||
<hr />
|
||||
|
||||
<!--
|
||||
(c) Copyright 1984-2018, [Leo Broukhis, Simon Cooper, Landon Curt Noll][judges] - All rights reserved
|
||||
This work is licensed under a [Creative Commons Attribution-ShareAlike 3.0 Unported License][cc].
|
||||
|
||||
[judges]: http://www.ioccc.org/judges.html
|
||||
[cc]: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
-->
|
||||
|
||||
<TABLE><TR>
|
||||
<TD><a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/88x31.png" /></a></TD>
|
||||
<TD><P>© Copyright 1984-2018,
|
||||
<A HREF="/judges.html">Leo Broukhis, Simon Cooper, Landon Curt Noll</A>
|
||||
- All rights reserved<BR>
|
||||
This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.</P></TD>
|
||||
<TD> <!--<a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-html401" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a>--></TD>
|
||||
</TR></TABLE>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,397 @@
|
||||
# Best abuse of the rules
|
||||
|
||||
Dave Burton
|
||||
<http://ioccc.snox.net>
|
||||
|
||||
|
||||
## Judges' comments:
|
||||
### To use:
|
||||
|
||||
make
|
||||
|
||||
./prog [-tcksri] < file.c
|
||||
|
||||
### Try:
|
||||
|
||||
./prog -i < prog.c
|
||||
./prog -s < prog.c
|
||||
./prog -sk < prog.c
|
||||
|
||||
If you get stuck, try:
|
||||
|
||||
make test
|
||||
|
||||
If you get really stuck, try:
|
||||
|
||||
man ./tac.man
|
||||
|
||||
### Selected Judges Remarks:
|
||||
|
||||
They say size isn't everything, and in the case of IOCCC [iocccsize.c](http://www.ioccc.org/2018/iocccsize.c)
|
||||
that is saying something! What is this program weighing and how much does it weigh?
|
||||
|
||||
|
||||
## Author's comments:
|
||||
tac - tokenize and count C and derivative languages
|
||||
---------------------------------------------------
|
||||
|
||||
<blockquote><pre style="font-style:italic">
|
||||
tac computes C program size
|
||||
(obfuscated / otherwise)
|
||||
by splitting code as tokens small,
|
||||
ignoring space, then counting all.
|
||||
|
||||
It tries quite hard to act the same
|
||||
as counting tool of I-OCCC fame.
|
||||
Some bugs were fixed, it's faster too,
|
||||
and does more things, just add some glue!
|
||||
|
||||
It does far more than print the size:
|
||||
the token part's a better prize!
|
||||
With each upon on a single line,
|
||||
how many things can you design?
|
||||
|
||||
Included is a keyword sorter,
|
||||
de-obfuscator, freq reporter.
|
||||
With code produced as little parts,
|
||||
just add your own creative arts.
|
||||
|
||||
But first you have to figure out
|
||||
the braces part, without a doubt!
|
||||
And then the code - it isn't easy:
|
||||
for as else? That's rather cheesy!
|
||||
|
||||
The rest of it is standard fare:
|
||||
expressions strange, the globals bare,
|
||||
confusing symbols one and oh,
|
||||
and precedence you have to know.
|
||||
|
||||
The keyword list was made external,
|
||||
because of standards change eternal.
|
||||
Thus other languages can now,
|
||||
be counted just like C, somehow!
|
||||
|
||||
I hope you find this code obscure
|
||||
enough to win, and thus procure
|
||||
a public place for all to see
|
||||
how badly I can butcher C!
|
||||
</pre></blockquote>
|
||||
|
||||
Said another way....
|
||||
--------------------
|
||||
|
||||
`tac` is the inverse of `cat`(1): it un-concatenates its input into C tokens,
|
||||
writing the token stream to stdout. There are options to suppress the token ids,
|
||||
summarize the token counts, and several options to allow it to be compatible
|
||||
with `iocccsize`.
|
||||
|
||||
The token-based output is quite useful in analyzing source code (see below),
|
||||
and the counter is more accurate than `iocccsize` (see below); but this can be
|
||||
fixed by using the -c compatibility flag, restoring the following problems as
|
||||
in the official tool:
|
||||
|
||||
* whitespace **within** a string is **not** counted;
|
||||
this seems wrong, as these chars **are** significant
|
||||
* "`{;}`" is not counted within a double-quoted string
|
||||
* whitespace preceding a comment is char-counted, unless the comment starts a line
|
||||
* -k processes the reserved words within `//` comments(!), but not `/*`, changing the counts
|
||||
|
||||
The following bugs are always corrected:
|
||||
|
||||
* treats `/*/` as a complete comment (`/*/int if for/*/` has three keywords)
|
||||
* -k enables `//*` or within a line comment `/*` to initiate a comment block until following `*/`
|
||||
* -k processes double quote characters in `//` comments -
|
||||
thus a solitary quote consumes everything until the next quote
|
||||
* single quotes are not handled correctly (`'"'` starts a dquote in `iocccsize`)
|
||||
* `#ifndef` is omitted as a keyword (but `#ifdef` is allowed?)
|
||||
* I is not a reserved word.
|
||||
* `tac` correctly penetrates [23rd century cloaking technology][1]. ;-)
|
||||
|
||||
There is one (known) remaining problem with detection:
|
||||
|
||||
* comments between preprocessor hash and keyword are not replaced with whitespace;
|
||||
the following counts #include as two tokens, `#` and `include` (not as a reserved word):
|
||||
|
||||
#/*this will not be counted*/include/*correctly*/<stdio.h>
|
||||
|
||||
Fixing this properly hampers backwards compatibility in counting. This requires
|
||||
eliding comments during readsource (ISO 5.1.1.2, phase 3), and yet still
|
||||
counting words correctly absent -k.
|
||||
|
||||
[1]: http://ioccc.org/2014/birken/hint.html "Best use of port 1701"
|
||||
|
||||
Backwards Compatibility
|
||||
-----------------------
|
||||
|
||||
`tac` was run over all 366 previous IOCCC winning entries:
|
||||
|
||||
find ~/src/obc -type f -name \*.c | wc
|
||||
|
||||
The discrepancies found are documented and explained in the file "discrepancies".
|
||||
|
||||
find ~/src/obc -type f -a -name "*.c" | xargs ./spotcheck ./prog | ./spotdiff |
|
||||
grep -v keep | diff -bw - discrep* | grep "[<>] cl "
|
||||
|
||||
In summary, there are only 6 unique entries out of 366 that have any variation
|
||||
in Rule 2 counts; in all cases, the differences are due to bugs within
|
||||
`iocccsize`, described in detail in the file `discrepancies`. There are only 40
|
||||
files (40/366, ~11%, including originals and variations) that differ at all, 31
|
||||
only in word count; most word count differences appear related to comment block
|
||||
detection, weighted more towards recent entries (2011 and onward = 20/40 ~50%).
|
||||
|
||||
This (minor) variation in word count values has not been addressed since
|
||||
it is not a qualifying metric, nor is it very useful in a programming context:
|
||||
whitespace surrounds English words, not necessarily C tokens, as in `a+=1;`.
|
||||
|
||||
Accuracy (versus compatibility) is demonstrated through a series of small test cases
|
||||
designed to allow exhaustive, hand-counted values for comparison with the tool values.
|
||||
These test cases were vital in debugging and regression testing, and provided a way to
|
||||
determine which tool was correct when there were differences.
|
||||
|
||||
> A version of this tool in more clearly written C (tac.c)
|
||||
> is presented for the Judge's consideration as a more accurate replacement for `iocccsize`.
|
||||
> This obfuscated entry is derived from (and compatible with) that code,
|
||||
> but due to obfuscation, has had some significant, deep changes for the contest.
|
||||
> Nevertheless, the more clearly written code remains a spoiler for this entry.
|
||||
|
||||
NB: `iocccsize` gets a different answer from `tac` on its own (iocccsize.c) source code;
|
||||
`tac` gets the correct answer. This is due to the aforementioned bugs within `iocccsize`,
|
||||
proved by fixing iocccsize.c with the included patch, so `iocccsize` reports
|
||||
the correct answer for itself.
|
||||
|
||||
But wait... There's More!
|
||||
-------------------------
|
||||
|
||||
There is no limit on line length, file length, comment length, or identifier length.
|
||||
|
||||
`tac` features an accurate tokenizer; which presents each C token on a line by itself.
|
||||
This feature enables interesting analyses of C (and C++) code, hard to get any other way.
|
||||
|
||||
For instance, here is a simple token counter, useful in finding repeated long tokens, or operator
|
||||
frequency, or counting the references to identifiers, constants, or breadth of use of the
|
||||
language:
|
||||
|
||||
!/bin/sh
|
||||
cat $* | ./prog -t | sort | uniq -c | sort -k1nr
|
||||
|
||||
And here is a C keyword frequency counter:
|
||||
|
||||
#!/bin/sh
|
||||
function iskeyword {
|
||||
awk 'BEGIN{f="c11";while(getline<f)k[$1]=0}
|
||||
{if($1 in k)k[$1]++}END{for(i in k)print k[i],i}'
|
||||
}
|
||||
cat $* | ./prog -t | iskeyword | sort -k1nr
|
||||
|
||||
This script was used to "optimize" the reserved word order so the most frequent IOCCC
|
||||
winning entry keywords are checked first.
|
||||
|
||||
> An interesting aside: as might be expected, obfuscation has changed the frequency of
|
||||
> keyword distribution over time. The top five for the decades:
|
||||
>
|
||||
> 1980s 1990s 2000s 2010s
|
||||
> ----- ----- ----- -----
|
||||
> 94 if 532 char 375 if 254 int
|
||||
> 69 for 524 if 372 char 217 for
|
||||
> 59 while 417 int 332 int 198 if
|
||||
> 48 char 223 for 184 for 121 return
|
||||
> 43 int 223 void 165 return 104 char
|
||||
|
||||
The keyword list is external to the program, and is easily changed, sorted, checked, verified.
|
||||
This allows such additional programs in concert with the tool's primary operation.
|
||||
|
||||
A poor-man's de-obfuscator can be based upon the output of `tac -t` and a handful of
|
||||
simple rules (and another use of the external keyword file).
|
||||
A more refined version of this is included in the file `unob.sh`,
|
||||
but the simple code below is a serviceable obfuscated C de-obfuscator in a _scripting language_.
|
||||
It really is this easy with `tac`:
|
||||
|
||||
#!/bin/sh
|
||||
script='
|
||||
BEGIN {
|
||||
last=nl="\n";
|
||||
f="c11"; while(getline <f > 0) kw[$0]++; close(f);
|
||||
}
|
||||
|
||||
function iskw(a) { return a in kw }
|
||||
function indent(a) { return sprintf("%*s", n*3, " ") }
|
||||
function newline() { if (!infor && last != nl) printf last=nl; }
|
||||
function show(a) {
|
||||
if (last==nl) printf "%s", indent()
|
||||
printf "%s%s", space(), a
|
||||
last=a
|
||||
}
|
||||
function space() {
|
||||
return iskw(last) ||
|
||||
(last ~ /[A-Za-z0-9_+-\/%^[&\]\)=:<>;]$/ && $0 !~ /[:;()\[\],]/) ? " " : ""
|
||||
}
|
||||
|
||||
/^\(/ { ++paren }
|
||||
/^\)/ { --paren }
|
||||
|
||||
/^for/ { newline(); infor=1 }
|
||||
infor && /^;/ { ++infor; show($0 " "); next }
|
||||
infor==1 && /^:/ { ++infor }
|
||||
infor>1 && paren==0 { infor=0 }
|
||||
|
||||
/^\?/ { tern++; n++; newline(); show($0 " "); next }
|
||||
tern && /^:/ { newline(); show($0); --tern; --n; next }
|
||||
|
||||
/^#/ { newline(); show($0); newline(); next }
|
||||
/^;/ { show($0); newline(); next }
|
||||
/^{/ { show($0); ++n; newline(); next }
|
||||
/^}/ { n--; newline(); show($0); newline(); next }
|
||||
|
||||
{ show($0) }
|
||||
'
|
||||
cat $* | sed 's/#include/##include/' | cpp -E -trigraphs |
|
||||
sed 's/^# .*$//' | sed 's/^#//' |
|
||||
./prog -t | awk "$script"
|
||||
|
||||
On the question of obfuscation
|
||||
------------------------------
|
||||
|
||||
* I assume you noticed the [braces][2]?
|
||||
|
||||
_Every brace is sacred,
|
||||
Every brace is great.
|
||||
If a brace is wasted,
|
||||
Clang gets quite irate_.
|
||||
|
||||
* Keywords follow the 2010s top five, without the `if`, for interesting flow control.
|
||||
* Sometimes a `for` can be an `else` - [inconceivable][3]!
|
||||
* I see the `getopt`. But "[where's the beef?][4]"
|
||||
* No character constants, several int constants, and one string
|
||||
(hint: which does not encode letters) -- how does this code parse code?
|
||||
> Interesting aside: how many is "several int constants"?
|
||||
>
|
||||
> ./prog < prog.c 2>/dev/null | awk '$1~/260/{a[$2]++}END{for(i in a)print i,a[i]}'
|
||||
> ... | wc
|
||||
>
|
||||
> Which numbers are most often used?
|
||||
>
|
||||
> ... | sort -k2nr | sed 5q
|
||||
|
||||
* The code describes its function by careful arrangement of variables up front...
|
||||
* ...coupled with a description of the typical IOCCC contestant, or at least the author
|
||||
* Why shouldn't trigraph parsing be written in trigraph?
|
||||
* Where iocccsize.c mocks, this code flaunts:
|
||||
"_no matter how well you may think you understand this code, you don't, so don't mess with it. :-)_"
|
||||
* `O,0,l,1` are used to confusing effect, local names obscure global names.
|
||||
* Globals are used to pass information between routines: don't reorder "unrelated" statements....
|
||||
* How does the compatibility mode account for the fundamentally different manner of computation?
|
||||
* The algorithm is quite efficient; reserved word detection and trigraph parsing particularly so.
|
||||
* This may be the first obfuscated submission that provides the means for its own de-obfuscation.
|
||||
|
||||
[2]: https://www.youtube.com/watch?v=fUspLVStPbk&start=53 "Meaning of Life"
|
||||
[3]: https://www.youtube.com/watch?v=OHVjs4aobqs "Inconceivable"
|
||||
[4]: https://www.youtube.com/watch?v=Ug75diEyiA0 "Where's the beef?"
|
||||
|
||||
Rule 2
|
||||
------
|
||||
|
||||
The keyword list is externalized as an include file. This presents a useful
|
||||
feature for the source code: not only can the reserved word script above be
|
||||
produced, but the tool can be rebuilt with any set of reserved words, just by
|
||||
using a different list. For instance: removing all the secondary keywords;
|
||||
removing all the C11 keywords; trying just K&R C. Since C++ and Java share the
|
||||
same operators as C, just change the keyword list and `tac` will correctly
|
||||
tokenize both of these; the tokens >>> and :: can be handled with a short
|
||||
post-filter (tokenfix, included).
|
||||
|
||||
I believe this entry may also satisfy the request for gratuitous use of all the C11 keywords?
|
||||
Whether this context satisfies "intended C language context" is a matter of interpretation. ;-)
|
||||
And `tac` is ready for the IOCCC++ contest. And the IOJCC trashcan.
|
||||
|
||||
The following reserved word files are included:
|
||||
|
||||
kandr from my venerable 1978 18th printing "The C Programming Language"
|
||||
v7unix 7th edition Unix source code, extracted from c00.c
|
||||
kandr2 from my 1988 1st printing "The C Programming Language", 2e
|
||||
ansi ANS X3.159-1989
|
||||
c99 ISO/IEC 9899:1999(E)
|
||||
c11 ISO/IEC 9899:201x(E) N1570
|
||||
c++98 http://en.cppreference.com/w/cpp/keyword
|
||||
c++11 ISO/IEC 14882:2011(E) N3337 2012-01-16
|
||||
c++14 ISO/IEC 14882:2014(E) N4296 2014-11-09
|
||||
java8 http://docs.oracle.com/javase/specs/index.html
|
||||
ioccc.kw.freq c11 + additional words, sorted on frequency of occurrence in ioccc winners
|
||||
|
||||
NB: The keyword file used in this code is derived from the list in iocccsize.c,
|
||||
which is neither complete (`#define`, `#ifndef`, `#undef` are missing
|
||||
-- yes, Virginia knows about `#define` omitted on purpose),
|
||||
nor correct (many more are added: `I`, `true`, `bool`, `compl`, ...):
|
||||
|
||||
(sed -n '1p' c11; sed -n '2,$p' ioccc.kw.freq | sort) | comm -3 - c11
|
||||
|
||||
That is an obfuscated way to say "diff". But it also more clearly shows the, um, diffs.
|
||||
|
||||
The usage string is similarly externalized, providing a better usability string than
|
||||
the limits would otherwise allow. If this is considered bending the rules too far,
|
||||
just -DU=O to get a passible, if cryptic, usage message using no additional octets
|
||||
than without the define.
|
||||
This also allows the builder to configure the amount of help a utility should provide
|
||||
([you want a manpage? Because that's how you get a manpage.][5])
|
||||
-- a rather significant point of contention made moot.
|
||||
|
||||
There is definitely abuse of the Rule2 counter, exploiting a "feature" of `iocccsize`:
|
||||
`iocccsize` does not Rule2 count {, } and ; when followed by whitespace -- even within a string!
|
||||
Thus: Freecode, a simple, uncounted, highly obfuscated encoding of a central part
|
||||
of this program. (The reserved word list is not Freecoded, both because it is useful
|
||||
apart from the program, and because Freecode is not entirely free.*)
|
||||
|
||||
* Some restrictions apply. Not approved for all uses. Some assembly required.
|
||||
|
||||
[5]: https://www.youtube.com/watch?v=VU0GYSA1POs "Ants"
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
The code is ANSI compliant, but it does have a string longer than 509 characters.
|
||||
It has been tested on OSX and Linux, Clang and GCC.
|
||||
|
||||
The code uses trigraphs, ironically: it parses trigraphs with trigraph code.
|
||||
Because that's funny. And it remains obscure after, so trigraphs are not used for obfuscation.
|
||||
Thus -trigraphs -Wno-trigraphs (which is also humorous).
|
||||
|
||||
It's obfuscated C. Thus -Wno-parentheses -Wno-empty-body -Wno-char-subscripts.
|
||||
|
||||
The code uses unsigned chars, but C strings are signed. Thus -Wno-pointer-sign.
|
||||
NB: This is *not* a portability concern, the one string holds only printable ASCII characters.
|
||||
|
||||
Thus:
|
||||
|
||||
clang -ansi -Wall -trigraphs -Wno-trigraphs -Wno-parentheses -Wno-empty-body -Wno-char-subscripts -Wno-pointer-sign -DU=O -DW=\"keywords\" -o prog prog.c
|
||||
|
||||
Coda
|
||||
----
|
||||
|
||||
Cody Ferguson was relentless in his pursuit of bugs.
|
||||
Thanks to his reports, the version of `unob.sh` is stronger,
|
||||
`tac` groks digraphs, `tokenfix` corrects for missing digraphs in `prog.c`,
|
||||
and the program `manpage` was added to the corpus.
|
||||
|
||||
`manpage` is a useful program that turns ASCII versions of man pages (e.g. `tac.man`) into real
|
||||
man pages. It works for C and C-like languages, command lines,
|
||||
and was designed to handle man page sections 1-8.
|
||||
It works well on most inputs, but tends to need some raw nroff for more fancy constructions,
|
||||
such as combined arguments
|
||||
(e.g. the -r and -s arguments to tac are not recognised when written as -rs in prose).
|
||||
|
||||
Try:
|
||||
|
||||
manpage tac
|
||||
|
||||
manpage -h
|
||||
manpage -h | manpage
|
||||
manpage -h | manpage -R | less
|
||||
manpage -h | manpage -P | lpr
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
<!--
|
||||
(c) Copyright 1984-2018, [Leo Broukhis, Simon Cooper, Landon Curt Noll][judges] - All rights reserved
|
||||
This work is licensed under a [Creative Commons Attribution-ShareAlike 3.0 Unported License][cc].
|
||||
|
||||
[judges]: http://www.ioccc.org/judges.html
|
||||
[cc]: http://creativecommons.org/licenses/by-sa/3.0/
|
||||
-->
|
||||
@@ -0,0 +1,396 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
int cl,cw,cc,ci,ck,xc; // counts: line, word, char, ioc, keywords, xtra_char
|
||||
int ts,tk; // totals: saved from counting, keyword lengths
|
||||
int fk,fs,fr,fi,fc,ft; // flags: keep, suppress, reserved, ioccc, compat, tokenize, comments
|
||||
unsigned char *buf, *p, *e, *w, *s; // ptr, end, white, start
|
||||
|
||||
char *kw[] = {
|
||||
"void", "char", "short", "int", "long", "unsigned",
|
||||
"typedef", "struct", "union", "enum", "sizeof", "signed",
|
||||
"for", "while", "do", "if", "else", "switch",
|
||||
"case", "default", "break", "continue", "return", "goto",
|
||||
"static", "volatile", "register", "auto", "const", "extern",
|
||||
"float", "double",
|
||||
|
||||
"#ifdef", "#ifndef", "#if", "#else", "#elif", "#endif",
|
||||
"#include", "#error", "#warning", "#line", "#pragma",
|
||||
|
||||
"restrict", "inline", "_Bool", "_Complex", "_Imaginary",
|
||||
|
||||
"_Atomic", "_Generic", "_Noreturn", "_Pragma",
|
||||
"_Static_assert", "_Thread_local", "_Alignas", "_Alignof",
|
||||
"static_assert", "thread_local", "alignas", "alignof", "noreturn",
|
||||
"bool", "true", "false", "compl", "bitand", "bitor",
|
||||
"and", "and_eq", "not", "not_eq",
|
||||
"or", "or_eq", "xor", "xor_eq",
|
||||
0
|
||||
};
|
||||
|
||||
enum {
|
||||
CPP = 256
|
||||
, CMT = 257
|
||||
, KEYW = 258
|
||||
, DIVEQ = 259
|
||||
, MULEQ = 260
|
||||
, MODEQ = 261
|
||||
, ADDEQ = 262
|
||||
, SUBEQ = 263
|
||||
, XOREQ = 264
|
||||
, OREQ = 265
|
||||
, ANDEQ = 266
|
||||
, RSHEQ = 267
|
||||
, LSHEQ = 268
|
||||
, NOTEQ = 269
|
||||
, EQ = 270
|
||||
, GE = 271
|
||||
, LE = 272
|
||||
, OR2 = 273
|
||||
, AND2 = 274
|
||||
, PP = 275
|
||||
, MM = 276
|
||||
, PTR = 277
|
||||
, LSH = 278
|
||||
, RSH = 279
|
||||
, INT = 300
|
||||
, FLOAT = 301
|
||||
, ID = 302
|
||||
};
|
||||
|
||||
enum {
|
||||
O=0x01 // octal
|
||||
, H=0x02 // hex
|
||||
, D=0x04 // decimal
|
||||
, I=0x08 // identifier
|
||||
, W=0x10 // whitespace
|
||||
, B=0x20 // brace, semi
|
||||
, OHD = O|H|D
|
||||
, HD = H|D
|
||||
, HI = H|I
|
||||
};
|
||||
|
||||
char tab[256] = {
|
||||
W, 00, 00, 00, 00, 00, 00, 00, 00, W, W, W, W, W, 00, 00,
|
||||
00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
|
||||
W, 00, 00, I, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
|
||||
OHD,OHD,OHD,OHD,OHD,OHD,OHD,OHD, HD, HD, 00, B, 00, 00, 00, 00,
|
||||
|
||||
00, HI, HI, HI, HI, HI, HI, I, I, I, I, I, I, I, I, I,
|
||||
I, I, I, I, I, I, I, I, I, I, I, 00, 00, 00, 00, I,
|
||||
00, HI, HI, HI, HI, HI, HI, I, I, I, I, I, I, I, I, I,
|
||||
I, I, I, I, I, I, I, I, I, I, I, B, 00, B, 00, 00,
|
||||
};
|
||||
|
||||
#define is(x, c) (tab[c]&(x))
|
||||
|
||||
// discard backslash-newline as-if not present, even if trigraph backslash
|
||||
// iocccsize counts tri as xbcount+=2, and bs-nl as xb+=2, xw++, xl++ to match wc(1)
|
||||
|
||||
void readsource() {
|
||||
int c, o=2, len=128;
|
||||
buf = malloc(len);
|
||||
while ((c = getchar()) != EOF) {
|
||||
if (c == '\r') {
|
||||
if (fc) ++xc;
|
||||
continue;
|
||||
}
|
||||
if (buf[o-1] == '?' && buf[o-2] == '?') {
|
||||
char *tri = "=#([/\\)]'^<{!|>}-~";
|
||||
while (*tri && *tri++ != c)
|
||||
++tri;
|
||||
if (*tri) {
|
||||
c = *tri;
|
||||
o -= 2;
|
||||
if (fc) xc += 2;
|
||||
}
|
||||
}
|
||||
if (c == '\n' && buf[o-1] == '\\') {
|
||||
--o;
|
||||
cl++;
|
||||
if (fc) {
|
||||
++cw;
|
||||
xc += 2;
|
||||
}
|
||||
} else
|
||||
buf[o++] = c;
|
||||
if (o == len)
|
||||
buf = realloc(buf, len+=128);
|
||||
}
|
||||
buf[o] = 0;
|
||||
e = buf + o;
|
||||
p = buf += 2;
|
||||
}
|
||||
|
||||
int iskw(char *s, int len) {
|
||||
char **k;
|
||||
if (len > 1)
|
||||
for (k = kw; *k; ++k)
|
||||
if (s[0] == k[0][0] && k[0][len] == 0 && strncmp(s, *k, len) == 0)
|
||||
return ++ck, (tk+=len-1), 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int want(char m) {
|
||||
while (p < e) {
|
||||
if (*p == '\\') {
|
||||
// need to only ignore: ntbrfva?, octal, x, hex
|
||||
// 0111 = two chars, \091 = three chars, \xabc = one char(!)
|
||||
// for our purposes, only worry about: and \ newline,
|
||||
// which is not a newline, but an embedded line break
|
||||
if (p[1] == '\\' || p[1] == '\'' || p[1] == '"' || p[1] == '\n') {
|
||||
if (p[1] == '\n') ++cl;
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (*p == '\n') ++cl;
|
||||
if (*p++ == m) return m;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int follow(char m, int y, int n) {
|
||||
if (*p == m) return ++p,y;
|
||||
return n;
|
||||
}
|
||||
|
||||
int follow2(char m, int y, int n, int y2) {
|
||||
if (*p == m) return ++p,y;
|
||||
if (*p == n) return ++p,y2;
|
||||
return n;
|
||||
}
|
||||
|
||||
int skipwhite() {
|
||||
int c;
|
||||
for (c=*p++; p<=e && is(W, c); c=*p++) {
|
||||
++cc;
|
||||
if (c == '\n') ++cl;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPP tokens may extend beyond a single line of text and include comments
|
||||
* To do this correctly, need to scan and remove comments during readsource
|
||||
* and present comments as whitespace. Note that "# include" is a valid CPP
|
||||
* token, and does not match "#include".
|
||||
*
|
||||
* Because the global pointers p, s and w are well-known, and because comments
|
||||
* are not consumed during readsource, it is more difficult to parse CPP stuff.
|
||||
* Also note that for backwards compat, we need to also count words correctly,
|
||||
* with and without -k.
|
||||
*/
|
||||
|
||||
int lex() {
|
||||
int c, t;
|
||||
|
||||
w = p;
|
||||
c = skipwhite();
|
||||
s = p-1;
|
||||
|
||||
if (p >= e)
|
||||
return c;
|
||||
|
||||
switch (c) {
|
||||
case '\'': want(c); return c;
|
||||
case '"': want(c); return c;
|
||||
case '/':
|
||||
t = follow2('=', DIVEQ, c, CMT);
|
||||
if (t == CMT)
|
||||
want('\n');
|
||||
else if (t == c && (t = follow('*', CMT, c)) == CMT)
|
||||
do want('*'); while (p<e && follow('/', 0, 1));
|
||||
return t;
|
||||
case '*': return follow ('=', MULEQ, c);
|
||||
case '%': return follow ('=', MODEQ, c);
|
||||
case '=': return follow ('=', EQ, c);
|
||||
case '!': return follow ('=', NOTEQ, c);
|
||||
case '^': return follow ('=', XOREQ, c);
|
||||
case '+': return follow2('=', ANDEQ, c, PP);
|
||||
case '|': return follow2('=', OREQ, c, OR2);
|
||||
case '&': return follow2('=', ANDEQ, c, AND2);
|
||||
case '-': t = follow2('=', SUBEQ, c, MM); return t==c ?follow('>', PTR, t):t;
|
||||
case '>': t = follow2('=', GE, c, RSH); return t==RSH?follow('=', RSHEQ, t):t;
|
||||
case '<': t = follow2('=', LE, c, LSH); return t==LSH?follow('=', LSHEQ, t):t;
|
||||
case '#': if (ft) { want('\n'); return CPP; }
|
||||
}
|
||||
|
||||
if (is(D, c)) {
|
||||
if (c == '0' && (*p|32) == 'x')
|
||||
c = *++p;
|
||||
while (is(OHD, c))
|
||||
c = *p++;
|
||||
if (c == '.') {
|
||||
(void)strtod(s, (char**)&p);
|
||||
return FLOAT;
|
||||
}
|
||||
return --p, INT;
|
||||
}
|
||||
|
||||
if (is(I, c)) {
|
||||
while (is(I|D, c))
|
||||
c = *p++;
|
||||
--p;
|
||||
return iskw(s, p-s) ? KEYW : ID;
|
||||
}
|
||||
|
||||
return c ? c : ' '; // allows nulls in source code
|
||||
}
|
||||
|
||||
// iocccsize compat: adjust whitespace counts
|
||||
void compat_words(int tlen, int inword) {
|
||||
int c;
|
||||
for (c=0; c<tlen; ++c) {
|
||||
int isw = is(W,s[c]);
|
||||
if (isw) --ci;
|
||||
else if (is(B,s[c]) && is(W,s[c+1])) --ci;
|
||||
if (inword) inword = !isw;
|
||||
else if (!isw) inword = ++cw;
|
||||
}
|
||||
}
|
||||
|
||||
void compat_kw(int tlen, int inword) {
|
||||
int c, w=0;
|
||||
for (w=c=2; c<tlen; ++c) { // skip comment lead-in chars
|
||||
int isw = !is(I|D,s[c]);
|
||||
if (inword) {
|
||||
inword = !isw;
|
||||
if (!inword && iskw(s+w, c-w))
|
||||
ci -= c-w-1;
|
||||
} else if (!isw)
|
||||
inword = w = c;
|
||||
}
|
||||
}
|
||||
|
||||
void compat(int t, int tlen) {
|
||||
static int tprev;
|
||||
int c;
|
||||
if (tprev == CMT) {
|
||||
// do not bcount spaces following comment blocks, including the newline!
|
||||
// nor lcount the newline following a comment block if last on line
|
||||
unsigned char *ow = w;
|
||||
while (ow<s && *ow++ != '\n')
|
||||
;
|
||||
if (!fk && ow[-1] == '\n')
|
||||
--cl;
|
||||
cc -= ow-w;
|
||||
tprev = 0;
|
||||
}
|
||||
// new-style comments eat preceding newline, leaving no whitespace if token starts line
|
||||
// add it back here for compat word boundary detection
|
||||
if (w == s && w[-1] == '\n')
|
||||
--w;
|
||||
if (t == CMT) {
|
||||
// do not count newlines within comments
|
||||
// and checks for keywords in new-style comments
|
||||
if (fk) {
|
||||
int inword = w==s && s>buf;
|
||||
ci += tlen;
|
||||
if (s[1] == '/')
|
||||
compat_kw(tlen, inword);
|
||||
compat_words(tlen, inword);
|
||||
} else for (c=0; c<tlen; ++c)
|
||||
if (s[c] == '\n') --cl;
|
||||
|
||||
// nor spaces before comments IFF the comment is first non-white on line
|
||||
unsigned char *os = s;
|
||||
while (os > w) {
|
||||
if (*--os == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
// look for last ends with newline, only remove ccounts if whitespace
|
||||
if (*os == '\n' || os == buf || w[-1] == '\n')
|
||||
cc -= s - os - (*os == '\n');
|
||||
|
||||
// look for whitespace before the comment, as well as after
|
||||
if (!fk && (w != s || s == buf) && !is(W, s[tlen]) && s[1] == '*')
|
||||
++cw;
|
||||
|
||||
if (s[1] == '*')
|
||||
tprev = CMT;
|
||||
} else if (t == '"' || t == '\'') {
|
||||
// iocccsize -i does not count whitespace WITHIN a string!!!
|
||||
// and {;} can be "free" within a string if follows the "rules"
|
||||
compat_words(tlen, 1);
|
||||
}
|
||||
// count words, not tokens (a preceding old-style CMT is whitespace)
|
||||
if (t && t != CMT && w == s && s > buf)
|
||||
--cw;
|
||||
if (!fs && (t != CMT || fk)) {
|
||||
printf("%.*s", (int)(s-w), w);
|
||||
printf("%.*s", tlen, s);
|
||||
}
|
||||
}
|
||||
|
||||
void count() {
|
||||
int t, tlen;
|
||||
while ((t=lex())) {
|
||||
tlen = p-s;
|
||||
if (t != CMT) {
|
||||
cc += tlen;
|
||||
++cw;
|
||||
if (fr && t == KEYW)
|
||||
ci += 1;
|
||||
else if (t < 256 && is(B, t) && (is(W, *p) || !*p))
|
||||
;
|
||||
else
|
||||
ci += tlen;
|
||||
}
|
||||
if (fc)
|
||||
compat(t, tlen);
|
||||
else if (!fs && (t != CMT || fk)) {
|
||||
if (!ft) printf("%3d ", t);
|
||||
printf("%.*s", tlen, s);
|
||||
if (p[-1] != '\n') printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (fc) {
|
||||
compat(0, e-w);
|
||||
if (!fs && w<e)
|
||||
printf("%.*s", (int)(e-w), w);
|
||||
}
|
||||
|
||||
if (fk) cc = e - buf;
|
||||
if (!fr) tk = 0;
|
||||
ts = cc - ci - tk;
|
||||
cc += xc;
|
||||
}
|
||||
|
||||
int main(int ac, char **av) {
|
||||
static char usage[] = "[flags]\n\
|
||||
-t tokenize input\n\
|
||||
-c compatible with iocccsize\n\
|
||||
-k keep comments\n\
|
||||
-s suppress source\n\
|
||||
-r reserved words count as 1\n\
|
||||
-i ioccc size (implies -rsc)";
|
||||
|
||||
int c;
|
||||
while ((c = getopt(ac, av, "?tcksri")) != EOF) {
|
||||
switch (c) {
|
||||
case 't': ft=1; break;
|
||||
case 'c': fc=1; break;
|
||||
case 'k': fk=1; break;
|
||||
case 's': fs=1; break;
|
||||
case 'r': fr=1; break;
|
||||
case 'i': fi=fr=fs=fc=1; break;
|
||||
default:
|
||||
fprintf(stderr, "%s %s\n", av[0], usage);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
readsource();
|
||||
count();
|
||||
|
||||
if (!ft) {
|
||||
if (fi) printf("%d\n", ci);
|
||||
else fprintf(stderr, "%d %d %d %d %d %d %d\n", cl, cw, cc, ci, ts, ck, tk);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
$ derived from iocccsize.c, which has many additions, and three cpp ommisions $
|
||||
if
|
||||
int
|
||||
for
|
||||
return
|
||||
void
|
||||
char
|
||||
while
|
||||
typedef
|
||||
else
|
||||
goto
|
||||
do
|
||||
#include
|
||||
case
|
||||
sizeof
|
||||
struct
|
||||
long
|
||||
unsigned
|
||||
break
|
||||
const
|
||||
double
|
||||
float
|
||||
short
|
||||
union
|
||||
switch
|
||||
default
|
||||
continue
|
||||
static
|
||||
volatile
|
||||
enum
|
||||
auto
|
||||
extern
|
||||
register
|
||||
signed
|
||||
#ifdef
|
||||
#else
|
||||
#endif
|
||||
inline
|
||||
restrict
|
||||
#ifndef
|
||||
#if
|
||||
#elif
|
||||
#error
|
||||
#warning
|
||||
#line
|
||||
#pragma
|
||||
_Bool
|
||||
_Complex
|
||||
_Imaginary
|
||||
_Atomic
|
||||
_Generic
|
||||
_Noreturn
|
||||
_Pragma
|
||||
_Static_assert
|
||||
_Thread_local
|
||||
_Alignas
|
||||
_Alignof
|
||||
static_assert
|
||||
thread_local
|
||||
alignas
|
||||
alignof
|
||||
noreturn
|
||||
bool
|
||||
true
|
||||
false
|
||||
compl
|
||||
bitand
|
||||
bitor
|
||||
and
|
||||
and_eq
|
||||
not
|
||||
not_eq
|
||||
or
|
||||
or_eq
|
||||
xor
|
||||
xor_eq
|
||||
@@ -0,0 +1,4 @@
|
||||
/*/int if while -- how many keywords?/*/
|
||||
main() {
|
||||
printf("howdy!\n");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// bug shows under -k
|
||||
//* int if while
|
||||
main(int ac, char **av) {
|
||||
if (ac > 1)
|
||||
while(--ac) puts(*++av);
|
||||
else
|
||||
puts("none");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/* word count incorrect except with -k; view output with no args */
|
||||
main(){
|
||||
printf(/*testing*/
|
||||
"hello, world\n");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// this shows the impact of " quotes with -k
|
||||
char*dquote="dquote";
|
||||
main(int ac, char **av) {
|
||||
if (ac > 1 && av[1][0] == '\"')
|
||||
printf(dquote);
|
||||
if (ac == 0)
|
||||
printf("int ac is zero\n");
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// I is an identifier, not a reserved word
|
||||
enum Foo {
|
||||
I = 1,
|
||||
O = 2
|
||||
};
|
||||
main(int ac, char**av) {
|
||||
for(--ac; *++av; )
|
||||
switch (*av[0]-'0') {
|
||||
case I: puts("input"); break;
|
||||
case O: puts("output"); break;
|
||||
default: puts("whatever"); break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include<stdio.h>
|
||||
#undef FOO
|
||||
#ifndef FOO
|
||||
int main() {
|
||||
printf("howdy!\n");
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
$ http://docs.oracle.com/javase/specs/index.html, Java SE 8 $
|
||||
abstract
|
||||
assert
|
||||
boolean
|
||||
break
|
||||
byte
|
||||
case
|
||||
catch
|
||||
char
|
||||
class
|
||||
const
|
||||
continue
|
||||
default
|
||||
do
|
||||
double
|
||||
else
|
||||
enum
|
||||
extends
|
||||
final
|
||||
finally
|
||||
float
|
||||
for
|
||||
if
|
||||
goto
|
||||
implements
|
||||
import
|
||||
instanceof
|
||||
int
|
||||
interface
|
||||
long
|
||||
native
|
||||
new
|
||||
package
|
||||
private
|
||||
protected
|
||||
public
|
||||
return
|
||||
short
|
||||
static
|
||||
strictfp
|
||||
super
|
||||
switch
|
||||
synchronized
|
||||
this
|
||||
throw
|
||||
throws
|
||||
transient
|
||||
try
|
||||
void
|
||||
volatile
|
||||
while
|
||||
true
|
||||
false
|
||||
null
|
||||
@@ -0,0 +1,38 @@
|
||||
$ Kernighan & Ritchie, (1978) _The C Programming Language_, 180,207-208 $
|
||||
#define
|
||||
#else
|
||||
#endif
|
||||
#if
|
||||
#ifdef
|
||||
#ifndef
|
||||
#include
|
||||
#line
|
||||
#undef
|
||||
auto
|
||||
break
|
||||
case
|
||||
char
|
||||
continue
|
||||
default
|
||||
do
|
||||
double
|
||||
else
|
||||
entry
|
||||
extern
|
||||
float
|
||||
for
|
||||
goto
|
||||
if
|
||||
int
|
||||
long
|
||||
register
|
||||
return
|
||||
short
|
||||
sizeof
|
||||
static
|
||||
struct
|
||||
switch
|
||||
typedef
|
||||
union
|
||||
unsigned
|
||||
while
|
||||
@@ -0,0 +1,48 @@
|
||||
$ Kernighan & Ritchie, (1988) _The C Programming Manual, Second Edition_, 192,230-233 $
|
||||
#define
|
||||
#elif
|
||||
#else
|
||||
#endif
|
||||
#error
|
||||
#if
|
||||
#ifdef
|
||||
#ifndef
|
||||
#include
|
||||
#line
|
||||
#pragma
|
||||
#undef
|
||||
asm
|
||||
auto
|
||||
break
|
||||
case
|
||||
char
|
||||
const
|
||||
continue
|
||||
default
|
||||
defined
|
||||
do
|
||||
double
|
||||
else
|
||||
enum
|
||||
extern
|
||||
float
|
||||
for
|
||||
fortran
|
||||
goto
|
||||
if
|
||||
int
|
||||
long
|
||||
register
|
||||
return
|
||||
short
|
||||
signed
|
||||
sizeof
|
||||
static
|
||||
struct
|
||||
switch
|
||||
typedef
|
||||
union
|
||||
unsigned
|
||||
void
|
||||
volatile
|
||||
while
|
||||
@@ -0,0 +1,484 @@
|
||||
#!/bin/sh
|
||||
|
||||
function usage {
|
||||
cat <<'EoT'
|
||||
NAME
|
||||
manpage - format an ascii text file into man page format
|
||||
|
||||
SYNOPSIS
|
||||
manpage [-PR] [-d pdate] [-r rel] [-v vol] [-s sect] [-t title]
|
||||
[-w ncols] [filename]
|
||||
|
||||
DESCRIPTION
|
||||
manpage converts its input into the man(7) macros as used by man(1),
|
||||
but without the user needed to understand any formatting syntax.
|
||||
It is designed to process a flat ascii file that roughly appears in the format
|
||||
desired, adding the specific flourishes that distinguish a readable man page:
|
||||
|
||||
- font changes for names and arguments throughout the document
|
||||
- automatic recognition of command, C, C++, and Java names, types and arguments
|
||||
- automatic recognition of paragraphs, list formats, sections, and non-formatted displays
|
||||
- constant width fonts for code and displays
|
||||
- cross-references highlighted
|
||||
- pass-through of nroff formatting commands
|
||||
|
||||
The input resembles the man page to be formatted, with just a few rules of presentation:
|
||||
Sections ~
|
||||
Section names are input lines in all upper case. If a section name begins
|
||||
with spaces, it starts a subsection.
|
||||
Paragraphs ~
|
||||
A blank line between text marks a paragraph. All text in the same
|
||||
paragraph must be left-aligned relative to itself.
|
||||
Line Breaks ~
|
||||
A twiddle (~) at the end of a line is consumed and replaced
|
||||
with a line break. This is especially useful in tag lists (this section).
|
||||
Displays ~
|
||||
These are sections not to be formatted further. They are introduced
|
||||
by a preceding blank line and indented relative to the surrounding text.
|
||||
Displays are output in constant width font, and all code escapes may
|
||||
be presented naturally:
|
||||
|
||||
printf("Hello, World\n");
|
||||
|
||||
Tag Lists ~
|
||||
The tag is identified by three (3) or more spaces between itself
|
||||
and its description. These rules are tag lists. Note especially
|
||||
the trailing spaces on the tags in this section.
|
||||
Bullet Lists ~
|
||||
These are identified by a leading dash (-), dot (.), or star (*).
|
||||
Numbered Lists ~
|
||||
These begin with a number and a dot; e.g. "3. Number three".
|
||||
Cross References ~
|
||||
This is a word followed by a number in parenthesis; e.g. man(1).
|
||||
Bold / Italic ~
|
||||
A word surrounded by underscores (_) is rendered in _italic_;
|
||||
and by stars (*) is rendered as *boldface*.
|
||||
|
||||
There are two sections handled specially:
|
||||
NAME
|
||||
This is the name of the command or function, followed by a terse,
|
||||
one-line summary of its primary use.
|
||||
SYNOPSIS
|
||||
This section is scanned for names, arguments, and flags. Names found
|
||||
in this area are remembered, and each occurance throughout the document
|
||||
are presented in bold. Arguments are similarly detected and remembered,
|
||||
and presented in italics. Other text in this area is presented in
|
||||
a constant width font.
|
||||
|
||||
OPTIONS
|
||||
-d pdate set the man page date
|
||||
-r rel set the release name
|
||||
-v vol set the volume name
|
||||
-s sect set the section name
|
||||
-t title set the man page title
|
||||
-w ncols force the output to be ncols wide
|
||||
-P PostScript output
|
||||
-R roff output
|
||||
|
||||
EXAMPLES
|
||||
This usage is in the proper format for manpage. Try:
|
||||
|
||||
manpage -h | manpage -tmanpage -s1
|
||||
|
||||
To view the nroff(1) formatted page, perhaps for inclusion in the system man pages:
|
||||
|
||||
manpage -h | manpage -tmanpage -s1 -R > manpage.1
|
||||
|
||||
ENVIRONMENT
|
||||
PAGER defaults to less(1); used if neither -P nor -R is specified as arguments
|
||||
MANPATH input .man files searched in current dir, then HOME/man, then MANPATH
|
||||
ORG name of organization (e.g. $ORG Reference Manual)
|
||||
|
||||
SEE ALSO
|
||||
man(1), man(7), groff(1), less(1), txt2man(1)
|
||||
|
||||
PORTABILITY
|
||||
This program uses generic bourne shell and awk syntax, POSIX compatible systems should work.
|
||||
|
||||
BUGS
|
||||
Code displays that span page breaks do not restore the troff(1) escape
|
||||
during header / footer processing.
|
||||
|
||||
AUTHOR
|
||||
Dave Burton <ioccc@snox.net>
|
||||
|
||||
manpage(1) input format is inspired by txt2man(1) by Marc Vertes <mvertes@free.fr>,
|
||||
although the code is a clean redesign from first principles.
|
||||
EoT
|
||||
}
|
||||
|
||||
function getwidth {
|
||||
echo $(stty -a 2>/dev/null) |
|
||||
if test $(uname) == "Darwin"; then
|
||||
awk '/columns/{for(i=1;i<=NF;++i)if($i~/columns/)print$(i-1)+0}'
|
||||
else
|
||||
awk '/columns/{for(i=1;i<=NF;++i)if($i~/columns/)print$(i+1)+0}'
|
||||
fi
|
||||
}
|
||||
|
||||
function scan {
|
||||
for dir in . $HOME/man $(echo $MANPATH | tr : '\n'); do
|
||||
test -r $dir/$1 && { echo $dir/$1; break; }
|
||||
done
|
||||
}
|
||||
|
||||
function searchfor {
|
||||
# examine the current dir for name, name.man, or scan a searchpath for name.man
|
||||
test "$1" || { echo ""; return; }
|
||||
filename=$1
|
||||
if ! test -r $filename || test -x $filename; then
|
||||
expr "$filename" : .*.man >/dev/null 2>&1 || filename=$filename.man
|
||||
result=$(scan $filename)
|
||||
if ! test "$result"; then
|
||||
echo cannot find $filename in curdir, $HOME/man or MANPATH >&2
|
||||
exit 1
|
||||
fi
|
||||
filename=$result
|
||||
fi
|
||||
echo $filename
|
||||
}
|
||||
|
||||
width=0
|
||||
sys=($uname -s)
|
||||
date= rel= vol= sect= title=
|
||||
post=
|
||||
|
||||
args="?hPRd:r:v:s:t:w:"
|
||||
while getopts $args opt
|
||||
do
|
||||
case $opt in
|
||||
d) date=$OPTARG;;
|
||||
r) rel=$OPTARG;;
|
||||
v) vol=$OPTARG;;
|
||||
s) sect=$OPTARG;;
|
||||
t) title=$OPTARG;;
|
||||
w) width=$OPTARG;;
|
||||
P) post="groff -man -Tps";;
|
||||
R) post=cat;;
|
||||
\?) echo "usage: $0 [-$args] [filename]" >&2; exit 1;;
|
||||
h|*) usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
date=${date:-$(date +'%d %B %Y')}
|
||||
|
||||
# format and display the page now
|
||||
if test $width -eq 0 -a ! "$post" ; then
|
||||
width=$(getwidth)
|
||||
fi
|
||||
|
||||
# -is for pre-el-capitan
|
||||
: ${post:="groff -man -Tascii | ${PAGER:-less -R}"}
|
||||
|
||||
# seek for it, perhaps without the .man extension
|
||||
filename=$(searchfor $1) || exit; shift;
|
||||
# echo $filename; exit
|
||||
|
||||
# intuit the title, section and volume from the input file
|
||||
if ! test "$title" && test "$filename"; then
|
||||
title=${filename##*/}; title=${title%.man}
|
||||
title=$(echo $title | tr a-z A-Z)
|
||||
if sed '/DESCRIPTION/q' $filename | egrep -q "#include|.import"; then
|
||||
sect=${sect:-3}
|
||||
vol=${vol:-"$ORG Programmer's Manual"}
|
||||
else
|
||||
sect=${sect:-1}
|
||||
vol=${vol:-"$ORG Reference Manual"}
|
||||
fi
|
||||
fi
|
||||
|
||||
# --------------------------------------------------
|
||||
# the formatting engine
|
||||
|
||||
expand $filename |
|
||||
awk -v width=$width -v th="$title $sect \"$date\" \"$rel\" \"$vol\"" '
|
||||
BEGIN {
|
||||
if (width+0 > 0) {
|
||||
# adjust output to a specific width; useful as man(1) replacement
|
||||
cpi = (width+0)/11
|
||||
print ".ll " cpi "i"
|
||||
print ".nr LL " cpi "i"
|
||||
}
|
||||
print ".TH", th
|
||||
print ".if n .na\n.if n .nh"
|
||||
block = 0 # depth of the block display
|
||||
list = 0 # depth of the list indent
|
||||
indent = 0 # current RS indent level
|
||||
rs[indent] = 0 # array of starting indents
|
||||
lw = 0 # leading whitespace
|
||||
blank = 0 # NR of last blank line
|
||||
}
|
||||
|
||||
function bold(w) { return "\\fB" w "\\fP" }
|
||||
function italic(w) { return "\\fI" w "\\fP" }
|
||||
|
||||
function before(s) { return substr(s, 1, RSTART-1) }
|
||||
function after(s) { return substr(s, RSTART+RLENGTH) }
|
||||
|
||||
function add(t, r) {
|
||||
gsub(/\./, "\\.", t)
|
||||
pattern[t] = r
|
||||
}
|
||||
|
||||
# replace logic is W*P, but both W (words) and P (patterns) are small,
|
||||
# and this is the only way to portably, reliably detect substitutable patterns
|
||||
# within the larger text
|
||||
#
|
||||
function replace(dummy, i,count,other,pat,re) {
|
||||
other = "[^-_A-Za-z0-9]+"
|
||||
for (i=1; i<=NF; ++i) {
|
||||
count = 0
|
||||
for (pat in pattern) {
|
||||
# re = "^" pat "$|^" other pat "$|^" pat other "$|^" other pat other "$"
|
||||
re = "^" pat "$|" other pat "$|^" pat other "|" other pat other
|
||||
if ($i ~ re) {
|
||||
count = sub(pat, pattern[pat], $i)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
# one-time, in place bold, italic, and cross-ref
|
||||
if (section != "SYNOPSIS" && count == 0) {
|
||||
# match foo(1) or foo(1pkg) not foo(1 2) or foo(1, 2) or foo(int)
|
||||
# but not in a see also section
|
||||
if ((section != "SEE") && # aka SEE ALSO
|
||||
($i ~ /[_A-Za-z0-9]+\([0-9][A-Za-z0-9]*\)/)) {
|
||||
a = index($i, "(")
|
||||
$i = bold(substr($i, 1, a-1)) substr($i, a)
|
||||
}
|
||||
else if (match($i, "[A-Za-z]_.*_[A-Za-z]"))
|
||||
;
|
||||
else if (match($i, "_.*_"))
|
||||
$i = before($i) italic(substr($i, RSTART+1, RLENGTH-2)) after($i)
|
||||
else if (match($i, "\\*.*\\*"))
|
||||
$i = before($i) bold(substr($i, RSTART+1, RLENGTH-2)) after($i)
|
||||
}
|
||||
}
|
||||
if ($NF == "~") {
|
||||
$NF = ""
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function paragraph() {
|
||||
print ".PP"
|
||||
list = lw # reset list on a paragraph
|
||||
tplen = ""
|
||||
}
|
||||
|
||||
function restore() {
|
||||
if (blank == NR-1) # restore the blank line to the display
|
||||
print ""
|
||||
n = lw - block
|
||||
if (n==0) n=1 # avoid eating leading dots in displays
|
||||
printf "%*s", n, " " # restore leading spaces in displays
|
||||
}
|
||||
|
||||
function block_start() {
|
||||
block = lw
|
||||
blank = 0
|
||||
paragraph()
|
||||
print ".RS " block
|
||||
print ".eo\n.nf\n.fam C"
|
||||
}
|
||||
|
||||
function block_end() {
|
||||
if (block > 0) {
|
||||
block = 0
|
||||
print ".ec\n.fi\n.fam T"
|
||||
print ".RE"
|
||||
}
|
||||
}
|
||||
|
||||
function reset_indent() {
|
||||
rs[0] = indent = 0
|
||||
}
|
||||
|
||||
# pass through nroff formatting commands
|
||||
/^\..+/ {
|
||||
print $0
|
||||
next
|
||||
}
|
||||
|
||||
# remember leading whitespace, strip blank lines
|
||||
{
|
||||
lw = match($0, /[^ ]/)
|
||||
if (lw == 0) {
|
||||
blank = NR
|
||||
next
|
||||
} else {
|
||||
$0 = substr($0, lw)
|
||||
# sub(/^ */, "")
|
||||
# sub(/ *$/, "") # prevents TP match if desc on next line
|
||||
}
|
||||
--lw
|
||||
if (rs[indent] == 0)
|
||||
rs[indent] = lw
|
||||
}
|
||||
|
||||
# new section (must end with a cap letter to avoid tag list match)
|
||||
/^[A-Z ]+[A-Z]$/ {
|
||||
if (section == "SYNOPSIS")
|
||||
print ".fi"
|
||||
block_end()
|
||||
reset_indent()
|
||||
list = 0
|
||||
tplen = ""
|
||||
section = $1
|
||||
print (lw>0) ? ".SS" : ".SH", $0
|
||||
if (section == "SYNOPSIS") {
|
||||
print ".nf"
|
||||
print ".fam C"
|
||||
}
|
||||
if (section == "SEE")
|
||||
print ".nf"
|
||||
next
|
||||
}
|
||||
|
||||
section == "NAME" && name == "" && $1 != "" {
|
||||
name = $1
|
||||
# add(name, bold(name))
|
||||
sub(/ - /, " \\- ")
|
||||
}
|
||||
|
||||
section == "SYNOPSIS" {
|
||||
if (format == "") {
|
||||
block = lw # this is a pseudo-block; see new section
|
||||
if ($0 ~ /\(/) format = "code"
|
||||
else if ($1 == "struct" || $2 == "struct") format = "struct"
|
||||
else if ($1 == "union" || $2 == "union") format = "struct"
|
||||
else if ($1 == "class" || $2 == "class") format = "class"
|
||||
else if ($1 !~ /#|import|typedef|enum/) format = "cmd"
|
||||
}
|
||||
if (format == "cmd") {
|
||||
# cmd [-a N] [-b N] [-m ab] [-p S]
|
||||
# [-r] [-qst] @interface ip:port [filename ...]
|
||||
i = 1
|
||||
if ($1 !~ /^\[/) {
|
||||
++i
|
||||
a = $1
|
||||
gsub("-", "\\-", a)
|
||||
add(a, bold(a))
|
||||
}
|
||||
for (; i<=NF; ++i) {
|
||||
a = $i
|
||||
gsub(/[\[\]]/, "", a) # remove all brackets
|
||||
if (a == "-") ;
|
||||
else if (a ~ /^-./) add(a, bold("\\" a))
|
||||
else if (a !~ /\.\.\./) add(a, italic(a))
|
||||
}
|
||||
} else if (format == "code") {
|
||||
# void foo()
|
||||
# void foo(int);
|
||||
# void foo(int a);
|
||||
# void foo(int a, char *b);
|
||||
# void *foo(char **b, int *a, ...)
|
||||
# void (*signal(int sig, void (*func)(int)))(int);
|
||||
# ssize_t readv(int d, const struct iovec *iov, int iovcnt);
|
||||
if ($1 != "typedef")
|
||||
for (i = 1; i<=NF; ++i) {
|
||||
a = $i
|
||||
gsub("[\\*]", "", a) # dont want to mess with pointers
|
||||
if (match(a, "[A-Za-z0-9_]+\\(")) {
|
||||
a = substr(a, RSTART, RLENGTH-1)
|
||||
add(a, bold(a))
|
||||
} else if (match(a, "[A-Za-z0-9_]+[,\\)]")) {
|
||||
a = substr(a, RSTART, RLENGTH-1)
|
||||
add(a, italic(a))
|
||||
}
|
||||
}
|
||||
} else if (format == "struct") {
|
||||
if (match($0, "[A-Za-z0-9_]+[ ]*{")) {
|
||||
a = substr($0, RSTART)
|
||||
sub("[ ]*{", "", a);
|
||||
add(a, bold(a));
|
||||
}
|
||||
format = "code"
|
||||
} else if (format == "class") {
|
||||
# public class Foo extends Bar implements Snox, Blah, Nuts {
|
||||
# so remove from extends or implements, play as if C++
|
||||
a = $0
|
||||
if (match(a, "extends|implements"))
|
||||
a = substr(a, 1, RSTART-1)
|
||||
if (match(a, "[A-Za-z0-9_]+[ ]*{")) {
|
||||
a = substr(a, RSTART)
|
||||
sub("[ ]*{", "", a);
|
||||
add(a, bold(a));
|
||||
format = "code"
|
||||
}
|
||||
}
|
||||
replace()
|
||||
}
|
||||
|
||||
block == 0 {
|
||||
if (lw > rs[indent]+2) {
|
||||
if (blank == NR-1)
|
||||
block_start()
|
||||
else if (lw > list) {
|
||||
# XXX list?
|
||||
rs[++indent] = lw;
|
||||
print ".RS"
|
||||
}
|
||||
} else while (lw < rs[indent]) {
|
||||
print ".RE"
|
||||
--indent
|
||||
list = 0
|
||||
tplen = ""
|
||||
}
|
||||
}
|
||||
|
||||
block > 0 {
|
||||
if (lw < block)
|
||||
block_end()
|
||||
else {
|
||||
restore()
|
||||
print $0
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
# if replace before matching, awk collapses multiple spaces into one....
|
||||
if (match($0, /[^ ] +/)) { # tag description
|
||||
list = lw + RSTART + RLENGTH
|
||||
tag = substr($0, 1, RSTART)
|
||||
def = substr($0, RSTART + RLENGTH)
|
||||
$0 = tag
|
||||
if (tplen == "" && def !~ /[.~]/)
|
||||
tplen = list-3
|
||||
replace()
|
||||
print ".TP " tplen "\n" bold($0)
|
||||
$0 = def
|
||||
|
||||
} else if (match($0, /^[o\.\*+] +/)) { # bullet list
|
||||
list = lw + RSTART + RLENGTH
|
||||
print ".IP \\(bu 3"
|
||||
$0 = substr($0, RLENGTH+1)
|
||||
|
||||
} else if (match($0, /^- +/)) { # dash list
|
||||
list = lw + RSTART + RLENGTH
|
||||
print ".IP \\(em 3"
|
||||
$0 = substr($0, RLENGTH+1)
|
||||
|
||||
} else if (match($0, /^[0-9]+\. +/)) { # number list
|
||||
list = lw + RSTART + RLENGTH
|
||||
print ".IP " $1 " 3"
|
||||
$0 = substr($0, RLENGTH+1)
|
||||
|
||||
} else {
|
||||
# the lists above supply their own vertical separation
|
||||
if (blank == NR-1)
|
||||
paragraph()
|
||||
}
|
||||
|
||||
# if there is anything left to print...
|
||||
if (length $0 > 0) {
|
||||
br = replace();
|
||||
if (length $0 > 0)
|
||||
print $0
|
||||
if (br) print ".br"
|
||||
}
|
||||
}
|
||||
' | eval $post
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
awk '/^[^$]/{l=length;m=m>l?m:l;a[l]=a[l]$1}
|
||||
END{printf"\"";for(i=1;i<=m;++i)printf" "a[i];print"\""}' $*
|
||||
@@ -0,0 +1,63 @@
|
||||
--- iocccsize.orig.c 2018-01-13 10:01:16.000000000 -0600
|
||||
+++ iocccsize.c 2018-01-14 16:23:57.000000000 -0600
|
||||
@@ -181,6 +181,7 @@
|
||||
{ STRLEN("#ident"), "#ident" },
|
||||
{ STRLEN("#if"), "#if" },
|
||||
{ STRLEN("#ifdef"), "#ifdef" },
|
||||
+ { STRLEN("#ifndef"), "#ifndef" }, /* XXX bugfix */
|
||||
{ STRLEN("#include"), "#include" },
|
||||
{ STRLEN("#line"), "#line" },
|
||||
{ STRLEN("#pragma"), "#pragma" },
|
||||
@@ -225,7 +226,7 @@
|
||||
{ STRLEN("float"), "float" },
|
||||
{ STRLEN("for"), "for" },
|
||||
{ STRLEN("goto"), "goto" },
|
||||
- { STRLEN("I"), "I" },
|
||||
+ /* { STRLEN("I"), "I" }, */ /* XXX seriously? */
|
||||
{ STRLEN("if"), "if" },
|
||||
{ STRLEN("inline"), "inline" },
|
||||
{ STRLEN("int"), "int" },
|
||||
@@ -365,6 +366,7 @@
|
||||
int lcount, wcount, bcount;
|
||||
int is_comment, is_word, dquote, escape;
|
||||
int count, keywords, saved, kw_saved;
|
||||
+ int squote;
|
||||
|
||||
/* Start of buffer sentinel. */
|
||||
buf[0] = ' ';
|
||||
@@ -374,6 +376,7 @@
|
||||
keywords = kw_saved = 0;
|
||||
lcount = wcount = bcount = 0;
|
||||
is_comment = is_word = dquote = escape = 0;
|
||||
+ squote = 0;
|
||||
|
||||
/*
|
||||
* "no matter how well you may think you understand this code,
|
||||
@@ -422,6 +425,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ else if (squote) {
|
||||
+ if (escape)
|
||||
+ escape = 0;
|
||||
+ else if (*p == '\\')
|
||||
+ escape = 1;
|
||||
+ else if (*p == '\'')
|
||||
+ squote = 0;
|
||||
+ }
|
||||
+
|
||||
/* Not quote string. */
|
||||
else {
|
||||
/* In C comment block? */
|
||||
@@ -499,6 +511,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ /* Open single quote string? */
|
||||
+ else if (*p == '\'') {
|
||||
+ squote = 1;
|
||||
+ }
|
||||
+
|
||||
/* Open quoted string? */
|
||||
else if (*p == '"') {
|
||||
dquote = 1;
|
||||
@@ -0,0 +1,67 @@
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
#include<unistd.h>
|
||||
typedef unsigned char*_;
|
||||
_ s,i,z,e,O,F=
|
||||
#include W
|
||||
|
||||
"; ; } { { { { { } { { } ; } { ; ; { } ; { { } ; } ; { { { { } { { } \
|
||||
; } { { { { { } { ; } { } { { } { { } { { { ; ; { { } { { } { ; } { \
|
||||
} { { { { { } ; } { } { { } { { { \
|
||||
} { ; } { } { { { { { } ; } { } { \
|
||||
{ { } { { } ; ; { ; ; { { } { { } \
|
||||
{ ; } { } { { { } { } { { { ; { { \
|
||||
{ } { { } { { { { } { { { { { } ; \
|
||||
; { ; ; { { { { { } { ; } { } { { { { { } ; ; { ; ; { { ; { ; { { { \
|
||||
} { } { { { ; { ; { { { ; ; { } { { { } { ; } { } { { { { { } { { { \
|
||||
; ; { { } { ; { { { } { } { { { ; { ; { { { ; ; { { { { { } { { { { \
|
||||
\
|
||||
} { { { { { } ; } { } { { { { { { } { { { { } ; } ; { { { } \
|
||||
{ { { { { } { { } { { { } { { ; { { } { ; { { { { ; { { ; { \
|
||||
{ { { { } } { { ; { } { { { ; ; { \
|
||||
{ ; { ; { } { } ; { } { } ; { ; } { { { } { ; { } \
|
||||
{ ; { { { } { ; } } { ; ; { { } { ; ; } { ; ; ; { \
|
||||
} } { { } } { ; { { } } { } } } { \
|
||||
; } { } } { ; } } { ; ; { } } } { \
|
||||
{ { { { { } } } ; ; { } { ; ; } { ; } ; } { } { } } } { } { } { } \
|
||||
{ ; } { ; ; { } { } } { } { ; } } } { } } } } } } ; } } } } ; { } \
|
||||
{ } ; } } } } ; } } } ; { { } } } ; ; } } { { } } } ; { { " ; ; ; ;
|
||||
unsigned v,o,l,A,T,I,L,E,c,H,a,C,K,R,Z=256; char n,M[2053]; int*q; int h(_ F,int l) { for(;
|
||||
l--&&*F; putchar(*F++)); return 0; } int r(int I,int O){ return!fprintf(O&2?stderr:stdout
|
||||
,"%d%c",I,O&1?l:32); } int m(char O){ for(a=0; a-O&&z<e; L+=((a=*z++)==92?*z==a||*
|
||||
z==39||*z==
|
||||
34||*z==l?*
|
||||
z++:a:a)==l); return a; } int d(_ e,int l){ _ F=M+Z+q[l]; _ O=M+Z+q[n=-~l]; for(
|
||||
; l<*q&&n-l&&F<O; F+=l)for(n=0; n<l&&e[n]==F[n]; ++n); return l&&n==l?T +=--l,++K:0; }
|
||||
int j(){ for(c=0,o=Z,O=z; z<=e&&M[c=*z++]&16; ++C,L+=c==l); for(s=z-1; z>=e; )return C-=
|
||||
!c,c; for(a=M[c]; v&32&&c==35; )return m(l),o+2; for(; a&8; ){ for( ++o; M[c] &12; c=*z++);
|
||||
return d(s,--z-s)?o:++o; } for(; a&4; ){ for(c=c==48&&(*z|32)==120? *++z:c; M[c]&6; c=*z++)
|
||||
; return o+(c==46?strtod(s,(char**)&z),5:!--z^4); } for(; c==47&& (*z==c||*z==42); ){ for(;
|
||||
c==*z++; )return m(l),o; for(m(42); z<e &&*z++-47; --z, m(42)); return o; }
|
||||
return c==39||c==34?m(c),c:*z++==61&& a&64?c|128: a&128&&z[~0]==c?*z==61
|
||||
&&a&32&&c-45?++z,c|o|128:c|o:a&/* 2052 */32&&z[~0]==62?o+6:*--z?c:32; }
|
||||
void g(int l,int O){ for(c=0; c<l; ++c)(a=M[s[c]]&16)?--I:M[s
|
||||
[c]]&1&&M[s[c+1]]&16?--I:0, O=O ?!a:! a?++A:O; } void f(int T
|
||||
,int F){ int n; _ o=O; for (c= v&2; H==Z; L-=!(v&2 ||o[~0]
|
||||
-l),C-=o-O)for(H=0; o<s&& *o++ -l; ); O-=O==s &&O[~0]==l;
|
||||
T-=T==34||T==39? g(F,1), 0:Z ; a=O==s&&s>i; for(A-=T&&
|
||||
a&&M[O[-1]]-16,!(v&8)&& (T| c)?h(O,s-O), h(s,F):T; !T
|
||||
++; H=Z+s[1]-42){ for( n=a ; c; c^=c)for(I+=F,g(F,n
|
||||
),n=c=2; c<F&&s[1]==47 ; ++ c)a? (a=M[s[c]]&8)||d(s+
|
||||
n,c-n)&&(I-=c-++n):M[s [c] ]&8?a= n=c:a; for(c=v&2;
|
||||
!c;A+=!a&&M[s[F]]-16&& s[1]-47)for(c=0; c<F; ++
|
||||
c)L-=s[c]==l; for(o=s; o>O &&*--o-l; ); C-=*o==
|
||||
l||o==i||O[~0]==l?s-o- (*o==l):0; } } int b(int
|
||||
l){ for(a=0; l--; a+=c- 59?c==125:-~1) for(a+=a+
|
||||
a; (c=* F++)== 32||c==9; ); return a; } int main (
|
||||
int S,char**H){ for(s=e= M+Z,q= (int*)s,e+=Z/2; *F-
|
||||
59; *e++=*F++)for(; *F==32 ; ++F,q[++*q]=e-s); for(q[++
|
||||
*q]=e-s,z=M,v=b(3); v--; )for (z+= b(3),o= b(3),c=b(5); o--;
|
||||
*z++=c); for(*++e=l=10,O=s=M+4* Z,v=b (3); v--; O[o]=b(5))o=b(5)
|
||||
; for(O+=32,v=0; ~(a=getopt(S,H,O) ); )for(v|=s[a&31]; !(a ^~-Z >>2); )
|
||||
return h(U,~0); for(O+=32,a=0,o=2; c= getchar(),o<a|| (i=realloc(i ,a+=Z)),~c;
|
||||
i[o]=c,c==l+3?v&1&&++E: c==l&&i[~-o]==92 ?--o,++L,v&1?E+=2,++A:A:++o)O ??(c??)&&!(i??(??--o
|
||||
??)&i??(o-2??)??'63)?o-=2,E+=2,c=O??(c??):0; for(F=e,e=i+o,z=i+=2,*e=0; o=j(); v&1?f(o,a):v
|
||||
&8?v:v&2||o-Z?~v&32&&r(o,0),h(s,a),z[-1]-l&&h(F,1):0)a=z-s,o-Z?C+=a,++A,I+=v&4&&o==-~Z?1:M[
|
||||
o]==1&&(M[*z]==16||!*z)?0:a:a; a=e-O; a&&v&1?f(0,a),v&8||h(O,a):a; v&2?C=e-i:0; T*=v>>2&1;R
|
||||
=C-I-T; C+=E; return v&32?0:v&16?r(I,1):r(L,2)+r(A,2)+r(C,2)+r(I,2)+r(R,2)+r(K,2)+r(T,3); }
|
||||
@@ -0,0 +1,16 @@
|
||||
test01.c: 11 32 198 106 58 10 34
|
||||
test02.c: 1 5 37 29 8 0 0
|
||||
test03.c: 3 26 147 98 37 3 12
|
||||
test04.c: 6 12 88 47 16 5 25
|
||||
test05.c: 4 28 154 100 40 4 14
|
||||
test06.c: 7 10 74 41 24 2 9
|
||||
test07.c: 15 42 258 147 70 11 37
|
||||
test08.c: 11 32 198 106 58 10 34
|
||||
test09.c: 11 32 198 106 58 10 34
|
||||
test10.c: 7 15 163 94 17 6 18
|
||||
test11.c: 82 84 325 126 16 11 41
|
||||
test12.c: 6 10 73 41 23 2 9
|
||||
test13.c: 0 1 5 2 1 1 2
|
||||
test14.c: 4 8 62 41 12 2 9
|
||||
fixed_iocccsize.c: 408 1278 8781 5833 2464 156 484
|
||||
ioc_without_quote: 354 1383 7817 4488 2692 221 625
|
||||
@@ -0,0 +1,16 @@
|
||||
test01.c: 11 32 198 106 58 10 34
|
||||
test02.c: 14 39 241 173 68 0 0
|
||||
test03.c: 3 26 147 98 37 3 12
|
||||
test04.c: 6 12 88 47 16 5 25
|
||||
test05.c: 4 31 167 107 46 4 14
|
||||
test06.c: 7 14 95 53 33 2 9
|
||||
test07.c: 16 46 291 173 77 11 37
|
||||
test08.c: 12 38 224 120 70 10 34
|
||||
test09.c: 13 38 232 130 68 10 34
|
||||
test10.c: 7 15 163 94 17 6 18
|
||||
test11.c: 82 84 325 126 16 11 41
|
||||
test12.c: 7 10 89 51 29 2 9
|
||||
test13.c: 0 1 5 2 1 1 2
|
||||
test14.c: 4 8 73 52 12 2 9
|
||||
fixed_iocccsize.c: 637 2723 16904 12007 4369 168 528
|
||||
ioc_without_quote: 396 1740 9917 6135 3097 247 673
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
: ${prog:=./prog}
|
||||
: ${opt:=crs}
|
||||
|
||||
exec 2>&1
|
||||
for name in test*c fixed_iocccsize.c ioc_without_quote; do
|
||||
/bin/echo -n "$name: "; $prog -$opt < $name
|
||||
done
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
ioc=$1
|
||||
: ${iocccsize:=iocccsize}
|
||||
: ${ioc:=prog}
|
||||
|
||||
opt=-crs
|
||||
kopt=-kcrs
|
||||
|
||||
for name in $*; do
|
||||
|
||||
if test $name != $ioc; then
|
||||
|
||||
a=$($ioc $opt < $name 2>&1)
|
||||
b=$($iocccsize $opt < $name 2>&1)
|
||||
if test "$a" != "$b"; then
|
||||
echo $a , $b , $name
|
||||
fi
|
||||
|
||||
a=$($ioc $kopt < $name 2>&1)
|
||||
b=$($iocccsize $kopt < $name 2>&1)
|
||||
if test "$a" != "$b"; then
|
||||
echo $a , $b , $name " keep"
|
||||
fi
|
||||
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
# filename is 17
|
||||
# 1=cl 2=cw 3=cc 4=ci 5=ts 6=kw 7=kws
|
||||
# 9 10 11 12 13 14 15
|
||||
awk '{
|
||||
printf "cl %d cw %d cc %d ci %d ts %d kw %d ks %d %s %s\n",
|
||||
$9-$1, $10-$2, $11-$3, $12-$4, $13-$5, $14-$6, $15-$7, $17, $18
|
||||
}'
|
||||
@@ -0,0 +1,81 @@
|
||||
zrzbel znc: Z (bssfrg, yratgu)
|
||||
0,256 gbxravmngvba syntf
|
||||
256,128 xrljbeq yratgu bssfrg znc
|
||||
384,640 xrljbeq fgevatf (453 punef)
|
||||
1024,32 bcgvba ovg znc
|
||||
1056,32 trgbcg fgevat
|
||||
1088,256 gevtencu znc
|
||||
|
||||
serrpbqr: gevanel, nycunorg 0 { 1 } 2 ;
|
||||
hfvat guerr gevgf, gur nycunorg N-M jvgu n fcnpr/ahyy pna or rapbqrq:
|
||||
0 fcnpr/ahyy, 1 N, 2 O, 3 P, ...
|
||||
nygubhtu guvf rapbqvat vf abg gur bayl bar
|
||||
pbqr vf serr-sbez, fcnpr ntabfgvp (bar be zber fcnprf ner erdhverq sbe vg gb or 'serr')
|
||||
qrpbqvat vf pbagrkg qrcraqrag, glcvpnyyl hfvat 3 be zber gevgf
|
||||
gur cebtenz pbzzragf hfr o(3) naq gur nycunorg rapbqvat nobir,
|
||||
ohg gurl ner abg pyrnayl fcyvg ba 3-gevg obhaqnevrf(!)
|
||||
|
||||
v N pj
|
||||
O
|
||||
v P pp
|
||||
Q
|
||||
v R pk
|
||||
c S[] xrljbeqf, gbxra vavg, bcgvbaf vavg, bcgvba fgevat, gevtencu vavg
|
||||
T
|
||||
v U pbzzrag uvfgbel
|
||||
v V pv
|
||||
W
|
||||
v X px
|
||||
v Y py
|
||||
p Z[2053] zrzbel: gbxra syntf, xrljbeq gnoyr, bcgvba gnoyr, gevtencu gnoyr
|
||||
A
|
||||
c B fgneg bs juvgrfcnpr
|
||||
C
|
||||
D
|
||||
v E gf
|
||||
F
|
||||
v G gx
|
||||
q H qrsvarq hfntr zrffntr (-QH="\"uryc grkg\"")
|
||||
I
|
||||
q J qrsvarq erfreirq jbeq fgevat (-QJ=\"xrljbeqf\")
|
||||
K
|
||||
L
|
||||
v M 256
|
||||
v n tybony
|
||||
v o(v) qrpbqr serrpbqr hfvat v ovgf
|
||||
v p tybony
|
||||
v q(f,v) vf n xrljbeq?
|
||||
c r raq bs ohssre
|
||||
i s(v,v) pbzcng
|
||||
i t(v,v) pbzcng_jbeqf
|
||||
v u(f,v) cevag v punef bs f, be hagvy ahyy
|
||||
c v ohssre
|
||||
v w() yrkre
|
||||
x
|
||||
v y 10 / arjyvar
|
||||
v z(p) jnag(p), unaqyrf rfpncrf
|
||||
a tybony
|
||||
v b tybony
|
||||
c
|
||||
c d xrljbeq vaqrk gnoyr
|
||||
v e(v,v) cevag net1 nf vag gb fgqbhg/ree ol net2, genvyrq ol fcnpr/arjyvar
|
||||
f fgneg bs gbxra
|
||||
g
|
||||
h
|
||||
v i pzq yvar syntf
|
||||
j
|
||||
k
|
||||
l
|
||||
c m pheerag cbvagre
|
||||
|
||||
shapgvbaf
|
||||
|
||||
v u(f,v) cevag v punef bs f, be hagvy ahyy
|
||||
v e(v,v) cevag net1 nf vag gb fgqbhg/ree ol net2, genvyrq ol fcnpr/arjyvar
|
||||
v z(p) jnag(p), unaqyrf rfpncrf
|
||||
v q(f,v) vf n xrljbeq?
|
||||
v w() yrkre
|
||||
i t(v,v) pbzcng_jbeqf
|
||||
i s(v,v) pbzcng
|
||||
v o(v) qrpbqr serrpbqr hfvat v ovgf
|
||||
v znva(p,cc) vavg, bcgvbaf, ernq, pbhag, cevag
|
||||
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* tac - tokenize and count C-like languages
|
||||
*
|
||||
* tac is offered as a replacement for iocccsize.
|
||||
* Version 2018.04.18
|
||||
* Public Domain 2015 by Dave Burton.
|
||||
* Please keep this attribution with the source code.
|
||||
*
|
||||
* cc -O3 -DW=\"keywords\" -Wall -pedantic -o tac tac.c
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* The keywords recognized by tac should be defined in an external file,
|
||||
* filtered by the following script into a new file, and #included
|
||||
* by defining the preprocessor variable W as this new file.
|
||||
*
|
||||
* The input to the script is a list of keywords, one word per line,
|
||||
* in any order. The output is a single string suitable for tac.
|
||||
* --------------------------------------------------
|
||||
|
||||
#!/bin/sh
|
||||
awk '/^[^$]/{l=length;m=m>l?m:l;a[l]=a[l]$1}
|
||||
END{printf"\"";for(i=1;i<=m;++i)printf" "a[i];print"\""}' $*
|
||||
|
||||
* --------------------------------------------------
|
||||
* The pre-defined string below is the IOCCC reserved word list for 2016.
|
||||
* It was created by the above script, then inserted here in case the
|
||||
* source keywords file is not available.
|
||||
*
|
||||
* The format of the keyword string is precise; addition or removal of
|
||||
* a single character will cause improper operation.
|
||||
*
|
||||
* DO NOT EDIT the string kw; instead:
|
||||
*
|
||||
* mkkeywords < my.list > my.keywords
|
||||
* cc -DW=\"my.keywords\" -o tac tac.c
|
||||
*/
|
||||
|
||||
char *keywords = ""
|
||||
#ifndef W
|
||||
|
||||
" ifdoor intfor#ifandnotxor voidcharelsegotocaselongenumautobooltr\
|
||||
ue whilebreakconstfloatshortunion#else#elif#line_Boolfalsecomplbito\
|
||||
ror_eq returnsizeofstructdoubleswitchstaticexternsigned#ifdef#endif\
|
||||
inline#errorbitandand_eqnot_eqxor_eq typedefdefault#ifndef#pragma_A\
|
||||
tomic_Pragmaalignasalignof #includeunsignedcontinuevolatileregister\
|
||||
restrict#warning_Complex_Generic_Alignas_Alignofnoreturn _Noreturn \
|
||||
_Imaginary thread_local _Thread_localstatic_assert _Static_assert"
|
||||
|
||||
#else
|
||||
# include W
|
||||
# undef W
|
||||
#endif
|
||||
;
|
||||
|
||||
int cline, cword, cbyte, cioccc, ckey, cextra; // counts
|
||||
int tsaved, tkey; // totals
|
||||
int fkeep, fsuppress, freserved, fioccc, fcompat, ftoken; // flags
|
||||
|
||||
unsigned char *buf, *cur, *end, *white, *start;
|
||||
|
||||
enum {
|
||||
CPP = 256
|
||||
, CMT = 257
|
||||
, KEYW = 258
|
||||
, DIVEQ = 259
|
||||
, MULEQ = 260
|
||||
, MODEQ = 261
|
||||
, ADDEQ = 262
|
||||
, SUBEQ = 263
|
||||
, XOREQ = 264
|
||||
, OREQ = 265
|
||||
, ANDEQ = 266
|
||||
, RSHEQ = 267
|
||||
, LSHEQ = 268
|
||||
, NOTEQ = 269
|
||||
, EQ = 270
|
||||
, GE = 271
|
||||
, LE = 272
|
||||
, OR2 = 273
|
||||
, AND2 = 274
|
||||
, PP = 275
|
||||
, MM = 276
|
||||
, PTR = 277
|
||||
, LSH = 278
|
||||
, RSH = 279
|
||||
, INT = 300
|
||||
, FLOAT = 301
|
||||
, ID = 302
|
||||
, DOT3 = 303
|
||||
};
|
||||
|
||||
enum {
|
||||
O=0x01 // octal
|
||||
, H=0x02 // hex
|
||||
, D=0x04 // decimal
|
||||
, I=0x08 // identifier
|
||||
, W=0x10 // whitespace
|
||||
, B=0x20 // brace, semi
|
||||
, OHD = O|H|D
|
||||
, HD = H|D
|
||||
, HI = H|I
|
||||
};
|
||||
|
||||
char tab[256] = {
|
||||
W, 00, 00, 00, 00, 00, 00, 00, 00, W, W, W, W, W, 00, 00,
|
||||
00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
|
||||
W, 00, 00, I, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
|
||||
OHD,OHD,OHD,OHD,OHD,OHD,OHD,OHD, HD, HD, 00, B, 00, 00, 00, 00,
|
||||
|
||||
00, HI, HI, HI, HI, HI, HI, I, I, I, I, I, I, I, I, I,
|
||||
I, I, I, I, I, I, I, I, I, I, I, 00, 00, 00, 00, I,
|
||||
00, HI, HI, HI, HI, HI, HI, I, I, I, I, I, I, I, I, I,
|
||||
I, I, I, I, I, I, I, I, I, I, I, B, 00, B, 00, 00,
|
||||
};
|
||||
|
||||
char tri[256] = {
|
||||
['='] = '#',
|
||||
['('] = '[',
|
||||
['/'] = '\\',
|
||||
[')'] = ']',
|
||||
['\'']= '^',
|
||||
['<'] = '{',
|
||||
['!'] = '|',
|
||||
['>'] = '}',
|
||||
['-'] = '~',
|
||||
};
|
||||
|
||||
#define is(x, c) (tab[c]&(x))
|
||||
|
||||
/*
|
||||
* discard backslash-newline as-if not present, even if trigraph backslash
|
||||
* iocccsize counts tri as xbcount+=2,
|
||||
* and bs-nl as xb+=2, xw++, xl++ to match wc(1)
|
||||
*/
|
||||
|
||||
void readsource() {
|
||||
int c, d, o=2, len=4096;
|
||||
buf = malloc(len);
|
||||
while ((c = getchar()) != EOF) {
|
||||
if (c == '\r') {
|
||||
if (fcompat) ++cextra;
|
||||
continue;
|
||||
}
|
||||
if (buf[o-1] == '?' && buf[o-2] == '?' && tri[c]) {
|
||||
c = tri[c];
|
||||
o -= 2;
|
||||
if (fcompat) cextra += 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* digraph support:
|
||||
* translating digraphs after trigraphs is,
|
||||
* for the purposes of this code, essentially during tokenization.
|
||||
*/
|
||||
d = c;
|
||||
switch (buf[o-1]) {
|
||||
case '<': d = (c == ':' ? '[' : c == '%' ? '{' : c); break;
|
||||
case '%': d = (c == '>' ? '}' : c == ':' ? '#' : c); break;
|
||||
case ':': d = (c == '>' ? ']' : c); break;
|
||||
}
|
||||
if (c != d) {
|
||||
c = d;
|
||||
o--;
|
||||
if (fcompat) ++cextra;
|
||||
}
|
||||
|
||||
if (c == '\n' && buf[o-1] == '\\') {
|
||||
--o;
|
||||
cline++;
|
||||
if (fcompat) {
|
||||
++cword;
|
||||
cextra += 2;
|
||||
}
|
||||
} else
|
||||
buf[o++] = c;
|
||||
if (o == len)
|
||||
buf = realloc(buf, len+=4096);
|
||||
}
|
||||
buf[o] = 0;
|
||||
end = buf + o;
|
||||
cur = buf += 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* keyword recognition: kw is sorted by length, space separated between lengths
|
||||
* e.g. " ifdo forint#if charvoidelsegoto..."
|
||||
* build a table of offsets for each length
|
||||
* e.g. [0]0, [1]1, [2]2, [3]7, [4]17, ...
|
||||
* this allows scans for only words of same length instead of entire list
|
||||
* trade-off is a small, but constant start-up cost to get a configurable
|
||||
* word list (backported from obfuscated code: it was too nice to give up)
|
||||
*/
|
||||
|
||||
int nkwoff, kwoff[32]; // allows keywords up to 32 characters in length
|
||||
|
||||
void kwinit() {
|
||||
char *k = keywords;
|
||||
for (nkwoff = 0; *k; ++k)
|
||||
while (*k == ' ')
|
||||
kwoff[++nkwoff] = ++k - keywords;
|
||||
kwoff[++nkwoff] = ++k - keywords;
|
||||
}
|
||||
|
||||
int iskw(unsigned char *s, int len) {
|
||||
int found = 0;
|
||||
if (len < nkwoff) {
|
||||
char *off = keywords + kwoff[len];
|
||||
char *end = keywords + kwoff[len + 1] - 1;
|
||||
for (; !found && off < end; off += len)
|
||||
found = *s == *off && !strncmp((char*)s, off, len);
|
||||
if (found) {
|
||||
++ckey;
|
||||
tkey += len - 1;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
int want(char m) {
|
||||
while (cur < end) {
|
||||
if (*cur == '\\') {
|
||||
/*
|
||||
* need to only ignore: ntbrfv\'"a?, octal, x, hex
|
||||
* 0111 = two chars, \091 = three, \xabc = one char(!)
|
||||
* for our purposes, only need: ' " and \ newline,
|
||||
* but readsource eats \ newline, so only quotes here
|
||||
*/
|
||||
if (cur[1] == '\\' || cur[1] == '\'' || cur[1] == '"') {
|
||||
cur += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (*cur == '\n') ++cline;
|
||||
if (*cur++ == m) return m;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int follow(char m, int y, int n) {
|
||||
if (*cur == m) return ++cur,y;
|
||||
return n;
|
||||
}
|
||||
|
||||
int follow2(char m, int y, int n, int y2) {
|
||||
if (*cur == m) return ++cur,y;
|
||||
if (*cur == n) return ++cur,y2;
|
||||
return n;
|
||||
}
|
||||
|
||||
int skipwhite() {
|
||||
int c;
|
||||
for (c=*cur++; cur<=end && is(W, c); c=*cur++) {
|
||||
++cbyte;
|
||||
if (c == '\n') ++cline;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPP tokens may extend beyond a single line of text and include comments
|
||||
* To do this correctly, need to scan and remove comments during readsource
|
||||
* and present comments as whitespace. Note that "# include" is a valid CPP
|
||||
* token, and does not match "#include".
|
||||
*
|
||||
* Because the global pointers cur, start and white are well-known, and
|
||||
* because comments are not consumed during readsource, it is more difficult
|
||||
* to parse CPP stuff. Also note that for backwards compat, we need to also
|
||||
* count words correctly, with and without -k.
|
||||
*/
|
||||
|
||||
int lex() {
|
||||
int c, t;
|
||||
|
||||
white = cur;
|
||||
c = skipwhite();
|
||||
start = cur-1;
|
||||
|
||||
if (cur >= end)
|
||||
return c;
|
||||
|
||||
switch (c) {
|
||||
case '\'': want(c); return c;
|
||||
case '"': want(c); return c;
|
||||
case '/':
|
||||
t = follow2('=', DIVEQ, c, CMT);
|
||||
if (t == CMT)
|
||||
want('\n');
|
||||
else if (t == c && (t = follow('*', CMT, c)) == CMT)
|
||||
do want('*'); while (cur<end && follow('/', 0, 1));
|
||||
return t;
|
||||
case '*': return follow ('=', MULEQ, c);
|
||||
case '%': return follow ('=', MODEQ, c);
|
||||
case '=': return follow ('=', EQ, c);
|
||||
case '!': return follow ('=', NOTEQ, c);
|
||||
case '^': return follow ('=', XOREQ, c);
|
||||
case '|': return follow2('=', OREQ, c, OR2);
|
||||
case '&': return follow2('=', ANDEQ, c, AND2);
|
||||
case '+': return follow2('=', ADDEQ, c, PP);
|
||||
case '-': t = follow2('=', SUBEQ, c, MM);
|
||||
return t==c? follow ('>', PTR, t) : t;
|
||||
case '>': t = follow2('=', GE, c, RSH);
|
||||
return t==RSH? follow ('=', RSHEQ, t) : t;
|
||||
case '<': t = follow2('=', LE, c, LSH);
|
||||
return t==LSH? follow ('=', LSHEQ, t):t;
|
||||
|
||||
case '#': if (ftoken || !fcompat) return want('\n'), CPP;
|
||||
case '.': if (cur[0] == c && cur[1] == c) return cur+=2, DOT3;
|
||||
}
|
||||
|
||||
if (is(D, c)) {
|
||||
if (c == '0' && (*cur|32) == 'x')
|
||||
c = *++cur;
|
||||
while (is(OHD, c))
|
||||
c = *cur++;
|
||||
if (c == '.' || (c|32) == 'e') {
|
||||
(void)strtod((char*)start, (char**)&cur); // find end of float
|
||||
return FLOAT;
|
||||
}
|
||||
return --cur, INT;
|
||||
}
|
||||
|
||||
if (is(I, c)) {
|
||||
while (is(I|D, c))
|
||||
c = *cur++;
|
||||
--cur;
|
||||
return iskw(start, cur-start) ? KEYW : ID;
|
||||
}
|
||||
|
||||
return c ? c : ' '; // allows nulls in source code
|
||||
}
|
||||
|
||||
// iocccsize compat: adjust whitespace counts
|
||||
void compat_words(int tlen, int inword) {
|
||||
int c;
|
||||
for (c=0; c<tlen; ++c) {
|
||||
int isw = is(W,start[c]);
|
||||
if (isw || (is(B,start[c]) && is(W,start[c+1])))
|
||||
--cioccc;
|
||||
if (inword) inword = !isw;
|
||||
else if (!isw) inword = ++cword;
|
||||
}
|
||||
}
|
||||
|
||||
void compat_kw(int tlen, int inword) {
|
||||
int c, w=0;
|
||||
for (w=c=2; c<tlen; ++c) { // skip comment lead-in chars
|
||||
int isw = !is(I|D,start[c]);
|
||||
if (inword) {
|
||||
inword = !isw;
|
||||
if (!inword && iskw(start+w, c-w))
|
||||
cioccc -= c-w-1;
|
||||
} else if (!isw)
|
||||
inword = w = c;
|
||||
}
|
||||
}
|
||||
|
||||
void compat(int t, int tlen) {
|
||||
static int tprev;
|
||||
int c;
|
||||
if (tprev == CMT) {
|
||||
/*
|
||||
* do not bcount spaces following comment blocks,
|
||||
* including the newline! nor lcount the newline following
|
||||
* a comment block if last on line
|
||||
*/
|
||||
unsigned char *ow = white;
|
||||
while (ow<start && *ow++ != '\n')
|
||||
;
|
||||
if (!fkeep && ow[-1] == '\n')
|
||||
--cline;
|
||||
cbyte -= ow-white;
|
||||
tprev = 0;
|
||||
}
|
||||
// new-style comments eat preceding newline,
|
||||
// leaving no whitespace if token starts line
|
||||
// add it back here for compat word boundary detection
|
||||
if (white == start && white[-1] == '\n')
|
||||
--white;
|
||||
if (t == CMT) {
|
||||
// do not count newlines within comments
|
||||
// and checks for keywords in new-style comments
|
||||
if (fkeep) {
|
||||
int inword = white==start && start>buf;
|
||||
cioccc += tlen;
|
||||
if (start[1] == '/')
|
||||
compat_kw(tlen, inword);
|
||||
compat_words(tlen, inword);
|
||||
} else for (c=0; c<tlen; ++c)
|
||||
if (start[c] == '\n') --cline;
|
||||
|
||||
// nor spaces before comments IFF the comment
|
||||
// is first non-white on line
|
||||
unsigned char *os = start;
|
||||
while (os > white) {
|
||||
if (*--os == '\n')
|
||||
break;
|
||||
}
|
||||
|
||||
// look for last ends with newline,
|
||||
// only remove ccounts if whitespace
|
||||
if (*os == '\n' || os == buf || white[-1] == '\n')
|
||||
cbyte -= start - os - (*os == '\n');
|
||||
|
||||
// look for whitespace before the comment, as well as after
|
||||
if (!fkeep
|
||||
&& (white != start || start == buf)
|
||||
&& !is(W, start[tlen]) && start[1] == '*')
|
||||
++cword;
|
||||
|
||||
if (start[1] == '*')
|
||||
tprev = CMT;
|
||||
} else if (t == '"' || t == '\'') {
|
||||
// iocccsize -i does not count whitespace WITHIN a string!!!
|
||||
// and {;} can be "free" within a string if follows the "rules"
|
||||
compat_words(tlen, 1);
|
||||
}
|
||||
// count words, not tokens (a preceding old-style CMT is whitespace)
|
||||
if (t && t != CMT && white == start && start > buf)
|
||||
--cword;
|
||||
if (!fsuppress && (t != CMT || fkeep))
|
||||
printf("%.*s", (int)(start-white)+tlen, white);
|
||||
}
|
||||
|
||||
void count() {
|
||||
int t, tlen;
|
||||
kwinit();
|
||||
while ((t=lex())) {
|
||||
tlen = cur-start;
|
||||
if (t != CMT) {
|
||||
cbyte += tlen;
|
||||
++cword;
|
||||
if (freserved && t == KEYW)
|
||||
cioccc += 1;
|
||||
else if (t < 256 && is(B, t) && (is(W, *cur) || !*cur))
|
||||
;
|
||||
else
|
||||
cioccc += tlen;
|
||||
}
|
||||
if (fcompat)
|
||||
compat(t, tlen);
|
||||
else if (!fsuppress && (t != CMT || fkeep)) {
|
||||
if (!ftoken) printf("%3d ", t);
|
||||
printf("%.*s", tlen, start);
|
||||
if (cur[-1] != '\n') printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (fcompat) {
|
||||
// process comments just before end of file
|
||||
compat(0, end-white);
|
||||
}
|
||||
|
||||
if (fkeep) cbyte = end - buf;
|
||||
if (!freserved) tkey = 0;
|
||||
tsaved = cbyte - cioccc - tkey;
|
||||
cbyte += cextra;
|
||||
}
|
||||
|
||||
int main(int ac, char **av) {
|
||||
static char usage[] = "[flags] [file]\n\
|
||||
-t tokenize input\n\
|
||||
-c compatible count and display token ids\n\
|
||||
-k keep comments\n\
|
||||
-s suppress source\n\
|
||||
-r reserved words count as 1\n\
|
||||
-i ioccc size (implies -rs)\n\
|
||||
-h this help";
|
||||
|
||||
fcompat = 1; // default: compact with iocccsize: invert sense of -c
|
||||
|
||||
int c;
|
||||
while ((c = getopt(ac, av, "?tcksrih")) != EOF) {
|
||||
switch (c) {
|
||||
case 't': ftoken = 1; // fallthru - -t implies -c
|
||||
case 'c': fcompat = 0; break;
|
||||
case 'k': fkeep = 1; break;
|
||||
case 's': fsuppress = 1; break;
|
||||
case 'r': freserved = 1; break;
|
||||
case 'i': fioccc = freserved = fsuppress = fcompat = 1; break;
|
||||
case 'h': // fallthru, alias for -?
|
||||
default:
|
||||
fprintf(stderr, "%s %s\n", av[0], usage);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
ac -= optind;
|
||||
av += optind;
|
||||
if (ac > 0 && !freopen(*av, "r", stdin)) {
|
||||
perror(*av);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
readsource();
|
||||
count();
|
||||
|
||||
if (!ftoken) {
|
||||
if (fioccc) printf("%d\n", cioccc);
|
||||
else fprintf(stderr, "%d %d %d %d %d %d %d\n",
|
||||
cline,cword,cbyte,cioccc,tsaved,ckey,tkey);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
NAME
|
||||
tac - tokenize and count C-like program source code
|
||||
|
||||
SYNOPSIS
|
||||
tac [-t] [-c] [-k] [-r] [-s] [-i] [file]
|
||||
|
||||
DESCRIPTION
|
||||
tac reads C or C-like source code from stdin or file, breaking it into tokens (lexemes),
|
||||
counting these according to the rules of the IOCCC, and outputting the tokens,
|
||||
counts, or source code as directed by the command line options.
|
||||
The default output is the source code with comments removed on stdout,
|
||||
and the counts on stderr.
|
||||
|
||||
The count format is a set of integer values in the following order:
|
||||
|
||||
lines number of newlines
|
||||
words number of words
|
||||
chars number of characters
|
||||
ioccc number of IOCCC countable tokens; the IOCCC rule 2 value
|
||||
saved number of characters not counted due to rules and keywords
|
||||
kwcnt number of keywords
|
||||
kwlen number of characters saved by counting keyword length as 1
|
||||
|
||||
For the following code:
|
||||
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
/* example for loop */
|
||||
for (int ac=1; ac<10; ++ac)
|
||||
printf("%d\n", ac);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tac prints the code to stdout, and to stderr:
|
||||
|
||||
6 15 96 71 25 5 0
|
||||
|
||||
which means 6 lines, 15 words, 96 chars, 71 IOCCC counts, 25 chars saved,
|
||||
5 kwcnt, and no characters saved from kwlen. (By default, comments are not
|
||||
counted.)
|
||||
|
||||
The following options are supported:
|
||||
|
||||
-t output just the token stream
|
||||
-c correct the accounting bugs in iocccsize(1), output the token id and the token
|
||||
-k keep the comments
|
||||
-r reserved words count as size 1
|
||||
-s suppress the source code, summarize counts only
|
||||
-i report only the official IOCCC rule 2 size: number of octets (implies -r and -s)
|
||||
|
||||
Counts are impacted by the -k, -r and -c options.
|
||||
By default, the counts do not include comments, words are counted as in wc(1),
|
||||
and reserved words count in full.
|
||||
Using the example code above, the options change the counts as follows:
|
||||
|
||||
-r 6 15 96 53 25 5 18
|
||||
-k 7 20 120 89 31 5 0
|
||||
-c 7 36 98 71 27 5 0
|
||||
|
||||
The -c option counts tokens instead of words, and is therefore more accurate for source code.
|
||||
Note the aberration for line count with -r; it is as-if the comment never existed.
|
||||
|
||||
The -c and -t options change the output, instead writing a stream of tokens to stdout,
|
||||
one token per line.
|
||||
The -t option suppresses both the counts and the token ids, writing only the tokens.
|
||||
|
||||
EXAMPLES
|
||||
Summarize the source code counts:
|
||||
|
||||
tac -rs tac.c
|
||||
|
||||
Count the 10 most frequently used unique tokens in the source code:
|
||||
|
||||
tac -t tac.c 2>/dev/null | sort | uniq -c | sort -k1nr | sed 10q
|
||||
|
||||
Count the unique constants in the source:
|
||||
|
||||
tac -c tac.c 2>/dev/null | grep ^300 | sort | uniq -c | sort -k1nr | awk '{print $1,$3}'
|
||||
|
||||
BUGS
|
||||
tac does not properly count preprocessor tokens with embedded space or
|
||||
comments as reserved words:
|
||||
|
||||
echo '#/*comment*/include/*comment*/<stdio.h>' | tac -i
|
||||
echo '# include<stdio.h>' | tac -i
|
||||
echo '#include<stdio.h>' | tac -i
|
||||
|
||||
reports no reserved words on the first two inputs, and thus 17 as the IOCCC size,
|
||||
although 10 is the correct answer (as in the final example).
|
||||
|
||||
The original meaning of the -c option was "become compatible with iocccsize(1)".
|
||||
Its meaning was flipped so the default behavior is now compatible.
|
||||
|
||||
SEE ALSO
|
||||
iocccsize(1), wc(1)
|
||||
http://ioccc.org/2015/rules.txt
|
||||
@@ -0,0 +1,11 @@
|
||||
main(int ac, char **av) {
|
||||
if (av[0][0] == '"') {
|
||||
switch (av[0][1]) {
|
||||
case 'a': printf("a\n"); break;
|
||||
case 'b': printf("b\n"); break;
|
||||
}
|
||||
}
|
||||
while (--ac > 0)
|
||||
printf("%d", ac);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
main(int ac, char **av) {
|
||||
if (av[0][0] == '"') {
|
||||
switch (av[0][1]) {
|
||||
case 'a': printf("a\n"); break;
|
||||
case 'b': printf("b\n"); break;
|
||||
}
|
||||
}
|
||||
while (--ac > 0)
|
||||
printf("%d", ac);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
main() { printf("hello, world\n"); }
|
||||
@@ -0,0 +1,3 @@
|
||||
#include <stdio.h>
|
||||
char a[] = "main() { return printf(\" hello, world!\n\" \" } ; // this is not a comment";
|
||||
int main() { printf("%s\n", a); }
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef FOO
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
return printf("hello, world!\n");
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
#include <stdio.h>
|
||||
/* one */ int b;
|
||||
char a[] = "main() { return printf(\" hello, world!\n\" \" } ; // this is not a comment";
|
||||
int main() { printf("%s\n", a); }
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
/* a comment */ printf(
|
||||
"hello, world!\n"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// leading whitespace comment
|
||||
main(int ac, char **av) {
|
||||
char foo[] = "line one\n\
|
||||
two\n\
|
||||
three\n";
|
||||
if (av[0][0] == '"') {
|
||||
switch (av[0][1]) {
|
||||
case 'a': printf("a\n"); break;
|
||||
case 'b': printf("b\n"); break;
|
||||
}
|
||||
}
|
||||
while (--ac > 0)
|
||||
printf("%d", ac);
|
||||
printf(foo);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* one */ /* two */
|
||||
main(int ac, char **av) {
|
||||
if (av[0][0] == '"') {
|
||||
switch (av[0][1]) {
|
||||
case 'a': printf("a\n"); break;
|
||||
case 'b': printf("b\n"); break;
|
||||
}
|
||||
}
|
||||
while (--ac > 0)
|
||||
printf("%d", ac);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
main(int ac, char **av) {
|
||||
if (av[0][0] == '"') {
|
||||
switch (av[0][1]) {
|
||||
case 'a': printf("a\n"); break;
|
||||
case 'b': printf("b\n"); break;
|
||||
}
|
||||
}
|
||||
// comment one
|
||||
// comment two
|
||||
while (--ac > 0)
|
||||
printf("%d", ac);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
??=include <stdio.h>
|
||||
??/
|
||||
int main(int ac, char **av)??<
|
||||
if (!(ac??'1))??<printf("hello, world?????/n");
|
||||
??>else??<pr??/
|
||||
intf("hello,%s!??/n", av??(1??));??>
|
||||
??>
|
||||
@@ -0,0 +1,82 @@
|
||||
#i\
|
||||
nc\
|
||||
lu\
|
||||
de\
|
||||
<s\
|
||||
td\
|
||||
io\
|
||||
.h>
|
||||
|
||||
ma\
|
||||
in(
|
||||
in\
|
||||
t \
|
||||
ac\
|
||||
, \
|
||||
ch\
|
||||
ar\
|
||||
**\
|
||||
av)
|
||||
{i\
|
||||
f(\
|
||||
av\
|
||||
[0\
|
||||
][\
|
||||
0]\
|
||||
==\
|
||||
'"\
|
||||
')\
|
||||
{s\
|
||||
wi\
|
||||
tc\
|
||||
h(\
|
||||
av\
|
||||
[0\
|
||||
][\
|
||||
1]\
|
||||
){\
|
||||
ca\
|
||||
se\
|
||||
'a\
|
||||
':\
|
||||
pr\
|
||||
in\
|
||||
tf\
|
||||
("\
|
||||
a\\
|
||||
n"\
|
||||
);\
|
||||
br\
|
||||
ea\
|
||||
k;\
|
||||
ca\
|
||||
se\
|
||||
'b\
|
||||
':\
|
||||
pr\
|
||||
in\
|
||||
tf(
|
||||
"b\
|
||||
\n\
|
||||
");
|
||||
br\
|
||||
ea\
|
||||
k;\
|
||||
}}\
|
||||
wh\
|
||||
il\
|
||||
e(\
|
||||
--\
|
||||
ac>
|
||||
0)\
|
||||
pr\
|
||||
in\
|
||||
tf(
|
||||
"%\
|
||||
d",
|
||||
ac)
|
||||
;r\
|
||||
et\
|
||||
ur\
|
||||
n \
|
||||
0;}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
/***/printf(/***/
|
||||
"hello, world!\n"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{int}
|
||||
@@ -0,0 +1,4 @@
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
printf/*testing*/("hello, world!\n");
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
awk '
|
||||
r==1 { r=0; if (/>=/){print ">>>="; next}
|
||||
if (/>/) {print ">>>"; next}
|
||||
print ">>" }
|
||||
r==3 { if (/\./){ r=4; next } r=0; print "." }
|
||||
r==4 { if (/\./){ r=0; print "..."; next} print ".\n."}
|
||||
|
||||
# digraphs
|
||||
r==2 { r=0; if (/>/){print "]"; next}
|
||||
if (/:/){print "::"; next}
|
||||
print ":" }
|
||||
r==5 { r=0; if (/:/){print "["; next}
|
||||
if (/%/){print "{"; next}
|
||||
print "<" }
|
||||
r==6 { r=0; if (/>/){print "}"; next}
|
||||
if (/:/){print "#"; next}
|
||||
print "%" }
|
||||
|
||||
/^>>$/ {r=1;next} # java
|
||||
/^:$/ {r=2;next} # c++, java classes, digraph
|
||||
/^\.$/ {r=3;next} # mea culpa - ellipsis
|
||||
/^<$/ {r=5;next} # digraph
|
||||
/^%$/ {r=6;next} # digraph
|
||||
|
||||
{ print $0 }' $*
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/bin/sh
|
||||
|
||||
# unob is a proof-of-concept script.
|
||||
# Prior to tac, scripting the layout of C code was intractable.
|
||||
#
|
||||
# There are uncountably many possible refinements to this script.
|
||||
# Enjoy the madness of getting a perfect layout of C code via
|
||||
# an automated process. Or just enjoy a significant help in
|
||||
# reading obfuscated C code!
|
||||
|
||||
: ${PROG:=./prog} # PROG=tac ./unob.sh prog.c
|
||||
: ${KW:=c11} # KW=ioccc.kw.freq ./unob.sh prog.c
|
||||
: ${DEF:=} # DEF="-DW=\\\"kandr2\\\" -DU=\\\"usage\ string\\\"" ./unob.sh -p prog.c
|
||||
|
||||
# optionally preprocess up to 3 times to handle #ifdef X #define Y
|
||||
# unob prog.c # nonce
|
||||
# unob -p prog.c # once
|
||||
# unob -p -p prog.c # twice
|
||||
# unob -p -p -p prog.c # thrice
|
||||
# unob -p -p -p -p prog.c # yer out
|
||||
|
||||
function pp {
|
||||
cpp2=cat
|
||||
cpp3=cat
|
||||
if [ "x$1" == "x-p" ]; then
|
||||
shift
|
||||
if [ "x$1" == "x-p" ]; then
|
||||
cpp2="cpp -E"
|
||||
shift
|
||||
fi
|
||||
if [ "x$1" == "x-p" ]; then
|
||||
cpp3="cpp -E"
|
||||
shift
|
||||
fi
|
||||
cat $* |
|
||||
sed 's/# *include/##include/' |
|
||||
eval cpp -E "$DEF" |
|
||||
eval $cpp2 |
|
||||
eval $cpp3 |
|
||||
sed 's/^#[^#].*$//' |
|
||||
sed 's/##inc/#inc/'
|
||||
else
|
||||
cat $*
|
||||
fi
|
||||
}
|
||||
|
||||
script='
|
||||
BEGIN {
|
||||
last=nl="\n";
|
||||
f="'$KW'"; while(getline <f > 0) ++kw[$0]; close(f);
|
||||
if (length(kw) == 0) {
|
||||
print "cannot find",f "; check KW environment variable" | "cat 1>&2"
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
function iskw(a) { return a in kw }
|
||||
function indent() { return sprintf("%.*s", n*3, " ") }
|
||||
function newline() { if (!infor && last != nl) printf last=nl }
|
||||
function show(a) {
|
||||
if (len>80 || (len>60 && (last ~ /^;/ || (last ~ /^,/ && paren==0))))
|
||||
printf "%c%s", last=nl, indent() # yes, double indent here
|
||||
if (len>60 && last ~ /^\)/ && a !~ /^\)/ && paren == 0)
|
||||
printf last=nl
|
||||
if (last==nl) { len=0; printf "%s", indent() }
|
||||
|
||||
if (length(a) > 80 && a ~ /\"/) {
|
||||
newline()
|
||||
len = 80
|
||||
max = length(a)
|
||||
for (off=0; off <= max; off += len)
|
||||
printf "%s%s%c%s",
|
||||
substr(a, off, len),
|
||||
(off+len > max) ? "" : "\\",
|
||||
last=nl, indent()
|
||||
len = 0
|
||||
return
|
||||
}
|
||||
|
||||
printf "%s%s", space(), a
|
||||
len += length(a space())
|
||||
last=a
|
||||
}
|
||||
function space() {
|
||||
return iskw(last) ||
|
||||
(last != "." &&
|
||||
last ~ /[A-Za-z0-9_+-\/%^|&\]\)=:<>;]$/ && $0 !~ /[\.:;()\[\],]/) ? " " : ""
|
||||
}
|
||||
|
||||
/^\(/ { ++paren }
|
||||
/^\)/ { --paren }
|
||||
|
||||
/^(if|while|for|return|switch|case|default|union|struct|typedef|#)/ { newline() }
|
||||
|
||||
/^for$/ { newline(); infor=1 }
|
||||
infor && /^;/ { ++infor; show($0 " "); next }
|
||||
infor==1 && /^:/ { ++infor }
|
||||
infor>1 && paren==0 { infor=0 }
|
||||
|
||||
/^\?/ { ++tern; ++n; newline(); show($0 " "); next }
|
||||
tern && /^:/ { newline(); show($0); --tern; --n; next }
|
||||
|
||||
/^#/ { newline(); show($0); newline(); next }
|
||||
/^;/ { show($0); newline(); next }
|
||||
/^{/ { show($0); ++n; newline(); next }
|
||||
/^}/ { --n; newline(); show($0); newline(); next }
|
||||
|
||||
{ show($0) }
|
||||
'
|
||||
|
||||
pp $* | $PROG -t | ./tokenfix | awk "$script" # sed 's/""//g'
|
||||
@@ -0,0 +1,38 @@
|
||||
$ 7th edition Unix source code, c00.c, 1979 cc, cpp.c
|
||||
#define
|
||||
#else
|
||||
#endif
|
||||
#if
|
||||
#ifdef
|
||||
#ifndef
|
||||
#include
|
||||
#line
|
||||
#undef
|
||||
auto
|
||||
break
|
||||
case
|
||||
char
|
||||
continue
|
||||
default
|
||||
do
|
||||
double
|
||||
else
|
||||
enum
|
||||
extern
|
||||
float
|
||||
for
|
||||
goto
|
||||
if
|
||||
int
|
||||
long
|
||||
register
|
||||
return
|
||||
short
|
||||
sizeof
|
||||
static
|
||||
struct
|
||||
switch
|
||||
typedef
|
||||
union
|
||||
unsigned
|
||||
while
|
||||
Reference in new issue
Block a user