#include <time.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

void show_header(void);		/* gibt den HTML-Kopf und den Anfang der Tabelle aus */
void show_error(int fehler);	/* gibt Fehlermeldung aus */
void show_footer(unsigned long size);	/* gibt das Ende der Tabelle/der HTML-Datei aus */
unsigned long lrand(void);	/* erzeugt zufaelligen unsigned long-Wert (srand nicht vergessen!) */

int main(void)
{
	FILE *fp;				/* fuer die schlau.dat */
	unsigned long nr;		/* Eine Laufvariable */
	unsigned long size;		/* Groesse der Datei */
	char buffer[1];			/* Ein Puffer */
	int i,rets;				/* Lauf-/Hilfs-Variablen */
	char *befehle;			/* Fuer das Argument */

	show_header();		/* Mia san mia und wer saz ihr? */

	fp=fopen("schlau.dat","rb");		/* Diese Datei wollen wir einlesen */
	if(!fp)
	{		/* uebliche Fehlerbehandlung */
		show_error(0);
		return -1;
	}

	srand((int) time(NULL));		/* Zufallszahlengenerator initiallisieren (ueber Uhrzeit) */

		/* Groesse der Datei bestimmen */
		/* Leider gibt es keinen Befehl dafuer, daher zunaechst ein Sprung ans */
		/* Ende der Datei, dann nachschauen, wo der Dateizeiger jetzt ist und */
		/* anschliessend zurueckspringen */
	if(fseek(fp,0,SEEK_END))
	{
		show_error(1);
		if(fclose(fp))
			show_error(2);

		return -1;
	}

	size=(unsigned long) ftell(fp);			/* Hier wird die Groesse abgefragt! */
	if(errno>0 && size==(unsigned long) -1)
	{
		show_error(1);
		if(fclose(fp))
			show_error(2);

		return -1;
	}

	if(fseek(fp,0,SEEK_SET))
	{
		show_error(1);
		if(fclose(fp))
			show_error(2);

		return -1;
	}

	befehle=getenv("QUERY_STRING");		/* cgi-Scripts erhalten so ihre Argumente */

	if((!befehle) || strcmp("alle",befehle))	/* Kein Befehl oder nicht "alle" */
	{
		nr=lrand();					/* Zufaelliger Spruch */

		if(befehle && befehle[0]!='\0')		/* Gab einen Befehl */
		{
			if(sscanf(befehle,"%ld",&nr)!=1)	/* Zahl einlesen */
			{							/* War keine Zahl -> Fehlermeldung */
				show_error(4);
				if(fclose(fp))
					show_error(2);
		
				return -1;
			}
		}

		nr=nr % size;					/* unsigned long Zufallszahl in den passenden Bereich bringen */

			/* Jetzt wird (ab der nr. Stelle) nach dem Beginn eines Spruchs gesucht */
		i=0;
		do {
			if(fseek(fp,nr,SEEK_SET) ||
				fread(buffer,sizeof(char),1,fp)!=1)		/* Rueckwaerts, also jedesmal Zeiger neu setzen */
			{
				show_error(3);
				if(fclose(fp))
					show_error(2);

				return -1;
			}

			if(buffer[0]=='-' && i)						/* '-' ist das Trennzeichen, zaehlen wenn nach NL */
			{
				if(i==3)
					i=0;
				else
					i++;
			}
			else if(buffer[0]=='\n' || buffer[0]=='\r')		/* Rueckwaerts, also ist NL das 1. zu suchende Zeichen */
			{
				if(i==3)
					i=4;
				else
					i=1;
			}
			else									/* Tja, normales Zeichen */
				i=0;

			if(nr>0)								/* Duerfen wir noch weiter vor? */
				nr--;
			else
				i=50;								/* Wenn Trennlinie, dann ist i=4, 50 wenn Dateianfang */
		} while(i<4);

		if(i<20)		/* Trennlinie? */
		{
			nr+=4;									/* Damit wir auf dem <CR> nach dem '<CR>--' stehen */

				/* CRs ueberspringen */
			do {
				if(fseek(fp,nr,SEEK_SET) ||
					fread(buffer,sizeof(char),1,fp)!=1)
				{
					show_error(3);
					if(fclose(fp))
						show_error(2);
	
					return -1;
				}

				nr++;
			} while(buffer[0]=='\n' || buffer[0]=='\r');

			nr--;		/* 1 Zeichen drueber -> korrigieren */
		}

		if(fseek(fp,nr,SEEK_SET))			/* Dateizeiger korrigieren (um 1 Byte zurueck) */
		{
			show_error(3);
			if(fclose(fp))
				show_error(2);

			return -1;
		}

			/* Diesen Spruch ausgeben */
		i=0;
		rets=0;
		do {
			if(fread(buffer,sizeof(char),1,fp)!=1)	/* Zeichen holen */
			{
				show_error(3);
				if(fclose(fp))
					show_error(2);
	
				return -1;
			}

			nr++;								/* mitzaehlen */

			if(rets>1)							/* In der letzten Runde ein NL vermerkt? */
			{
				printf("<br>\n");
				rets=1;						/* Return schon geschrieben (fuer "CR, LF" bzw "LF, CR") */
			}

			if(buffer[0]=='-' && (rets || i))		/* Koennte Trennzeichen sein, fuer spaetere Ausgabe merken */
				i++;
			else if(buffer[0]=='\n' || buffer[0]=='\r')	/* Return */
			{
				if(i==2)						/* 2 '-' davor? */
					nr=size;					/* Abbruchkriterium setzen */
				else
				{							/* nur normales Return */
					while(i)					/* gemerkte '-' nachtragen (i!=2) */
					{
						printf("-");
						i--;
					}
				}

				if(rets==0)					/* Vorgaengerzeichen kein NL? */
					rets=buffer[0];				/* dieses merken (rets>0) */
			}
			else		/* normales Zeichen */
			{
				while(i)		/* gemerkte '-' schreiben */
				{
					printf("-");
					i--;
				}

				printf("%c",buffer[0]);	/* Zeichen ausgeben */
			}

			if(buffer[0]!='\n' && buffer[0]!='\r')		/* Normales Zeichen -> Returnmerker zuruecksetzen */
				rets=0;
		} while(nr<size);	/* Bis Datei zuende oder Abbruchkriterium bewusst gesetzt */
	}
	else /* befehle=="alle" */
	{
		nr=0;		/* von vorne ausgeben */
		i=0;
		rets=0;
		do {
			if(fread(buffer,sizeof(char),1,fp)!=1)	/* zeichen holen */
			{
				show_error(3);
				if(fclose(fp))
					show_error(2);
	
				return -1;
			}

			nr++;								/* mitzaehlen */

			if(rets>1)							/* 1. Return ausgeben */
			{
				printf("<br>\n");
				rets=1;						/* weitere unterdruecken */
			}

			if(buffer[0]=='-' && (rets || i))	/* Trennlinie? */
			{
				i++;
			}
			else if(buffer[0]=='\n' || buffer[0]=='\r')	/* Return */
			{
				if(i==2)						/* Trennlinie! */
				{
					printf("<hr>\n");			/* Einfuegen */
					i=0;
					rets=0;
				}
				else
				{
					while(i)					/* gemerkte '-' */
					{
						printf("-");
						i--;
					}

					if(rets==0)				/* 1. Return in Folge? */
						rets=buffer[0];
				}
			}
			else
			{	/* normales Zeichen */
				while(i)						/* gemerkte Trennzeichen */
				{
					printf("-");
					i--;
				}

				printf("%c",buffer[0]);			/* Zeichen selber */
			}

			if(buffer[0]!='\n' && buffer[0]!='\r')		/* kein Return mehr merken */
				rets=0;
		} while(nr<size);					/* Bis Dateiende weitermachen */

		size=0;		/* Damit kein Button "Alle Sprueche" erscheint */
	}

	show_footer(size);			/* Verabschieden */

	fclose(fp);				/* Wieder schliessen */

	return 0;					/* Danke, alles okay */
}

