<- PW: Introduzione al Pascal - Copertina - SL: Intro ->

PlutoWare


In-C-omprensibile è sempre più bello!

a cura di Mano


L'articolo...

La decisione dei giudici si avvicina, noi continuiamo con la nostra carrellata sui più interessanti e meravigliosamente astrusi programmi premiati gli scorsi anni. Il nostro viaggio ora ci porta tra succose prodezze linguistiche, in vista del prossimo numero, quando presenteremo i novelli vincitori dell'edizione 2001!


Indice


Riprendiamo un discorso...

    Eccoci arrivati alla nostra seconda incursione nel meraviglioso mondo del C in-C-asinato, l'IOCCC. Per chi si fosse perso la prima, oltre all'invito a leggere l'articolo contenuto nel numero precedente del Pluto Journal, ricordiamo che dietro a tale criptico (e non potrebbe essere che così) acronimo si cela nient'altro che un concorso di programmazione. Vedo già le facce... ma la noia è solo apparente, perché ai concorrenti si chiede non solo di produrre del codice C, ma di renderlo quanto più strano possibile... si richiede che sia offuscato, innanzitutto, cioè (in poche parole) che il suo funzionamento sia quanto più possibile celato alla comprensione umana, ad una semplice lettura del sorgente; inoltre il codice dev'essere corto, conciso e "fare" più cose possibili... ed è molto gradito che sia anche bello da vedere. Neanche a dirlo, con delle regole così il risultato non può essere che... stra-ordinario!!

Ogni promessa è un debito!

    Nella scorsa puntata ci eravamo lasciati con la promessa di alcuni consigli per meglio dipanare le matasse di filo informatico che ci troviamo davanti leggendo un programma premiato. Beh, i consigli che si possono dare sono pochini:

Giusto un po' di preprocessing:
Spesso l'offuscamento viene raggiunto utilizzando macro e direttive #define, che vengono interpretate dal preprocessore prima della compilazione vera e propria. Visto che per una volta non sono usate per semplificare il codice ma per complicarlo, la prima cosa da fare è "metterci nei panni del compilatore", facendo "a manina" la parte del preprocessore. sed e l'opzione -E di gcc ci vengono in grosso aiuto, in questo.
    sed -e '/^#.*include/d' src.c|gcc -E -
Anche l'occhio vuole la sua parte:
Un'altro artificio usato per l'offuscamento è la "spaziatura selvaggia" dei blocchi. Il compilatore C, bontà sua, non fa granché distinzione se trova uno spazio, un ritorno a capo, una tabulazione, o cinquecento combinazioni di questi elementi: per lui è tutto equivalente. Questo facilita di molto il programmatore, di certo, e lo sgrava di un peso... ma i perfidi offuscatori abusano di questa grazia che è loro concessa per eliminare qualunque parvenza di struttura simil-C, o per conferire al loro codice una disposizione "carina" (a forma di trenino, di bandierina, di scritta, di circuito logico... spulciare negli archivi per credere!). Per fortuna, in molti casi basta un colpetto di indent (o di qualunque altro "beautifier" per C) per riportare le cose ad una parvenza di normalità. In molti casi. Alcune volte, i programmi sono talmente contorti che il povero indent (scritto per persone più rispettose) fallisce miseramente...

    Tutto questo spesso aiuta molto. Ma non troppo... date un'occhiata al risultato: le variabili non vogliono dire, nel migliore dei casi, nulla, e nel peggiore sono tutte talmente simili tra di loro che ci si confonde (...e maledirete chi ha disegnato il set di caratteri del terminale, che ha reso l'"uno" così simile alla "elle" minuscola! ;-), e pure le costanti spesso non sono quello che sembrano; tenete ad esempio conto che un carattere, 'P' per esempio, indica effettivamente un intero, ossia il valore ASCII della lettera P... e così via. A questo punto... un po' di debug può essere utile, e le tecniche sono quelle tradizionali. Piazzate nel codice dei printf(); che tengano traccia di dove siete, o che stampino il valore delle variabili; provate a cambiare i valori delle costanti e a vedere che succede; commentate pezzi di codice apparentemente inutili e vedete (se riuscite ancora!) cosa cambia; per finire, quando avete capito cosa indica una variabile, rinominatela, e usate (con attenzione!) il trova-sostituisci del vostro editor preferito per sostituire tutti i nomi nuovi ai vecchi. Come ultima risorsa, compilate con le opzioni -O0 -g e usate gdb per eseguire il programma passo-passo e stampare sistematicamente le variabili e i loro valori, il contenuto dello stack, eccetera. Sperando che non si confonda anche il debugger... ;o)

    Ma perché tutta questa pena? Beh, innanzitutto... perché è divertente! :) Ok, magari non siete di quelli che si divertono a scrivere linee su linee di codice, e ci sono sicuramente molti altri modi migliori di passare una giornata, ma un rompicapo è la sfida per eccellenza, da che mondo è mondo, e un po' di sana "enigmistica informatica" è un passatempo mica male! E cosa più importante, alla fine avrete imparato un sacco. Sicuro come un'automobilina a pedali. E nei programmi che avrete modo di fare in C (o nei linguaggi C-like, Java, C++, etc) potrete applicare queste conoscenze, per produrre codice scritto meglio, in meno tempo, e maggiormente ottimizzato. Tutto ciò divertendovi... :-)

    Passiamo ora a vedere qualche altro esempio... un'avvertenza: spesso, per la presenza di caratteri "strani", il copia-incolla del file C risulta impossibile. Per questo motivo, il nome del file contenuto nel titolo punta allo scaricamento del sorgente originale.

