[rfk-dev] Proposed extensions to HTTP to support remote kitten-finding
Martin Pool
mbp@samba.org
Sat, 19 May 2001 13:24:12 +1000
--hK8Uo4Yp55NZU70L
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
http://gnukeyring.sourceforge.net/cgi-bin/robotfindskitten.cgi
Comments very welcome.
diff -x CVS -x install-sh -x COPYING -x INSTALL -x aclocal.m4 -x config.* -=
x configure -x ltmain.sh -x missing -x mkinstalldirs -x texinfo.tex -N -x M=
akefile.in -x *~ -urpw robotfindskitten-cvs/configure.in robotfindskitten/c=
onfigure.in
--- robotfindskitten-cvs/configure.in Sat Mar 10 21:30:04 2001
+++ robotfindskitten/configure.in Sat May 19 12:56:45 2001
@@ -1,6 +1,6 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(src/draw.h)
-AM_INIT_AUTOMAKE(robotfindskitten,v1600005.248b)
+AM_INIT_AUTOMAKE(robotfindskitten,v1600005.248b-www)
AM_CONFIG_HEADER(config.h)
AC_LANG_C
=20
diff -x CVS -x install-sh -x COPYING -x INSTALL -x aclocal.m4 -x config.* -=
x configure -x ltmain.sh -x missing -x mkinstalldirs -x texinfo.tex -N -x M=
akefile.in -x *~ -urpw robotfindskitten-cvs/src/Makefile.am robotfindskitte=
n/src/Makefile.am
--- robotfindskitten-cvs/src/Makefile.am Thu Feb 22 15:29:33 2001
+++ robotfindskitten/src/Makefile.am Fri May 18 18:25:23 2001
@@ -4,3 +4,6 @@ execgames_PROGRAMS=3Drobotfindskitten
robotfindskitten_SOURCES=3D draw.h messages.h robotfindskitten.c=20
#robotfindskitten_LDADD=3D -lncurses=20
=20
+noinst_PROGRAMS=3Drobotfindskitten.cgi
+
+robotfindskitten_cgi_SOURCES=3D draw.h messages.h robotfindskitten.cgi.c r=
andom.c
diff -x CVS -x install-sh -x COPYING -x INSTALL -x aclocal.m4 -x config.* -=
x configure -x ltmain.sh -x missing -x mkinstalldirs -x texinfo.tex -N -x M=
akefile.in -x *~ -urpw robotfindskitten-cvs/src/colors.h robotfindskitten/s=
rc/colors.h
--- robotfindskitten-cvs/src/colors.h Thu Jan 1 10:00:00 1970
+++ robotfindskitten/src/colors.h Sat May 19 02:32:33 2001
@@ -0,0 +1,12 @@
+#define N_COLORS ((int) (sizeof colors / sizeof colors[0]))
+
+static char const *colors[] =3D
+{
+ "LightGrey", "Red", "Green", "Blue", "Yellow", "Cyan", "Purple", "White",
+ "Pink", "Coral", "Gold", "OliveGreen", "Violet", "Thistle", "Tan", "Salm=
on",
+ "Plum", "Orange", "Maroon", "Magenta", "SeaGreen", "LimeGreen", "Firebri=
ck",
+ "SteelBlue", "LightBlue", "CadetBlue", "Aquamarine", "YellowGreen", "Sea=
Green"
+};
+
+static char const *bg_color =3D "Black";
+static char const *fg_color =3D "LightGrey";
diff -x CVS -x install-sh -x COPYING -x INSTALL -x aclocal.m4 -x config.* -=
x configure -x ltmain.sh -x missing -x mkinstalldirs -x texinfo.tex -N -x M=
akefile.in -x *~ -urpw robotfindskitten-cvs/src/random.c robotfindskitten/s=
rc/random.c
--- robotfindskitten-cvs/src/random.c Thu Jan 1 10:00:00 1970
+++ robotfindskitten/src/random.c Fri May 18 23:50:30 2001
@@ -0,0 +1,65 @@
+/*
+ * Taken from the libuuid library, which is licensed under the GNU
+ * GPL.
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * Hacked up by Martin Pool in 2001 to assist robots finding kittens.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "random.h"
+
+/*
+ * Generate a series of random bytes. Use /dev/urandom if possible,
+ * and if not, use srandom/random.
+ */
+void get_random_bytes(void *buf, int nbytes)
+{
+ static int fd =3D -2;
+ int i;
+ char *cp =3D (char *) buf;
+
+ if (fd =3D=3D -2) {
+ fd =3D open("/dev/urandom", O_RDONLY);
+ srand((getpid() << 16) ^ getuid() ^ time(0));
+ }
+ if (fd >=3D 0) {
+ while (nbytes > 0) {
+ i =3D read(fd, cp, nbytes);
+ if (i < 0) {
+ if ((errno =3D=3D EINTR) || (errno =3D=3D EAGAIN))
+ continue;
+ break;
+ }
+ nbytes -=3D i;
+ cp +=3D i;
+ }
+ }
+ if (nbytes =3D=3D 0)
+ return;
+
+ /* XXX put something better here if no /dev/random! */
+ for (i=3D0; i < nbytes; i++)
+ *cp++ =3D rand() & 0xFF;
+ return;
+=09
+}
+
+
+
+unsigned int get_random_int (void)
+{
+ unsigned int r;
+
+ get_random_bytes (&r, sizeof r);
+
+ return r;
+}
diff -x CVS -x install-sh -x COPYING -x INSTALL -x aclocal.m4 -x config.* -=
x configure -x ltmain.sh -x missing -x mkinstalldirs -x texinfo.tex -N -x M=
akefile.in -x *~ -urpw robotfindskitten-cvs/src/random.h robotfindskitten/s=
rc/random.h
--- robotfindskitten-cvs/src/random.h Thu Jan 1 10:00:00 1970
+++ robotfindskitten/src/random.h Fri May 18 23:50:35 2001
@@ -0,0 +1,2 @@
+unsigned int get_random_int (void);
+void get_random_bytes(void *buf, int nbytes);
diff -x CVS -x install-sh -x COPYING -x INSTALL -x aclocal.m4 -x config.* -=
x configure -x ltmain.sh -x missing -x mkinstalldirs -x texinfo.tex -N -x M=
akefile.in -x *~ -urpw robotfindskitten-cvs/src/robotfindskitten.cgi.c robo=
tfindskitten/src/robotfindskitten.cgi.c
--- robotfindskitten-cvs/src/robotfindskitten.cgi.c Thu Jan 1 10:00:00 1970
+++ robotfindskitten/src/robotfindskitten.cgi.c Sat May 19 13:03:11 2001
@@ -0,0 +1,663 @@
+/*
+ * robotfindskitten.cgi: A Zen-Themed Knick-Knack
+ *
+ * Copyright (C) 2001 by Martin Pool <mbp@sourcefrog.net>
+ * This program is dedicated to Stephane Miller.
+ *
+ * Original robotfindskitten, Copyright (C) 1997-2000 by Leonard Richardson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or EXISTANCE OF KITTEN. See the GNU General
+ * Public License for more details.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* This is the e-rfk, which runs as a CGI.
+=20
+ It keeps no state on the server -- everything is bounced to the
+ client and back again on every request, which makes it clean for
+ the server, but a little tricky to program. Each instance of the
+ CGI lasts only for a single web request. It's like juggling cute
+ little kittens.
+ =20
+ We want to be fairly paranoid about string parsing, since an
+ overflow in a CGI is considered harmful.
+
+ For extra marks, we really should gzip all the output if the
+ browser has indicated that it can accept that encoding. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#if HAVE_CONFIG_H
+#include <config.h>
+static char* ver =3D VERSION;
+#endif
+
+#include "random.h"
+
+#include "messages.h"
+#include "colors.h"
+
+enum { FALSE =3D 0, TRUE =3D 1};
+
+
+/* An arena of classical dimensions. */
+#define WIDTH 80
+#define HEIGHT 25
+
+/* This struct contains all the information we need to display an
+ object on the screen*/
+typedef struct
+{
+ int color, bold;
+ unsigned char character;
+ int message;
+} screen_object;
+
+/* Constants for our internal representation of the screen. nki's are
+ numbered from 2 upwards, matching entries in bogus[]. */
+#define EMPTY 0
+#define ROBOT 1
+#define KITTEN 2
+#define FIRST_NKI 3
+
+#define N_OBJECTS ((int) MESSAGES + FIRST_NKI)
+screen_object objects[N_OBJECTS];
+screen_object * const robot =3D &objects[ROBOT];
+screen_object * const kitten =3D &objects[KITTEN];
+
+int robotx, roboty;
+
+/* If we can do compressed output, then this points to the compressor. */
+FILE *gz_out;
+ =20
+/* Screen array, indexed as [x][y]. Each value is EMPTY, ROBOT,
+ KITTEN, or an index into bogus[]. */
+int screen[WIDTH][HEIGHT];
+
+/* String representation of game state. */
+char packed_state[4 * WIDTH * HEIGHT + 1];
+
+char const *script_name; /* =3D SCRIPT_NAME */
+
+static int randx (void)
+{
+ return get_random_int () % WIDTH;
+}
+
+static int randy (void) /* i'm feeling randy! */
+{
+ return get_random_int () % HEIGHT;
+}
+
+static int randchar (void)
+{
+ /* Assume the other guy can handle 8bit. We're not all american. */
+ return 32 + (get_random_int () % 96) + (get_random_int() & 0x80);
+}
+
+static int randcolor (void)
+{
+ return get_random_int() % N_COLORS;
+}
+
+static int randbold (void)
+{
+ return (get_random_int() % 2 ? TRUE:FALSE);
+}
+
+static void print_env (void)
+{
+ extern char **environ;
+ char **e;
+
+ for (e =3D environ; *e; e++)
+ {
+ // printf ("<BR>%s", *e);
+ }
+}
+
+static int validchar(char a)
+{
+ switch (a)
+ {
+ case '#':
+ case ' ':
+ case '_':
+ case 127:
+ return 0;
+ }
+ return 1;
+}
+
+
+/* Return TRUE if this cell can be directly reached from the current
+ robot position.
+
+ This is the most interesting part of translation to the web.
+ Moving a single step at a time is probably too painful. Jumping
+ straight to objects is possibly too easy. Moving in straight lines
+ looks funny on the screen.
+
+ Perhaps we should allow ~20 random moves at any point? */
+static int is_reachable (int x, int y)
+{
+#if defined(ROOK_MOVES)
+ if (x =3D=3D robotx)
+ {
+ /* in the same column. is there anything between us? */
+ int delta =3D x > robotx ? -1 : +1;
+ int sx;
+ for (sx =3D x; sx !=3D robotx; sx +=3D delta)
+ if (screen[sx][y] !=3D EMPTY)
+ return FALSE;
+ return TRUE;
+ } =20
+ else if (y =3D=3D roboty)
+ {
+ /* in the same row. is there anything between us? */
+ int delta =3D y > roboty ? -1 : +1;
+ int sy;
+ for (sy =3D y; sy !=3D roboty; sy +=3D delta)
+ if (screen[x][sy] !=3D EMPTY)
+ return FALSE;
+ return TRUE;
+ }
+ else
+ return FALSE;
+#elif defined(MOVE_ANYWHERE)
+ return TRUE;
+#else
+ return screen[x][y] !=3D EMPTY;
+#endif
+}
+
+
+static void draw_cell (int x, int y)
+{
+ int reachable;
+ screen_object const *o =3D NULL;
+ =20
+ if ((reachable =3D is_reachable(x, y)))
+ printf ("<A\n HREF=3D\"%s?x=3D%d&y=3D%d%s\">", script_name, x, y, pack=
ed_state);
+ =20
+ if (robotx =3D=3D x && roboty =3D=3D y)
+ /* Robot hides whatever object is underneath */
+ o =3D robot;
+ else if (screen[x][y] > 0 && screen[x][y] < N_OBJECTS)
+ o =3D &objects[screen[x][y]];
+ =20
+ if (o)
+ {
+ // fprintf (stderr, "character %c at %d, %d\n", o->character, x=
, y);
+ if (o =3D=3D robot)
+ fputs ("<BLINK>", stdout);
+ printf ("<FONT COLOR=3D\"%s\">", colors[o->color]);
+ if (o->bold)
+ fputs("<B>", stdout);
+ printf ("&#%d;", o->character);
+ if (o->bold)
+ fputs("</B>", stdout);
+ printf ("</FONT>");
+ if (o =3D=3D robot)
+ fputs ("</BLINK>", stdout);
+ }
+ else
+ {
+ printf (" ");
+ }
+
+ if (reachable)
+ printf ("</A>");
+}
+
+
+static void send_arena (void)
+{
+ int x, y;
+
+ for (y =3D 0; y < HEIGHT; y++)
+ {
+ printf ("\n<br>");
+ for (x =3D 0; x < WIDTH; x++)
+ draw_cell (x, y);
+ }
+}
+
+
+static void send_head (int hug_distance, int hug_kitten_char)
+{
+ char host[60];
+ char const *a =3D "<A HREF=3D\"http://robotfindskitten.org\">",
+ *un_a =3D "</A>";
+
+ if (gethostname (host, sizeof host) =3D=3D -1)
+ host[0] =3D 0;
+ host[59] =3D 0;
+ =20
+ printf ("Content-Type: text/html\n");
+ if (gz_out)
+ printf ("Content-Encoding: gzip\n");
+ if (hug_kitten_char)=20
+ printf ("Refresh: 1;URL=3D%s?hug=3D%d&kitten=3D%x\n",
+ script_name, hug_distance, hug_kitten_char);
+
+ printf ("\n");
+ fflush (stdout);
+
+ if (gz_out)
+ stdout =3D gz_out;
+ =20
+ printf ("<HTML><HEAD><TITLE>robotfindskitten %s", ver);
+ if (*host)
+ printf (" on %s", host);
+ printf ("</TITLE></HEAD>\n");
+ printf ("<BODY BGCOLOR=3D\"%s\" TEXT=3D\"%s\" LINK=3D\"%s\" VLINK=3D\"%s=
\"><TT>\n",
+ bg_color, fg_color, fg_color, fg_color);
+ printf ("<H2>%sr%sobot%sf%sinds%sk%sitten ver %s</H2>",
+ a, un_a, a, un_a, a, un_a, ver);
+}
+
+
+static void offer_restart (void)
+{
+ printf ("(<A HREF=3D\"%s\">restart</A>)<P>\n", script_name);
+}
+
+
+static void send_screen (void)
+{
+ send_arena ();
+ =20
+ printf ("</TT>\n");
+
+ print_env ();=20
+
+ printf ("</BODY>\n");
+ fflush (stdout);
+}
+
+
+static void send_instructions (void)
+{
+ printf ("<P>Web version (C) 2001 by "
+ "<A HREF=3D\"http://sourcefrog.net/mbp/\">Martin Pool</A>,\n"
+ "<BR>based on the original by the illustrious Leonard Richardson (C) 19=
97, 2000.\n"
+ "<P>In this game, you are robot (<BLINK>#</BLINK>). "
+ "Your job is to find kitten. This task\n"
+ "is complicated by the existence of various things which are not kitten=
. \n" =20
+ "Robot must touch items to determine if they are kitten or not. The ga=
me\n"
+ "ends when robotfindskitten. \n"
+ "<P>Click to move around the board.\n"
+ "<P>\n");
+}
+
+static void place_robot (int x, int y)
+{
+ robotx =3D x;
+ roboty =3D y;
+ =20
+ robot->character =3D '#';
+ robot->color =3D 0;
+ robot->bold =3D FALSE;
+
+ /* fprintf (stderr, "robot placed at (%d, %d)\n", robotx, roboty); */
+}
+
+
+
+/* Conduct Zen tuition for non-kitten objects. (If we found the
+ kitten, hugging_kitten would have been called instead.) */
+static void show_found (void)
+{
+ int s =3D screen[robotx][roboty];
+
+ if (s)
+ {
+ printf ("<b>%s</b>\n", messages[objects[s].message]);
+ }
+ printf ("<p>\n");
+}
+
+
+/* Game state is stored encoded as hex in the PATH_INFO section, and
+ transmitted in a slash after the name of the CGI.
+
+ Returns TRUE if everything was loaded successfully, otherwise FALSE
+ (in which case we must start a new game.) */
+static int load_state (void)=20
+{
+ char const *q =3D getenv ("QUERY_STRING");
+ int r;
+ int x, y;
+
+ if (!q)
+ {
+ fprintf (stderr, "no query string -- a new game\n");
+ return FALSE;
+ }
+
+ r =3D sscanf (q, "x=3D%d&y=3D%d", &x, &y);
+ if (r !=3D 2)
+ {
+ fprintf (stderr, "didn't find robot -- starting a new game\n");
+ return FALSE;
+ }
+
+ q =3D strchr (q, '&');
+ while ((q =3D strchr (q + 1, '&')))
+ {
+ screen_object tmp_obj;
+ int x, y, s;
+ =20
+ if (sscanf (q, "&c%x.%x.%x.%x.%x.%x.%hhx",
+ &x, &y, &s, &tmp_obj.bold, &tmp_obj.color, &tmp_obj.message,
+ &tmp_obj.character) !=3D 7)
+ {
+ fprintf (stderr, "couldn't parse screen encoding at %s\n", q);
+ return FALSE;
+ }
+
+ if (x < 0 || x >=3D WIDTH || y < 0 || y >=3D HEIGHT)
+ {
+ fprintf (stderr, "coordinates (%d, %d) are bogus\n", x, y);
+ return FALSE;
+ }
+
+ if (s < 1 || s >=3D N_OBJECTS)
+ {
+ fprintf (stderr, "object index %d is bogus\n", s);
+ return FALSE;
+ }
+
+ if (tmp_obj.color < 0 || tmp_obj.color >=3D N_COLORS)
+ {
+ fprintf (stderr, "color %d is bogus\n", tmp_obj.color);
+ return FALSE;
+ }
+
+ if (tmp_obj.message < 0 || tmp_obj.message >=3D MESSAGES)
+ {
+ fprintf (stderr, "message index %d is bogus\n", tmp_obj.message);
+ return FALSE;
+ }
+
+ screen[x][y] =3D s;
+ objects[s] =3D tmp_obj;
+ }
+
+ /* fprintf (stderr, "found robot at (%d, %d)\n", x, y); */
+ place_robot (x, y);
+
+ return TRUE;
+}
+
+
+static void empty_screen (void)
+{
+ int x, y;
+
+ for (x =3D 0; x < WIDTH; x++)
+ for (y =3D 0; y < HEIGHT; y++)
+ screen[x][y] =3D EMPTY;
+}
+
+
+static void randomize_robot (void)
+{
+ int x, y;
+
+ do
+ {
+ x =3D randx ();
+ y =3D randy ();
+ }
+ while (screen[x][y] !=3D EMPTY);
+ =20
+ place_robot (x, y);
+}
+
+
+/*intialize kitten, well, intializes kitten.*/
+void randomize_kitten()
+{
+ int kx, ky;
+ =20
+ /* Assign the kitten a unique position. */
+ do
+ {
+ kx =3D randx();
+ ky =3D randy();
+ } while (screen[kx][ky]);
+ =20
+ /* Assign the kitten a character and a color. */
+ do {
+ kitten->character =3D randchar();
+ } while (!(validchar(kitten->character)));=20
+ screen[kx][ky] =3D KITTEN;
+
+ kitten->color =3D randcolor();
+ kitten->bold =3D randbold();
+
+ /* fprintf (stderr, "kitten %c placed at (%d, %d)\n", kitten->character=
, kx, ky); */
+}
+
+
+void randomize_bogus (void)
+{
+ int num_bogus =3D 20;
+ int used_messages[MESSAGES];
+ int i;
+ screen_object *o =3D &objects[FIRST_NKI];
+ int which_msg;
+ int x, y;
+ char const *p;
+
+ p =3D getenv ("QUERY_STRING");
+ if ((p =3D strstr (p, "n=3D")))
+ sscanf (p, "n=3D%d", &num_bogus);
+ =20
+ bzero(used_messages, sizeof used_messages);
+
+ for (i =3D 0; i < num_bogus; i++)
+ {
+ o[i].color =3D randcolor();
+ o[i].bold =3D randbold();
+
+ do=20
+ o[i].character =3D randchar();
+ while (!(validchar(o[i].character)));
+
+ do
+ which_msg =3D get_random_int() % MESSAGES;
+ while (used_messages[which_msg]);
+
+ do
+ {
+ x =3D randx(); y =3D randy();
+ }
+ while (screen[x][y] !=3D EMPTY);
+ screen[x][y] =3D FIRST_NKI + i;
+
+ used_messages[which_msg] =3D 1;
+ o[i].message =3D which_msg;
+
+ // fprintf (stderr, "nki %d character %c at (%d, %d)\n", i, o[i=
].character, x, y);
+ }
+}
+
+
+static void randomize (void)
+{
+ randomize_kitten ();
+ randomize_bogus ();
+ randomize_robot ();
+}
+
+
+static void pack_state (void)
+{
+ int x, y;
+ char *p =3D packed_state;
+ int s;
+
+ for (x =3D 0; x < WIDTH; x++)
+ for (y =3D 0; y < HEIGHT; y++)
+ {
+ s =3D screen[x][y];
+ /* Robots are not packed. That would be cruel, and also leave
+ leftover robots all over the screen. */
+ if (s !=3D EMPTY && s !=3D ROBOT)
+ {
+ int used;
+ int remain =3D sizeof packed_state - (p - packed_state) - 1;
+ screen_object const *o =3D &objects[s];
+
+ used =3D snprintf(p, remain, "&c%x.%x.%x.%x.%x.%x.%x",
+ x, y, s, o->bold, o->color, o->message, o->character);
+ if (used > remain)
+ {
+ fprintf (stderr, "not enough packed buffer space\n");
+ abort ();
+ }
+ p +=3D used;
+ }
+ }
+
+ *p++ =3D 0;
+}
+
+
+static void check_for_gzip (void)
+{
+ char const *hdr =3D getenv("HTTP_ACCEPT_ENCODING");
+
+ gz_out =3D NULL;
+ =20
+ /* dodgy to just look for a substring, but it will always work */
+ if (strstr (hdr, "gzip"))
+ {
+ gz_out =3D popen ("gzip -c9", "w");
+ if (!gz_out)
+ {
+ perror ("failed to start gzip");
+ /* just continue without */
+ }
+ }
+}
+
+
+static void no_buffer (void)
+{
+ setvbuf (stdout, NULL, _IOLBF, 0);
+ setvbuf (stderr, NULL, _IOLBF, 0);
+}
+
+
+static void spaceout (int d)
+{
+ while (d-- > 0)
+ fputs (" ", stdout);
+}
+
+
+/* Called to do the auto-refresh kitten hug. We can get here either
+ because the robot just moved onto the kitten, or because we called
+ ourselves back through a Refresh to move the two friends closer
+ together. */
+static int hugging_kitten (void)
+{
+ int distance;
+ int kchar;
+ char const *p =3D getenv ("QUERY_STRING");
+
+ if (screen[robotx][roboty] =3D=3D KITTEN)
+ {
+ distance =3D 20;
+ kchar =3D objects[KITTEN].character;
+ }
+ else
+ {
+ if (!p)
+ return FALSE;
+
+ if (!(p =3D strstr (p, "hug=3D")))
+ return FALSE;
+ =20
+ if (sscanf (p, "hug=3D%d&kitten=3D%x", &distance, &kchar) !=3D 2)
+ return FALSE;
+ }
+
+ if (distance > 0)
+ {
+ send_head (distance-4, kchar);
+ =20
+ spaceout (40 - distance/2);
+ putc ('#', stdout);
+ spaceout (distance);
+ printf ("&#%d;", kchar);
+ }
+ else
+ {
+ send_head (0, 0);
+
+ printf ("<P><B>You found kitten! Way to go, robot!</b>\n");
+ printf ("<P>");
+ offer_restart ();
+ }
+
+ return 1;
+}
+
+
+int main (void) /* no arguments to CGI */
+{
+ int instruct =3D FALSE;
+ =20
+ script_name =3D getenv ("SCRIPT_NAME");
+
+ no_buffer ();
+ check_for_gzip ();
+
+ empty_screen ();
+
+ /* If we're already in the middle of a game, load in the state
+ arrays submitted from the browser. If not, then we must be
+ starting a new game. Set up the game state, and show the
+ starting screen. */
+ if (!load_state ())
+ {
+ randomize ();
+ instruct =3D TRUE;
+ }
+
+ if (hugging_kitten ())
+ return 0;
+ =20
+ /* must wait, because kitten-hugging manouever requires access to
+ HTTP headers. */
+ send_head (0, 0);
+ offer_restart ();
+ if (instruct)
+ send_instructions ();
+
+ show_found ();
+
+ fflush (stdout);
+ pack_state ();
+ fflush (stdout);
+ send_screen ();
+
+ return 0;
+}
--hK8Uo4Yp55NZU70L
Content-Type: application/pgp-signature
Content-Disposition: inline
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.5 (GNU/Linux)
Comment: For info see http://www.gnupg.org
iD8DBQE7BedbPGPKP6Cz6IsRAsrEAKCH7JcLkgrZdBj+wrFIfuZtKLNuEgCeLjTq
Gv2drhVpSFnAlj+Hg5ybB3c=
=ZAF9
-----END PGP SIGNATURE-----
--hK8Uo4Yp55NZU70L--