void show_header(void)
{	/* Zeigt Header */
	printf("Content-type: text/html\n\n");		/* Fuer den Browser, Typenkennung, Rest: HTML-Code */

	printf("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
				"<html>\n"
				"<head>\n"
				"	<link rel=\"contents\" href=\"../index.html\">\n"
				"	<link rel=\"index\" href=\"../index.html\">\n"
				"	<link rel=\"home\" href=\"../index.html\">\n"
				"	<link rel=\"made\" href=\"mailto:daniel@hoepfl.de\">\n"
				"	<link rel=\"Copyright\" href=\"../about/rechtliches.html\">\n"
				"	<link rel=\"shortcut icon\" href=\"../bilder/hoepfl.ico\">\n"
				"\n"
				"	<title>Home of Daniel</title>\n"
				"\n"
				"	<link rel=\"stylesheet\" media=\"screen\" href=\"../screen.css\">\n"
				"	<link rel=\"stylesheet\" media=\"print\" href=\"../print.css\">\n"
				"</head>\n"
				"\n"
				"<body bgcolor=\"#d0d0d0\">\n"
				"\n"
				"<table width=\"100%\" cellspacing=0 cellpadding=0><tr><td>\n"
				"<table class=mainbox bgcolor=\"#f0f0f0\" width=\"100%\" align=right><tr><td>\n"
				"\n"
				"<table class=\"navkruemel\" align=left><tr><td><small class=\"kruemeltext\">\n"
            "<a class=\"barrierefrei\" href=\"#content\">Zum Inhalt springen<br></a>\n"
            "<a class=\"barrierefrei\" href=\"#navigation\">Zur Navigation springen<br></a>\n"
				"<a class=\"kruemellink\" href=\"../index.html\">Home</a> :\n"
				"<a class=\"kruemellink\" href=\"../wiese/spielwiese.html\">Spielwiese</a> :\n"
				"<a class=\"kruemellink\" href=\"schlau.cgi\">Schlaues</a></small>\n"
				"</td></tr></table>\n"
				"\n"
				"<table class=\"navmenu\" border=0 cellpadding=3 cellspacing=0 align=right>\n"
				"<tr><th class=\"navmenutopic\" valign=top align=right bgcolor=\"#cccccc\">\n"
				"	<a id=\"navigation\" href=\"../index.html\">Navigation</a>\n"
				"</th></tr><tr><td class=\"navmenusec\" valign=top align=right bgcolor=white>\n"
				"	<a class=\"navlink\" href=\"../about/about.html\">&Uuml;ber diese Site</a><br>\n"
				"	<a class=\"navlink\" href=\"../stand/stand.html\">Auf Stand</a><br>\n"
				"	<a class=\"navlink\" href=\"../archiv/archiv.html\">Archiv</a>\n"
				"</td></tr><tr><td class=\"navmenusec\" valign=top align=right bgcolor=white>\n"
				"	<a class=\"navlink\" href=\"../comp/hist.html\">Geschichte</a><br>\n"
				"	<a class=\"navlink\" href=\"../comp/atari.html\">Atari</a><br>\n"
				"	<a class=\"navlink\" href=\"../comp/apple.html\">Apple</a>\n"
				"</td></tr><tr><td class=\"navmenusec\" valign=top align=right bgcolor=white>\n"
				"	<a class=\"navlink\" href=\"../aktex/aktex.html\">Akte X</a><br>\n"
				"	<a class=\"navlink\" href=\"../kino/kino.html\">Kino</a><br>\n"
				"	<a class=\"navlink\" href=\"../links/links.html\">Links</a>\n"
				"</td></tr><tr><td valign=top align=right bgcolor=white>\n"
				"	<a class=\"navlink\" href=\"../cgi-bin/schlau.cgi\">Schlaues</a><br>\n"
				"	<a class=\"navlink\" href=\"../wiese/spielwiese.html\">Spielwiese</a>\n"
				"</td></tr></table>\n"
				"<br clear=left>\n"
				"\n"
				"<h1 id=\"content\" align=center>Schlaues</h1>\n"
            "\n"
            "<div align=center>\n"
            "  <table>\n"
            "  <tr><td>\n"
            "    <table border=1 bgcolor=\"#306030\" class=\"tabelle\" cellspacing=1>\n"
            "    <tr bgcolor=\"#f0f0f0\"><td>\n");
}