(1987) westley.c: Napoleone aveva ragione

    Il 20 febbraio 2002 è stato un giorno davvero interessante. Come radio, televisione, giornali e tutti i media hanno ripetuto fino alla nausea, chiamando perfino in causa astrologi e cartomanti, il 20/02/2002 è stata una data palindroma, cioè che si può leggere indifferentemente in un senso o nell'altro; è stata tra l'altro la prima del nuovo millennio (e l'ultima per altri 1001 anni, 1 mese e 10 giorni, ma per quella data avremo altro da fare...). Ci sarebbero parecchie considerazioni: innanzitutto, una buona fetta degli abitanti della terra non contano gli anni dalla nascita di Cristo; inoltre, gli anglosassoni scrivono questa data 02/20/2002, quindi per loro il discorso non vale; metà dei rimanenti probabilmente la scriverebbe 20/2/2002, quindi anche per loro nisba... ma non facciamo i guastafeste, è stata davvero una data carina! E ha riportato in auge i palindromi, per molto tempo relegati alle rubriche di enigmistica sui quotidiani e niente più.

    Ok, ma che c'entra? Beh, i palindromi a pensarci bene sono delle entità che si prestano molto bene a un... trattamento informatico, per così dire! Hanno una bella struttura, facile da realizzare automaticamente; è facile stampare una stringa palindroma, e verificare che lo sia... di sicuro meno facile è fare quello che ha fatto Brian Westley nel 1987, meritandosi il premio di "Best layout". E dando un'occhiata al codice è facile capire perché! Vediamo:

					char rahc
					   [ ]
					    =
					  "\n/"
					    ,
					redivider
					   [ ]
					    =
			       "Able was I ere I saw elbA"
					    ,
					    *
				     deliver,reviled
					    =
					   1+1
					    ,
			 	       niam ; main
				 	   ( )
					  {/*\}
					   \*/
					 int tni
					    =
					   0x0
					    ,
				     rahctup,putchar
					   ( )
		 	           ,LACEDx0 = 0xDECAL,
					rof ; for
				     (;(int) (tni);)
				       (int) (tni)
				  = reviled ; deliver =
					redivider
					    ;
	for ((int)(tni)++,++reviled;reviled* *deliver;deliver++,++(int)(tni)) rof
				            =
				     (int) -1- (tni)
		 	          ;reviled--;--deliver;
			 	     (tni)  =  (int)
				  - 0xDECAL + LACEDx0 -
					rof ; for
  	       (reviled--,(int)--(tni);(int) (tni);(int)--(tni),--deliver)
				    rahctup = putchar
			 	   (reviled* *deliver)
				  	    ;
				    rahctup * putchar
				    ((char) * (rahc))
					    ;
					   /*\
					  {\*/}

    Signore e signori, ecco a voi un programma palindromo! Avrete sicuramente notato che OGNI RIGA, spazi compresi, può essere letta indifferentemente in un senso o nell'altro. Se un giorno uscirà una legge che ci costringerà a scrivere da destra a sinistra, e i compilatori si adegueranno, questo sarà probabilmente l'unico pezzo di codice che potrà essere compilato così com'è. Una bella soddisfazione!

    Copiatelo in un file, nominatelo westley.c e compilatelo con gcc westley.c -o westley. L'esecuzione del binario così ottenuto è fedele al programma: stampa a video la stringa, ovviamente palindroma, "Able was I ere I saw elbA"... vale a dire, "ero capace, prima di vedere l'Elba"... evidente lamento di un Napoleone esiliato nell'isola toscana, triste e solo... probabilmente l'inizio della riscossa? Non ci è dato saperlo, ma noteremo che questa è pari pari la stringa che compare alla nona riga del programma. Questo è un ottimo indizio per cominciare a spulciare la struttura di questo programma assieme bellissimo e interessante.

    O meglio, lo sarebbe se fosse vero... perché? ;-)

