This fascinating piece of code was featured in the very first (1984) edition of the International Obfuscated C Code Contest:
http://www.ioccc.org/years.html#1984 (decot)
After clearing through the debris of preprocessor abuse and unused code caused by a goto and some sneaky comments, you end up with the following surviving code (please correct me if I am wrong!):
#include <stdio.h> //used to suppress warnings
#include <math.h> //used to suppress warnings
extern int fl00r; //renamed to not clash with floor from math.h - unless it's part of the trickery???
int b, k['a'] = {
sizeof(int(*)()),
};
struct tag {int x0,*xO;}
*main(int i, int dup, int signal) { //int added to suppress warnings
for(signal=0;*k *= * __FILE__ *i;) {
printf(&*"'\",=); /*\n\\", (*((int(*)())&fl00r))(i)); //see line 3
if(b&&k+sin(signal)/ * ((main) (b)-> xO));
}
}
There is a single compiler error left to conquer:
decot.c: In function 'main':
decot.c:12:28: error: too few arguments to function 'main'
12 | if(b&&k+sin(signal)/ * ((main) (b)-> xO));
| ^
decot.c:9:2: note: declared here
9 | *main(int i, int dup, int signal) {
| ^~~~
I suspect that the way compilers worked back in the day meant that you could somehow call main with just 1 argument even though it was specifically defined with 3 as in this case.
Is this accurate? Am I missing something? What are the least changes necessary to enable this code to compile nowadays?
I used GCC 9.2.0 with the suggested build command in the Makefile.
Thanks in advance and apologies if I have missed something very obvious!
tl;dr; your mistake was to give an ANSI C prototype to the main
function (i.e. changing i
to int i
, etc), which directed the compiler to check its arguments where it was called and cause that too few arguments
error.
Example:
echo 'int foo(a,b){return a+b;} int main(){return foo(3);}' |
cc -Wall -std=c89 -xc -
# K&R C function OK, no errors
echo 'int foo(int a, int b){return a+b;} int main(){return foo(3);}' |
cc -Wall -std=c89 -xc -
...
<stdin>:1:54: error: too few arguments to function ‘foo’
That code should be preprocessed with a traditional C preprocessor, not with an "ANSI C" one. Using a standard preprocessor will result in some artifacts, like << =
instead of <<=
, * =
instead of *=
, etc.
cpp -traditional-cpp -P decot.c > decot1.c
After adding correct function declarations, and adding a cast --see the diff & result at the end of this answer-- you get something that compiles with a single warning in c89 (and a couple of them in c99), and, as described, prints some garbage to stdout:
$ cc -std=c89 decot1.c -lm -o decot1
decot1.c: In function ‘main’:
decot1.c:13:33: warning: function called through a non-compatible type
(printf(&*"'\",x); /*\n\\", (*((int(*)())&floor))(i)));
~^~~~~~~~~~~~~~~~~~~~
$ ./decot1
'",x); /*
\
Which is exactly the same thing I get when compiling & running the original on V7 Unix, so it should be just right ;-)
double floor(double);
double sin(double);
int printf(const char*, ...);
int b,
k['a'] = {sizeof(
int(*)())
,};
struct tag{int x0,*xO;}
*main(i, dup, signal) {
{
for(signal=0;*k *= * "decot.c" *i;) do {
(printf(&*"'\",x); /*\n\\", (*((int(*)())&floor))(i)));
goto _0;
_O: while (!(k['a'] <<= - dup)) {
static struct tag u ={4};
}
}
while(b = 3, i); {
k['a'] = b,i;
_0:if(b&&k+
(int)(sin(signal) / * ((main) (b)-> xO)));}}}
$ diff -u decot1.c~ decot1.c
--- decot1.c~
+++ decot1.c
@@ -1,4 +1,6 @@
-extern int floor;
+double floor(double);
+double sin(double);
+int printf(const char*, ...);
int b,
k['a'] = {sizeof(
int(*)())
@@ -20,4 +22,4 @@
while(b = 3, i); {
k['a'] = b,i;
_0:if(b&&k+
- sin(signal) / * ((main) (b)-> xO));}}}
+ (int)(sin(signal) / * ((main) (b)-> xO)));}}}
You cannot compile the original code anymore as it is. The earliest C standard GCC claims to support is C89, and the code from this contest is from before that. You did already a good job at porting it to more modern compilers. But there are more issues left than just the number of arguments of main()
:
sin()
returns a double
, even if you don't #include <math.h>
, and refuse to do add a double
to an int *
in the subexpression k+sin(signal)
. TCC seems to accept it though.fl00r
is declared but not defined, the linker will complain about an undefined reference to it.Note that the original code can be compiled by TCC by just avoiding using x
in combinations such as <<x
(the preprocessor nowadays only substitutes complete tokens, and <<x
counts as one token), and by calling main()
with three arguments.
Your version of the code can be compiled by GCC by removing the #include
statements, going back to using floor()
, but forward declaring it as following:
floor();
To avoid complaints about sin()
, use the -fno-builtin
compiler flag. Then fix the issue with main()
by calling it like (main) (b,b,b)
.
tl;dr; your mistake was to give an ANSI C prototype to the main
function (i.e. changing i
to int i
, etc), which directed the compiler to check its arguments where it was called and cause that too few arguments
error.
Example:
echo 'int foo(a,b){return a+b;} int main(){return foo(3);}' |
cc -Wall -std=c89 -xc -
# K&R C function OK, no errors
echo 'int foo(int a, int b){return a+b;} int main(){return foo(3);}' |
cc -Wall -std=c89 -xc -
...
<stdin>:1:54: error: too few arguments to function ‘foo’
That code should be preprocessed with a traditional C preprocessor, not with an "ANSI C" one. Using a standard preprocessor will result in some artifacts, like << =
instead of <<=
, * =
instead of *=
, etc.
cpp -traditional-cpp -P decot.c > decot1.c
After adding correct function declarations, and adding a cast --see the diff & result at the end of this answer-- you get something that compiles with a single warning in c89 (and a couple of them in c99), and, as described, prints some garbage to stdout:
$ cc -std=c89 decot1.c -lm -o decot1
decot1.c: In function ‘main’:
decot1.c:13:33: warning: function called through a non-compatible type
(printf(&*"'\",x); /*\n\\", (*((int(*)())&floor))(i)));
~^~~~~~~~~~~~~~~~~~~~
$ ./decot1
'",x); /*
\
Which is exactly the same thing I get when compiling & running the original on V7 Unix, so it should be just right ;-)
double floor(double);
double sin(double);
int printf(const char*, ...);
int b,
k['a'] = {sizeof(
int(*)())
,};
struct tag{int x0,*xO;}
*main(i, dup, signal) {
{
for(signal=0;*k *= * "decot.c" *i;) do {
(printf(&*"'\",x); /*\n\\", (*((int(*)())&floor))(i)));
goto _0;
_O: while (!(k['a'] <<= - dup)) {
static struct tag u ={4};
}
}
while(b = 3, i); {
k['a'] = b,i;
_0:if(b&&k+
(int)(sin(signal) / * ((main) (b)-> xO)));}}}
$ diff -u decot1.c~ decot1.c
--- decot1.c~
+++ decot1.c
@@ -1,4 +1,6 @@
-extern int floor;
+double floor(double);
+double sin(double);
+int printf(const char*, ...);
int b,
k['a'] = {sizeof(
int(*)())
@@ -20,4 +22,4 @@
while(b = 3, i); {
k['a'] = b,i;
_0:if(b&&k+
- sin(signal) / * ((main) (b)-> xO));}}}
+ (int)(sin(signal) / * ((main) (b)-> xO)));}}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With