void show_footer(unsigned long size)
{	/* Zeigt Footer, ggf. mit Button "Alle Sprueche" (size !=0) */
	char mask[20];

		/* Tabelle beenden */
	printf("</td></tr>\n"
		"</table>\n");

		/* Schoene Ausgabe der Dateigroesse */
	if(size<0x400l)
		sprintf(mask,"&lt;1 KB");
	else if(size<0x20000l)
		sprintf(mask,"%ld KB",(size+0x200l)/0x400l);
	else
		sprintf(mask,"%ld MB",(size+0x10000l)/0x20000l);

	if(size>0)	/* ggf. Verweis auf alle Sprueche */
		printf("<br>\n<p align=right><a href=\"./schlau.cgi?alle\">Alle Spr&uuml;che (%s)</a></p>\n",mask);

		/* Tabelle beenden */
	printf("</td></tr>\n"
		"</table></div>\n");

		/* restlicher Footer */
	printf("</td></tr></table>\n"
				"</td></tr>\n"
				"<tr><td>\n"
				"<table align=right class=\"copybox\" bgcolor=\"#f0f0f0\"><tr><td valign=middle>\n"
				"	<small class=\"copytext\">&copy; <a href=\"http://www.hoepfl.de/\">hoepfl.de</a></small>\n"
				"</td></tr></table>\n"
				"</td></tr></table>\n"
				"\n"
				"</body></html>\n");
}

void show_error(int fehler)
{
	char err[][100]=
	{
		"Datei &gt;schlau.dat&lt; konnte nicht ge&ouml;ffnet werden.",
		"L&auml;nge der Datei &gt;schlau.dat&lt; kann nicht bestimmt werden.",
		"Datei kann nicht korrekt geschlossen werden.",
		"Spruch kann nicht gelesen werden.",
		"Parameter nicht korrekt."
	};
		/* Einfache Fehlerausgabe */
	printf("<font color=red>Fehler #%d:<br>\n",fehler);
	printf("%s<br></font>\n",err[fehler]);

		/* Footer ohne "alle Sprueche" */
	show_footer(0);
}

unsigned long lrand(void)
{	/* Berechnet unsigned long Zufallszahl */
	unsigned long answer=0;
	unsigned long l;

		/* rand() liefert nur 3 Byte-Zufallszahlen, daher durch Shiften eine */
		/* sizeof(unsigned long)-Byte Zufallszahl erzeugen */
		/* Anmerkung: Da generierte Zufallszahlen in den unteren Bits haeufig nicht */
		/* sonderlich gut zufaellig sind wird das mittlere Byte benutzt */
	for(l=0;l<sizeof(unsigned long);l++)
		answer=answer*0x100+((rand() & 0xff00l) / 0x0100);

	return answer;
}