(1990) westley.c: Ah, l'amore!

    San Valentino è passato da poco, e dopo una breve parentesi di romanticismo il bravo programmatore ritorna alla vita di tutti i giorni... è comune opinione che tecnologia informatica e sentimenti vadano poco d'accordo; forse perché le donne che si dedicano ad essa sono in numero inspiegabilmente minore rispetto agli uomini, o per il frequente pregiudizio che la vorrebbe "arida" e priva di quella soggettività che ben si concilia con le questioni di cuore. Eppure ogni giorno milioni di innamorati si scambiano e-mail con contenuti variabili dai giuramenti di amore eterno ai rifiuti più netti e decisi, e altri milioni di single (che non tengono particolarmente a questa qualifica) tormentano indifese margherite per sapere se l'amata/o corrisponde il loro sentimento...

    Molto poco informatico... voi dite? Eppure, ravanando negli archivi dell'IOCCC, si ritrova il buon Brian Wesley, che evidentemente nel 1990 ha passato un periodo un po' turbolento. E lo ha raccontato ai giudici, a suo modo, riuscendo anche ad aggiudicarsi un'altra volta il premio di Best Layout... praticamente un'abituée!! Ma lasciamo parlare lui:

	char*lie;

		double time, me= !0XFACE,

		not; int rested,   get, out;

		main(ly, die) char ly, **die ;{

	   	 signed char lotte,


	dear; (char)lotte--;

		for(get= !me;; not){

		1 -  out & out ;lie;{

		char lotte, my= dear,

		**let= !!me *!not+ ++die;

	   	 (char*)(lie=


	"The gloves are OFF this time, I detest you, snot\n\0sed GEEK!");

		do {not= *lie++ & 0xF00L* !me;

		#define love (char*)lie -

		love 1s *!(not= atoi(let

		[get -me?

	    	(char)lotte-


	(char)lotte: my- *love -
	
		'I'  -  *love -  'U' -

		'I'  -  (long)  - 4 - 'U' ])- !!
	
		(time  =out=  'a'));} while( my - dear

		&& 'I'-1l  -get-  'a'); break;}}

	 	   (char)*lie++;


	(char)*lie++, (char)*lie++; hell:0, (char)*lie;

		get *out* (short)ly   -0-'R'-  get- 'a'^rested;

		do {auto*eroticism,

		that; puts(*( out

		    - 'c'
	
	-('P'-'S') +die+ -2 ));}while(!"you're at it");


	for (*((char*)&lotte)^=

		(char)lotte; (love ly) [(char)++lotte+

		!!0xBABE];){ if ('I' -lie[ 2 +(char)lotte]){ 'I'-1l ***die; }

		else{ if ('I' * get *out* ('I'-1l **die[ 2 ])) *((char*)&lotte) -=

		'4' - ('I'-1l); not; for(get=!


	get; !out; (char)*lie  &  0xD0- !not) return!!

		(char)lotte;}


	(char)lotte;

		do{ not* putchar(lie [out

		*!not* !!me +(char)lotte]);

		not; for(;!'a';);}while(

		    love (char*)lie);{


	register this; switch( (char)lie

		[(char)lotte] -1s *!out) {

		char*les, get= 0xFF, my; case' ':

		*((char*)&lotte) += 15; !not +(char)*lie*'s';

		this +1s+ not; default: 0xF +(char*)lie;}}}

		get - !out;

		if (not--)
	
		goto hell;

		    exit( (char)lotte);}

    Da brividi, vero? Possiamo assistere così al tragico rifiuto di Charlotte di corrispondere il povero Charlie... tra inviti decisamente poco femminili ad attività (auspicatamente letali) di sesso improprio, e perle di saggezza come "love is not a toilet"... Questo esilarante spaccato di vita sentimentale è però, ovviamente, ben di più! Avrete certamente capito che questo è tutto codice C, parecchio contorto per la verità, e con ampi stralci di codice totalmente inutile ai fini del risultato; ma si sa, l'amore getta la mente in confusione...

    Come se non bastasse, questo programma, drammaticità a parte, è scritto per tornare utile! Se avete problemi sentimentali, copiate il programma in un file westley.c e compilatelo così:

    cc westley.c -o nome_della_persona_amata

    Purtroppo i compilatori ANSI C non riconoscono "1s" (="is") come la costante short 1, quindi se usate ad esempio gcc dovrete purtroppo sostituire le occorrenze di "1s" con altrettanti semplici "1". Una volta compilato, pensate un numero (n) ed avviate il binario generato con:

    nome_della_persona_amata n

    ...utile, no? E pratico se non si hanno margherite a disposizione... con quello che costano, poi... ;-)

    E' interessante, comunque, che (come fa notare l'autore stesso) se si passa il programma a lint per un doveroso controllo, tra tutti i report che si ottengono si può leggere un bel "warning: eroticism unused in function main"... indubbiamente vero! E, sempre secondo l'autore, ovviamente (char)lotte e (char*)lie sono proprio... tipi incompatibili! :-)))

