|
|
One of the interesting things that one can do as a geek is to try to write interesting programs in various languages all of which fit in the size of a standard 4 line .signature file. I got interested in doing this after I saw various interesting programs to do RC4 encryption in both perl and C in very few lines.
Since I am a graphics guy, I decided that making a program to output the Mandelbrot set might be a nice place to start. I managed to get that program down to a mere 2 lines:
float e,a,b,c,d;main(i){for(;b<4;b+=.1){for(a=0;a<4;a+=1./19){c=d=0;for(i=99;--
i&&c*c+d*d<4;)e=c*c-d*d+a-2,d=2*c*d+b-2,c=e;putchar("X =."[i&3]);}puts("");}}
Because there was so much extra room left, there was plenty of space to add all sorts of vain, big letters to make the .signature a bit more flashy. I also changed all the variable names to letters of my normal login.
/* __ __ __ ____ __ */ float m,a,r,k,v;main(_){for(;r<4;r+=.1){for(a=0;
/*| \/ |\ \ / /\ \ / / */ a<4;a+=.06){k=v=0;for(_=99;--_&&k*k+v*v<4;)m=k*k
/*| |\/| | \ V / \ \/\/ / */ -v*v+a-2,v=2*k*v+r-2,k=m;putchar("X =."[_&3]);}
/*|_| |_ark\_/ande\_/\_/ettering <markv@telescopemaking.org> */ puts("");}}
The brainf*ck programming language is a simple programming language inspired by Turing Machines. The brainf*ck machine contains 9999 bytes of addressable memory which are initialized to zero and a single data pointer which is initialized to point to the beginning of storage. The brainf*ck language consists of 8 single letter commands:
| < | Move the data pointer to the left |
| > | Move the data pointer to the right |
| + | Increment the byte under the data pointer |
| - | Decrement the byte under the data pointer |
| , | Read a character and store it under the data pointer |
| . | Print the byte under the data pointer |
| [ | If the byte under the data pointer is nonzero, continue execution at the next character, otherwise jump past the next matching bracket. |
| ] | Jump back to the matching open bracket. |
All other characters in a brainf*ck program are silently ignored.
The first thinrg I wrote was a compiler that will accept brainf*ck programs from standard input and write C code as standard output.
#define P(C,X)case C:printf(#X);break; /* bftoc.c by markv@pixar.com */
main(){int c;printf("char a[9999];main(){char*p=a;");while((c=getchar())>=0)
{switch(c){P(62,p++;)P(60,p--;)P(43,++*p;)P(45,--*p;)P(46,putchar(*p);)P(44,
*p=getchar();)P(91,while(*p){)P(93,});}}printf("exit(0);}");exit(0);}
Next I had to write some simple programs that would print my email address. Some examples are follow:
>+++++++++[-<++++++++++++>]<+.>++[-<------>]<.>++++[-<++++>]<+.-------.++++++++ +++.>++++++[-<--------->]<.>++++++[-<++++++++>]<.-------.>+++[-<+++++>]<.>++++[ -<------>]<+.>++++[-<++++>]<+.>++++++++[-<-------->]<----.>+++++++[-<+++++++>]< ++++.>++++[-<+++>]<.--.>+++++++++[-<----------->]< my brainf*ck .signature
and
>+++++++++[-<++++++++++++>]<+.>++[-<------>]<.>++++[-<++++>]<+.-------.++++++++ +++.>++++++[-<--------->]<.>+++++++++++[-<+++++>]<.>+++++++++++[-<-->]<.++++++ +++++.-.--.+++++.-------.-.+++.++++++++++.-----------.>++++++[-<---------->]<++ .>+++++[-<+++++++++++++>]<.+++.-----------.>+++++++++[-<---------->]<---.
Nothing too amazing. You can write some simple programs fairly
easily in brainf*ck. The cat.bf program works fairly
well, at least on files that aren't binary.
,+[-.,+]
The RC4 or ARCFOUR algorithm is a rather nice, simple encryption algorithm, so simple in fact that it can be easily memorized. I wrote a pair of programs to do encryption and decryption. These are compabile with the Cipher Saber programs (try hunting around on the web). A couple of brief notes:
gcc
uses signed characters, so when you compile, add -funsigned-char to
your CFLAGS.
"/dev/urandom".
I used the following command lines to compile these files:
gcc -O -funsigned-char -DR=\"/dev/urandom\" -o ensaber ensaber.c gcc -O -funsigned-char -o desaber desaber.c
#define W S[a],t=S[a],S[a]=S[b],S[b]=t, /* markv@walkingfish.org desaber.c */
char a,b,t,*p,S[256],K[256];main(int c,char**v){int k=strlen(p=*++v)+10;for(;
++a;S[a]=a)if(a<k)K[a]=(a<k-10)?*(p+a):getchar();*S=a;*K=*p;a=0;do b+=K[a%k]+
W++a;while(a);b=a;while((c=getchar())>=0)a++,b+=W putchar(c^S[t+=S[a]]);}
#define W S[a],t=S[a],S[a]=S[b],S[b]=t /* markv@walkingfish.org ensaber.c */
char a,b,t,*p,S[256],K[256],k;main(c,v)char**v;{k=strlen(p=*++v);for(;++a;S[a]
=a)K[a]=p[a];*S=a;*K=*p;write(1,K+k,read(open(R,0),K+k,10));a=0;k+=10;do b+=K[
a%k]+W;while(++a);b=a;while((c=getchar())>=0)a++,b+=W,putchar(c^S[t+=S[a]]);}
Quines are programs which produce their source code when you run them. They are named after Willard van Orman Quine, an American mathematician who introduced the concept.
When you first sit down to write one of these programs, you might think it is pretty easy. After a few terrible tries, you might think it is impossible. With a bit of study however, you will realize that they in fact are pretty easy to write, although getting them small and pretty is a bit of a challenge.
Below are two that I worked on. The first is rather straightforward, and worked the first time I got it to compile. The second is a bit trickier. I hope to get a good 2 line 80 column version of it someday.
char data[] = {
0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x73,0x74,
0x64,0x69,0x6f,0x2e,0x68,0x3e,0x20,0x2f,0x2a,0x20,0x61,0x20,
0x73,0x69,0x6d,0x70,0x6c,0x65,0x20,0x69,0x66,0x20,0x73,0x6f,
0x6d,0x65,0x77,0x68,0x61,0x74,0x20,0x76,0x65,0x72,0x62,0x6f,
0x73,0x65,0x20,0x71,0x75,0x69,0x6e,0x65,0x20,0x62,0x79,0x20,
0x6d,0x61,0x72,0x6b,0x76,0x40,0x70,0x69,0x78,0x61,0x72,0x2e,
0x63,0x6f,0x6d,0x20,0x2a,0x2f,0x0a,0x6d,0x61,0x69,0x6e,0x28,
0x29,0x7b,0x69,0x6e,0x74,0x20,0x69,0x3b,0x70,0x75,0x74,0x73,
0x28,0x22,0x63,0x68,0x61,0x72,0x20,0x64,0x61,0x74,0x61,0x5b,
0x5d,0x20,0x3d,0x20,0x7b,0x22,0x29,0x3b,0x66,0x6f,0x72,0x28,
0x69,0x3d,0x30,0x3b,0x69,0x3c,0x73,0x69,0x7a,0x65,0x6f,0x66,
0x28,0x64,0x61,0x74,0x61,0x29,0x3b,0x29,0x7b,0x70,0x72,0x69,
0x6e,0x74,0x66,0x28,0x22,0x30,0x78,0x25,0x30,0x32,0x78,0x2c,
0x22,0x2c,0x0a,0x64,0x61,0x74,0x61,0x5b,0x69,0x5d,0x29,0x3b,
0x69,0x66,0x28,0x28,0x2b,0x2b,0x69,0x25,0x31,0x32,0x29,0x3d,
0x3d,0x30,0x29,0x70,0x75,0x74,0x63,0x68,0x61,0x72,0x28,0x27,
0x5c,0x6e,0x27,0x29,0x3b,0x7d,0x70,0x75,0x74,0x73,0x28,0x22,
0x7d,0x3b,0x22,0x29,0x3b,0x66,0x6f,0x72,0x28,0x69,0x3d,0x30,
0x3b,0x69,0x3c,0x73,0x69,0x7a,0x65,0x6f,0x66,0x28,0x64,0x61,
0x74,0x61,0x29,0x3b,0x69,0x2b,0x2b,0x29,0x0a,0x70,0x75,0x74,
0x63,0x68,0x61,0x72,0x28,0x64,0x61,0x74,0x61,0x5b,0x69,0x5d,
0x29,0x3b,0x7d,0x0a,};
#include <stdio.h> /* a simple if somewhat verbose quine by markv@pixar.com */
main(){int i;puts("char data[] = {");for(i=0;i<sizeof(data);){printf("0x%02x,",
data[i]);if((++i%12)==0)putchar('\n');}puts("};");for(i=0;i<sizeof(data);i++)
putchar(data[i]);}
char *p="012345678923:;4<5=19>?8@A<BCA<DA3<E86D;F945@GGHD5IID8J:5=194<0123456789:;A<=>?@AABCDEFGHIJKLMNOPQA3<L?8MMINONP66DQR";
main(c){printf("char *p=\"%s\";\n",p);for(c=115;c--;putchar("main(c){prtf\"h *=\\%s;,o15-u7[+'0]}\n"[*p++-'0']));}
This quine is a bit different than the others. It stores itself in a
DES encrypted form. It makes use of the cipher library that
is distributed as part of FreeBSD.
char p[]={
79,116, 14,208, 14,124,154,161,101, 49,209,174,199, 38,229, 73, 28,234,122,
177,225,254, 53, 76,231,122,224, 81,132, 77,149,187,139,108,169, 88, 30,126,
29, 52,173,114,213, 6,230, 75,121,122,241,212, 16,157,238, 35, 40, 77, 67,
48, 86,252, 39,231,118,162, 40, 85, 22, 85,243,237, 23, 85, 92, 85, 90,217,
205, 30, 49,225, 88, 58, 56,246, 65,151,182,101, 42,230, 23, 29, 39,114, 98,
74,243,179, 89,208, 56, 22,111,167,108,204,196,157, 84,126,111,123,253, 30,
11,248,238, 85, 19,142,117, 75,158,104, 15, 47,230,153, 30,207,140,208, 6,
127,172,220, 95, 28,188,153,118,192, 27,112, 73, 90,189,211,127, 34,223, 90,
71, 15,201, 30,166,172, 86, 46,169, 76, 43,136, 54, 88, 92,226, 57,207,211,
211, 1,118,127,119, 97,122,231, 97, 84,246,169,201,149, 59, 44, 95, 72,159,
26,116, 38, 54,130,120,172,237, 89, 55, 60,204,240, 5,212, 13,207, 81,192,
48,201,129,211,113,172,207,190,243,186,156,231, 35,237,206, 22, 39,136,113,
12,209, 14,237,148, 51,132,215,245,148,235, 27, 83,253,247,195, 31,127,100,
10,161, 7,205, 60, 85,225,214,185, 79, 56,178,133,198,116, 45,144,134,234,
166,110,113,206, 38,176,228,213, 61, 70,233,122, 32, 78, 59, 50, 44, 28, 22,
229,240, 82, 66, 42, 2, 0,217, 41,101,107, 76,194, 26,135, 48,130, 59, 2,
};
#include <unistd.h> /* cc -o c c.c -lcipher, by markv@telescopemaking.org */
main(i){printf("char p[]={\n");for(i=0;i<sizeof(p);){printf("%3d,",(unsigned
char)p[i++]);if((i%19)==0)printf("\n");}printf("};\n");des_setkey("decipher");
for(i=0;i<sizeof(p);i+=8)des_cipher(p+i,p+i,1313,16);printf("%s",p);}
For fun, try compiling and running this program from an xterm.
main(){puts("\033[?38h\033\014\035\035 b`&R(or)N c`)N a`*V*ah*V*ah(\\!gj%\\*ch"
"\"[*bh!A b`!A a`\"I(mr\"I a`%F b`&R\035)dc0V)fc4A*bh4A*ah,@)ec,@)dc/L ``/L ``"
"0V)dc0V\035 ``9Z*`h=L*bh;_!fr9D*ah6D*ah4W ``8M ``9Z\035+bw!@\037Email: mark@v"
"andewettering.net\n\035*b~!@\037WWW: http://vandewettering.net\n\033\003");}
Another attempt, graphically a bit more interesting.
main(){char*p="vandewettering.net";printf("\33[?38h\33\14\35\35(z)O\"oO!kHa(F "
"yFy,D!aDj+D\"j*](q])q+Dz,D*bDb)J\"\177&G*b\"_b F)zFq!I(qQ\"{Q!mIa E yEy#O!aOn"
"\"M\"{D(pD y%%Uy\\(z)O\35*b6Ib3M)zMt4GgN(fE#a2C'y/_(|P)kMy0AzN*bNb,W)zWq-P(k."
"F t1Ut\\(p5@)rRz6I*bI\35+w!@\37mark@%s\35*~@\37http://%s\33\3",p,p);}
To show a bit more language versatility, here is a 5 line PostScript program using some of the same ideas.
%!PS % run "gs -q -sDEVICE=nullpage sig.ps" from an xterm
/d{def}def/w{s exch write}d/b{cvi dup -5 bitshift 32 or w 31 and}d/s(%stdout)
(w)file d /Times-Roman findfont 80 scalefont setfont newpath 50 200 moveto
(mark@vandewettering.net) false charpath flattenpath /o {b 96 or w b 64 or w}d
27 w([?38h)print 27 w 12 w {29 w 2 copy o}{o}{}{o}pathforall 27 w 3 w quit