Add 2020 source files for real
This commit is contained in:
1 parent
a3c1748648
commit
9d61fc0fb4
147 files changed
+32101
No files matched your search
@@ -0,0 +1,587 @@
|
||||
#!/usr/bin/env make
|
||||
#
|
||||
# 2020 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/
|
||||
|
||||
#############################
|
||||
# shell used by this Makefile
|
||||
#############################
|
||||
#
|
||||
SHELL= /bin/bash
|
||||
|
||||
#######################
|
||||
# common tool locations
|
||||
#######################
|
||||
#
|
||||
A2P= a2p
|
||||
AR= ar
|
||||
ASA= asa
|
||||
AT= at
|
||||
ATQ= atq
|
||||
ATRM= atrm
|
||||
AWK= awk
|
||||
BANNER= banner
|
||||
BASE64= base64
|
||||
BASENAME= basename
|
||||
BATCH= batch
|
||||
BC= bc
|
||||
BINHEX= binhex
|
||||
BISON= bison
|
||||
BUNZIP2= bunzip2
|
||||
BZCAT= bzcat
|
||||
BZCMP= bzcmp
|
||||
BZDIFF= bzdiff
|
||||
BZEGREP= bzegrep
|
||||
BZFGREP= bzfgrep
|
||||
BZGREP= bzgrep
|
||||
BZIP2= bzip2
|
||||
BZLESS= bzless
|
||||
BZMORE= bzmore
|
||||
C2PH= c2ph
|
||||
C89= c89
|
||||
C99= c99
|
||||
CAL= cal
|
||||
CALC= calc
|
||||
CAT= cat
|
||||
CD= cd
|
||||
CHFLAGS= chflags
|
||||
CHGRP= chgrp
|
||||
CHMOD= chmod
|
||||
CKSUM= cksum
|
||||
CLANG= clang
|
||||
CLANG_PLUSPLUS= clang++
|
||||
CLEAR= clear
|
||||
CMP= cmp
|
||||
COL= col
|
||||
COLLDEF= colldef
|
||||
COLRM= colrm
|
||||
COLUMN= column
|
||||
COMPRESS= compress
|
||||
CP= cp
|
||||
CPIO= cpio
|
||||
CPP= cpp
|
||||
CRC32= crc32
|
||||
CSH= csh
|
||||
CSPLIT= csplit
|
||||
CURL= curl
|
||||
CUT= cut
|
||||
C_PLUSPLUS= c++
|
||||
C_PLUSPLUS_FILT= c++filt
|
||||
DATE= date
|
||||
DC= dc
|
||||
DD= dd
|
||||
DF= df
|
||||
DIFF3= diff3
|
||||
DIFF= diff
|
||||
DIG= dig
|
||||
DIRNAME= dirname
|
||||
ECHO= echo
|
||||
ED= ed
|
||||
EGREP= egrep
|
||||
ENV= env
|
||||
EQN= eqn
|
||||
ETAGS= etags
|
||||
EXPECT= expect
|
||||
EXPR= expr
|
||||
FALSE= false
|
||||
FGREP= fgrep
|
||||
FILE= file
|
||||
FIND2PERL= find2perl
|
||||
FIND= find
|
||||
FLEX= flex
|
||||
FLEX_PLUSPLUS= flex++
|
||||
FMT= fmt
|
||||
FOLD= fold
|
||||
FS_USAGE= fs_usage
|
||||
FUNZIP= funzip
|
||||
FUSER= fuser
|
||||
GCC= gcc
|
||||
GDIFFMK= gdiffmk
|
||||
GENCAT= gencat
|
||||
GENSTRINGS= genstrings
|
||||
GETOPT= getopt
|
||||
GETOPTS= getopts
|
||||
GINDENT= gindent
|
||||
GNUMAKE= gnumake
|
||||
GREP= grep
|
||||
GROFF= groff
|
||||
GROFFER= groffer
|
||||
GROG= grog
|
||||
GROPS= grops
|
||||
GROTTY= grotty
|
||||
GUNZIP= gunzip
|
||||
GVIM= gvim
|
||||
GZCAT= gzcat
|
||||
GZEXE= gzexe
|
||||
GZIP_PROG= gzip
|
||||
G_PLUSPLUS= g++
|
||||
H2PH= h2ph
|
||||
H2XS= h2xs
|
||||
HASH= hash
|
||||
HEAD= head
|
||||
HOSTNAME_PROG= hostname
|
||||
ICONV= iconv
|
||||
ID= id
|
||||
INDENT= indent
|
||||
INFO= info
|
||||
JOT= jot
|
||||
KILL= kill
|
||||
KSH= ksh
|
||||
LAST= last
|
||||
LD= ld
|
||||
LESSECHO= lessecho
|
||||
LEX= len
|
||||
LINK= link
|
||||
LN= ln
|
||||
LS= ls
|
||||
M4= m4
|
||||
MAKE= make
|
||||
MAN= man
|
||||
MKDIR= mkdir
|
||||
MKFIFo= mkfifo
|
||||
MKTEMP= mktemp
|
||||
MV= mv
|
||||
NANO= nano
|
||||
NASM= nasm
|
||||
NEQN= neqn
|
||||
NICE= nice
|
||||
NL= nl
|
||||
NM= nm
|
||||
NOHUP= nohup
|
||||
NROFF= nroff
|
||||
NSLOOKUP= nslookup
|
||||
OD= od
|
||||
OPENSSL= openssl
|
||||
PASTE= paste
|
||||
PATCH= patch
|
||||
PATHCHK= pathchk
|
||||
PAX= pax
|
||||
PERL= perl
|
||||
PICO= pico
|
||||
PR= pr
|
||||
PRINTENV= printenv
|
||||
PS= ps
|
||||
PTAR= ptar
|
||||
PTARDIFF= ptardiff
|
||||
PTARGREP= ptargrep
|
||||
PWD= pwd
|
||||
PYDOC= pydoc
|
||||
PYTHON= python
|
||||
PYTHONW= pythonw
|
||||
READLINK= readlink
|
||||
RENICE= renice
|
||||
RESET= reset
|
||||
REV= rev
|
||||
RI= ri
|
||||
RM= rm
|
||||
RMDIR= rmdir
|
||||
RSYNC= rsync
|
||||
RUBY= ruby
|
||||
RVIM= rvim
|
||||
SAY= say
|
||||
SCP= scp
|
||||
SCREEN= screen
|
||||
SCRIPT= script
|
||||
SDIFF= sdiff
|
||||
SED= sed
|
||||
SEQ= seq
|
||||
SFTP= sftp
|
||||
SH= sh
|
||||
SHA1= sha1
|
||||
SHA= sha
|
||||
SHAR= shar
|
||||
SHASUM5_18= shasum5.18
|
||||
SHASUM= shasum
|
||||
SIZE= size
|
||||
SLEEP= sleep
|
||||
SORT= sort
|
||||
SSH= ssh
|
||||
STAT= stat
|
||||
STRIP= strip
|
||||
STTY= stty
|
||||
SUM= sum
|
||||
SYNC= sync
|
||||
TABS= tabs
|
||||
TAIL= tail
|
||||
TAR= tar
|
||||
TEE= tee
|
||||
TEST= test
|
||||
TFTP= tftp
|
||||
TIDY= tidy
|
||||
TIME= time
|
||||
TOP= top
|
||||
TOUCH= touch
|
||||
TPUT= tout
|
||||
TPUT= tput
|
||||
TR= tr
|
||||
TROFF= troff
|
||||
TRUE= true
|
||||
TSET= tset
|
||||
TSORT= tsort
|
||||
UL= ul
|
||||
UNAME= uname
|
||||
UNCOMPRESS= uncompress
|
||||
UNEXPAND= unexpand
|
||||
UNIFDEF= unifdef
|
||||
UNIFDEFALL= unifdefall
|
||||
UNIQ= uniq
|
||||
UNITS= units
|
||||
UNLINK= unlink
|
||||
UNZIP= unzip
|
||||
UNZIPSFX= unzipsfx
|
||||
UPTIME= uptime
|
||||
UUDECODE= uudecode
|
||||
UUENCODE= uuencode
|
||||
UUIDGEN= uuidgen
|
||||
VI= vi
|
||||
VIEW= view
|
||||
VIM= vim
|
||||
VIMDIFF= vimdiff
|
||||
W= w
|
||||
WAIT4PATH= wait4path
|
||||
WAIT= wait
|
||||
WC= wc
|
||||
WHAT= what
|
||||
WHATIS= whatis
|
||||
WHICH= which
|
||||
WHO= who
|
||||
WHOAMI= whoami
|
||||
WHOIS= whois
|
||||
WRTIE= write
|
||||
XAR= xar
|
||||
XARGS= xargs
|
||||
XATTR= xattr
|
||||
XXD= xxd
|
||||
YACC= yacc
|
||||
YES= yes
|
||||
ZCAT= zcat
|
||||
ZCMP= zcmp
|
||||
ZDIFF= zdiff
|
||||
ZEGREP= zegrep
|
||||
ZFGREP= zfgrep
|
||||
ZFORCE= zforce
|
||||
ZGREP= zgrep
|
||||
ZIP= zip
|
||||
ZIPCLOAK= zipcloak
|
||||
ZIPGREp= zipgrep
|
||||
ZIPINFo= zipinfo
|
||||
ZIPNOTE= zipnote
|
||||
ZIPSPLIT= zipsplit
|
||||
ZLESS= zless
|
||||
ZMORE= zmore
|
||||
ZNEW= znew
|
||||
ZPRINT= zprint
|
||||
ZSH= zsh
|
||||
|
||||
# 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
|
||||
|
||||
# CWARN - Silence warnings that ${CWARN} would normally complain about
|
||||
# CSILENCE -Compiler warnings
|
||||
#
|
||||
ifeq ($(CC),clang)
|
||||
|
||||
# clang CSILENCE and CWARN
|
||||
#
|
||||
#CSILENCE=
|
||||
#CSILENCE= -Wno-implicit-int
|
||||
CSILENCE= -Wno-comma -Wno-format-nonliteral -Wno-implicit-int-conversion -Wno-missing-prototypes \
|
||||
-Wno-missing-variable-declarations -Wno-redundant-parens -Wno-shadow -Wno-shorten-64-to-32 \
|
||||
-Wno-sign-conversion -Wno-conversion -Wno-unknown-warning-option
|
||||
#
|
||||
#CWARN= $CSILENCE}
|
||||
#CWARN= -Wall $CSILENCE}
|
||||
#CWARN= -Wall -Wextra $CSILENCE}
|
||||
#CWARN= -Wall -Wextra -pedantic ${CSILENCE}
|
||||
#CWARN= -Wall -Wextra -Weverything $CSILENCE}
|
||||
CWARN= -Wall -Wextra -Weverything -pedantic ${CSILENCE}
|
||||
|
||||
else
|
||||
ifeq ($(CC),gcc)
|
||||
|
||||
# gcc CSILENCE and CWARN
|
||||
#
|
||||
CSILENCE=
|
||||
#CSILENCE= -Wno-implicit-int
|
||||
#
|
||||
#CWARN= $CSILENCE}
|
||||
#CWARN= -Wall $CSILENCE}
|
||||
#CWARN= -Wall -Wextra $CSILENCE}
|
||||
CWARN= -Wall -Wextra -pedantic ${CSILENCE}
|
||||
|
||||
else
|
||||
|
||||
# default CSILENCE and WARN
|
||||
#
|
||||
CSILENCE=
|
||||
#CSILENCE= -Wno-implicit-int
|
||||
#
|
||||
#CWARN= $CSILENCE}
|
||||
#CWARN= -Wall $CSILENCE}
|
||||
#CWARN= -Wall -Wextra $CSILENCE}
|
||||
CWARN= -Wall -Wextra -pedantic ${CSILENCE}
|
||||
#CWARN= -Wall -Wextra -Weverything $CSILENCE}
|
||||
#CWARN= -Wall -Wextra -Weverything -pedantic ${CSILENCE}
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
# 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=
|
||||
#CDEFINE= -DIOCCC
|
||||
|
||||
# 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= cppp.cc
|
||||
TARGET= prog
|
||||
#
|
||||
ALT_OBJ=
|
||||
ALT_TARGET=
|
||||
|
||||
|
||||
#################
|
||||
# build the entry
|
||||
#################
|
||||
#
|
||||
all: ${TARGET} ${DATA}
|
||||
@${TRUE}
|
||||
|
||||
prog: prog.c
|
||||
${CC} ${CFLAGS} $< -o $@ ${LIBS}
|
||||
|
||||
# alternative executable
|
||||
#
|
||||
alt: ${ALT_TARGET}
|
||||
@${TRUE}
|
||||
|
||||
# data files
|
||||
#
|
||||
data: ${DATA}
|
||||
@${TRUE}
|
||||
|
||||
|
||||
###############
|
||||
# utility rules
|
||||
###############
|
||||
#
|
||||
everything: all alt
|
||||
|
||||
clean:
|
||||
${RM} -f ${OBJ} ${ALT_OBJ}
|
||||
@-if [ -f indent.c ]; then \
|
||||
${ECHO} ${RM} -f indent.c; \
|
||||
${RM} -f indent.c; \
|
||||
fi
|
||||
|
||||
clobber: clean
|
||||
${RM} -f ${TARGET} ${ALT_TARGET}
|
||||
@-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. Science!"
|
||||
@${TRUE}
|
||||
|
||||
# backwards compatibility
|
||||
#
|
||||
build: all
|
||||
@${TRUE}
|
||||
|
||||
# you may be able to mis-understand the source by reading indent.c
|
||||
#
|
||||
indent.c: prog.c
|
||||
@if which "${GINDENT}" >/dev/null 2>&1; then \
|
||||
${ECHO} ${RM} -f $@; \
|
||||
${RM} -f $@; \
|
||||
${ECHO} "${GINDENT} < $< > $@"; \
|
||||
${GINDENT} < $< > $@; \
|
||||
elif which "${INDENT}" >/dev/null 2>&1; then \
|
||||
${ECHO} ${RM} -f $@; \
|
||||
${RM} -f $@; \
|
||||
${ECHO} "${INDENT} < $< > $@"; \
|
||||
${INDENT} < $< > $@; \
|
||||
else \
|
||||
${ECHO} "no intent prog found, ident $< yourself, sorry"; \
|
||||
${ECHO} "exit 1"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
##################
|
||||
# 133t hacker rulz
|
||||
##################
|
||||
#
|
||||
love:
|
||||
@${ECHO} 'not war?'
|
||||
@${TRUE}
|
||||
|
||||
haste:
|
||||
$(MAKE) waste
|
||||
@${TRUE}
|
||||
|
||||
waste:
|
||||
@${ECHO} 'haste'
|
||||
@${TRUE}
|
||||
|
||||
make:
|
||||
@${ECHO} 'We encourage you to attend a maker faire'
|
||||
@${TRUE}
|
||||
|
||||
easter_egg:
|
||||
@${ECHO} you expected to often mis-understand this $${RANDOM} magic
|
||||
@${ECHO} chongo '<was here>' "/\\oo/\\"
|
||||
@${ECHO} Eggy and Kitty
|
||||
|
||||
fabricate fashion form frame manufacture produce: make
|
||||
@${TRUE}
|
||||
|
||||
sandwich:
|
||||
@if [ `id -u` -eq 0 ]; then \
|
||||
${ECHO} 'Okay.'; \
|
||||
${ECHO} $${RANDOM}`date +%s`$${RANDOM} | \
|
||||
${SHASUM} -a 512256 | \
|
||||
${SED} -e 's/ .*//' > $@; \
|
||||
else \
|
||||
${ECHO} 'Reall?!? 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 a few silly IAU members, 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 253d8c80830e2df66c018820004b3d4939821bbd7d781fc50d9e633797e44f65d1d62c707bb7e5a82c328a37bbf9ecb4c46fb56aa3f08641fcf45d10ba3c6081 --FNV1a_hash_512_bit "prog"; \
|
||||
else \
|
||||
${ECHO} "Wrong! Do it again!"; \
|
||||
sleep 1; \
|
||||
${ECHO} "Wrong! Do it again!"; \
|
||||
sleep 1; \
|
||||
${ECHO} "Wrong! Do it again!"; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
magic: deep_magic
|
||||
@-if [ -r .code_anal ]; then \
|
||||
ccode_analysis --level 391581 --mode 216193 --state Riesel --FNV1a_hash_512_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?!"; \
|
||||
${ECHO} "p.s. Be cafeful with that axe, Eugene."; \
|
||||
fi
|
||||
@${TRUE}
|
||||
|
||||
# The IOCCC resident astronomer states
|
||||
#
|
||||
charon: supernova
|
||||
@${ECHO} $@ is a dwarf planet
|
||||
@${ECHO} dwarf is a type of planet
|
||||
@${ECHO} therefore we continue to tell the IAU that $@ is a planet
|
||||
|
||||
pluto: supernova
|
||||
${MAKE} charon
|
||||
@${ECHO} $@ is a dwarf planet
|
||||
@${ECHO} dwarf is a type of planet
|
||||
@${ECHO} therefore, $@ is a planet
|
||||
@${ECHO} having lots of planets is good feature because $< can help ${MAKE} a lot of them
|
||||
@@ -0,0 +1,859 @@
|
||||
// CPPP stands for "C Pre-PreProcessor", so named because the output
|
||||
// is meant to be fed into a real C preprocessors before they are
|
||||
// useful. Not to be confused with CPP (a real C preprocessor) or
|
||||
// CCCP (something else entirely).
|
||||
//
|
||||
// This tool is written to do primarily one thing, which is to
|
||||
// evaluate files that are heavy on ifdef/ifndef and self-includes,
|
||||
// and unroll those to something that is more palatable to typical
|
||||
// preprocessors. To that end, it is able to handle these directives:
|
||||
//
|
||||
// if elif else ifdef ifndef endif define undef warning error include
|
||||
//
|
||||
// With lots of caveats, such as:
|
||||
//
|
||||
// - #if and #elif only works if the expressions are shaped like these:
|
||||
// #if A % B > C
|
||||
// #if A > B
|
||||
// #if A
|
||||
//
|
||||
// - #include only works if right hand side evaluates to __FILE__,
|
||||
// otherwise the directive is copied to output.
|
||||
//
|
||||
// - __LINE__ is not preserved. Or rather, they are copied as is.
|
||||
// So if a __LINE__ moved to a different line after preprocessor
|
||||
// directives are evaluated, it will take on a different value as
|
||||
// opposed to whatever they would have been in the original source
|
||||
// location.
|
||||
//
|
||||
// - #define with right-hand side or macro parameters are copied to
|
||||
// output, otherwise they are evaluated and dropped. The intent is
|
||||
// to allow the next preprocessor to parse these and do macro
|
||||
// expansion there, since this tool doesn't do any macro expansion
|
||||
// on its own.
|
||||
//
|
||||
// That's right, no macro expansions. The one thing that is the main job
|
||||
// for most other preprocessors, we don't do that here. Don't mistake
|
||||
// this utility for a real preprocessor, it's just a pre-preprocessor.
|
||||
// But for the one task it is implemented to do, it does so significantly
|
||||
// faster than GCC or Clang's preprocessors.
|
||||
//
|
||||
// Requires compilers that supports C++17 or later.
|
||||
|
||||
#include<ctype.h>
|
||||
#include<inttypes.h>
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
|
||||
#include<array>
|
||||
#include<string_view>
|
||||
#include<string>
|
||||
#include<unordered_map>
|
||||
#include<unordered_set>
|
||||
#include<vector>
|
||||
|
||||
namespace {
|
||||
|
||||
// Maximum depth to avoid runaway includes.
|
||||
//
|
||||
// GCC supports 200 and said standard requires at least 15:
|
||||
// https://gcc.gnu.org/onlinedocs/cpp/Implementation-limits.html
|
||||
//
|
||||
// We cap our max inclusion depth at 15 so that this tool can be used
|
||||
// to flag inputs that might be troublesome for other preprocessors.
|
||||
static constexpr int kMaxIncludeDepth = 15;
|
||||
|
||||
// List of characters that passes isspace().
|
||||
static constexpr char kSpace[] = " \t\r\n\f\v";
|
||||
|
||||
// List of characters to match start of macro parameter or value.
|
||||
static constexpr char kSpaceOrLeftParen[] = " \t(";
|
||||
|
||||
// Table of (macro name, macro definition).
|
||||
//
|
||||
// Note that there is no place to store macro arguments here. This is
|
||||
// intentional, since we don't do any macro expansions.
|
||||
using MacroTable = std::unordered_map<std::string_view, std::string_view>;
|
||||
|
||||
// Preprocessor directives.
|
||||
enum class Directive
|
||||
{
|
||||
kIf, // arg = expression
|
||||
kElif, // arg = expression
|
||||
kElse,
|
||||
kIfDef, // arg = macro to test
|
||||
kIfNDef, // arg = macro to test
|
||||
kEndIf,
|
||||
kDefine, // arg = macro to insert
|
||||
kUndef, // arg = macro to remove
|
||||
kInclude, // arg = filename or macro
|
||||
kWarning, // arg = output text
|
||||
kError, // arg = output text
|
||||
kRawDefine, // arg = input line
|
||||
kText // arg = input line
|
||||
};
|
||||
|
||||
// A single preprocessor instruction step.
|
||||
struct Operation
|
||||
{
|
||||
// Operation parameters.
|
||||
Directive directive;
|
||||
std::string_view arg;
|
||||
|
||||
// Index of next instruction after evaluating conditional expression.
|
||||
size_t true_branch, false_branch;
|
||||
};
|
||||
|
||||
// Debugging stats.
|
||||
struct Stats
|
||||
{
|
||||
// Number of #include counts.
|
||||
//
|
||||
// Number of includes overflowing signed 32bits? It's more likely
|
||||
// than you think. Although if you did observe this number going
|
||||
// even above just a few hundred, you are likely going to have a
|
||||
// terrible experience feeding the same file to a real preprocessor.
|
||||
int64_t include_count = -1;
|
||||
|
||||
// Number of Operation steps executed, which is roughly the number
|
||||
// of lines that we didn't skip.
|
||||
int64_t operation_steps = 0;
|
||||
|
||||
// Maximum level of self-includes observed.
|
||||
int max_include_depth = 0;
|
||||
};
|
||||
|
||||
// Load input from opened file handle and append to output string.
|
||||
static void LoadInputFromHandle(FILE *infile, std::string *text)
|
||||
{
|
||||
std::array<char, 1024> buffer;
|
||||
for(size_t read_size;;)
|
||||
{
|
||||
read_size = fread(buffer.data(), 1, buffer.size(), infile);
|
||||
text->append(std::string(buffer.data(), read_size));
|
||||
if( read_size < buffer.size() )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Load input to memory.
|
||||
static bool LoadInput(const char *file_name, std::string *text)
|
||||
{
|
||||
FILE *infile;
|
||||
if( (infile = fopen(file_name, "rb")) == nullptr )
|
||||
{
|
||||
fprintf(stderr, "Error reading from %s\n", file_name);
|
||||
return false;
|
||||
}
|
||||
LoadInputFromHandle(infile, text);
|
||||
fclose(infile);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return list of strings, roughly one string per line except multiline
|
||||
// comments and preprocessor directives will be coalesced as one string.
|
||||
static std::vector<std::string_view> TokenizeToLines(std::string *text)
|
||||
{
|
||||
// State machine for parsing various regions of text.
|
||||
enum class State
|
||||
{
|
||||
Normal,
|
||||
SingleQuotedString,
|
||||
DoubleQuotedString,
|
||||
RawString,
|
||||
SingleLineComment,
|
||||
BlockComment
|
||||
};
|
||||
State state = State::Normal;
|
||||
|
||||
// List of offsets that are the start of each line.
|
||||
std::vector<size_t> line_offsets = {0};
|
||||
|
||||
// r = offset we are reading from, w = offset we are writing to. w <= r.
|
||||
//
|
||||
// These indices are used to modify the input string in-place, to
|
||||
// skip over uninteresting things such as backslash-newline pairs
|
||||
// and single line comments.
|
||||
size_t w = 0;
|
||||
for(size_t r = 0; r < text->size();)
|
||||
{
|
||||
const char c = (*text)[r];
|
||||
switch( state )
|
||||
{
|
||||
case State::Normal:
|
||||
// Normal state, check if we are about to enter enclosed things.
|
||||
if( c == '\'' )
|
||||
{
|
||||
// Single quoted literal.
|
||||
state = State::SingleQuotedString;
|
||||
(*text)[w++] = (*text)[r++];
|
||||
}
|
||||
else if( c == '\"' )
|
||||
{
|
||||
// Double quoted literal.
|
||||
state = State::DoubleQuotedString;
|
||||
(*text)[w++] = (*text)[r++];
|
||||
}
|
||||
else if( c == 'R' )
|
||||
{
|
||||
// Start a C++11 raw string if we saw a R"( trigram not
|
||||
// preceded by what might be an identifier.
|
||||
//
|
||||
// Note that this is the only kind of raw string we
|
||||
// support, and not the full R"token( type of raw strings.
|
||||
if( r == 0 ||
|
||||
(!isalnum((*text)[r - 1]) && (*text)[r - 1] != '_') )
|
||||
{
|
||||
// While C++11 guarantees that we can read +1 past
|
||||
// the end of a string, it doesn't guarantee that we
|
||||
// can read +2, so we must check the bounds here
|
||||
// (unlike other places).
|
||||
if( r + 2 < text->size() &&
|
||||
(*text)[r + 1] == '\"' &&
|
||||
(*text)[r + 2] == '(' )
|
||||
{
|
||||
state = State::RawString;
|
||||
}
|
||||
}
|
||||
(*text)[w++] = (*text)[r++];
|
||||
}
|
||||
else if( c == '/' )
|
||||
{
|
||||
// Start of comments.
|
||||
if( (*text)[r + 1] == '/' )
|
||||
{
|
||||
// Single line comments are dropped entirely.
|
||||
state = State::SingleLineComment;
|
||||
r += 2;
|
||||
}
|
||||
else if( (*text)[r + 1] == '*' )
|
||||
{
|
||||
// Whitespaces inside block comments are preserved, such
|
||||
// that line numbers and code alignment after the block
|
||||
// comments are preserved.
|
||||
state = State::BlockComment;
|
||||
(*text)[w++] = ' ';
|
||||
(*text)[w++] = ' ';
|
||||
r += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*text)[w++] = (*text)[r++];
|
||||
}
|
||||
}
|
||||
else if( c == '\\' && (*text)[r + 1] == '\n' )
|
||||
{
|
||||
// Skip over backslash-newline pairs, thus joining backslash
|
||||
// escaped line breaks. Note that this is only done in
|
||||
// Normal state. We specifically want to preserve
|
||||
// backslash-newline pairs inside C++11 raw strings.
|
||||
r += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy other characters as is, recording offsets to
|
||||
// next character just after a newline.
|
||||
(*text)[w++] = (*text)[r++];
|
||||
if( c == '\n' )
|
||||
line_offsets.push_back(w);
|
||||
}
|
||||
break;
|
||||
|
||||
case State::SingleQuotedString:
|
||||
// Skip over backslash escapes, and end single quoted
|
||||
// literals on single quotes.
|
||||
if( c == '\\' )
|
||||
{
|
||||
(*text)[w++] = (*text)[r++];
|
||||
}
|
||||
else if( c == '\'' )
|
||||
{
|
||||
state = State::Normal;
|
||||
}
|
||||
(*text)[w++] = (*text)[r++];
|
||||
break;
|
||||
|
||||
case State::DoubleQuotedString:
|
||||
// Skip over backslash escapes, and end double quoted
|
||||
// literals on double quotes.
|
||||
if( c == '\\' )
|
||||
{
|
||||
(*text)[w++] = (*text)[r++];
|
||||
}
|
||||
else if( c == '\"' )
|
||||
{
|
||||
state = State::Normal;
|
||||
}
|
||||
(*text)[w++] = (*text)[r++];
|
||||
break;
|
||||
|
||||
case State::RawString:
|
||||
// End C++11 raw strings on )" bigram.
|
||||
if( c == ')' && (*text)[r + 1] == '\"' )
|
||||
{
|
||||
(*text)[w++] = (*text)[r++];
|
||||
state = State::Normal;
|
||||
}
|
||||
(*text)[w++] = (*text)[r++];
|
||||
break;
|
||||
|
||||
case State::SingleLineComment:
|
||||
// Drop all single line comment contents until the trailing
|
||||
// newline. Only the final newline is copied to output.
|
||||
if( c == '\n' )
|
||||
{
|
||||
state = State::Normal;
|
||||
(*text)[w++] = (*text)[r++];
|
||||
line_offsets.push_back(w);
|
||||
}
|
||||
else
|
||||
{
|
||||
r++;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::BlockComment:
|
||||
// Replace block comments with spaces, and end comments on
|
||||
// */ bigram.
|
||||
if( c == '*' && (*text)[r + 1] == '/' )
|
||||
{
|
||||
state = State::Normal;
|
||||
(*text)[w++] = ' ';
|
||||
(*text)[w++] = ' ';
|
||||
r += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the byte we are replacing is already a space character,
|
||||
// then just copy it as is. Otherwise, replace it with a
|
||||
// normal whitespace. This is meant to preserve layout for
|
||||
// when block comments are used within a single line. If we
|
||||
// were to support __LINE__, this may also help in keeping that
|
||||
// consistent, but since we don't support macro expansions of
|
||||
// any kind, it doesn't matter.
|
||||
//
|
||||
// We don't have to worry about embedded newlines mess up line
|
||||
// splitting, since line splitting is done by remembering the
|
||||
// offsets of all newlines outside of all enclosed regions.
|
||||
// This means something like this works:
|
||||
// # /*
|
||||
// */ define NEXT_LINE
|
||||
if( isspace(c) )
|
||||
(*text)[w++] = c;
|
||||
else
|
||||
(*text)[w++] = ' ';
|
||||
r++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
text->erase(w);
|
||||
|
||||
// Build list of lines from line offsets.
|
||||
std::vector<std::string_view> lines;
|
||||
if( w > 0 )
|
||||
{
|
||||
lines.reserve(line_offsets.size());
|
||||
if( line_offsets.back() != w )
|
||||
line_offsets.push_back(w);
|
||||
for(size_t i = 0; i + 1 < line_offsets.size(); i++)
|
||||
{
|
||||
std::string_view l(*text);
|
||||
l.remove_prefix(line_offsets[i]);
|
||||
l.remove_suffix(l.size() - (line_offsets[i + 1] - line_offsets[i]));
|
||||
lines.push_back(l);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Remove leading and trailing spaces.
|
||||
static std::string_view Trim(std::string_view s)
|
||||
{
|
||||
const size_t l_space = s.find_first_not_of(kSpace);
|
||||
if( l_space > 0 )
|
||||
s.remove_prefix(l_space);
|
||||
const size_t r_space = s.find_last_not_of(kSpace);
|
||||
if( r_space != std::string_view::npos )
|
||||
s.remove_suffix(s.size() - r_space - 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Convert a line into our operation struct.
|
||||
static Operation ParseSingleLine(std::string_view line)
|
||||
{
|
||||
Operation op;
|
||||
op.directive = Directive::kText;
|
||||
op.arg = line;
|
||||
|
||||
// Skip past leading space and '#'.
|
||||
const size_t i = line.find_first_not_of(kSpace);
|
||||
if( i == std::string_view::npos || line[i] != '#' )
|
||||
return op;
|
||||
const size_t j = line.find_first_not_of(kSpace, i + 1);
|
||||
if( j == std::string_view::npos )
|
||||
return op;
|
||||
|
||||
// Extract directive and optional argument.
|
||||
std::string_view directive(line.substr(j));
|
||||
std::string_view arg;
|
||||
const size_t k = directive.find_first_of(kSpace);
|
||||
if( k != std::string_view::npos )
|
||||
{
|
||||
arg = Trim(directive.substr(k));
|
||||
directive.remove_suffix(directive.size() - k);
|
||||
}
|
||||
|
||||
if( directive == "define" )
|
||||
{
|
||||
// If this is a macro with parameters or substitution value, we
|
||||
// will copy the full directive (minus the leading and trailing
|
||||
// space) to output on evaluation, so we will need to preserve
|
||||
// the full line here.
|
||||
if( arg.find_first_of(kSpaceOrLeftParen) != std::string_view::npos )
|
||||
{
|
||||
op.directive = Directive::kRawDefine;
|
||||
op.arg = Trim(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
op.directive = Directive::kDefine;
|
||||
op.arg = arg;
|
||||
}
|
||||
}
|
||||
#define MATCH_DIRECTIVE(s, o) \
|
||||
else if( directive == s ) { op.directive = o; op.arg = arg; }
|
||||
MATCH_DIRECTIVE("if", Directive::kIf)
|
||||
MATCH_DIRECTIVE("elif", Directive::kElif)
|
||||
MATCH_DIRECTIVE("else", Directive::kElse)
|
||||
MATCH_DIRECTIVE("ifdef", Directive::kIfDef)
|
||||
MATCH_DIRECTIVE("ifndef", Directive::kIfNDef)
|
||||
MATCH_DIRECTIVE("endif", Directive::kEndIf)
|
||||
MATCH_DIRECTIVE("undef", Directive::kUndef)
|
||||
MATCH_DIRECTIVE("include", Directive::kInclude)
|
||||
MATCH_DIRECTIVE("warning", Directive::kWarning)
|
||||
MATCH_DIRECTIVE("error", Directive::kError)
|
||||
#undef MATCH_DIRECTIVE
|
||||
return op;
|
||||
}
|
||||
|
||||
// Convert lines to operations.
|
||||
static std::vector<Operation> ParseLines(
|
||||
const std::vector<std::string_view> &input_lines)
|
||||
{
|
||||
std::vector<Operation> output;
|
||||
output.reserve(input_lines.size());
|
||||
for(const auto &line : input_lines)
|
||||
output.push_back(ParseSingleLine(line));
|
||||
return output;
|
||||
}
|
||||
|
||||
// Check for nonempty stack.
|
||||
template <typename T>
|
||||
static void AssertNonEmptyStack(const T &stack, const char *directive)
|
||||
{
|
||||
if( stack.empty() )
|
||||
{
|
||||
fprintf(stderr, "Unmatched %s\n", directive);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
// Find branch locations. This makes skipping over conditional blocks
|
||||
// much faster. This optimization probably doesn't make sense for
|
||||
// typical code since most people aren't doing "#include __FILE__",
|
||||
// but for our purposes it makes a big difference.
|
||||
//
|
||||
// Empirically, cppp's processing time is roughly linear to the number
|
||||
// of "#include __FILE__" directives because it is able to jump to the
|
||||
// relevant #if blocks quickly. Whereas GNU's cpp exhibits quadratic
|
||||
// time because it always reads in the entire file for each #include.
|
||||
static void IndexOps(std::vector<Operation> *code)
|
||||
{
|
||||
// first = list of indices looking for #else destination.
|
||||
// second = list of indices looking for #endif destination.
|
||||
using StackFrame = std::pair<std::vector<size_t>, std::vector<size_t>>;
|
||||
std::vector<StackFrame> stack;
|
||||
for(size_t i = 0; i < code->size(); i++)
|
||||
{
|
||||
Operation *op = &((*code)[i]);
|
||||
op->true_branch = op->false_branch = i + 1;
|
||||
switch( op->directive )
|
||||
{
|
||||
case Directive::kIf:
|
||||
case Directive::kIfDef:
|
||||
case Directive::kIfNDef:
|
||||
// Start of conditional block.
|
||||
stack.push_back(StackFrame());
|
||||
stack.back().first.push_back(i);
|
||||
break;
|
||||
|
||||
case Directive::kElif:
|
||||
// End of one conditional block and start of the next block.
|
||||
// Update previous false_branch destinations to point at the
|
||||
// current conditional instruction.
|
||||
AssertNonEmptyStack(stack, "#elif");
|
||||
for(size_t f : stack.back().first)
|
||||
(*code)[f].false_branch = i;
|
||||
stack.back().first.clear();
|
||||
stack.back().first.push_back(i);
|
||||
break;
|
||||
|
||||
case Directive::kElse:
|
||||
// End of conditional block. Update previous false_branch
|
||||
// destinations to skip past current instruction directly
|
||||
// into the body of the conditional branch.
|
||||
AssertNonEmptyStack(stack, "#else");
|
||||
for(size_t f : stack.back().first)
|
||||
(*code)[f].false_branch = i + 1;
|
||||
stack.back().first.clear();
|
||||
break;
|
||||
|
||||
case Directive::kEndIf:
|
||||
// End of conditional block. Update previous false_branch
|
||||
// destinations to point at current instruction.
|
||||
AssertNonEmptyStack(stack, "#endif");
|
||||
for(size_t f : stack.back().first)
|
||||
(*code)[f].false_branch = i;
|
||||
for(size_t f : stack.back().second)
|
||||
(*code)[f].true_branch = (*code)[f].false_branch = i;
|
||||
stack.pop_back();
|
||||
|
||||
[[fallthrough]];
|
||||
|
||||
case Directive::kDefine:
|
||||
case Directive::kUndef:
|
||||
case Directive::kInclude:
|
||||
case Directive::kWarning:
|
||||
case Directive::kRawDefine:
|
||||
case Directive::kText:
|
||||
// Other non-branching directives, including #endif. If
|
||||
// we are currently inside a conditional block and the
|
||||
// next instruction ends the block, we want to update the
|
||||
// current instruction later to jump to the matching #endif.
|
||||
// #if ...
|
||||
// #if ...
|
||||
// step 1
|
||||
// #endif // this will branch to #endif just before step 2
|
||||
// #else
|
||||
// ...
|
||||
// #endif
|
||||
// step 2
|
||||
if( !stack.empty() &&
|
||||
i + 1 < code->size() &&
|
||||
((*code)[i + 1].directive == Directive::kElif ||
|
||||
(*code)[i + 1].directive == Directive::kElse) )
|
||||
{
|
||||
stack.back().second.push_back(i);
|
||||
}
|
||||
break;
|
||||
|
||||
case Directive::kError:
|
||||
// Error directives don't have any other place to go.
|
||||
op->true_branch = op->false_branch = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to evaluate a possible macro reference as integer.
|
||||
static int LookupInt(const MacroTable &table, std::string_view token)
|
||||
{
|
||||
std::unordered_set<std::string_view> used_macros;
|
||||
for(MacroTable::const_iterator f = table.find(token); f != table.end();)
|
||||
{
|
||||
if( !used_macros.insert(token).second )
|
||||
{
|
||||
fprintf(stderr, "Recursive macro reference: %s\n",
|
||||
std::string(token).c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
token = f->second;
|
||||
f = table.find(token);
|
||||
}
|
||||
int value;
|
||||
if( sscanf(std::string(token).c_str(), "%d", &value) == 1 )
|
||||
return value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Evaluate a conditional expression.
|
||||
static bool Evaluate(const MacroTable &table, std::string_view expr)
|
||||
{
|
||||
// Try matching something like "a % b > c".
|
||||
const std::string expr_string(expr);
|
||||
std::array<char, 64> token[5];
|
||||
if( sscanf(expr_string.c_str(), "%s %s %s %s %s",
|
||||
token[0].data(),
|
||||
token[1].data(),
|
||||
token[2].data(),
|
||||
token[3].data(),
|
||||
token[4].data()) == 5 )
|
||||
{
|
||||
const int value1 = LookupInt(table, std::string_view(token[0].data()));
|
||||
const std::string_view op1(token[1].data());
|
||||
const int value2 = LookupInt(table, std::string_view(token[2].data()));
|
||||
const std::string_view op2(token[3].data());
|
||||
const int value3 = LookupInt(table, std::string_view(token[4].data()));
|
||||
|
||||
#define INNER_EXPR(first_op, second_op) \
|
||||
if( op2 == #second_op ) \
|
||||
return value1 first_op value2 second_op value3
|
||||
#define OUTER_EXPR(first_op) \
|
||||
if( op1 == #first_op ) \
|
||||
{ \
|
||||
INNER_EXPR(first_op, ==); \
|
||||
INNER_EXPR(first_op, !=); \
|
||||
INNER_EXPR(first_op, <); \
|
||||
INNER_EXPR(first_op, >); \
|
||||
INNER_EXPR(first_op, <=); \
|
||||
INNER_EXPR(first_op, >=); \
|
||||
}
|
||||
OUTER_EXPR(+) else
|
||||
OUTER_EXPR(-) else
|
||||
OUTER_EXPR(*) else
|
||||
OUTER_EXPR(/) else
|
||||
OUTER_EXPR(%) else
|
||||
OUTER_EXPR(<<) else
|
||||
OUTER_EXPR(>>)
|
||||
#undef OUTER_EXPR
|
||||
#undef INNER_EXPR
|
||||
}
|
||||
|
||||
// Try matching something like "a > b";
|
||||
if( sscanf(expr_string.c_str(), "%s %s %s",
|
||||
token[0].data(),
|
||||
token[1].data(),
|
||||
token[2].data()) == 3 )
|
||||
{
|
||||
const int lhs = LookupInt(table, std::string_view(token[0].data()));
|
||||
const std::string_view op(token[1].data());
|
||||
const int rhs = LookupInt(table, std::string_view(token[2].data()));
|
||||
#define EXPR(o) if( op == #o ) return lhs o rhs
|
||||
EXPR(==);
|
||||
EXPR(!=);
|
||||
EXPR(<);
|
||||
EXPR(>);
|
||||
EXPR(<=);
|
||||
EXPR(>=);
|
||||
#undef EXPR
|
||||
}
|
||||
|
||||
// Check if there are multiple tokens in this expression. If so,
|
||||
// since it didn't match one of the earlier multi-token branches,
|
||||
// it's an expression that we don't support.
|
||||
//
|
||||
// Also check if there are operators in this expression. This is
|
||||
// a best effort attempt to issue warnings for single-token
|
||||
// expressions like "x>0" (as opposed to "x > 0"), instead of
|
||||
// silently evaluating them to zero.
|
||||
if( expr.find_first_of(kSpace) != std::string_view::npos ||
|
||||
expr.find_first_of("+-*/%^~!&|<>()") != std::string_view::npos )
|
||||
{
|
||||
fprintf(stderr, "Expression not supported by this preprocessor:\n%s\n",
|
||||
std::string(expr).c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Assume single value expression.
|
||||
return LookupInt(table, expr) != 0;
|
||||
}
|
||||
|
||||
// Execute preprocessor instructions.
|
||||
static void Run(int depth,
|
||||
const std::vector<Operation> &code,
|
||||
MacroTable *macros,
|
||||
Stats *stats)
|
||||
{
|
||||
stats->include_count++;
|
||||
if( stats->max_include_depth < depth )
|
||||
{
|
||||
if( depth > kMaxIncludeDepth )
|
||||
{
|
||||
fprintf(stderr, "Include stack too deep: %d\n", depth);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
stats->max_include_depth = depth;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < code.size();)
|
||||
{
|
||||
stats->operation_steps++;
|
||||
|
||||
const Operation &op = code[i];
|
||||
i = op.true_branch;
|
||||
switch( op.directive )
|
||||
{
|
||||
case Directive::kIf:
|
||||
case Directive::kElif:
|
||||
if( !Evaluate(*macros, op.arg) )
|
||||
i = op.false_branch;
|
||||
break;
|
||||
|
||||
case Directive::kIfDef:
|
||||
if( macros->find(op.arg) == macros->end() )
|
||||
i = op.false_branch;
|
||||
break;
|
||||
|
||||
case Directive::kIfNDef:
|
||||
if( macros->find(op.arg) != macros->end() )
|
||||
i = op.false_branch;
|
||||
break;
|
||||
|
||||
case Directive::kElse:
|
||||
case Directive::kEndIf:
|
||||
break;
|
||||
|
||||
case Directive::kDefine:
|
||||
(*macros)[op.arg] = "";
|
||||
break;
|
||||
|
||||
case Directive::kRawDefine:
|
||||
{
|
||||
const size_t j = op.arg.find_first_of(kSpace);
|
||||
const size_t k = op.arg.find_first_not_of(kSpace, j + 1);
|
||||
const size_t l = op.arg.find_first_of(kSpaceOrLeftParen, k + 1);
|
||||
const std::string_view macro_name = op.arg.substr(k, l - k);
|
||||
if( op.arg[l] == '(' )
|
||||
{
|
||||
// Macro with parameters. Since we don't do any macro
|
||||
// expansion, there is no way to expand the value
|
||||
// correctly, so we will mark the macro as defined but
|
||||
// don't bother with putting the value in our table.
|
||||
//
|
||||
// That said, we will mark the macro as having *some*
|
||||
// value, so we will copy #undef directives when we
|
||||
// encounter those.
|
||||
(*macros)[macro_name] = op.arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Marco without parameters, but with a value. Here we
|
||||
// have a chance of making some substitutions work, like
|
||||
// these kind of things:
|
||||
//
|
||||
// #define X __FILE__
|
||||
// #define Y X
|
||||
// #include Y
|
||||
//
|
||||
// So we will record the value in our table.
|
||||
const size_t value = op.arg.find_first_not_of(kSpace, l);
|
||||
if( value != std::string_view::npos )
|
||||
(*macros)[macro_name] = Trim(op.arg.substr(value));
|
||||
else
|
||||
(*macros)[macro_name] = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the whole macro directive to output, so that the next
|
||||
// preprocessor might have some chance to make use of it.
|
||||
fwrite(op.arg.data(), op.arg.size(), 1, stdout);
|
||||
fputc('\n', stdout);
|
||||
break;
|
||||
|
||||
case Directive::kUndef:
|
||||
if( const MacroTable::iterator f = macros->find(op.arg);
|
||||
f != macros->end() )
|
||||
{
|
||||
// If a macro has value, then we also want to include the
|
||||
// #undef statement in the output, otherwise a subsequent
|
||||
// #define for the same macro name might not work.
|
||||
if( !(f->second.empty()) )
|
||||
{
|
||||
fputs("#undef ", stdout);
|
||||
fwrite(op.arg.data(), op.arg.size(), 1, stdout);
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
macros->erase(f);
|
||||
}
|
||||
break;
|
||||
|
||||
case Directive::kInclude:
|
||||
{
|
||||
std::string_view include_file = op.arg;
|
||||
std::unordered_set<std::string_view> used_macros;
|
||||
for(auto f = macros->find(include_file); f != macros->end();)
|
||||
{
|
||||
if( !used_macros.insert(include_file).second )
|
||||
{
|
||||
fprintf(stderr, "Recursive macro reference: %s\n",
|
||||
std::string(include_file).c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
include_file = f->second;
|
||||
f = macros->find(include_file);
|
||||
}
|
||||
if( include_file == "__FILE__" )
|
||||
{
|
||||
Run(depth + 1, code, macros, stats);
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs("#include ", stdout);
|
||||
fwrite(op.arg.data(), op.arg.size(), 1, stdout);
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Directive::kWarning:
|
||||
fwrite(op.arg.data(), op.arg.size(), 1, stderr);
|
||||
fputc('\n', stderr);
|
||||
break;
|
||||
|
||||
case Directive::kError:
|
||||
fwrite(op.arg.data(), op.arg.size(), 1, stderr);
|
||||
fputc('\n', stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
|
||||
case Directive::kText:
|
||||
fwrite(op.arg.data(), op.arg.size(), 1, stdout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Initialize macros and load input.
|
||||
MacroTable macros;
|
||||
std::string input_text;
|
||||
int input_file_count = 0;
|
||||
for(int i = 1; i < argc; i++)
|
||||
{
|
||||
if( argv[i][0] == '-' && argv[i][1] == 'D' )
|
||||
{
|
||||
const std::string_view d(argv[i] + 2);
|
||||
const size_t e = d.find('=');
|
||||
if( e != std::string_view::npos )
|
||||
macros[d.substr(0, e)] = d.substr(e + 1);
|
||||
else
|
||||
macros[d] = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !LoadInput(argv[i], &input_text) )
|
||||
return 1;
|
||||
input_file_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// If no input is specified, try reading from stdin.
|
||||
if( input_file_count == 0 )
|
||||
LoadInputFromHandle(stdin, &input_text);
|
||||
|
||||
// Parse lines.
|
||||
std::vector<Operation> code = ParseLines(TokenizeToLines(&input_text));
|
||||
IndexOps(&code);
|
||||
|
||||
// Execute preprocessor.
|
||||
Stats stats;
|
||||
Run(0, code, ¯os, &stats);
|
||||
printf("# /* include_count = %" PRId64 " */\n", stats.include_count);
|
||||
printf("# /* operation_steps = %" PRId64 " */\n", stats.operation_steps);
|
||||
printf("# /* max_include_depth = %d */\n", stats.max_include_depth);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
# Best abuse of CPP
|
||||
|
||||
Don Yang
|
||||
<http://uguu.org/>
|
||||
|
||||
|
||||
The code for this entry can be found in prog.c
|
||||
|
||||
## Judges' comments:
|
||||
### To use:
|
||||
|
||||
make
|
||||
./prog
|
||||
|
||||
### Try:
|
||||
|
||||
echo "International Obfuscated C Code Contest 2020" | ./prog 23209 > code.c
|
||||
|
||||
# compile without a PIN
|
||||
cc code.c -o decode
|
||||
# yes, you must compile with -DPIN=<number>
|
||||
|
||||
# compile with the wrong pin
|
||||
cc -DPIN=555 code.c -o wrongcoded # wait for it ...
|
||||
./wrongcoded | hexdump -C
|
||||
# Use the wrong PIN: You're gonna have a bad time.
|
||||
|
||||
# compile with a PIN
|
||||
cc -DPIN=23209 code.c -o decoded # wait for it ...
|
||||
./decoded
|
||||
# weeeeeeeeeeeeee! You are having a good time!
|
||||
|
||||
### Selected Judges Remarks:
|
||||
|
||||
You might think of this entry as code obfuscater with a code.
|
||||
If you use the wrong code, you're gonna have a bad time.
|
||||
What is compiling this entry about?
|
||||
[Having a good .. time](https://southpark.fandom.com/wiki/Asspen/Script)!
|
||||
|
||||
This winning entry also comes with a [JavaScript timelapse
|
||||
spoiler](spoiler.html) that shows how this entry was written.
|
||||
|
||||
|
||||
## Author's comments:
|
||||
### Synopsis
|
||||
|
||||
echo "Hello world" | ./mile 12345 > encoded.c
|
||||
|
||||
gcc -DPIN=12345 encoded.c -o ./decoded && ./decoded
|
||||
|
||||
### Details
|
||||
|
||||
Mile is a C code generator. Run without arguments for the help message below:
|
||||
|
||||
usage: [PIN] < input.txt > output.c
|
||||
|
||||
where [PIN] is an integer in the range of 1..65535
|
||||
|
||||
When run with an encoding PIN (first command line argument), Mile will
|
||||
read bytes from stdin and generate C code on stdout. Example:
|
||||
|
||||
echo "Hello, world!" | ./mile 56789 > encoded.c
|
||||
|
||||
This generated code will reproduce the same input bytes on stdout when
|
||||
executed, if it was compiled with the same encoding PIN.
|
||||
|
||||
gcc -DPIN=56789 encode.c -o decoded
|
||||
|
||||
The generated code reconstructs all data at compile time by seeding a
|
||||
random number generator with the given PIN to reproduce the input bytes,
|
||||
and simply does one fwrite() call at run time to print those bytes. Not
|
||||
specifying a PIN is a compile time error. Specify the wrong PIN and
|
||||
output code will still compile, but will likely produce garbage instead of
|
||||
the input bytes. If the original input was empty then any PIN works, but
|
||||
you might get a compile time warning complaining about empty arrays.
|
||||
|
||||
Because all data is reconstructed at compile time, expect the compile time
|
||||
to be excruciatingly long if you try to encode too much data. Code
|
||||
generated by encoding a haiku will probably compile in seconds, a limerick
|
||||
may require tens of seconds, a sonnet will take minutes. Give Mile an
|
||||
epic poem and you can probably write a faster preprocessor before GCC is
|
||||
done (Clang probably already failed long before then).
|
||||
|
||||
I see that there is a lack of preprocessor abuse in recent years, probably
|
||||
because programming in preprocessor is too tedious. Mile is meant to help
|
||||
with that by producing C code filled with at least 98% preprocessor lines
|
||||
for any input. Now you too can enjoy long compile times with minimal
|
||||
effort.
|
||||
|
||||
### Features
|
||||
|
||||
+ Output is randomized using system clock, so if you wait a second between
|
||||
runs, you should get a different generated code even for the same
|
||||
encoding PIN.
|
||||
|
||||
+ Customized bytecode encoding of preprocessor boilerplates, mostly for
|
||||
compression but should also make reverse engineering more fun.
|
||||
|
||||
+ CRC32 of the source code is embedded in the source code itself.
|
||||
|
||||
+ Code layout shaped to resemble "Mile" from "Didn't I Say to Make My
|
||||
Abilities Average in the Next Life?!". Mile is known to be able to talk
|
||||
to nanomachines directly, which is exactly what programming in
|
||||
preprocessor feels like.
|
||||
|
||||
### Compatibility
|
||||
|
||||
Mile has been verified to work in these environments (also, compiles
|
||||
without warnings on all of these):
|
||||
|
||||
+ gcc 9.2.0 on Cygwin
|
||||
+ gcc 8.3.0 on Linux
|
||||
+ gcc 7.4.0 on Cygwin
|
||||
+ gcc 6.1.0 on JSLinux
|
||||
+ gcc 5.4.0 on Linux
|
||||
+ clang 8.0.1 on Linux
|
||||
+ clang 8.0.1 on Cygwin
|
||||
+ tcc 0.9.27 on Linux
|
||||
+ tcc 0.9.25 on JSLinux
|
||||
|
||||
Output of Mile is a bit more taxing, and only works with a subset of the
|
||||
above:
|
||||
|
||||
+ gcc: works just fine when using 8.3.0 and below, but may trigger warning
|
||||
for large files. gcc 9.2.0 and above may crash with large files.
|
||||
+ clang: works fine for small files, fails on large files due to
|
||||
[preprocessor bug](https://bugs.llvm.org/show_bug.cgi?id=44480).
|
||||
+ tcc: works just fine on Linux. Fails silently on JSLinux.
|
||||
|
||||
For people eager to encode large files, an extra utility has been packaged
|
||||
with this entry to preprocess the generated output to make them more
|
||||
acceptable to typical compilers. View the [cppp.cc](cppp.cc) source and
|
||||
then compile it with a c++ compiler of your choice.
|
||||
|
||||
-----------------------------------------------------------------------------------------------------
|
||||
(c) Copyright 1984-2020, [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,185 @@
|
||||
<!doctype html public "-//W3C//DTD HTML 4.0 Transitional //EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta name="GENERATOR" content="mkd2html 2.2.2">
|
||||
<meta http-equiv="Content-Type"
|
||||
content="text/html; charset=utf-8"> <link rel="stylesheet"
|
||||
type="text/css"
|
||||
href="../hint.css" />
|
||||
<title></title>
|
||||
<title>Best abuse of CPP</title>
|
||||
</head>
|
||||
<body>
|
||||
<CENTER>
|
||||
<IMG SRC="/png/ioccc.png" ALT="IOCCC" height=78 width=267>
|
||||
<P><FONT size="6"><I>The International Obfuscated C Code Contest</I></FONT></P>
|
||||
<div id="bar">
|
||||
<ul>
|
||||
<li><a href="../index.html">About the 27th IOCCC</a></li>
|
||||
<li><a href="../whowon.html">27th IOCCC Winners</a></li>
|
||||
<li><a href="/index.html">IOCCC home</a></li>
|
||||
<li><a href="/winners.html">List of All Winners</a></li>
|
||||
<li><a href="/judges.html">The Judges</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<P><FONT size="6"><B>A 27<sup>th</sup> IOCCC Winner</B></FONT></P>
|
||||
</CENTER>
|
||||
|
||||
|
||||
<h1>Best abuse of CPP</h1>
|
||||
|
||||
<p>Don Yang<br/>
|
||||
<a href="http://uguu.org/">http://uguu.org/</a></p>
|
||||
|
||||
<p>The code for this entry can be found in <a href="prog.c">prog.c</a></p>
|
||||
|
||||
<h2>Judges' comments:</h2>
|
||||
|
||||
<h3>To use:</h3>
|
||||
|
||||
<pre><code>make
|
||||
./prog
|
||||
</code></pre>
|
||||
|
||||
<h3>Try:</h3>
|
||||
|
||||
<pre><code>echo "International Obfuscated C Code Contest 2020" | ./prog 23209 > code.c
|
||||
|
||||
# compile without a PIN
|
||||
cc code.c -o decode
|
||||
# yes, you must compile with -DPIN=<number>
|
||||
|
||||
# compile with the wrong pin
|
||||
cc -DPIN=555 code.c -o wrongcoded # wait for it ...
|
||||
./wrongcoded | hexdump -C
|
||||
# Use the wrong PIN: You're gonna have a bad time.
|
||||
|
||||
# compile with a PIN
|
||||
cc -DPIN=23209 code.c -o decoded # wait for it ...
|
||||
./decoded
|
||||
# weeeeeeeeeeeeee! You are having a good time!
|
||||
</code></pre>
|
||||
|
||||
<h3>Selected Judges Remarks:</h3>
|
||||
|
||||
<p>You might think of this entry as code obfuscater with a code.
|
||||
If you use the wrong code, you’re gonna have a bad time.
|
||||
What is compiling this entry about?
|
||||
<a href="https://southpark.fandom.com/wiki/Asspen/Script">Having a good .. time</a>!</p>
|
||||
|
||||
<p>This winning entry also comes with a <a href="spoiler.html">JavaScript timelapse
|
||||
spoiler</a> that shows how this entry was written.</p>
|
||||
|
||||
<h2>Author’s comments:</h2>
|
||||
|
||||
<h3>Synopsis</h3>
|
||||
|
||||
<pre><code>echo "Hello world" | ./mile 12345 > encoded.c
|
||||
|
||||
gcc -DPIN=12345 encoded.c -o ./decoded && ./decoded
|
||||
</code></pre>
|
||||
|
||||
<h3>Details</h3>
|
||||
|
||||
<p>Mile is a C code generator. Run without arguments for the help message below:</p>
|
||||
|
||||
<pre><code>usage: [PIN] < input.txt > output.c
|
||||
|
||||
where [PIN] is an integer in the range of 1..65535
|
||||
</code></pre>
|
||||
|
||||
<p>When run with an encoding PIN (first command line argument), Mile will
|
||||
read bytes from stdin and generate C code on stdout. Example:</p>
|
||||
|
||||
<pre><code>echo "Hello, world!" | ./mile 56789 > encoded.c
|
||||
</code></pre>
|
||||
|
||||
<p>This generated code will reproduce the same input bytes on stdout when
|
||||
executed, if it was compiled with the same encoding PIN.</p>
|
||||
|
||||
<pre><code>gcc -DPIN=56789 encode.c -o decoded
|
||||
</code></pre>
|
||||
|
||||
<p>The generated code reconstructs all data at compile time by seeding a
|
||||
random number generator with the given PIN to reproduce the input bytes,
|
||||
and simply does one fwrite() call at run time to print those bytes. Not
|
||||
specifying a PIN is a compile time error. Specify the wrong PIN and
|
||||
output code will still compile, but will likely produce garbage instead of
|
||||
the input bytes. If the original input was empty then any PIN works, but
|
||||
you might get a compile time warning complaining about empty arrays.</p>
|
||||
|
||||
<p>Because all data is reconstructed at compile time, expect the compile time
|
||||
to be excruciatingly long if you try to encode too much data. Code
|
||||
generated by encoding a haiku will probably compile in seconds, a limerick
|
||||
may require tens of seconds, a sonnet will take minutes. Give Mile an
|
||||
epic poem and you can probably write a faster preprocessor before GCC is
|
||||
done (Clang probably already failed long before then).</p>
|
||||
|
||||
<p>I see that there is a lack of preprocessor abuse in recent years, probably
|
||||
because programming in preprocessor is too tedious. Mile is meant to help
|
||||
with that by producing C code filled with at least 98% preprocessor lines
|
||||
for any input. Now you too can enjoy long compile times with minimal
|
||||
effort.</p>
|
||||
|
||||
<h3>Features</h3>
|
||||
|
||||
<ul>
|
||||
<li><p>Output is randomized using system clock, so if you wait a second between
|
||||
runs, you should get a different generated code even for the same
|
||||
encoding PIN.</p></li>
|
||||
<li><p>Customized bytecode encoding of preprocessor boilerplates, mostly for
|
||||
compression but should also make reverse engineering more fun.</p></li>
|
||||
<li><p>CRC32 of the source code is embedded in the source code itself.</p></li>
|
||||
<li><p>Code layout shaped to resemble “Mile” from “Didn’t I Say to Make My
|
||||
Abilities Average in the Next Life?!”. Mile is known to be able to talk
|
||||
to nanomachines directly, which is exactly what programming in
|
||||
preprocessor feels like.</p></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3>Compatibility</h3>
|
||||
|
||||
<p>Mile has been verified to work in these environments (also, compiles
|
||||
without warnings on all of these):</p>
|
||||
|
||||
<ul>
|
||||
<li>gcc 9.2.0 on Cygwin</li>
|
||||
<li>gcc 8.3.0 on Linux</li>
|
||||
<li>gcc 7.4.0 on Cygwin</li>
|
||||
<li>gcc 6.1.0 on JSLinux</li>
|
||||
<li>gcc 5.4.0 on Linux</li>
|
||||
<li>clang 8.0.1 on Linux</li>
|
||||
<li>clang 8.0.1 on Cygwin</li>
|
||||
<li>tcc 0.9.27 on Linux</li>
|
||||
<li>tcc 0.9.25 on JSLinux</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>Output of Mile is a bit more taxing, and only works with a subset of the
|
||||
above:</p>
|
||||
|
||||
<ul>
|
||||
<li>gcc: works just fine when using 8.3.0 and below, but may trigger warning
|
||||
for large files. gcc 9.2.0 and above may crash with large files.</li>
|
||||
<li>clang: works fine for small files, fails on large files due to
|
||||
<a href="https://bugs.llvm.org/show_bug.cgi?id=44480">preprocessor bug</a>.</li>
|
||||
<li>tcc: works just fine on Linux. Fails silently on JSLinux.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>For people eager to encode large files, an extra utility has been packaged
|
||||
with this entry to preprocess the generated output to make them more
|
||||
acceptable to typical compilers. View the <a href="cppp.cc">cppp.cc</a> source and
|
||||
then compile it with a c++ compiler of your choice.</p>
|
||||
<BR>
|
||||
<HR>
|
||||
<TABLE><TR>
|
||||
<TD><a rel="license" href="https://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="/png/by-sa-3.0-88x31.png" /></a></TD>
|
||||
<TD><P>© Copyright 1984-2020,
|
||||
<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="https://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.</P></TD>
|
||||
<TD> <!--<a href="https://validator.w3.org/check?uri=referer"><img src="https://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,37 @@
|
||||
#\
|
||||
define C(c /**/)#c
|
||||
/*size=3173*/#include<stdio.h>
|
||||
/*crc=b7f9ecff.*/#include<stdlib.h>
|
||||
/*Mile/Adele_von_Ascham*/#include<time.h>
|
||||
typedef/**/int(I);I/*:3*/d,i,j,a,b,l,u[16],v
|
||||
[18],w[36],x,y,z,k;char*P="\n\40(),",*p,*q,*t[18],m[4];
|
||||
void/**/O(char*q){for(;*q;q++)*q>32?z=111-*q?z=(z+*q)%185,(k?
|
||||
k--:(y=z%37,(x=z/37%7)?printf(*t,t[x],y?w[y-1]:95):y>14&&y<33?x
|
||||
=y>15,printf(t[15+x],x?2<<y%16:l,x?(1<<y%16)-1:1):puts(t[y%28])))
|
||||
,0:z+82:0;}void/**/Q(I(p),I*q){for(x=0;x<p;x++){q[x]=x;}for(;--p
|
||||
>1;q[p]=y)y =q[x=rand()%-~p],q[x]=q[p];}char/**/n[999]=C(Average?!nQVQd%R>Rd%
|
||||
R% %RNIPRfi#VQ}R;TtuodtsRUd%RUd%RUOSetirwf!RnruterR{RTSniamRtniQ>h.oidts<edulc
|
||||
ni #V>rebmun<=NIPD-RhtiwRelipmocResaelPRrorre#QNIPRfednfi#V__ELIF__R_
|
||||
Re nifed#V~-VU0V;}V{R= R][ORrahcRdengisnuRtsnocRcitatsVesle#Vfidne#V53556
|
||||
. .1RfoRegnarRehtRniRre getniRnaRsiR]NIP[R erehwQQc.tuptuoR>Rtxt.tupniR
|
||||
< R]NIP[R:egasuV_Redulcn i#VfednfiVfednuVenife dVfedfiVQc%Rs%#V);I/**/main(
|
||||
I( f),char**e){if(f){for(i= time(NULL),p=n,q= n+998,x=18;x;p++){*p>32&&!(
|
||||
*--q=*p>80&&*p<87?P[*p- 81]:* p)?t [( -- x)]=q+1:q;}if(f-2||(d=atoi
|
||||
(e[1]))<1||65536<d){;O(" \""); goto O;}srand(i);Q(16,u);i=0;Q(
|
||||
36,w);for(;i<36; i++){w[i] +=w [i]<26 ? 97:39; }O(C(ouoo9oBotoo%]#
|
||||
ox^#oy_#ozoou#o{ a#o|b#o}c# o~d#oo-e #oo. f#oo/g#oo0h#oo1i#oo
|
||||
2j#oo3k#oo4l#o p));for(j =8;EOF -(i= getchar());l+=1){a=1+
|
||||
rand()%16;for(b =0;b<a||i- main (0,e);b++)x=d^d/4^d/8^d/
|
||||
32,d= (d/ 2|x<<15)&65535; b|= !l<<17;Q(18,v);for(a=0;a<18;
|
||||
a++ ){if( (b&(1<<(i=v[a] ))))* m=75+i,O(m),j=i<17&&j<i?i:j;}O(C(
|
||||
!) ); }O(C(oqovoo97o /n!));i= 0;for(;i<8;O(m))m[2]=35,*m=56+u[i],m[1
|
||||
]= 75 +i++;O(C(oA!oro oqoo9) );k=112-j*7;O(C(6o.!Z!Z#5o-!Y!Y#4~!X!X#3}
|
||||
!W !W #2 |!V!V#1{!U!U#0z! T!T#/y!S!S#.x!R!R#-w!Q!Q#ooAv!P!P#+o#!O!O#*t!N!
|
||||
N# oo >s!M!M#oo=r!L!L#oo<q!K!K# &pIo@:;= oUm#oo98m##oo9=8m#oo9oUm###oo9;=8m#o
|
||||
o9 oUm##oo9=oUm#oo98m#### o09] #o1:^#o2;_#o3<o ou#o4=a#o5>b#o6?c#o
|
||||
7@d#o8A e#o 9B f#o:Cg#o; D h#o<Ei #o=Fj#o> Gk#o?Hl#oo9os#####
|
||||
));d=0 ;} O: for(x=y=0;x<8;++
|
||||
x)y|= d&(1<<u[x])?
|
||||
1<< x:0;return
|
||||
/* :9 */
|
||||
y ; }
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title>Replay text</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||
<style type="text/css">
|
||||
.tmp{color:#00f}.final{color:#000}</style>
|
||||
</head><body>
|
||||
<noscript><span style="color:red">JavaScript required</span></noscript>
|
||||
<script type="text/javascript">
|
||||
var realtime_step=80;var fps=1000/realtime_step;var current_event=0;var current_time;var step=0;var slots=new Array(864);var update_slots=new Array(slots.length);var max_time=20507000;var max_file_size=349;var timer=null;var follow=1;var fast_scroll=1;var opacity=100;var opacity_timer=null;var string_table=
|
||||
[" Hl#oo9os####oo","e#QNIPRfednfi#V__ELIF__R_Renifed#V~-VU0V;}V{R=R][ORrahcRdengisnuRtsnocRcitatsVesle#Vfidne#V5 3556..1RfoRegnarRehtRniRregetniRnaRsiR]NIP[RerehwQQc.tuptuoR>Rtxt.tupniR<R]NIP[R:egasuV_Redulcni#VfednfiVfednuVenifedVfedfiVQc%Rs%#V),","if(f-2||(d=atoi(e[1]))<","tep_bits","n[999] =","\"#\"","d>","*m=75+i,O(m),j=i<17&&j<i?i:","o1i#","f,char**e){if(f){",") |","#define BIT%d\\n\"","s!M!M#oo=r!L!L#oo<q!K!K #&pIo@:; =","()","rand()","a]))))*m=75+i,O(m),j=i<17&&j<i?i:","utput",";Q(36,","/* #define STEP%d */","put","c *p, *q, *string_table[17], cmd[4], string_data[999] =","b#o}c#o~ d#oo -e#oo.f#oo/g#oo0h#o","16)","<18;","3556..1RfoRegnarRehtRniRregetniRnaRsiR]NIP[RerehwQQc.tuptuoR>Rtxt.tupniR<R]NIP[R:egasuV_Redulcni#VfednfiVfednuVenifedVfedfiVQc%Rs%#V),","+i++","=0;a",",","6..1","l","2|!V!V#1{!U!U#0","0;} x= y=0;for(;x<8;x++","TPUT_BYTE","huffledArray(16, bit_indices);","m[ 1]=75+i++;O(C(o A!oroo 9));k=112-j*7;O(C(6o.!Z!Z#5o-!Y!Y#4~!X!X#","_R enifed#V~-VU0V;}V{R =R][ORrahcRdengisnuRtsnocRcitatsVesle#Vfidne#V5355","\\n\"","I d, i, j, a, b, l, u[16],","P","!(","%R %%RNIPRfi#VQ}R;TtuodtsRUd%RUd%RUOSetirwf!RnruterR{RTSniamRtniQ>h.oidts<edul","4","|","}","{",">","5","80&&*p<","y","-","B","E","\'","]#","g",":","N","S","7","[","w)",".","O (\"\\\"\" )","x= y=0;for(;x<8;x++","?","R enifed#V~-VU0V;}V{R =R][ORrahcRdengisnuRtsnocRcitatsVesle#Vfidne#V5355","U","&","kString * 37 + 35","G"," */"," /* Initialize minimum number of ste","$","void (I p, I *q)","z!T!T#/y!S!S#.x!R!R#-w!Q!Q#ooAv!P!P#+u!O!O#*t!N"," {O (\"\\\"\" )","9","D","^"," /* Output directives to advance PRNG steps, with shuffle"," >1;q[p]=y)y =q[x=rand()%-~p],q[x]=q[p];}char/**/n[4096]=C(XVQd%R>Rd%R%%RNIPRfi#VQ}R;TtuodtsRUd%RUd%RUOSetirwf!RnruterR{RTSniamRtniQ>h.oidts<edulcni#V>rebmun<=NIPD-RhtiwRelipmocResaelPRrorre#QNIPRfednfi#V__ELIF__R_Renifed#V~-VU0V;}V{R=R][ORrahcRdengisnuRtsnocRcitatsVesle#Vfidne#V53556..1RfoRegnarRehtRniRregetniRnaRsiR]NIP[RerehwQQc.tuptuoR>Rtxt.tupniR<R]NIP[R:egasuV_Redulcni#VfednfiVfednuVenifedVfedfiVQc%Rs%#V);"," int code = byte ","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"," puts(\"#Define BI"," {"," {a=rand()%16+1"," /* Serialize PRNG state to b","L","XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX);I/**/main"," m[1] = 75 + i++,"," } &"," }","A","Y","q"," }"," puts(\"#de"," /* #endif */"," /* #endif */"," i = int(ran"," max_Step_b"," puts(\"#define output","C *p, *q, *string_table[17], cmd[4], string_data[999] ="," #ifdef BIT%d */"," #include _"," #define STEP2"," \"#endif\\n\" /* #ifdef OUTPUT_BYTE */"," /* 33 */ \"#include _\","," OUtpu"," Output(b2 ? /* #ifdef BIT2 */ - ("," THis is"," Output(/* #define _ __FILE","F"," #endif */"," #ifdef BIT%d */"," /* #endif */"," /* #else */"," : q;}"," #define STEP1"," #endif */"," for(b = 0; b < a || main(0, NULL) - i; b++)x=d^d "," w[i] += w[i] < 26 ? 97 : 39;O(C(ouoo9"," w);for(i=0;i<36; i++){w[i] +=w [i]<26 ? 97:39 ;}O(C(ouoo9oBotoo%]#"," O( 6o.!Z!Z# 5o-!Y!Y# 4~!X!X# 3}!W!W# 2|!V!V# 1{!U!U# 0z!T!T# /y!S!S# .x!R!R# -w!Q!Q# ooAv!P!P# +u!O!O# *t!N!N# oo>s!M!M# oo=r!L!L# oo<q!K!K# & p I o@ :;=oUm#oo98m##oo9=8m#oo9oUm###oo9;=8m#oo9oUm##oo9=oUm#oo98m#### o09]# o1:^# o2;_# o3<`# o4=a# o5>b# o6?c# o7@d# o8Ae# o9Bf# o:Cg# o;Dh# o<Ei# o=Fj# o>Gk# o?Hl# oo9 os # # # # # \");"," int opcode = bytecode / 36 % 7, operand = bytecode $ "," {"," { for(x = 0; x < p; x++) { q[x] = x; } for(; --p > 1; q[p] = y) y = q[x = rand() % -~p], q[x] = q[p]; }"," }","@d#o8Ae #o9 Bf #o:Cg#o;D h #o<Ei# o=Fj#o>G k#o?","static void Output( bytecode)"," \"where [encoding pin] is integer between [1,65535]\\n"," : byte + 82;"," #define STEP1"," /* SEcon"," WE wa"," ((0 * 37 + 35) << 16) |"," /* #endif */"," x = 17; x; p++)"," #define outpu"," in shuffled order so that it\'s less obvious which bits"," m[1] = 74 + ++i + 74"," w[i] += 26>w[i] <"," (e[1]))<1||65536<d){;O(\" \\\"\"); goto O;}srand(i);Q(16,u);Q(36,w"," );for(i=0;i<36;++ i){w[i]+= w[i ]<26? 97 :39;} O(C(ouoo9oBotoo%]#"," /* 6 */ \"static const unsigned char Z[] = {\","," ;"," This is needed since we use both"," \"#ifdef %c\", \"#ig"," for(b2 = 2; b2Line truncated
|
||||
var events=
|
||||
[0,500,0,0,0,0,0,1000,1,715,0,0,500,500,0,0,0,0,500,1000,0,0,0,0,0,778750,1,715,11,1,1000,250,0,0,0,1,0,777750,2,715,2,1,250,250,0,0,0,2,0,777500,2,715,9,1,250,250,0,0,0,3,0,777250,2,715,4,1,250,250,0,0,0,4,0,777000,2,715,6,1,250,142,0,0,0,5,0,776750,2,715,1,1,142,143,0,0,0,6,0,776608,2,715,9,1,143,143,0,0,0,7,0,776465,2,715,0,1,143,143,0,0,0,8,0,776322,2,715,38,1,143,143,0,0,0,9,0,776179,2,715,37,1,143,143,0,0,0,10,0,776036,2,715,56,1,143,143,0,0,0,11,0,143,1,717,0,0,143,500,0,0,1,0,0,775750,1,717,11,1,500,500,0,0,1,1,500,166,0,0,1,1,0,774750,2,717,1,1,166,167,0,0,1,2,0,774584,2,717,29,1,167,167,0,0,1,3,0,774417,2,717,12,1,167,166,0,0,1,4,0,774250,2,717,1,1,166,167,0,0,1,5,0,167,1,863,0,0,167,167,0,0,2,0,0,409167,1,863,11,1,167,125,0,0,2,1,0,409000,2,863,1,1,125,125,0,0,2,2,0,408875,2,863,4,1,125,125,0,0,2,3,0,408750,2,863,6,1,125,125,0,0,2,4,0,408625,2,863,2,1,125,125,0,0,2,5,0,408500,2,863,9,1,125,125,0,0,2,5,125,125,0,0,2,5,125,125,0,0,1,4,125,250,0,0,1,0,0,250,1,716,0,0,250,250,0,0,1,0,0,4416,1,716,0,3,250,250,0,0,1,3,0,4166,2,716,11,1,250,250,0,0,1,4,0,3916,2,716,1,1,250,166,0,0,1,5,0,3666,2,716,14,1,166,167,0,0,1,6,0,3500,2,716,14,1,167,167,0,0,1,7,0,3333,2,716,8,1,167,166,0,0,1,8,0,3166,2,716,14,1,166,167,0,0,1,9,0,3000,2,716,0,1,167,167,0,0,1,10,0,2833,2,716,38,1,167,111,0,0,1,11,0,2666,2,716,29,1,111,111,0,0,1,12,0,2555,2,716,1,1,111,111,0,0,1,13,0,2444,2,716,15,1,111,111,0,0,1,14,0,2333,2,716,12,1,111,111,0,0,1,15,0,2222,2,716,1,1,111,111,0,0,1,16,0,2111,2,716,0,1,111,111,0,0,1,17,0,2000,2,716,20,1,111,111,0,0,1,18,0,1889,2,716,8,1,111,112,0,0,1,19,0,1778,2,716,34,1,112,100,0,0,1,20,0,1666,2,716,19,1,100,100,0,0,1,21,0,1566,2,716,2,1,100,100,0,0,1,22,0,1466,2,716,29,1,100,100,0,0,1,23,0,1366,2,716,1,1,100,100,0,0,1,24,0,1266,2,716,0,1,100,100,0,0,1,25,0,1166,2,716,60,1,100,100,0,0,1,26,0,1066,2,716,2,1,100,100,0,0,1,27,0,966,2,716,3,1,100,100,0,0,1,28,0,866,2,716,33,1,100,100,0,0,1,29,0,766,2,716,0,1,100,333,0,0,1,30,0,666,2,716,49,1,333,333,0,0,1,31,0,333,2,716,6,1,333,334,0,0,1,32,0,768084,1,716,975,31,334,333,0,0,1,31,0,767750,2,716,77,1,333,333,0,0,1,32,0,767417,2,716,38,1,333,334,0,0,1,33,0,767084,2,716,37,1,334,333,0,0,1,34,0,766750,2,716,56,1,333,333,0,0,1,35,0,766417,2,716,26,1,333,334,0,0,1,36,0,766084,2,716,23,4,334,142,0,0,1,37,0,765750,2,716,4,1,142,143,0,0,1,38,0,765608,2,716,16,1,143,143,0,0,1,39,0,765465,2,716,34,1,143,143,0,0,1,40,0,765322,2,716,21,1,143,143,0,0,1,41,0,765179,2,716,1,1,143,143,0,0,1,42,0,765036,2,716,14,1,143,143,0,0,1,43,0,764893,2,716,45,4,143,200,0,0,1,43,200,200,0,0,1,43,200,200,0,0,2,4,200,200,0,0,3,0,0,200,1,718,0,0,200,200,0,0,3,0,0,1000,1,718,0,3,200,200,0,0,3,3,0,800,2,718,11,1,200,200,0,0,3,4,0,600,2,718,2,1,200,200,0,0,3,5,0,400,2,718,9,1,200,200,0,0,3,6,0,200,2,718,4,1,200,200,0,0,3,7,0,200,1,718,376,6,200,250,0,0,3,6,0,762750,1,718,376,5,250,250,0,0,3,5,0,762500,2,718,9,1,250,250,0,0,3,6,0,762250,2,718,4,1,250,250,0,0,3,7,0,762000,2,718,6,1,250,250,0,0,3,8,0,761750,2,718,1,1,250,250,0,0,3,9,0,761500,2,718,9,1,250,250,0,0,3,10,0,761250,2,718,0,1,250,250,0,0,3,11,0,761000,2,718,35,1,250,250,0,0,3,12,0,1145888,1,753,0,3,250,250,0,0,4,3,0,1145638,2,753,11,1,250,250,0,0,4,4,0,1145388,2,753,1,1,250,250,0,0,4,5,0,1145138,2,753,29,1,250,111,0,0,4,6,0,1144888,2,753,12,1,111,111,0,0,4,7,0,1144777,2,753,1,1,111,111,0,0,4,8,0,394778,1,862,0,3,111,111,0,0,5,3,0,394667,2,862,11,1,111,111,0,0,5,4,0,394556,2,862,1,1,111,111,0,0,5,5,0,394445,2,862,4,1,111,111,0,0,5,6,0,394334,2,862,6,1,111,111,0,0,5,7,0,394223,2,862,2,1,111,112,0,0,5,8,0,394112,2,862,9,1,112,333,0,0,5,8,333,333,0,0,5,8,333,334,0,0,4,7,334,500,0,0,4,3,0,3833,1,719,0,3,500,500,0,0,4,3,500,333,0,0,4,3,0,2833,2,719,0,3,333,333,0,0,4,6,0,2500,2,719,11,1,333,334,0,0,4,7,0,2167,2,719,6,1,334,111,0,0,4,8,0,1833,2,719,1,1,111,111,0,0,4,9,0,1722,2,719,9,1,111,111,0,0,4,10,0,1611,2,719,2,1,111,111,0,0,4,11,0,1500,2,719,4,1,111,111,0,0,4,12,0,1389,2,719,1,1,111,111,0,0,4,13,0,1278,2,719,0,1,111,111,0,0,4,14,0,1167,2,719,35,1,111,111,0,0,4,15,0,1056,2,719,0,1,111,112,0,0,4,16,0,945,2,719,35,1,112,166,0,0,4,17,0,833,2,719,35,1,166,167,0,0,4,18,0,667,2,719,9,1,167,167,0,0,4,19,0,500,2,719,2,1,167,166,0,0,4,20,0,333,2,719,29,1,166,167,0,0,4,21,0,167,2,719,1,1,167,167,0,0,4,22,0,167,1,719,336,21,167,250,0,0,4,21,0,250,1,719,336,20,250,250,0,0,4,20,0,250,1,719,336,19,250,250,0,0,4,19,0,1100,1,719,335,18,250,250,0,0,4,18,0,850,2,719,112,1,250,200,0,0,4,19,0,600,2,719,37,1,200,200,0,0,4,20,0,400,2,719,87,1,200,200,0,0,4,21,0,200,2,719,112,1,200,200,0,0,4,22,0,752150,1,719,335,21,200,200,0,0,4,21,0,751950,2,719,51,1,200,250,0,0,4,22,0,751750,2,719,35,1,250,250,0,0,4,23,0,751500,2,719,35,1,250,250,0,0,4,23,250,250,0,0,4,23,250,500,0,0,5,6,0,862000,1,721,0,6,500,500,0,0,5,6,0,861500,1,720,0,0,500,333,0,0,6,6,0,861000,2,721,18,1,333,333,0,0,6,7,0,860667,2,721,7,1,333,334,0,0,6,8,334,333,0,0,6,8,0,860000,2,721,0,1,333,333,0,0,6,9,0,859667,2,721,37,1,333,334,0,0,6,10,0,859334,2,721,4,1,334,200,0,0,6,11,0,859000,2,721,2,1,200,Line truncated
|
||||
events.push(1);events.push(0);function e(id){return document.getElementById(id);}function Init(){var t0=0;for(var i=0;i<events.length;i+=6){events[i]+=t0;t0=events[i];if(events[i+1]==0) events[i+1]=max_time+1;else events[i+1]+=events[i];}var background='<pre style="margin:0px">\n';for(var i=0;i<max_file_size;i++)
|
||||
background+="\n";background+="<\57pre>";var layer_style="position:absolute;top:0px;left:0px;margin:0px;";document.write(
|
||||
'<div style="position:fixed;top:0px;right:0px;padding:2px;background-color:#fff;border:1px solid #00f;z-index:3" onmouseover="ForegroundControl()" onmouseout="BackgroundControl()" id=control><input type=button value="play 1x" onclick="Play(1)"> <input type=button value="play 3x" onclick="Play(3)"> <input type=button value="play 10x" onclick="Play(10)"> <input type=button value="play 30x" onclick="Play(30)"> <input type=button value="play 60x" onclick="Play(60)"> <input type=button value="pause" onclick="Pause()"> <input type=button value="rewind" onclick="Rewind()"><br><input type=checkbox id=follow name=follow onclick="Follow(this.checked)" checked> <label for=follow>Follow cursor (<span id=cursor_text><\57span>)<\57label> <input type=checkbox id=fast name=fast onclick="FastScroll(this.checked)" checked> <label for=fast>Fast scroll<\57label> <input type=checkbox id=tmp name=tmp onclick="Highlight(this.checked)" checked> <label for=tmp>Highlight transient lines<\57label><br><input type=range name=position id=position min=0 max='+max_time+' step=1 value=0 style="width:400px" onchange="InputSeek(this.value)"> <span id=time><\57span><\57div><div style="position:relative;border:1px solid">'+background+
|
||||
'<pre style="'+layer_style+'background-color:#fff;color:#fff;z-index:1" id=cursor><\57pre><pre style="'+layer_style+'color:#000;z-index:2" id=text><\57pre><\57div>');var output=e("text");for(var i=0;i<slots.length;i++){var span=document.createElement("span");span.id="slot"+i;output.appendChild(span);}Rewind();}function Animate(){Seek(current_time+step);UpdateProgress();}function Play(speed){step=realtime_step*speed;if(timer)
|
||||
clearTimeout(timer);timer=setTimeout("Animate()",realtime_step);}function Pause(){if(step!=0){step=0;Seek(current_time);UpdateProgress();}else{Play(1);}}function Reset(){for(var i=0;i<slots.length;i++){slots[i]=null;update_slots[i]="";var x=e("slot"+i);x.innerHTML="";x.className="tmp";x.style.display="none";}current_event=0;}function Scroll(){var c=e("cursor_block");if(c){var y=c.offsetTop;y-=64;if(y<0) y=0;var delta=y-window.scrollY;if(!fast_scroll||(delta>-80 && delta<window.innerHeight-64))
|
||||
delta/=fps;if(Math.abs(delta)>0){window.scrollTo(window.scrollX,window.scrollY+delta);return true;}}return false;}function ResetScroll(){if(Scroll())
|
||||
timer=setTimeout("ResetScroll()",realtime_step);}function Rewind(){Reset();step=0;Seek(0);UpdateProgress();if(follow)
|
||||
ResetScroll();}function NextFrame(){if(step>0){if(timer)
|
||||
clearTimeout(timer);timer=setTimeout("Animate()",realtime_step);}if(follow)
|
||||
Scroll();}function Seek(position){current_time=position;if(current_time<0)
|
||||
current_time=0;if(current_time>=max_time){current_time=max_time;step=0;}var t=Math.floor(current_time/1000);var s=t%60;var m=Math.floor(t/60);t=m+(s>9?":":":0")+s;e("time").innerHTML=t;if(events[current_event]>current_time){if(current_event>0 && events[current_event-6]<current_time){NextFrame();return;}Reset();}var min_slot=slots.length;var max_slot=0;var cursor_event=-1;for(;events[current_event]<=current_time;current_event+=6){var end_time=events[current_event+1];if(end_time<=current_time)
|
||||
continue;var type=events[current_event+2];if(type==0||type>3){cursor_event=current_event;}else{var i=events[current_event+3];if(type==3){slots[i]=null;}else{var text=string_table[events[current_event+4]].substr(
|
||||
0,events[current_event+5]);if(type==1||slots[i]==null) slots[i]=text;else slots[i]+=text;}if(min_slot>i) min_slot=i;if(max_slot<i) max_slot=i;update_slots[i]=(end_time>=max_time?2:1);}}for(var i=min_slot;i<=max_slot;i++){if(update_slots[i]){var span=e("slot"+i);if(slots[i]!=null){span.innerHTML=slots[i]+"\n";span.style.display="inline";span.className=(update_slots[i]==1?"tmp":"final");}else{span.innerHTML="";span.style.display="none";}update_slots[i]=0;}}if(cursor_event>=0){var text="";var y;var x=events[cursor_event+5];if(events[cursor_event+2]==0){y=events[cursor_event+4];for(var i=0;i<y;i++)
|
||||
text+="\n";for(var i=0;i<x;i++)
|
||||
text+=" ";text+='<span style="background-color:#0f0" id=cursor_block> <\57span>';}else{var s=events[cursor_event+3];for(var i=y=0;i<s;i++){if(slots[i]!=null){y++;text+="\n";}}var line=slots[s]||"";var offset=events[cursor_event+4];while(line.length<=offset)
|
||||
line+=" ";text+=line.substr(0,offset)+
|
||||
'<span style="background-color:#0f0;color:#0f0" id=cursor_block>'+
|
||||
line.substr(offset,events[cursor_event+2]-3)+
|
||||
'<\57span>';}e("cursor").innerHTML=text;e("cursor_text").innerHTML=(y+1)+','+(x+1);}NextFrame();}function InputSeek(position){var t=parseInt(position);if(t>=0)
|
||||
Seek(t);}function UpdateProgress(){e("position").value=current_time;}function Follow(state){follow=state;}function FastScroll(state){fast_scroll=state;}function Highlight(state){var x=document.styleSheets;for(var i=0;i<x.length;i++){var y=x[i].cssRules;for(var j=0;j<y.length;y++){if(y[j].selectorText==".tmp"){y[j].style.color=state?"#00f":"#000";return;}}}}function ForegroundControl(){e("control").style.opacity="1.0";opacity=100;if(opacity_timer){clearTimeout(opacity_timer);opacity_timer=null;}}function BackgroundControl(){e("control").style.opacity=opacity/100;if(opacity>35){opacity-=5;opacity_timer=setTimeout("BackgroundControl()",100);}}Init();</script>
|
||||
</body></html>
|
||||
Reference in new issue
Block a user