(1988) phillipps.c: Un pizzico di poesia

    Ok, finora abbiamo scherzato, presentando delle acrobazie sintattiche molto belle, ma create proprio perché il sorgente colpisca l'occhio. Torniamo ora più vicini all'essenza dell'IOCCC, cioè all'offuscamento, con un programma di Ian Phillipps (ian@unipalm.co.uk) che nel 1988 vinse nella categoria "Least likely to compile successfully" (Meno probabile che si compili con successo), il che sembra oggi un po' fuori luogo, visto che almeno col GCC 2.96 (e parliamo di un compilatore già instabile di suo) e a 14 anni dalla sua creazione, si compila senza un singolo warning. Beh, meglio così, no? :)

main(t,_,a )
char
*
a;
{
                                return!

0<t?
t<3?

main(-79,-13,a+
main(-87,1-_,
main(-86, 0, a+1 )


+a)):

1,
t<_?
main( t+1, _, a )
:3,

main ( -94, -27+t, a )
&&t == 2 ?_
<13 ?

main ( 2, _+1, "%s %d %d\n" )

:9:16:
t<0?
t<-72?
main( _, t,
"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l,+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l q#'+d'K#!/+k#;q#'r
}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/w{%'l##w#' i; :{nl]'
/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# }'+}##(!!/")
:
t<-50?
_==*a ?
putchar(31[a]):

main(-65,_,a+1)
:
main((*a == '/') + t, _, a + 1 )
:

0<t?

main ( 2, 2 , "%s")
:*a=='/'||

main(0,

main(-61,*a, "!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry")

,a+1);}

    Questo sorgente è davvero meraviglioso per la... totale mancanza di struttura comprensibile! I giudici commentarono che questo è molto simile a quanto si otterrebbe pigiando a caso i tasti di una vecchia macchina da scrivere... e non hanno torto! Ammirate quel solitario "return!", le innumerevoli chiamate ricorsive alla funzione main(), perdetevi nelle lunghe stringhe che paiono promettere più di quanto mostrino... beatevi dell'assenza di qualsivoglia #define... non c'è della poesia, in questo?

    In effetti, letteralmente, della poesia c'è!! :) Copia-incollate il tutto in un file, nominatelo phillipps.c e compilatelo con un semplicissimo

    cc phillipps.c -o phillips

    Avviate l'output, ed eccovi appunto una bella poesia, a occhio una versione inglese del popolare "Alla fiera dell'est". Tra l'altro, completa, ben formattata, e divertente da leggere! Ora, ritornate al codice... la sfida è trovare dove vengono composti tutti questi 64 versi... in bocca al lupo!

    Una curiosità: al tempo dell'uscita, i giudici affermarono che il programma era più piccolo perfino della forma compressa del suo output, costituendo così una nuova frontiera nel campo degli archivi autoestraenti... Oggi le cose sono cambiate un po', i compattatori sono più capaci e i compilatori producono codice in media più grosso; però sembra un peccato che tale promettente strada sia stata abbandonata, negli anni... ;-)

(1998) bas1.c: Gran finale

    Ed ecco, come ormai tradizione, il programma "ad effetto" finale: questa volta l'effetto è ancora tutto nell'output, ma è equamente distribuito tra la forma ed il contenuto, per così dire...

char r[]="C#S3K+[;G'W7OB/_? ? « ( !IOCCC! ´ ³Ën £0 0 e÷|ÅtÑ%Ä ° 2u"
" »¾ß mÔ/p ª c p) nm «¨ªúïµ!!¶â¡a £à3sîÜ 9sz2{ +§ 9_°0 © h0 ºÂ / "
"¤)$1 ¬ a~ ) % y qÃ'¯ ¹â ° \" ! q 6 µy' 0 3;e{ ì kãÔs¾,Ê 2~a q t"
" 9dl 9 ¬.´ .ù_ &Á!ê '* ·­ ; ·± µÁ = <eàª1à B:29 ¦ 6 n´ o®¨b:_2"
" µ +úí h |÷t\177°#d`Ý l½ù r )þÁ @ ·´Ö m d©ià ¤6 e¦ v¸½^ ¶` rq"
"Á <'7Ã÷°Åù ´[|äè=ñ@îp (², /ä^ c ¦ . ­` £²î ¡­ñ`fæ /= :ù]y÷¢·"
" 2º » q ¢ v²4Õm³íÓ&=a¤¡ g ; dà,é+· +_¼ =è 5߬iêÔØû§ìì r£"
#define O(Q)p!O) { *i[25][41]|=U(642+Q); u e r[(Q)/2]&63); }
"solÞÄ&¾X¼[Ý; }ï? º= &c} 9è¾óà2 >ü 5 ,Þ &  Ã0  Úù*½ ³äÿgh";
#define C(                                 b)O(28+(B+b)%6)
#   define                                 a >> B &M[i X]
#include/*                                 s */<stdio.h>
#include/*                                 s */<time.h>
  unsigned                                 int    long
#   define                                 e putchar(
                   B,E,M,A,Z,I,N,G
# define g                                 ! (32 a)
# define u                                 U(533);
# define n                                 (65 a)
# define z                                 for (
[32768], i                                 [32]
# define p                                 if(
[48][3]={ { { 2} } } ; void**V,*D,*F[32][48];
void*Q(void**O){ return*O?(*O=Q(*O)):O; } U(
O){return E+=(O/=2)-1?U((A=A/2^57525273+(A&
1^O&1)*9583591<<5,O)),0:O,16; } main(O){ A
=O?time(D):953351751; z I=0; I<26; ++I){z
N=I/2; N<30+I/2; ++N){ z M=0; M<3; ++M){
p Z=O)z; (B=I+Z*!!M) <26&&N+Z*(M!=2)-B/
2<30; B[G]=N<<7|Z++<<13|I*4+M)z; G[B=A
# define X [ I+Z*!! M] [ N+Z*(M!= 2)]
&32767]; )u p!Z&&M[i X]){ z B=0; B<6
; B++){ p!g){ C(5)O(0)C(2)O(2)} C((
B&1?!!n:4-3*g))p!g|n)O(4)O(7)} z B
=0; B<6; B++){ p g&&!n)O(14)p!g)O
(8)p B&1|g){ O=!n; O(10)O=!(1040
a ); O(12 )O=0; } O(17+(B&1 ?!(
1040 a)!=!n:g))} } } O(24)} O(
21+(I&1))} O(26)p O)z E=0; E<
1<<15; I=16){ B=E++[G]; D=Q(
&F[I=B/4&31][N=B>>7&63]); M
=B%4; Z=B>>=13; V=Q(&F X);
p!(D==V|N[I[i]] [1]|i X[1
])){ z; --Z&&!(*i X%9|*i
X/9&*i X/9-1); ); p!Z){
*V=D; *i X|=32>>M; Z=B
; z*i X|=4>>M; --Z; i
X[*i X?2-!i X [1]:!(
F X=V)]=36>>M); } }
} p!O){ u u z I=8;
I--; )p(N=I[r]=(I
>3?E:A)>>(I*9&24
))==10||N==13){
e 0 ); I =8; u
u u u} e 1 );
} z E =1333;
++ I<8+469*
!!O; e(N=I
[r])-32|!
O?N:r[++
I]-N));
return
 O &&
main
(!O
);
}

    Notate, ancora una volta, la deliziosa incomprensibilità del tutto... sembra un file binario aperto con un editor di testo, non fosse per la forma e per quella scritta BEMAZING! Passiamo a compilarlo, e ad eseguirlo; la compilazione è piuttosto banale (gcc bas1.c -o bas1), ma avviandolo avremo un output alquanto criptico... vediamo un po' cos'è:

	[mano@mano mano]$ gcc ioccc/1998/bas1.c -o bas1
	[mano@mano mano]$ ./bas1|file -
	standard input:              gzip compressed data, deflated, ASCII, last modified: Wed Oct  5 05:58:01 2005

    Al di là della data un po' particolare, quindi, questo programma produce un file "gzippato". Basterebbe questo per essere stupiti, visto che non vi è link alle zlib di sorta; del resto, date un po' un'occhiata, nel codice, alla metà bassa del lato verticale sinistro del rettangolo contenente la scritta BEMAZING... :) Ma è un file gzippato di cosa? Vediamo:

	[mano@mano mano]$ ./bas1|gunzip -c|file -
	standard input:              PostScript document text conforming at level 2.0

    Et voilà! Un file Postscript, nientemeno! Il che spiega il titolo di "Best encapsulation" che questo incredibile programma di Bas de Bakker (basde.bakker@pica.nl) si è guadagnato... Dulcis in fundo, vediamolo un po, questo file PS!

	[mano@mano mano]$ ./bas1|gunzip -c|gv -

    Bello, no? :o) Potete anche passare come argomento un intero, per variare il percorso. Buon divertimento a risolverlo!

Conclusione

    Finalmente, il 12 Marzo è stata rilasciata la lista dei vincitori!! Passerà un po' di tempo prima che i loro programmi siano resi pubblici in tutto il loro splendore, quindi preparatevi per il prossimo numero, in cui parleremo proprio di essi! Per il momento, fate una capatina sul sito www.ioccc.org, se non potete contenere la curiosità... :-)



A cura di

Germano "Mano" Rizzo (mano@pluto.linux.it), studente in Ingegneria Elettronica, è sviluppatore Java e PHP presso una ditta di software web. Si interessa di Linux da parecchi anni, spaziando in svariati campi, in particolare la programmazione C e gli aspetti legati al kernel.


<- PW: Introduzione al Pascal - Copertina - SL: Intro ->